summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp12
-rw-r--r--ApiDocs.bp1
-rw-r--r--OWNERS36
-rw-r--r--ProtoLibraries.bp8
-rw-r--r--StubLibraries.bp2
-rw-r--r--TestProtoLibraries.bp2
-rw-r--r--apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java5
-rw-r--r--apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java12
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp8
-rw-r--r--apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java72
-rw-r--r--apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java33
-rw-r--r--apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java24
-rw-r--r--apct-tests/perftests/multiuser/AndroidTest.xml2
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java112
-rw-r--r--apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto20
-rw-r--r--apct-tests/perftests/packagemanager/Android.bp1
-rw-r--r--apct-tests/perftests/packagemanager/AndroidManifest.xml6
-rw-r--r--apct-tests/perftests/packagemanager/AndroidTest.xml4
-rw-r--r--apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java313
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java6
-rw-r--r--apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java15
-rw-r--r--apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java5
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java345
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java77
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobParameters.java6
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java3
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java84
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java446
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl24
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/OWNERS1
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java22
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java72
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java54
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java279
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java116
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java45
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java97
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java28
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java144
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java116
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java461
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java39
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java54
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Agent.java1317
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java383
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java123
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java182
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java113
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java425
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java179
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java815
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java357
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java135
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Modifier.java70
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/OWNERS5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java51
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java184
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING34
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java62
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java83
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java8
-rw-r--r--apex/media/framework/TEST_MAPPING2
-rw-r--r--apex/media/framework/java/android/media/Session2Command.java5
-rw-r--r--api/Android.bp8
-rw-r--r--boot/Android.bp8
-rw-r--r--cmds/bootanimation/Android.bp2
-rw-r--r--cmds/bootanimation/BootAnimation.cpp426
-rw-r--r--cmds/bootanimation/BootAnimation.h30
-rw-r--r--cmds/bootanimation/FORMAT.md35
-rw-r--r--cmds/bootanimation/OWNERS3
-rw-r--r--cmds/idmap2/Android.bp5
-rw-r--r--cmds/idmap2/tests/Idmap2BinaryTests.cpp44
-rw-r--r--cmds/screencap/screencap.cpp16
-rw-r--r--config/OWNERS8
-rw-r--r--core/api/current.txt103
-rw-r--r--core/api/module-lib-current.txt8
-rwxr-xr-x[-rw-r--r--]core/api/system-current.txt297
-rw-r--r--core/api/test-current.txt89
-rw-r--r--core/api/test-lint-baseline.txt14
-rw-r--r--core/java/Android.bp21
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java123
-rw-r--r--core/java/android/accessibilityservice/AccessibilityTrace.java216
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl6
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java3
-rw-r--r--core/java/android/accounts/Account.java7
-rw-r--r--core/java/android/accounts/AccountManager.java9
-rw-r--r--core/java/android/accounts/IAccountManager.aidl3
-rw-r--r--core/java/android/accounts/OWNERS7
-rw-r--r--core/java/android/app/Activity.java439
-rw-r--r--core/java/android/app/ActivityClient.java13
-rw-r--r--core/java/android/app/ActivityManager.java8
-rw-r--r--core/java/android/app/ActivityManagerInternal.java27
-rw-r--r--core/java/android/app/ActivityOptions.java24
-rw-r--r--core/java/android/app/ActivityTaskManager.java13
-rw-r--r--core/java/android/app/ActivityThread.java143
-rw-r--r--core/java/android/app/ActivityThreadInternal.java4
-rw-r--r--core/java/android/app/Application.java19
-rw-r--r--core/java/android/app/ApplicationPackageManager.java21
-rw-r--r--core/java/android/app/ConfigurationController.java38
-rw-r--r--core/java/android/app/ContextImpl.java13
-rw-r--r--core/java/android/app/GameManager.java16
-rw-r--r--core/java/android/app/IActivityClientController.aidl1
-rw-r--r--core/java/android/app/IActivityManager.aidl2
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl14
-rw-r--r--core/java/android/app/IApplicationThread.aidl2
-rw-r--r--core/java/android/app/IGameManagerService.aidl1
-rw-r--r--core/java/android/app/Instrumentation.java45
-rw-r--r--core/java/android/app/Notification.java49
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java252
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rw-r--r--core/java/android/app/Service.java49
-rw-r--r--core/java/android/app/SystemServiceRegistry.java11
-rw-r--r--core/java/android/app/TaskInfo.java34
-rw-r--r--core/java/android/app/WallpaperManager.java25
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java81
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl2
-rw-r--r--core/java/android/app/admin/ProvisioningIntentHelper.java178
-rw-r--r--core/java/android/app/assist/AssistStructure.java7
-rw-r--r--core/java/android/app/backup/BackupManager.java26
-rw-r--r--core/java/android/app/compat/PackageOverride.java17
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java20
-rw-r--r--core/java/android/app/time/LocationTimeZoneManager.java47
-rw-r--r--core/java/android/app/usage/OWNERS2
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java8
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java36
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java85
-rw-r--r--core/java/android/bluetooth/BluetoothLeAudio.java1
-rw-r--r--core/java/android/bluetooth/BluetoothProfile.java18
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java10
-rw-r--r--core/java/android/bluetooth/BluetoothVolumeControl.java313
-rw-r--r--core/java/android/bluetooth/OobData.java7
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java26
-rw-r--r--core/java/android/bluetooth/le/ResultStorageDescriptor.java3
-rw-r--r--core/java/android/bluetooth/le/TruncatedFilter.java3
-rw-r--r--core/java/android/companion/Association.java4
-rw-r--r--core/java/android/content/ClipDescription.java10
-rw-r--r--core/java/android/content/ContentResolver.java17
-rw-r--r--core/java/android/content/Context.java16
-rw-r--r--core/java/android/content/IContentService.aidl1
-rw-r--r--core/java/android/content/Intent.java175
-rw-r--r--core/java/android/content/IntentFilter.java34
-rw-r--r--core/java/android/content/OWNERS4
-rw-r--r--core/java/android/content/PermissionChecker.java4
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl9
-rw-r--r--core/java/android/content/pm/PackageInstaller.java4
-rw-r--r--core/java/android/content/pm/PackageItemInfo.java4
-rw-r--r--core/java/android/content/pm/PackageManager.aidl1
-rw-r--r--core/java/android/content/pm/PackageManager.java331
-rw-r--r--core/java/android/content/pm/PackageParser.java111
-rw-r--r--core/java/android/content/pm/PermissionInfo.java6
-rw-r--r--core/java/android/content/pm/Signature.java2
-rw-r--r--core/java/android/content/pm/SigningDetails.java928
-rw-r--r--core/java/android/content/pm/SigningInfo.java26
-rw-r--r--core/java/android/content/pm/dex/DexMetadataHelper.java41
-rw-r--r--core/java/android/content/pm/parsing/ApkLite.java6
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java25
-rw-r--r--core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java78
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackage.java6
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageImpl.java18
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageRead.java4
-rw-r--r--core/java/android/content/pm/parsing/ParsingPackageUtils.java379
-rw-r--r--core/java/android/content/pm/parsing/ParsingUtils.java5
-rw-r--r--core/java/android/content/pm/parsing/component/ComponentParseUtils.java4
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivity.java147
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedActivityUtils.java141
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedAttribution.java71
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedComponent.java68
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedComponentUtils.java26
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedInstrumentation.java22
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java8
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedIntentInfo.java30
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java24
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedMainComponent.java19
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java8
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermission.java35
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java183
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java53
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProcess.java61
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProcessUtils.java33
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProvider.java57
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedProviderUtils.java67
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedService.java9
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedServiceUtils.java35
-rw-r--r--core/java/android/content/pm/parsing/component/ParsedUsesPermission.java133
-rw-r--r--core/java/android/content/pm/parsing/result/ParseTypeImpl.java1
-rw-r--r--core/java/android/content/pm/permission/CompatibilityPermissionInfo.java57
-rw-r--r--core/java/android/content/pm/split/DefaultSplitAssetLoader.java16
-rw-r--r--core/java/android/content/pm/split/SplitAssetDependencyLoader.java24
-rw-r--r--core/java/android/content/pm/split/SplitAssetLoader.java7
-rw-r--r--core/java/android/content/rollback/PackageRollbackInfo.java4
-rw-r--r--core/java/android/debug/FingerprintAndPairDevice.aidl28
-rw-r--r--core/java/android/debug/IAdbCallback.aidl (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt)18
-rw-r--r--core/java/android/debug/IAdbManager.aidl18
-rw-r--r--core/java/android/debug/PairDevice.aidl (renamed from core/java/android/uwb/IUwbAdapterStateCallbacks.aidl)30
-rw-r--r--core/java/android/debug/PairDevice.java112
-rw-r--r--core/java/android/hardware/SensorManager.java1
-rw-r--r--core/java/android/hardware/biometrics/OWNERS7
-rw-r--r--core/java/android/hardware/display/DisplayManager.java28
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java28
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java23
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl10
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java56
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java267
-rw-r--r--core/java/android/hardware/hdmi/HdmiSwitchClient.java2
-rw-r--r--core/java/android/hardware/hdmi/HdmiTvClient.java2
-rw-r--r--core/java/android/hardware/hdmi/OWNERS1
-rw-r--r--core/java/android/hardware/input/InputManager.java16
-rw-r--r--core/java/android/hardware/location/ContextHubClient.java5
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java103
-rw-r--r--core/java/android/hardware/radio/ITuner.aidl10
-rw-r--r--core/java/android/hardware/radio/ITunerCallback.aidl4
-rw-r--r--core/java/android/hardware/radio/TunerCallbackAdapter.java2
-rw-r--r--core/java/android/hardware/soundtrigger/ConversionUtil.java38
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java140
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTriggerModule.java43
-rw-r--r--core/java/android/hardware/usb/UsbManager.java5
-rw-r--r--core/java/android/inputmethodservice/AbstractInputMethodService.java119
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java34
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java254
-rw-r--r--core/java/android/inputmethodservice/InputMethodServiceInternal.java86
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java470
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java378
-rw-r--r--core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java197
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java (renamed from core/java/com/android/internal/view/InputConnectionWrapper.java)301
-rw-r--r--core/java/android/net/NetworkIdentity.java32
-rw-r--r--core/java/android/net/NetworkStats.java47
-rw-r--r--core/java/android/net/NetworkTemplate.java15
-rw-r--r--core/java/android/net/VpnService.java2
-rw-r--r--core/java/android/net/vcn/OWNERS2
-rw-r--r--core/java/android/os/BatteryStatsManager.java77
-rw-r--r--core/java/android/os/BatteryUsageStats.java14
-rw-r--r--core/java/android/os/BinderProxy.java14
-rwxr-xr-xcore/java/android/os/Build.java13
-rw-r--r--core/java/android/os/DropBoxManager.java4
-rw-r--r--core/java/android/os/Environment.java16
-rw-r--r--core/java/android/os/GraphicsEnvironment.java190
-rw-r--r--core/java/android/os/OWNERS3
-rw-r--r--core/java/android/os/PersistableBundle.java2
-rw-r--r--core/java/android/os/PowerManager.java4
-rw-r--r--core/java/android/os/ServiceManager.java7
-rw-r--r--core/java/android/os/SharedMemory.java20
-rw-r--r--core/java/android/os/StrictMode.java30
-rw-r--r--core/java/android/os/UpdateEngine.java42
-rw-r--r--core/java/android/os/UserManager.java8
-rw-r--r--core/java/android/os/Vibrator.java29
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl12
-rw-r--r--core/java/android/permission/PermissionManager.java5
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java2
-rw-r--r--core/java/android/provider/ContactsContract.java22
-rw-r--r--core/java/android/provider/DeviceConfig.java70
-rw-r--r--core/java/android/provider/Settings.java879
-rw-r--r--core/java/android/provider/Telephony.java11
-rw-r--r--core/java/android/service/autofill/BatchUpdates.java3
-rw-r--r--core/java/android/service/autofill/CharSequenceTransformation.java7
-rw-r--r--core/java/android/service/autofill/CompositeUserData.java5
-rw-r--r--core/java/android/service/autofill/CustomDescription.java5
-rw-r--r--core/java/android/service/autofill/Dataset.java39
-rw-r--r--core/java/android/service/autofill/DateTransformation.java7
-rw-r--r--core/java/android/service/autofill/DateValueSanitizer.java5
-rw-r--r--core/java/android/service/autofill/FieldClassification.java7
-rw-r--r--core/java/android/service/autofill/FillRequest.java2
-rw-r--r--core/java/android/service/autofill/FillResponse.java7
-rw-r--r--core/java/android/service/autofill/ImageTransformation.java9
-rw-r--r--core/java/android/service/autofill/NegationValidator.java4
-rw-r--r--core/java/android/service/autofill/RegexValidator.java7
-rw-r--r--core/java/android/service/autofill/SaveCallback.java4
-rw-r--r--core/java/android/service/autofill/SaveInfo.java3
-rw-r--r--core/java/android/service/autofill/SaveRequest.java5
-rw-r--r--core/java/android/service/autofill/TextValueSanitizer.java7
-rw-r--r--core/java/android/service/autofill/UserData.java9
-rw-r--r--core/java/android/service/autofill/augmented/FillController.java5
-rw-r--r--core/java/android/service/autofill/augmented/FillWindow.java8
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java12
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java11
-rw-r--r--core/java/android/service/notification/ScheduleCalendar.java59
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java35
-rw-r--r--core/java/android/service/textclassifier/TextClassifierService.java33
-rw-r--r--core/java/android/service/timezone/TimeZoneProviderService.java12
-rw-r--r--core/java/android/service/translation/TranslationService.java10
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java12
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java188
-rw-r--r--core/java/android/text/TextLine.java2
-rw-r--r--core/java/android/util/ArrayMap.java23
-rw-r--r--core/java/android/util/ArraySet.java18
-rw-r--r--core/java/android/util/FeatureFlagUtils.java5
-rw-r--r--core/java/android/util/SparseArrayMap.java24
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java2
-rw-r--r--core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java2
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java163
-rw-r--r--core/java/android/util/apk/ApkSigningBlockUtils.java2
-rw-r--r--core/java/android/util/apk/VerityBuilder.java8
-rw-r--r--core/java/android/util/apk/ZipUtils.java6
-rw-r--r--core/java/android/uwb/AdapterStateListener.java199
-rw-r--r--core/java/android/uwb/AngleMeasurement.java157
-rw-r--r--core/java/android/uwb/AngleOfArrivalMeasurement.java169
-rw-r--r--core/java/android/uwb/DistanceMeasurement.java214
-rw-r--r--core/java/android/uwb/IUwbAdapter.aidl191
-rw-r--r--core/java/android/uwb/IUwbRangingCallbacks.aidl136
-rw-r--r--core/java/android/uwb/OWNERS6
-rw-r--r--core/java/android/uwb/RangingChangeReason.aidl63
-rw-r--r--core/java/android/uwb/RangingManager.java256
-rw-r--r--core/java/android/uwb/RangingMeasurement.java308
-rw-r--r--core/java/android/uwb/RangingReport.java160
-rw-r--r--core/java/android/uwb/RangingSession.java496
-rw-r--r--core/java/android/uwb/SessionHandle.java86
-rw-r--r--core/java/android/uwb/StateChangeReason.aidl50
-rw-r--r--core/java/android/uwb/TEST_MAPPING10
-rw-r--r--core/java/android/uwb/UwbAddress.java131
-rw-r--r--core/java/android/uwb/UwbManager.java308
-rw-r--r--core/java/android/view/AccessibilityInteractionController.java9
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java11
-rw-r--r--core/java/android/view/Display.java6
-rw-r--r--core/java/android/view/DisplayCutout.java148
-rw-r--r--core/java/android/view/DisplayInfo.java15
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java23
-rw-r--r--core/java/android/view/IWindowManager.aidl26
-rw-r--r--core/java/android/view/IWindowSession.aidl10
-rw-r--r--core/java/android/view/InputEventReceiver.java10
-rw-r--r--core/java/android/view/InputWindowHandle.java32
-rw-r--r--core/java/android/view/InsetsController.java47
-rw-r--r--core/java/android/view/InsetsSourceConsumer.java2
-rw-r--r--core/java/android/view/InsetsState.java11
-rw-r--r--core/java/android/view/InsetsVisibilities.aidl19
-rw-r--r--core/java/android/view/InsetsVisibilities.java130
-rw-r--r--core/java/android/view/KeyEvent.java61
-rw-r--r--core/java/android/view/MotionEvent.java30
-rw-r--r--core/java/android/view/RemoteAnimationAdapter.java22
-rw-r--r--core/java/android/view/RemoteAnimationTarget.java17
-rw-r--r--core/java/android/view/ScrollCaptureResponse.java49
-rw-r--r--core/java/android/view/Surface.java15
-rw-r--r--core/java/android/view/SurfaceControl.java22
-rw-r--r--core/java/android/view/SurfaceView.java13
-rw-r--r--core/java/android/view/TextureView.java85
-rw-r--r--core/java/android/view/ThreadedRenderer.java121
-rw-r--r--core/java/android/view/View.java8
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/ViewGroup.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java114
-rw-r--r--core/java/android/view/ViewRootInsetsControllerHost.java4
-rw-r--r--core/java/android/view/WindowInfo.java7
-rw-r--r--core/java/android/view/WindowManager.java103
-rw-r--r--core/java/android/view/WindowManagerImpl.java34
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java37
-rw-r--r--core/java/android/view/WindowlessWindowManager.java8
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java204
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java65
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java26
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java29
-rw-r--r--core/java/android/view/autofill/AutofillClientController.java483
-rw-r--r--core/java/android/view/autofill/AutofillManager.java95
-rw-r--r--core/java/android/view/autofill/AutofillRequestCallback.java72
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl8
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureCondition.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureContext.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureEvent.java13
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java22
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java13
-rw-r--r--core/java/android/view/contentcapture/DataRemovalRequest.java3
-rw-r--r--core/java/android/view/contentcapture/DataShareRequest.java5
-rw-r--r--core/java/android/view/contentcapture/ViewNode.java14
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java118
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java25
-rw-r--r--core/java/android/view/inputmethod/InputMethod.java15
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java5
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java101
-rw-r--r--core/java/android/view/textclassifier/TextLanguage.java10
-rw-r--r--core/java/android/view/textservice/TextServicesManager.java11
-rw-r--r--core/java/android/view/translation/TranslationManager.java13
-rw-r--r--core/java/android/webkit/WebViewLibraryLoader.java23
-rw-r--r--core/java/android/webkit/WebViewProvider.java11
-rw-r--r--core/java/android/widget/AbsListView.java76
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java23
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java4
-rw-r--r--core/java/android/widget/Editor.java2
-rw-r--r--core/java/android/widget/ImageView.java6
-rwxr-xr-xcore/java/android/widget/ListPopupWindow.java3
-rw-r--r--core/java/android/widget/PopupWindow.java3
-rw-r--r--core/java/android/widget/ProgressBar.java34
-rw-r--r--core/java/android/widget/ScrollView.java3
-rw-r--r--core/java/android/widget/TextView.java19
-rw-r--r--core/java/android/window/ConfigurationHelper.java132
-rw-r--r--core/java/android/window/DisplayAreaInfo.java13
-rw-r--r--core/java/android/window/DisplayAreaOrganizer.java11
-rw-r--r--core/java/android/window/IRemoteTransitionFinishedCallback.aidl6
-rw-r--r--core/java/android/window/ITaskFragmentOrganizer.aidl52
-rw-r--r--core/java/android/window/ITaskFragmentOrganizerController.aidl33
-rw-r--r--core/java/android/window/IWindowOrganizerController.aidl16
-rw-r--r--core/java/android/window/SizeConfigurationBuckets.java156
-rw-r--r--core/java/android/window/TaskFragmentAppearedInfo.aidl23
-rw-r--r--core/java/android/window/TaskFragmentAppearedInfo.java93
-rw-r--r--core/java/android/window/TaskFragmentCreationParams.aidl23
-rw-r--r--core/java/android/window/TaskFragmentCreationParams.java193
-rw-r--r--core/java/android/window/TaskFragmentInfo.aidl23
-rw-r--r--core/java/android/window/TaskFragmentInfo.java206
-rw-r--r--core/java/android/window/TaskFragmentOrganizer.java191
-rw-r--r--core/java/android/window/TaskFragmentOrganizerToken.java97
-rw-r--r--core/java/android/window/TaskOrganizer.java2
-rw-r--r--core/java/android/window/TransitionFilter.java119
-rw-r--r--core/java/android/window/TransitionInfo.java246
-rw-r--r--core/java/android/window/WindowContainerTransaction.java463
-rw-r--r--core/java/android/window/WindowContext.java54
-rw-r--r--core/java/android/window/WindowContextController.java21
-rw-r--r--core/java/android/window/WindowInfosListener.java63
-rw-r--r--core/java/android/window/WindowOrganizer.java41
-rw-r--r--core/java/android/window/WindowProvider.java39
-rw-r--r--core/java/android/window/WindowProviderService.java88
-rw-r--r--core/java/android/window/WindowTokenClient.java99
-rw-r--r--core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java20
-rw-r--r--core/java/com/android/internal/accessibility/util/AccessibilityUtils.java78
-rw-r--r--core/java/com/android/internal/app/IBatteryStats.aidl2
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java3
-rw-r--r--core/java/com/android/internal/content/NativeLibraryHelper.java3
-rw-r--r--core/java/com/android/internal/content/om/OWNERS2
-rw-r--r--core/java/com/android/internal/inputmethod/CallbackUtils.java121
-rw-r--r--core/java/com/android/internal/inputmethod/Completable.java72
-rw-r--r--core/java/com/android/internal/inputmethod/EditableInputConnection.java (renamed from core/java/com/android/internal/widget/EditableInputConnection.java)58
-rw-r--r--core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl (renamed from core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl)2
-rw-r--r--core/java/com/android/internal/inputmethod/IInputContextInvoker.java531
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl4
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl35
-rw-r--r--core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl29
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracing.java (renamed from core/java/android/util/imetracing/ImeTracing.java)51
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java (renamed from core/java/android/util/imetracing/ImeTracingClientImpl.java)14
-rw-r--r--core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java (renamed from core/java/android/util/imetracing/ImeTracingServerImpl.java)16
-rw-r--r--core/java/com/android/internal/inputmethod/InputBindResult.aidl (renamed from core/java/com/android/internal/view/InputBindResult.aidl)2
-rw-r--r--core/java/com/android/internal/inputmethod/InputBindResult.java (renamed from core/java/com/android/internal/view/InputBindResult.java)100
-rw-r--r--core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java (renamed from core/java/android/util/imetracing/InputConnectionHelper.java)63
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java2
-rw-r--r--core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java232
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java796
-rw-r--r--core/java/com/android/internal/inputmethod/ResultCallbacks.java196
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java159
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java229
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java992
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsProvider.java19
-rw-r--r--core/java/com/android/internal/os/Clock.java57
-rw-r--r--core/java/com/android/internal/os/KernelCpuProcStringReader.java11
-rw-r--r--core/java/com/android/internal/os/KernelCpuUidTimeReader.java75
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/java/com/android/internal/policy/TransitionAnimation.java273
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogImpl.java2
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl8
-rw-r--r--core/java/com/android/internal/statusbar/RegisterStatusBarResult.java20
-rw-r--r--core/java/com/android/internal/util/ContrastColorUtil.java19
-rw-r--r--core/java/com/android/internal/util/OWNERS1
-rw-r--r--core/java/com/android/internal/view/IInputConnectionWrapper.java930
-rw-r--r--core/java/com/android/internal/view/IInputContext.aidl5
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl2
-rw-r--r--core/java/com/android/internal/view/IInputMethodClient.aidl2
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java8
-rw-r--r--core/java/com/android/internal/widget/NotificationActionListLayout.java53
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java203
-rw-r--r--core/jni/Android.bp26
-rw-r--r--core/jni/AndroidRuntime.cpp8
-rw-r--r--core/jni/OWNERS1
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.cpp19
-rw-r--r--core/jni/android_hardware_input_InputApplicationHandle.h5
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.cpp127
-rw-r--r--core/jni/android_hardware_input_InputWindowHandle.h8
-rw-r--r--core/jni/android_media_AudioSystem.cpp9
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp11
-rw-r--r--core/jni/android_os_ServiceManager.cpp16
-rw-r--r--core/jni/android_util_AssetManager.cpp4
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp13
-rw-r--r--core/jni/android_view_InputEventSender.cpp1
-rw-r--r--core/jni/android_view_KeyEvent.cpp7
-rw-r--r--core/jni/android_view_MotionEvent.cpp25
-rw-r--r--core/jni/android_view_SurfaceControl.cpp24
-rw-r--r--core/jni/android_window_WindowInfosListener.cpp134
-rw-r--r--core/jni/com_android_internal_content_om_OverlayConfig.cpp6
-rw-r--r--core/jni/include_vm/android_runtime/vm.h24
-rw-r--r--core/proto/android/providers/settings/global.proto4
-rw-r--r--core/proto/android/providers/settings/secure.proto2
-rw-r--r--core/proto/android/server/OWNERS1
-rw-r--r--core/proto/android/server/accessibilitytrace.proto19
-rw-r--r--core/proto/android/server/inputmethod/OWNERS1
-rw-r--r--core/proto/android/server/windowmanagerservice.proto24
-rw-r--r--core/proto/android/service/usb.proto2
-rw-r--r--core/proto/android/view/OWNERS3
-rw-r--r--core/proto/android/view/inputmethod/OWNERS1
-rw-r--r--core/res/AndroidManifest.xml15
-rw-r--r--core/res/OWNERS3
-rw-r--r--core/res/res/drawable-watch/global_action_item_divider.xml20
-rw-r--r--core/res/res/drawable-watch/global_actions_item_grey_background.xml20
-rw-r--r--core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml22
-rw-r--r--core/res/res/drawable-watch/global_actions_item_red_background.xml20
-rw-r--r--core/res/res/drawable-watch/global_actions_item_red_background_shape.xml22
-rw-r--r--core/res/res/drawable-watch/ic_lock_bugreport.xml31
-rw-r--r--core/res/res/drawable-watch/ic_lock_power_off.xml26
-rw-r--r--core/res/res/drawable-watch/ic_restart.xml26
-rw-r--r--core/res/res/drawable/btn_notification_emphasized.xml4
-rw-r--r--core/res/res/layout-car/car_alert_dialog.xml2
-rw-r--r--core/res/res/layout-car/car_alert_dialog_button_bar.xml9
-rw-r--r--core/res/res/layout-watch/global_actions.xml5
-rw-r--r--core/res/res/layout-watch/global_actions_item.xml75
-rw-r--r--core/res/res/layout/notification_template_material_base.xml2
-rw-r--r--core/res/res/layout/notification_template_material_media.xml2
-rw-r--r--core/res/res/layout/notification_template_material_messaging.xml2
-rw-r--r--core/res/res/layout/notification_template_text.xml2
-rw-r--r--core/res/res/values-af/strings.xml1
-rw-r--r--core/res/res/values-am/strings.xml1
-rw-r--r--core/res/res/values-ar/strings.xml3
-rw-r--r--core/res/res/values-as/strings.xml1
-rw-r--r--core/res/res/values-az/strings.xml1
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml1
-rw-r--r--core/res/res/values-be/strings.xml1
-rw-r--r--core/res/res/values-bg/strings.xml1
-rw-r--r--core/res/res/values-bn/strings.xml1
-rw-r--r--core/res/res/values-bs/strings.xml1
-rw-r--r--core/res/res/values-ca/strings.xml1
-rw-r--r--core/res/res/values-cs/strings.xml1
-rw-r--r--core/res/res/values-da/strings.xml1
-rw-r--r--core/res/res/values-de/strings.xml1
-rw-r--r--core/res/res/values-el/strings.xml1
-rw-r--r--core/res/res/values-en-rAU/strings.xml1
-rw-r--r--core/res/res/values-en-rCA/strings.xml1
-rw-r--r--core/res/res/values-en-rGB/strings.xml1
-rw-r--r--core/res/res/values-en-rIN/strings.xml1
-rw-r--r--core/res/res/values-en-rXC/strings.xml1
-rw-r--r--core/res/res/values-es-rUS/strings.xml1
-rw-r--r--core/res/res/values-es/strings.xml1
-rw-r--r--core/res/res/values-et/strings.xml1
-rw-r--r--core/res/res/values-eu/strings.xml1
-rw-r--r--core/res/res/values-fa/strings.xml1
-rw-r--r--core/res/res/values-fi/strings.xml1
-rw-r--r--core/res/res/values-fr-rCA/strings.xml1
-rw-r--r--core/res/res/values-fr/strings.xml1
-rw-r--r--core/res/res/values-gl/strings.xml1
-rw-r--r--core/res/res/values-gu/strings.xml1
-rw-r--r--core/res/res/values-hi/strings.xml1
-rw-r--r--core/res/res/values-hr/strings.xml1
-rw-r--r--core/res/res/values-hu/strings.xml1
-rw-r--r--core/res/res/values-hy/strings.xml1
-rw-r--r--core/res/res/values-in/strings.xml7
-rw-r--r--core/res/res/values-is/strings.xml1
-rw-r--r--core/res/res/values-it/strings.xml7
-rw-r--r--core/res/res/values-iw/strings.xml1
-rw-r--r--core/res/res/values-ja/strings.xml1
-rw-r--r--core/res/res/values-ka/strings.xml1
-rw-r--r--core/res/res/values-kk/strings.xml1
-rw-r--r--core/res/res/values-km/strings.xml1
-rw-r--r--core/res/res/values-kn/strings.xml1
-rw-r--r--core/res/res/values-ko/strings.xml1
-rw-r--r--core/res/res/values-ky/strings.xml1
-rw-r--r--core/res/res/values-lo/strings.xml1
-rw-r--r--core/res/res/values-lt/strings.xml1
-rw-r--r--core/res/res/values-lv/strings.xml1
-rw-r--r--core/res/res/values-mk/strings.xml1
-rw-r--r--core/res/res/values-ml/strings.xml1
-rw-r--r--core/res/res/values-mn/strings.xml1
-rw-r--r--core/res/res/values-mr/strings.xml1
-rw-r--r--core/res/res/values-ms/strings.xml1
-rw-r--r--core/res/res/values-my/strings.xml1
-rw-r--r--core/res/res/values-nb/strings.xml1
-rw-r--r--core/res/res/values-ne/strings.xml1
-rw-r--r--core/res/res/values-nl/strings.xml1
-rw-r--r--core/res/res/values-notround-watch/dimens.xml3
-rw-r--r--core/res/res/values-or/strings.xml1
-rw-r--r--core/res/res/values-pa/strings.xml1
-rw-r--r--core/res/res/values-pl/strings.xml3
-rw-r--r--core/res/res/values-pt-rBR/strings.xml1
-rw-r--r--core/res/res/values-pt-rPT/strings.xml1
-rw-r--r--core/res/res/values-pt/strings.xml1
-rw-r--r--core/res/res/values-ro/strings.xml1
-rw-r--r--core/res/res/values-round-watch/dimens.xml3
-rw-r--r--core/res/res/values-ru/strings.xml1
-rw-r--r--core/res/res/values-si/strings.xml1
-rw-r--r--core/res/res/values-sk/strings.xml1
-rw-r--r--core/res/res/values-sl/strings.xml1
-rw-r--r--core/res/res/values-sq/strings.xml1
-rw-r--r--core/res/res/values-sr/strings.xml1
-rw-r--r--core/res/res/values-sv/strings.xml1
-rw-r--r--core/res/res/values-sw/strings.xml1
-rw-r--r--core/res/res/values-ta/strings.xml1
-rw-r--r--core/res/res/values-te/strings.xml9
-rw-r--r--core/res/res/values-television/strings.xml23
-rw-r--r--core/res/res/values-th/strings.xml1
-rw-r--r--core/res/res/values-tl/strings.xml1
-rw-r--r--core/res/res/values-tr/strings.xml1
-rw-r--r--core/res/res/values-uk/strings.xml1
-rw-r--r--core/res/res/values-ur/strings.xml1
-rw-r--r--core/res/res/values-uz/strings.xml1
-rw-r--r--core/res/res/values-vi/strings.xml1
-rw-r--r--core/res/res/values-watch/colors.xml (renamed from packages/SystemUI/res/values-h560dp-xhdpi/config.xml)15
-rw-r--r--core/res/res/values-watch/strings.xml7
-rw-r--r--core/res/res/values-zh-rCN/strings.xml11
-rw-r--r--core/res/res/values-zh-rHK/strings.xml1
-rw-r--r--core/res/res/values-zh-rTW/strings.xml1
-rw-r--r--core/res/res/values-zu/strings.xml1
-rw-r--r--core/res/res/values/attrs_manifest.xml12
-rw-r--r--core/res/res/values/config.xml168
-rw-r--r--core/res/res/values/dimens.xml11
-rw-r--r--core/res/res/values/dimens_car.xml6
-rw-r--r--core/res/res/values/public.xml151
-rw-r--r--core/res/res/values/strings.xml3
-rw-r--r--core/res/res/values/symbols.xml50
-rw-r--r--core/res/res/xml/config_user_types.xml8
-rw-r--r--core/res/res/xml/default_zen_mode_config.xml12
-rw-r--r--core/res/res/xml/sms_short_codes.xml12
-rw-r--r--core/tests/BroadcastRadioTests/OWNERS3
-rw-r--r--core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java3
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java126
-rw-r--r--core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java14
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java12
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java65
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java1
-rw-r--r--core/tests/coretests/src/android/app/assist/AssistStructureTest.java43
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java3
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java8
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java1
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java8
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerTests.java3
-rw-r--r--core/tests/coretests/src/android/content/pm/SigningDetailsTest.java19
-rw-r--r--core/tests/coretests/src/android/graphics/FontListParserTest.java48
-rw-r--r--core/tests/coretests/src/android/provider/DeviceConfigTest.java103
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java22
-rw-r--r--core/tests/coretests/src/android/view/InsetsVisibilitiesTest.java121
-rw-r--r--core/tests/coretests/src/android/view/KeyEventTest.java6
-rw-r--r--core/tests/coretests/src/android/view/ViewInputConnectionTest.java301
-rw-r--r--core/tests/coretests/src/android/view/WindowInfoTest.java3
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java13
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java5
-rw-r--r--core/tests/coretests/src/android/widget/ProgressBarTest.java184
-rw-r--r--core/tests/coretests/src/android/window/WindowContextControllerTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java129
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java280
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java41
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java12
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java14
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java40
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java24
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java16
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java27
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java36
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java14
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java26
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java18
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockClock.java (renamed from core/tests/coretests/src/com/android/internal/os/MockClocks.java)2
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java7
-rw-r--r--core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java201
-rw-r--r--core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java99
-rw-r--r--core/tests/coretests/src/com/android/internal/util/RingBufferTest.java178
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java2
-rw-r--r--core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java164
-rw-r--r--core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java379
-rw-r--r--core/tests/uwbtests/OWNERS1
-rw-r--r--core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java288
-rw-r--r--core/tests/uwbtests/src/android/uwb/RangingManagerTest.java267
-rw-r--r--core/tests/uwbtests/src/android/uwb/UwbTestUtils.java90
-rw-r--r--data/etc/car/Android.bp7
-rw-r--r--data/etc/car/com.android.car.activityresolver.xml1
-rw-r--r--data/etc/car/com.google.android.car.adaslocation.xml21
-rw-r--r--data/etc/privapp-permissions-platform.xml10
-rw-r--r--data/etc/services.core.protolog.json382
-rw-r--r--data/keyboards/Vendor_0171_Product_0419.kl55
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/ClientSidePermissionCheckChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java2
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java153
-rw-r--r--errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java176
-rw-r--r--errorprone/tests/res/android/app/AppOpsManager.java107
-rw-r--r--graphics/java/android/graphics/Canvas.java5
-rw-r--r--graphics/java/android/graphics/FontListParser.java39
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java55
-rw-r--r--graphics/java/android/graphics/HardwareRendererObserver.java26
-rw-r--r--graphics/java/android/graphics/RenderNode.java58
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedImageDrawable.java12
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java113
-rw-r--r--graphics/java/android/graphics/text/TextRunShaper.java4
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKey.java10
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java14
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java10
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java48
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java269
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java74
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java774
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java353
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java246
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin7613 -> 17281 bytes
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_button.xml10
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml30
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml1
-rw-r--r--libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml19
-rw-r--r--libs/WindowManager/Shell/res/layout/split_outline.xml26
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml12
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml10
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml18
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings_tv.xml2
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml20
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java67
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java285
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java275
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java165
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java83
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java265
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java86
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java364
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java132
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java147
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java167
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java177
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java97
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java127
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java76
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java262
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java324
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java480
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java491
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java485
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java71
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt63
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt8
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt25
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt36
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt43
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt45
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt66
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt44
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt56
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt58
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt52
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt111
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt80
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt64
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt62
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt26
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt48
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt39
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt37
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt60
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt52
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt124
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt121
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt131
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt)49
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt107
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt)86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt)85
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt)55
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt175
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt95
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt119
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt)67
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt60
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt44
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt107
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt41
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt9
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java15
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java17
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java193
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java59
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java85
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java275
-rw-r--r--libs/androidfw/PosixUtils.cpp4
-rw-r--r--libs/androidfw/include/androidfw/PosixUtils.h4
-rw-r--r--libs/androidfw/tests/PosixUtils_test.cpp4
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp20
-rw-r--r--libs/hwui/DeferredLayerUpdater.h4
-rw-r--r--libs/hwui/DisplayListOps.in1
-rw-r--r--libs/hwui/Layer.cpp6
-rw-r--r--libs/hwui/Layer.h25
-rw-r--r--libs/hwui/Properties.cpp19
-rw-r--r--libs/hwui/Properties.h13
-rw-r--r--libs/hwui/Readback.cpp23
-rw-r--r--libs/hwui/Readback.h3
-rw-r--r--libs/hwui/RecordingCanvas.cpp25
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/hwui/SkiaCanvas.cpp55
-rw-r--r--libs/hwui/SkiaCanvas.h26
-rw-r--r--libs/hwui/VectorDrawable.cpp8
-rw-r--r--libs/hwui/VectorDrawable.h2
-rw-r--r--libs/hwui/apex/android_matrix.cpp7
-rw-r--r--libs/hwui/apex/include/android/graphics/matrix.h10
-rw-r--r--libs/hwui/canvas/CanvasFrontend.cpp7
-rw-r--r--libs/hwui/hwui/AnimatedImageDrawable.cpp1
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.cpp2
-rw-r--r--libs/hwui/hwui/BlurDrawLooper.h10
-rw-r--r--libs/hwui/hwui/Canvas.h10
-rw-r--r--libs/hwui/hwui/ImageDecoder.cpp1
-rw-r--r--libs/hwui/hwui/Paint.h17
-rw-r--r--libs/hwui/hwui/PaintFilter.h5
-rw-r--r--libs/hwui/hwui/PaintImpl.cpp32
-rw-r--r--libs/hwui/jni/AnimatedImageDrawable.cpp55
-rwxr-xr-xlibs/hwui/jni/Bitmap.cpp2
-rw-r--r--libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp10
-rw-r--r--libs/hwui/jni/GIFMovie.cpp4
-rw-r--r--libs/hwui/jni/NinePatch.cpp2
-rw-r--r--libs/hwui/jni/Paint.cpp3
-rw-r--r--libs/hwui/jni/PaintFilter.cpp4
-rw-r--r--libs/hwui/jni/Utils.cpp21
-rw-r--r--libs/hwui/jni/YuvToJpegEncoder.cpp4
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp72
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp54
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp31
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRendererObserver.h3
-rw-r--r--libs/hwui/jni/android_graphics_Matrix.cpp7
-rw-r--r--libs/hwui/jni/android_graphics_Matrix.h3
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp94
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp1
-rw-r--r--libs/hwui/libhwui.map.txt1
-rw-r--r--libs/hwui/pipeline/skia/DumpOpsCanvas.h2
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp88
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp24
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp5
-rw-r--r--libs/hwui/renderthread/CanvasContext.h10
-rw-r--r--libs/hwui/tests/common/TestUtils.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp5
-rw-r--r--libs/hwui/tests/unit/SkiaBehaviorTests.cpp6
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp2
-rw-r--r--libs/hwui/utils/PaintUtils.h11
-rw-r--r--libs/input/SpriteController.cpp6
-rw-r--r--location/java/android/location/LastLocationRequest.java16
-rw-r--r--location/java/android/location/LocationManager.java9
-rw-r--r--location/java/android/location/LocationRequest.java4
-rw-r--r--media/Android.bp113
-rw-r--r--media/aidl/android/media/audio/common/AudioChannelMask.aidl76
-rw-r--r--media/aidl/android/media/audio/common/AudioConfig.aidl4
-rw-r--r--media/aidl/android/media/audio/common/AudioFormat.aidl1
-rw-r--r--media/aidl/android/media/audio/common/AudioOffloadInfo.aidl8
-rw-r--r--media/aidl/android/media/audio/common/AudioStreamType.aidl6
-rw-r--r--media/aidl/android/media/audio/common/AudioUsage.aidl6
-rw-r--r--media/aidl/android/media/permission/Identity.aidl1
-rw-r--r--media/aidl/android/media/soundtrigger/AudioCapabilities.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/ModelParameter.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/ModelParameterRange.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/Phrase.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/Phrase.aidl)4
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl)16
-rw-r--r--media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/Properties.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionConfig.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionEvent.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl)14
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionMode.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl)3
-rw-r--r--media/aidl/android/media/soundtrigger/RecognitionStatus.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/SoundModel.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger/SoundModelType.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl)10
-rw-r--r--media/aidl/android/media/soundtrigger/Status.aidl (renamed from media/aidl/android/media/soundtrigger_middleware/Status.aidl)8
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl27
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl13
-rw-r--r--media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl5
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl111
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl44
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl109
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl50
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl53
-rw-r--r--media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl53
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl40
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl53
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl46
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl42
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl43
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl41
-rw-r--r--media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl45
-rw-r--r--media/java/android/media/AudioAttributes.java111
-rw-r--r--media/java/android/media/AudioFormat.java89
-rw-r--r--media/java/android/media/AudioManager.java176
-rw-r--r--media/java/android/media/AudioSystem.java3
-rw-r--r--media/java/android/media/AudioTrack.java5
-rw-r--r--media/java/android/media/ExifInterface.java96
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl44
-rw-r--r--media/java/android/media/ISpatializerCallback.aidl28
-rw-r--r--media/java/android/media/Image.java2
-rw-r--r--media/java/android/media/MediaCodec.java1
-rw-r--r--media/java/android/media/MediaCodecList.java4
-rw-r--r--media/java/android/media/MediaMetrics.java10
-rw-r--r--media/java/android/media/MediaPlayer.java3
-rw-r--r--media/java/android/media/MediaRecorder.java4
-rw-r--r--media/java/android/media/MediaRouter.java8
-rw-r--r--media/java/android/media/RingtoneManager.java2
-rw-r--r--media/java/android/media/Spatializer.java331
-rw-r--r--media/java/android/media/audiopolicy/AudioMix.java14
-rw-r--r--media/java/android/media/audiopolicy/AudioMixingRule.java57
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicyConfig.java6
-rw-r--r--media/java/android/media/audiopolicy/AudioProductStrategy.java16
-rw-r--r--media/java/android/media/projection/MediaProjection.java28
-rw-r--r--media/java/android/media/session/ISessionManager.aidl7
-rw-r--r--media/java/android/media/session/MediaSessionManager.java65
-rw-r--r--media/java/android/media/tv/TvView.java15
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java60
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java224
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java68
-rw-r--r--media/java/android/media/tv/tuner/TunerVersionChecker.java9
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrPlayback.java19
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrRecorder.java8
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrSettings.java10
-rw-r--r--media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java8
-rw-r--r--media/java/android/media/tv/tuner/filter/AvSettings.java70
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java49
-rw-r--r--media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/RecordSettings.java100
-rw-r--r--media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java69
-rw-r--r--media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java98
-rw-r--r--media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java10
-rw-r--r--media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java92
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java97
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java88
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java165
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendSettings.java105
-rw-r--r--media/java/android/media/tv/tuner/frontend/FrontendStatus.java88
-rw-r--r--media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java48
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java37
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java36
-rw-r--r--media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java8
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/Android.bp3
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java1
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl2
-rw-r--r--media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl41
-rw-r--r--media/java/android/service/media/MediaBrowserService.java126
-rw-r--r--media/jni/Android.bp6
-rw-r--r--media/jni/OWNERS2
-rw-r--r--media/jni/android_media_MediaExtractor.cpp1
-rw-r--r--media/jni/android_media_tv_Tuner.cpp2992
-rw-r--r--media/jni/android_media_tv_Tuner.h163
-rw-r--r--media/jni/tuner/ClientHelper.h15
-rw-r--r--media/jni/tuner/DemuxClient.cpp269
-rw-r--r--media/jni/tuner/DemuxClient.h55
-rw-r--r--media/jni/tuner/DescramblerClient.cpp77
-rw-r--r--media/jni/tuner/DescramblerClient.h24
-rw-r--r--media/jni/tuner/DvrClient.cpp247
-rw-r--r--media/jni/tuner/DvrClient.h63
-rw-r--r--media/jni/tuner/DvrClientCallback.h12
-rw-r--r--media/jni/tuner/FilterClient.cpp892
-rw-r--r--media/jni/tuner/FilterClient.h147
-rw-r--r--media/jni/tuner/FilterClientCallback.h17
-rw-r--r--media/jni/tuner/FrontendClient.cpp1159
-rw-r--r--media/jni/tuner/FrontendClient.h144
-rw-r--r--media/jni/tuner/FrontendClientCallback.h14
-rw-r--r--media/jni/tuner/LnbClient.cpp91
-rw-r--r--media/jni/tuner/LnbClient.h48
-rw-r--r--media/jni/tuner/LnbClientCallback.h9
-rw-r--r--media/jni/tuner/OWNERS2
-rw-r--r--media/jni/tuner/TimeFilterClient.cpp87
-rw-r--r--media/jni/tuner/TimeFilterClient.h30
-rw-r--r--media/jni/tuner/TunerClient.cpp578
-rw-r--r--media/jni/tuner/TunerClient.h93
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java7
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java2
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java3
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java13
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java2
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java2
-rw-r--r--packages/CompanionDeviceManager/res/values-af/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-am/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ar/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-as/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-az/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bg/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-bn/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-bs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ca/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-cs/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-da/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-de/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-el/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rAU/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rGB/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rIN/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-en-rXC/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-es-rUS/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-es/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-et/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fa/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-fr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-gu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hu/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-hy/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-in/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-it/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-iw/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ka/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-km/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-kn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ko/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lo/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-lt/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-lv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ml/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mn/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-mr/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-ms/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-my/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ne/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-or/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pa/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-pt/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ru/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-si/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sq/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sv/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ta/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-th/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tl/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-tr/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-uk/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-uz/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-vi/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml8
-rw-r--r--packages/CompanionDeviceManager/res/values-zu/strings.xml8
-rw-r--r--packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java51
-rw-r--r--packages/DynamicSystemInstallationService/AndroidManifest.xml4
-rw-r--r--packages/PackageInstaller/AndroidManifest.xml8
-rw-r--r--packages/PackageInstaller/TEST_MAPPING5
-rw-r--r--packages/PackageInstaller/res/values-te/strings.xml2
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java4
-rw-r--r--packages/SettingsLib/Android.bp42
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-af/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-am/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ar/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-as/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-az/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-be/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bg/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-bs/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ca/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-cs/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-da/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-de/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-el/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-es/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-et/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-eu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fa/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-fr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-gl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-gu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hu/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-hy/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-in/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-is/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-it/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-iw/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ja/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ka/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-kk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-km/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-kn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ko/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ky/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lo/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lt/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-lv/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ml/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mn/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-mr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ms/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-my/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-nb/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ne/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-nl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-or/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pa/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-pt/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ro/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ru/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-si/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sq/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sv/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-sw/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ta/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-te/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-th/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-tl/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-tr/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-uk/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ur/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-uz/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-vi/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-zu/strings.xml21
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java29
-rw-r--r--packages/SettingsLib/RadioButtonPreference/Android.bp1
-rw-r--r--packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java6
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/Android.bp27
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/drawable/ic_settings_accent.xml29
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml123
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_checkbox.xml25
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.xml25
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-af/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-am/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ar/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-as/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-az/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-be/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-bg/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-bn/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-bs/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ca/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-cs/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-da/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-de/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-el/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rAU/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rGB/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rIN/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rXC/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-es-rUS/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-es/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-et/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-eu/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-fa/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-fi/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-gl/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-gu/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-hi/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-hr/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-hu/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-hy/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-in/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-is/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-it/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-iw/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ja/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ka/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-kk/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-km/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-kn/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ko/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-lo/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-lt/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-lv/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-mk/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ml/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-mn/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-mr/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ms/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-my/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-nb/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ne/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-nl/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-pa/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-pl/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ro/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ru/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-si/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sk/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sl/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sq/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sr/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sv/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-sw/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ta/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-te/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-th/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-tl/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-tr/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-uk/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-ur/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-uz/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-vi/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values-zu/strings.xml21
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/res/values/strings.xml23
-rw-r--r--packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java203
-rw-r--r--packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java6
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml58
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml31
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml31
-rw-r--r--packages/SettingsLib/res/values/strings.xml118
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java (renamed from packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java)96
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/users/AppCopyHelper.java276
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java41
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java314
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java14
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java55
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java280
-rw-r--r--packages/SettingsLib/tests/robotests/OWNERS2
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java (renamed from packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java)61
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java5
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java34
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java18
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java64
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java166
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java254
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java34
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java9
-rw-r--r--packages/SettingsProvider/res/values-mcc466/defaults.xml5
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml64
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java6
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java181
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java32
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java66
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java29
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java6
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java308
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java35
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java91
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java8
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/AndroidManifest.xml4
-rw-r--r--packages/SystemUI/OWNERS2
-rw-r--r--packages/SystemUI/TEST_MAPPING19
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt48
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt52
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java23
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java10
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java71
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java4
-rw-r--r--packages/SystemUI/res-keyguard/layout/footer_actions.xml105
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml10
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml71
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml315
-rw-r--r--packages/SystemUI/res-keyguard/values-land/donottranslate.xml (renamed from core/res/res/drawable-watch/global_action_icon_background.xml)12
-rw-r--r--packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml6
-rw-r--r--packages/SystemUI/res-keyguard/values/donottranslate.xml5
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml8
-rw-r--r--packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_arrow_forward.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_friction_lock_closed.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_qs_drag_handle.xml24
-rw-r--r--packages/SystemUI/res/drawable/ic_settings_24dp.xml29
-rw-r--r--packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml34
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_background.xml23
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_footer_background.xml30
-rw-r--r--packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml22
-rw-r--r--packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml3
-rw-r--r--packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml23
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml33
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_off.xml29
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_on.xml29
-rw-r--r--packages/SystemUI/res/drawable/settingslib_thumb_selector.xml22
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_off_background.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_on_background.xml26
-rw-r--r--packages/SystemUI/res/drawable/settingslib_track_selector.xml22
-rw-r--r--packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml44
-rw-r--r--packages/SystemUI/res/layout/communal_host_view.xml30
-rw-r--r--packages/SystemUI/res/layout/global_actions_toast.xml44
-rw-r--r--packages/SystemUI/res/layout/idle_host_view.xml23
-rw-r--r--packages/SystemUI/res/layout/internet_connectivity_dialog.xml374
-rw-r--r--packages/SystemUI/res/layout/internet_list_item.xml101
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel_content.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_detail.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_detail_header.xml2
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml88
-rw-r--r--packages/SystemUI/res/layout/qs_panel.xml28
-rw-r--r--packages/SystemUI/res/layout/quick_qs_status_icons.xml43
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml41
-rw-r--r--packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml6
-rw-r--r--packages/SystemUI/res/layout/rotate_suggestion.xml25
-rw-r--r--packages/SystemUI/res/layout/split_shade_header.xml89
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml24
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml13
-rw-r--r--packages/SystemUI/res/layout/system_icons.xml2
-rw-r--r--packages/SystemUI/res/layout/tile_service_request_dialog.xml (renamed from packages/SystemUI/res/layout/global_actions_change_panel.xml)27
-rw-r--r--packages/SystemUI/res/values-af/strings.xml19
-rw-r--r--packages/SystemUI/res/values-am/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml19
-rw-r--r--packages/SystemUI/res/values-as/strings.xml19
-rw-r--r--packages/SystemUI/res/values-az/strings.xml19
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml19
-rw-r--r--packages/SystemUI/res/values-be/strings.xml19
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml19
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml23
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml19
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml19
-rw-r--r--packages/SystemUI/res/values-da/strings.xml19
-rw-r--r--packages/SystemUI/res/values-de/strings.xml19
-rw-r--r--packages/SystemUI/res/values-el/strings.xml19
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml19
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml19
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml19
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml19
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml19
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml19
-rw-r--r--packages/SystemUI/res/values-es/strings.xml19
-rw-r--r--packages/SystemUI/res/values-et/strings.xml19
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml23
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml23
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml19
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml19
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml19
-rw-r--r--packages/SystemUI/res/values-in/strings.xml19
-rw-r--r--packages/SystemUI/res/values-is/strings.xml19
-rw-r--r--packages/SystemUI/res/values-it/strings.xml19
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml23
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml19
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml19
-rw-r--r--packages/SystemUI/res/values-km/strings.xml19
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml23
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml19
-rw-r--r--packages/SystemUI/res/values-land/config.xml3
-rw-r--r--packages/SystemUI/res/values-land/dimens.xml6
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml19
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml19
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml23
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml19
-rw-r--r--packages/SystemUI/res/values-my/strings.xml19
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml23
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-or/strings.xml23
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml23
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml19
-rw-r--r--packages/SystemUI/res/values-si/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml23
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml12
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml8
-rw-r--r--packages/SystemUI/res/values-sw720dp/config.xml3
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml19
-rw-r--r--packages/SystemUI/res/values-te/strings.xml23
-rw-r--r--packages/SystemUI/res/values-th/strings.xml19
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml19
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml19
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml19
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml19
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml19
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml19
-rw-r--r--packages/SystemUI/res/values/attrs.xml4
-rw-r--r--packages/SystemUI/res/values/colors.xml17
-rw-r--r--packages/SystemUI/res/values/config.xml48
-rw-r--r--packages/SystemUI/res/values/dimens.xml65
-rw-r--r--packages/SystemUI/res/values/flags.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml48
-rw-r--r--packages/SystemUI/res/values/styles.xml58
-rw-r--r--packages/SystemUI/shared/Android.bp1
-rw-r--r--packages/SystemUI/shared/lint-baseline.xml35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt146
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl33
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl34
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl (renamed from core/java/android/uwb/AdapterState.aidl)30
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java (renamed from packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java)45
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java557
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java246
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl20
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java76
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java55
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java21
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java85
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java)0
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt77
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt38
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt41
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt38
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt117
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt184
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt124
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt55
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt67
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt)22
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java40
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java66
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java90
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java99
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java73
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java9
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadButton.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadKey.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java84
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUI.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java (renamed from packages/SystemUI/src/com/android/systemui/BatteryMeterView.java)186
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java203
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java292
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java125
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/service/CommunalService.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourcePrimer.java235
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java)101
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java595
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt120
-rw-r--r--packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java394
-rw-r--r--packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java267
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java323
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java99
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java155
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java156
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt285
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt217
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt161
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooter.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java126
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java187
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSHost.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt101
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt110
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java205
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java595
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java939
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java389
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java108
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java201
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java171
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java344
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java321
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java (renamed from services/uwb/java/com/android/server/uwb/UwbService.java)31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java117
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java1899
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java671
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java143
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java133
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java94
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java239
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt221
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt126
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/Utils.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java198
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java186
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java59
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt (renamed from core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java36
-rw-r--r--packages/SystemUI/tests/AndroidManifest.xml7
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java157
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java77
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java155
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSourcePrimerTest.java223
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java191
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java111
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java119
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java142
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java572
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt134
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java226
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java176
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt167
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt23
-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/external/TileRequestDialogTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt162
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java491
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java292
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt173
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java154
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java92
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java114
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java)64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java165
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java242
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt59
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java143
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java170
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java239
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java113
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt176
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java371
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java78
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java13
-rw-r--r--packages/VpnDialogs/res/values-af/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-am/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ar/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-as/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-az/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-b+sr+Latn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-be/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bg/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-bs/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ca/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-cs/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-da/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-de/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-el/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rAU/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rCA/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rGB/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rIN/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-en-rXC/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-es-rUS/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-es/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-et/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-eu/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-fa/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fr-rCA/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-fr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-gl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-gu/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-hi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hu/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-hy/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-in/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-is/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-it/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-iw/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ja/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ka/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-kk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-km/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-kn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ko/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ky/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lo/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lt/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-lv/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mk/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-ml/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mn/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-mr/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-ms/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-my/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-nb/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ne/strings.xml5
-rw-r--r--packages/VpnDialogs/res/values-nl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-or/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-pa/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pt-rBR/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-pt-rPT/strings.xml7
-rw-r--r--packages/VpnDialogs/res/values-pt/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ro/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ru/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-si/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sq/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sv/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-sw/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ta/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-te/strings.xml3
-rw-r--r--packages/VpnDialogs/res/values-th/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-tl/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-tr/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-uk/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-ur/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-uz/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-vi/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rCN/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rHK/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zh-rTW/strings.xml1
-rw-r--r--packages/VpnDialogs/res/values-zu/strings.xml1
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-af/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-am/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-as/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-az/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-be/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-da/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-de/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-el/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-es/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-et/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-in/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-is/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-it/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-km/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-my/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-or/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-si/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-te/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-th/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml21
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml21
-rw-r--r--rs/java/android/renderscript/ScriptIntrinsicBlend.java3
-rw-r--r--services/Android.bp6
-rw-r--r--services/OWNERS2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java332
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java327
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java288
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java19
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java53
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java66
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java194
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java112
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java14
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java12
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java30
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java55
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java11
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java72
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java46
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java18
-rw-r--r--services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java293
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java183
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java55
-rw-r--r--services/core/Android.bp6
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java41
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java11
-rw-r--r--services/core/java/com/android/server/IntentResolver.java41
-rw-r--r--services/core/java/com/android/server/SensorPrivacyService.java4
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java138
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java4
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java20
-rw-r--r--services/core/java/com/android/server/accounts/OWNERS10
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java12
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java55
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java423
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java177
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java312
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java19
-rw-r--r--services/core/java/com/android/server/am/AppNotRespondingDialog.java4
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java44
-rw-r--r--services/core/java/com/android/server/am/BatteryExternalStatsWorker.java16
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java8
-rw-r--r--services/core/java/com/android/server/am/CacheOomRanker.java132
-rw-r--r--services/core/java/com/android/server/am/ComponentAliasResolver.java321
-rw-r--r--services/core/java/com/android/server/am/ConnectionRecord.java12
-rw-r--r--services/core/java/com/android/server/am/CoreSettingsObserver.java2
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java113
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java16
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java90
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java125
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java28
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java3
-rw-r--r--services/core/java/com/android/server/am/UidRecord.java12
-rw-r--r--services/core/java/com/android/server/am/UserController.java49
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java84
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java10
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java9
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java248
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java5
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java126
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java8
-rw-r--r--services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java6
-rw-r--r--services/core/java/com/android/server/broadcastradio/OWNERS3
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java17
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/Tuner.java6
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java5
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java5
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java9
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java17
-rw-r--r--services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java10
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java195
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java383
-rw-r--r--services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java408
-rw-r--r--services/core/java/com/android/server/compat/overrides/OWNERS2
-rw-r--r--services/core/java/com/android/server/compat/overrides/TEST_MAPPING12
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java120
-rw-r--r--services/core/java/com/android/server/content/ContentService.java18
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java24
-rw-r--r--services/core/java/com/android/server/content/SyncStorageEngine.java73
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java33
-rw-r--r--services/core/java/com/android/server/display/DeviceStateToLayoutMap.java4
-rw-r--r--services/core/java/com/android/server/display/DisplayDevice.java45
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceRepository.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java237
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerShellCommand.java18
-rw-r--r--services/core/java/com/android/server/display/DisplayModeDirector.java4
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java14
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java9
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java3
-rw-r--r--services/core/java/com/android/server/display/PersistentDataStore.java79
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java25
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java17
-rw-r--r--services/core/java/com/android/server/display/color/DisplayTransformManager.java4
-rw-r--r--services/core/java/com/android/server/hdmi/ActiveSourceHandler.java2
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java (renamed from services/core/java/com/android/server/hdmi/DeviceSelectAction.java)6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecConfig.java31
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java25
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecKeycode.java109
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java86
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java50
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java55
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java16
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java111
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessage.java20
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecNetwork.java14
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java135
-rw-r--r--services/core/java/com/android/server/hdmi/HotplugDetectionAction.java6
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java5
-rw-r--r--services/core/java/com/android/server/hdmi/PowerManagerWrapper.java50
-rw-r--r--services/core/java/com/android/server/hdmi/RequestSadAction.java176
-rw-r--r--services/core/java/com/android/server/hdmi/RoutingControlAction.java88
-rw-r--r--services/core/java/com/android/server/hdmi/SendKeyAction.java3
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java86
-rw-r--r--services/core/java/com/android/server/input/InputShellCommand.java7
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java86
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodMenuController.java1
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java67
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java1859
-rw-r--r--services/core/java/com/android/server/inputmethod/multi-client-ime.md194
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java13
-rw-r--r--services/core/java/com/android/server/location/GeocoderProxy.java2
-rw-r--r--services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java4
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java16
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java11
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java25
-rw-r--r--services/core/java/com/android/server/location/geofence/GeofenceProxy.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java4
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java102
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssManagerService.java2
-rw-r--r--services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java4
-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.java2
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java110
-rw-r--r--services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java9
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryDatabase.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationHistoryManager.java1
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java24
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java16
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLogger.java10
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java7
-rw-r--r--services/core/java/com/android/server/notification/VibratorHelper.java29
-rw-r--r--services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java23
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java49
-rw-r--r--services/core/java/com/android/server/pm/ApkChecksums.java24
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java6
-rw-r--r--services/core/java/com/android/server/pm/CommitRequest.java35
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java23
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageAction.java36
-rw-r--r--services/core/java/com/android/server/pm/FileInstallArgs.java263
-rw-r--r--services/core/java/com/android/server/pm/HandlerParams.java57
-rw-r--r--services/core/java/com/android/server/pm/IncrementalProgressListener.java43
-rw-r--r--services/core/java/com/android/server/pm/IncrementalStatesCallback.java46
-rw-r--r--services/core/java/com/android/server/pm/InstallArgs.java149
-rw-r--r--services/core/java/com/android/server/pm/InstallParams.java2162
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java (renamed from core/java/android/uwb/MeasurementStatus.aidl)30
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java31
-rw-r--r--services/core/java/com/android/server/pm/KeySetManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java11
-rw-r--r--services/core/java/com/android/server/pm/MoveInfo.java41
-rw-r--r--services/core/java/com/android/server/pm/MoveInstallArgs.java133
-rw-r--r--services/core/java/com/android/server/pm/OWNERS2
-rw-r--r--services/core/java/com/android/server/pm/OriginInfo.java68
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageFreezer.java91
-rw-r--r--services/core/java/com/android/server/pm/PackageInstalledInfo.java74
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java7
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java459
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerException.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6098
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java50
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java76
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java179
-rw-r--r--services/core/java/com/android/server/pm/PackageSender.java39
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageSignatures.java56
-rw-r--r--services/core/java/com/android/server/pm/PackageVerificationState.java2
-rw-r--r--services/core/java/com/android/server/pm/ParallelPackageParser.java5
-rw-r--r--services/core/java/com/android/server/pm/PrepareFailure.java45
-rw-r--r--services/core/java/com/android/server/pm/PrepareResult.java54
-rw-r--r--services/core/java/com/android/server/pm/ReconcileFailure.java (renamed from core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl)21
-rw-r--r--services/core/java/com/android/server/pm/ReconcileRequest.java74
-rw-r--r--services/core/java/com/android/server/pm/ReconciledPackage.java90
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java16
-rw-r--r--services/core/java/com/android/server/pm/ScanRequest.java87
-rw-r--r--services/core/java/com/android/server/pm/ScanResult.java72
-rw-r--r--services/core/java/com/android/server/pm/Settings.java33
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java10
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java341
-rw-r--r--services/core/java/com/android/server/pm/SystemDeleteException.java25
-rw-r--r--services/core/java/com/android/server/pm/TEST_MAPPING76
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java48
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java2
-rw-r--r--services/core/java/com/android/server/pm/UserTypeDetails.java34
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java2
-rw-r--r--services/core/java/com/android/server/pm/VerificationInfo.java40
-rw-r--r--services/core/java/com/android/server/pm/VerificationParams.java778
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java2
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageParser2.java9
-rw-r--r--services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java4
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java22
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java17
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/Permission.java4
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java101
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java3
-rw-r--r--services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java4
-rw-r--r--services/core/java/com/android/server/policy/AppOpsPolicy.java5
-rw-r--r--services/core/java/com/android/server/policy/DisplayFoldController.java27
-rw-r--r--services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java4
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java18
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java50
-rw-r--r--services/core/java/com/android/server/policy/SingleKeyGestureDetector.java39
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java2
-rw-r--r--services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java10
-rw-r--r--services/core/java/com/android/server/power/Notifier.java29
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java36
-rw-r--r--services/core/java/com/android/server/power/ScreenUndimDetector.java297
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverController.java20
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java71
-rw-r--r--services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java142
-rw-r--r--services/core/java/com/android/server/power/batterysaver/FileUpdater.java406
-rw-r--r--services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java15
-rw-r--r--services/core/java/com/android/server/rollback/AppDataRollbackHelper.java8
-rw-r--r--services/core/java/com/android/server/rollback/Rollback.java57
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java86
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java23
-rw-r--r--services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java22
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java42
-rw-r--r--services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java89
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java47
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java120
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java52
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java8
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java43
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java184
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java158
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java6
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/README.md154
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java456
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java305
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java168
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java (renamed from services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java)90
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java340
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java257
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java232
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java27
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java64
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java78
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java39
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java327
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java389
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java21
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java25
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java28
-rw-r--r--services/core/java/com/android/server/timedetector/ServerFlags.java40
-rw-r--r--services/core/java/com/android/server/timedetector/TEST_MAPPING20
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java15
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java11
-rw-r--r--services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java18
-rw-r--r--services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java2
-rw-r--r--services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java150
-rw-r--r--services/core/java/com/android/server/timezonedetector/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java62
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java67
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java113
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java134
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java136
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java14
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java43
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java211
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/TestCommand.java194
-rw-r--r--services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java15
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java32
-rw-r--r--services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java41
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java12
-rw-r--r--services/core/java/com/android/server/utils/OWNERS4
-rw-r--r--services/core/java/com/android/server/vcn/OWNERS2
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java11
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationThread.java6
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java98
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java72
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java601
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java89
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java41
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java689
-rw-r--r--services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java23
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java206
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java103
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java124
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java221
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java4
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java4
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java414
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java768
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java2
-rw-r--r--services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java96
-rw-r--r--services/core/java/com/android/server/wm/FadeRotationAnimationController.java19
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java11
-rw-r--r--services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java3
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java5
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java56
-rw-r--r--services/core/java/com/android/server/wm/InputWindowHandleWrapper.java17
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java98
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java12
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java2
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java30
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java59
-rw-r--r--services/core/java/com/android/server/wm/LetterboxConfiguration.java115
-rw-r--r--services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java2
-rw-r--r--services/core/java/com/android/server/wm/PinnedTaskController.java10
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java24
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java10
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java20
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java95
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java6
-rw-r--r--services/core/java/com/android/server/wm/SafeActivityOptions.java23
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java12
-rw-r--r--services/core/java/com/android/server/wm/Session.java18
-rw-r--r--services/core/java/com/android/server/wm/StartingData.java6
-rw-r--r--services/core/java/com/android/server/wm/StrictModeFlash.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java2546
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java82
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java2221
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java470
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java334
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java17
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotSurface.java5
-rw-r--r--services/core/java/com/android/server/wm/Transition.java555
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java177
-rw-r--r--services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java2
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java41
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java89
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java89
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java230
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java361
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java450
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java79
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java201
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowToken.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java3
-rw-r--r--services/core/jni/Android.bp11
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp271
-rw-r--r--services/core/jni/gnss/Android.bp51
-rw-r--r--services/core/jni/gnss/GnssAntennaInfoCallback.cpp269
-rw-r--r--services/core/jni/gnss/GnssAntennaInfoCallback.h84
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.cpp3
-rw-r--r--services/core/jni/gnss/GnssMeasurementCallback.h2
-rw-r--r--services/core/jni/gnss/Utils.cpp6
-rw-r--r--services/core/xsd/Android.bp2
-rw-r--r--services/core/xsd/display-layout-config/display-layout-config.xsd2
-rw-r--r--services/core/xsd/display-layout-config/schema/current.txt6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java74
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java98
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java28
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java12
-rw-r--r--services/java/com/android/server/SystemServer.java43
-rw-r--r--services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java2
-rw-r--r--services/tests/PackageManager/packageinstaller/Android.bp (renamed from core/tests/uwbtests/Android.bp)19
-rw-r--r--services/tests/PackageManager/packageinstaller/AndroidManifest.xml29
-rw-r--r--services/tests/PackageManager/packageinstaller/AndroidTest.xml29
-rw-r--r--services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt50
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml2
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java61
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp28
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml25
-rw-r--r--services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-sharedUser.xml24
-rw-r--r--services/tests/PackageManagerServiceTests/host/AndroidTest.xml7
-rw-r--r--services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt55
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp2
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml2
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/PackageInstallerSessionTest.kt120
-rw-r--r--services/tests/PackageManagerServiceTests/unit/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/unit/TEST_MAPPING7
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt572
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt413
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt59
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt47
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt73
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt36
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt95
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt34
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt152
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt53
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt35
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt55
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt43
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt78
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt32
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt35
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java56
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java41
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java118
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt53
-rw-r--r--services/tests/mockingservicestests/AndroidManifest.xml1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java46
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java284
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java329
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java107
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java302
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java688
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java129
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java8
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java62
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java62
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java333
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java305
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java42
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java77
-rw-r--r--services/tests/servicestests/Android.bp3
-rw-r--r--services/tests/servicestests/AndroidManifest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java113
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java168
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java203
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java171
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java26
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java82
-rw-r--r--services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java94
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java (renamed from services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java)54
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java519
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java116
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java114
-rwxr-xr-xservices/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java49
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java76
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java324
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java237
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java577
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java261
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java95
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java92
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/OWNERS4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java77
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java148
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java56
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt8
-rw-r--r--services/tests/servicestests/src/com/android/server/power/NotifierTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java87
-rw-r--r--services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java405
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java54
-rw-r--r--services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java1054
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java1239
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java446
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/AgentTest.java345
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/LedgerTest.java128
-rw-r--r--services/tests/servicestests/src/com/android/server/tare/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/uwb/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java365
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java17
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/Android.bp14
-rw-r--r--services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml (renamed from core/tests/uwbtests/AndroidManifest.xml)19
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml7
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java6
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleReceiver.java46
-rw-r--r--services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java67
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java46
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java14
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java458
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java49
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java223
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java152
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java117
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java42
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java33
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java47
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java199
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java100
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/StubTransaction.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java11
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java364
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java16
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java105
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java51
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java56
-rw-r--r--services/usage/OWNERS2
-rw-r--r--services/usage/java/com/android/server/usage/StorageStatsService.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceLogger.java143
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java29
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java11
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java4
-rw-r--r--services/uwb/Android.bp26
-rw-r--r--services/uwb/OWNERS1
-rw-r--r--services/uwb/java/com/android/server/uwb/UwbInjector.java83
-rw-r--r--services/uwb/java/com/android/server/uwb/UwbServiceImpl.java325
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java43
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java14
-rw-r--r--telecomm/TEST_MAPPING40
-rw-r--r--telecomm/java/android/telecom/CallRedirectionService.java25
-rw-r--r--telecomm/java/android/telecom/Connection.java21
-rw-r--r--telecomm/java/android/telecom/InCallService.java6
-rw-r--r--telecomm/java/android/telecom/Phone.java16
-rw-r--r--telecomm/java/android/telecom/PhoneAccountHandle.java47
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java26
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java6
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl6
-rw-r--r--telephony/TEST_MAPPING40
-rw-r--r--telephony/common/Android.bp8
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java6
-rw-r--r--telephony/java/android/telephony/AvailableNetworkInfo.java119
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java130
-rw-r--r--telephony/java/android/telephony/ServiceState.java13
-rw-r--r--telephony/java/android/telephony/SmsManager.java37
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java20
-rw-r--r--telephony/java/android/telephony/TelephonyDisplayInfo.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java16
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl5
-rw-r--r--tests/BatteryStatsPerfTest/AndroidManifest.xml2
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java129
-rw-r--r--tests/BootImageProfileTest/OWNERS2
-rw-r--r--tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java17
-rw-r--r--tests/FlickerTests/Android.bp22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt181
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt61
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt32
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt50
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt73
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt86
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt39
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt48
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt100
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt29
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt89
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt102
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt119
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt171
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt58
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt359
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt38
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt63
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt60
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml21
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml27
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml1
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml32
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java10
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java41
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java37
-rw-r--r--tests/Input/src/com/android/test/input/AnrTest.kt54
-rw-r--r--tests/InputMethodStressTest/Android.bp35
-rw-r--r--tests/InputMethodStressTest/AndroidManifest.xml28
-rw-r--r--tests/InputMethodStressTest/AndroidTest.xml (renamed from core/tests/uwbtests/AndroidTest.xml)21
-rw-r--r--tests/InputMethodStressTest/OWNERS (renamed from core/java/android/util/imetracing/OWNERS)2
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/ImeOpenCloseStressTest.java79
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/TestActivity.java64
-rw-r--r--tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java11
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java492
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java44
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java19
-rw-r--r--tests/SoundTriggerTestApp/res/layout/main.xml8
-rw-r--r--tests/SoundTriggerTestApp/res/values/strings.xml1
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java8
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java70
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java33
-rw-r--r--tests/StagedInstallTest/Android.bp1
-rw-r--r--tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java77
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java15
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp20
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml18
-rw-r--r--tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java13
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java174
-rw-r--r--tests/UpdatableSystemFontTest/testdata/Android.bp60
-rw-r--r--tests/componentalias/Android.bp48
-rwxr-xr-xtests/componentalias/AndroidManifest.xml29
-rw-r--r--tests/componentalias/AndroidTest.xml35
-rw-r--r--tests/componentalias/OWNERS2
-rw-r--r--tests/componentalias/apps/Android.bp76
-rwxr-xr-xtests/componentalias/apps/AndroidManifest_main.xml67
-rwxr-xr-xtests/componentalias/apps/AndroidManifest_sub.xml35
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java71
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java (renamed from core/java/android/uwb/UwbAddress.aidl)8
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java (renamed from core/java/android/uwb/SessionHandle.aidl)8
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java (renamed from core/java/android/uwb/RangingReport.aidl)8
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java19
-rw-r--r--tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java19
-rw-r--r--tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java215
-rw-r--r--tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java29
-rw-r--r--tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java194
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java322
-rw-r--r--tests/utils/testutils/Android.bp2
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java5
-rw-r--r--tests/vcn/OWNERS2
-rw-r--r--tools/aapt2/AppInfo.h13
-rw-r--r--tools/aapt2/Debug.cpp8
-rw-r--r--tools/aapt2/Main.cpp2
-rw-r--r--tools/aapt2/NameMangler.h3
-rw-r--r--tools/aapt2/ResourceParser.cpp102
-rw-r--r--tools/aapt2/ResourceParser.h8
-rw-r--r--tools/aapt2/ResourceParser_test.cpp34
-rw-r--r--tools/aapt2/ResourceTable.cpp24
-rw-r--r--tools/aapt2/ResourceTable.h24
-rw-r--r--tools/aapt2/ResourceTable_test.cpp8
-rw-r--r--tools/aapt2/ResourceUtils.cpp28
-rw-r--r--tools/aapt2/ResourceUtils.h19
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp49
-rw-r--r--tools/aapt2/ResourceValues.cpp4
-rw-r--r--tools/aapt2/ResourceValues.h7
-rw-r--r--tools/aapt2/SdkConstants.cpp10
-rw-r--r--tools/aapt2/SdkConstants.h2
-rw-r--r--tools/aapt2/Source.h7
-rw-r--r--tools/aapt2/cmd/Command.cpp2
-rw-r--r--tools/aapt2/cmd/Command.h24
-rw-r--r--tools/aapt2/cmd/Command_test.cpp2
-rw-r--r--tools/aapt2/cmd/Compile.cpp11
-rw-r--r--tools/aapt2/cmd/Compile.h19
-rw-r--r--tools/aapt2/cmd/Convert.cpp3
-rw-r--r--tools/aapt2/cmd/Convert.h4
-rw-r--r--tools/aapt2/cmd/Diff.cpp4
-rw-r--r--tools/aapt2/cmd/Dump.cpp15
-rw-r--r--tools/aapt2/cmd/Dump.h6
-rw-r--r--tools/aapt2/cmd/Link.cpp41
-rw-r--r--tools/aapt2/cmd/Link.h30
-rw-r--r--tools/aapt2/cmd/Optimize.cpp8
-rw-r--r--tools/aapt2/cmd/Optimize.h14
-rw-r--r--tools/aapt2/cmd/Util.cpp37
-rw-r--r--tools/aapt2/cmd/Util.h8
-rw-r--r--tools/aapt2/compile/IdAssigner_test.cpp14
-rw-r--r--tools/aapt2/compile/InlineXmlFormatParser.cpp4
-rw-r--r--tools/aapt2/compile/PseudolocaleGenerator.cpp2
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.cpp66
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.h19
-rw-r--r--tools/aapt2/configuration/ConfigurationParser.internal.h35
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp24
-rw-r--r--tools/aapt2/format/Archive.cpp2
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp6
-rw-r--r--tools/aapt2/format/binary/XmlFlattener.cpp2
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp6
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp6
-rw-r--r--tools/aapt2/io/FileSystem.cpp5
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp18
-rw-r--r--tools/aapt2/java/JavaClassGenerator.h10
-rw-r--r--tools/aapt2/java/ManifestClassGenerator.cpp8
-rw-r--r--tools/aapt2/java/ProguardRules.cpp14
-rw-r--r--tools/aapt2/link/AutoVersioner.cpp2
-rw-r--r--tools/aapt2/link/AutoVersioner_test.cpp16
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp7
-rw-r--r--tools/aapt2/link/ManifestFixer.h24
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp16
-rw-r--r--tools/aapt2/link/ReferenceLinker.h10
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp6
-rw-r--r--tools/aapt2/link/TableMerger.cpp4
-rw-r--r--tools/aapt2/link/TableMerger_test.cpp7
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp2
-rw-r--r--tools/aapt2/link/XmlReferenceLinker_test.cpp32
-rw-r--r--tools/aapt2/process/SymbolTable.cpp17
-rw-r--r--tools/aapt2/process/SymbolTable.h4
-rw-r--r--tools/aapt2/test/Builders.cpp6
-rw-r--r--tools/aapt2/test/Builders.h6
-rw-r--r--tools/aapt2/test/Common.cpp2
-rw-r--r--tools/aapt2/test/Common.h4
-rw-r--r--tools/aapt2/test/Context.h4
-rw-r--r--tools/aapt2/test/Fixture.cpp2
-rw-r--r--tools/aapt2/util/Files.cpp14
-rw-r--r--tools/aapt2/util/Files.h11
-rw-r--r--tools/aapt2/util/Maybe.h324
-rw-r--r--tools/aapt2/util/Maybe_test.cpp129
-rw-r--r--tools/aapt2/util/Util.cpp5
-rw-r--r--tools/aapt2/util/Util.h5
-rw-r--r--tools/aapt2/xml/XmlDom.cpp5
-rw-r--r--tools/aapt2/xml/XmlDom.h9
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp24
-rw-r--r--tools/aapt2/xml/XmlPullParser.cpp14
-rw-r--r--tools/aapt2/xml/XmlPullParser.h12
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp6
-rw-r--r--tools/aapt2/xml/XmlUtil.h5
-rw-r--r--tools/aapt2/xml/XmlUtil_test.cpp2
-rwxr-xr-xtools/aosp/aosp_sha.sh16
-rw-r--r--tools/codegen/OWNERS2
-rw-r--r--tools/codegen/src/com/android/codegen/ImportsProvider.kt21
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt4
-rw-r--r--tools/lint/Android.bp46
-rw-r--r--tools/lint/README.md50
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt48
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt573
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt82
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt814
-rw-r--r--tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt217
3272 files changed, 129699 insertions, 54382 deletions
diff --git a/Android.bp b/Android.bp
index aca706a91341..c156774a1c51 100644
--- a/Android.bp
+++ b/Android.bp
@@ -101,6 +101,7 @@ filegroup {
// AIDL sources from external directories
":android.hardware.security.keymint-V1-java-source",
":android.hardware.security.secureclock-V1-java-source",
+ ":android.hardware.tv.tuner-V1-java-source",
":android.security.apc-java-source",
":android.security.authorization-java-source",
":android.security.legacykeystore-java-source",
@@ -112,6 +113,7 @@ filegroup {
":framework_native_aidl",
":gatekeeper_aidl",
":gsiservice_aidl",
+ ":guiconstants_aidl",
":idmap2_aidl",
":idmap2_core_aidl",
":incidentcompanion_aidl",
@@ -154,6 +156,7 @@ java_library {
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
"framework-tethering.stubs.module_lib",
+ "framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
sdk_version: "module_current",
@@ -176,6 +179,7 @@ java_library {
"framework-sdkextensions.impl",
"framework-statsd.impl",
"framework-tethering.impl",
+ "framework-uwb.impl",
"framework-wifi.impl",
"updatable-media",
],
@@ -249,8 +253,6 @@ java_library {
"android.hardware.thermal-V1.1-java",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.tv.tuner-V1.0-java-constants",
- "android.hardware.tv.tuner-V1.1-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb-V1.2-java-constants",
@@ -318,6 +320,7 @@ java_defaults {
"icu4j-platform-compat-config",
"protolog.conf.json.gz",
"services-platform-compat-config",
+ "TeleService-platform-compat-config",
"documents-ui-compat-config",
"calendar-provider-compat-config",
],
@@ -329,6 +332,9 @@ java_defaults {
],
sdk_version: "core_platform",
static_libs: [
+ "android.hardware.common.fmq-V1-java",
+ // TODO(b/184162091)
+ "android.hardware.soundtrigger3-V1-java",
"bouncycastle-repackaged-unbundled",
"framework-internal-utils",
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
@@ -555,8 +561,6 @@ stubs_defaults {
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V2.0-java",
"android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.tv.tuner-V1.0-java-constants",
- "android.hardware.tv.tuner-V1.1-java-constants",
"android.hardware.usb-V1.0-java-constants",
"android.hardware.usb-V1.1-java-constants",
"android.hardware.usb.gadget-V1.0-java",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index aae4a7174d68..48ae72399aa3 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -152,6 +152,7 @@ droidstubs {
args: metalava_framework_docs_args +
" --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
write_sdk_values: true,
+ api_levels_sdk_type: "system",
}
/////////////////////////////////////////////////////////////////////
diff --git a/OWNERS b/OWNERS
index 4970dd122331..03cfac955858 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,23 +1,23 @@
# This top-level list should remain narrowly defined as team leads; individual
# teams are strongly encouraged to define narrower OWNERS files at deeper
# levels within the source tree; see OWNERS.md for more details
-akulian@google.com
-dsandler@android.com
-dsandler@google.com
-hackbod@android.com
-hackbod@google.com
-jjaggi@google.com
-jsharkey@android.com
-jsharkey@google.com
-lorenzo@google.com
-michaelwr@google.com
-nandana@google.com
-narayan@google.com
-ogunwale@google.com
-roosa@google.com
-svetoslavganov@android.com
-svetoslavganov@google.com
-yamasani@google.com
+akulian@google.com #{LAST_RESORT_SUGGESTION}
+dsandler@android.com #{LAST_RESORT_SUGGESTION}
+dsandler@google.com #{LAST_RESORT_SUGGESTION}
+hackbod@android.com #{LAST_RESORT_SUGGESTION}
+hackbod@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
+jsharkey@android.com #{LAST_RESORT_SUGGESTION}
+jsharkey@google.com #{LAST_RESORT_SUGGESTION}
+lorenzo@google.com #{LAST_RESORT_SUGGESTION}
+michaelwr@google.com #{LAST_RESORT_SUGGESTION}
+nandana@google.com #{LAST_RESORT_SUGGESTION}
+narayan@google.com #{LAST_RESORT_SUGGESTION}
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+roosa@google.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@android.com #{LAST_RESORT_SUGGESTION}
+svetoslavganov@google.com #{LAST_RESORT_SUGGESTION}
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
@@ -31,3 +31,5 @@ per-file Android.mk = file:platform/build/soong:/OWNERS
per-file ApiDocs.bp = file:platform/build/soong:/OWNERS
per-file StubLibraries.bp = file:platform/build/soong:/OWNERS
per-file ProtoLibraries.bp = file:platform/build/soong:/OWNERS
+per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
+per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index 7e3cc2739cd0..db5ba2fd031f 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -98,7 +98,7 @@ java_library_host {
},
// Protos have lots of MissingOverride and similar.
errorprone: {
- javacflags: ["-XepDisableAllChecks"],
+ enabled: false,
},
}
@@ -124,6 +124,10 @@ java_library {
"libs/incident/proto/android/os/**/*.proto",
":service-permission-protos",
],
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ enabled: false,
+ },
}
// ==== java proto device library (for test only) ==============================
@@ -150,7 +154,7 @@ java_library {
sdk_version: "core_current",
// Protos have lots of MissingOverride and similar.
errorprone: {
- javacflags: ["-XepDisableAllChecks"],
+ enabled: false,
},
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 44c55c26153d..1904c1f34da0 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,6 +250,7 @@ modules_public_stubs = [
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
"framework-tethering.stubs",
+ "framework-uwb.stubs",
"framework-wifi.stubs",
"i18n.module.public.api.stubs",
]
@@ -269,6 +270,7 @@ modules_system_stubs = [
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
"framework-tethering.stubs.system",
+ "framework-uwb.stubs.system",
"framework-wifi.stubs.system",
"i18n.module.public.api.stubs", // Only has public stubs
]
diff --git a/TestProtoLibraries.bp b/TestProtoLibraries.bp
index 8e269d0a85c5..9e2a64c652fc 100644
--- a/TestProtoLibraries.bp
+++ b/TestProtoLibraries.bp
@@ -31,6 +31,6 @@ java_library_host {
type: "full",
},
errorprone: {
- javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520
+ enabled: false,
},
}
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 5a04ba303b66..faf61a756170 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -85,10 +85,7 @@ public class BlobStorePerfTests {
@After
public void tearDown() {
- // TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding
- // job id like this.
- // From BlobStoreConfig.IDLE_JOB_ID = 191934935.
- runShellCommand("cmd jobscheduler run -f android 191934935");
+ runShellCommand("cmd blob_store idle-maintenance");
}
@Test
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
index d07ed375b2ab..ecc5112ab6dd 100644
--- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -84,18 +84,18 @@ public class MyContentCaptureService extends ContentCaptureService {
@Override
public void onDisconnected() {
- Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
-
- if (sServiceWatcher == null) {
+ final ServiceWatcher sw = sServiceWatcher;
+ Log.i(TAG, "onDisconnected: sServiceWatcher=" + sw);
+ if (sw == null) {
Log.e(TAG, "onDisconnected() without a watcher");
return;
}
- if (sServiceWatcher.mService == null) {
- Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+ if (sw.mService == null) {
+ Log.e(TAG, "onDisconnected(): no service on " + sw);
return;
}
- sServiceWatcher.mDestroyed.countDown();
+ sw.mDestroyed.countDown();
clearServiceWatcher();
}
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 85dd0c4be48d..766b8c455d23 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -23,15 +23,15 @@ package {
android_test_helper_app {
name: "LargeResourcesCompressed",
- static_libs: [ "androidx.appcompat_appcompat" ],
+ static_libs: ["androidx.appcompat_appcompat"],
}
genrule {
name: "LargeResourcesUncompressed",
- srcs: [ ":LargeResourcesCompressed" ],
+ srcs: [":LargeResourcesCompressed"],
out: ["LargeResourcesUncompressed.apk"],
- cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
- + " && zip $(out) resources.arsc"
+ cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc -d $(genDir)" +
+ " && zip -j $(out) $(genDir)/resources.arsc",
}
java_library {
diff --git a/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java
new file mode 100644
index 000000000000..b93a6ea18d8d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/util/ArrayMapPerfTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.util;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.BiConsumer;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArrayMapPerfTest {
+ private static final int NUM_ITERATIONS = 100;
+ private static final int SET_SIZE_SMALL = 10;
+ private static final int SET_SIZE_LARGE = 50;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void testForEach_Small() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ BiConsumer<String, Integer> consumer = (s, i) -> {
+ };
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ ArrayMap<String, Integer> map = new ArrayMap<>();
+ for (int j = 0; j < SET_SIZE_SMALL; j++) {
+ map.put(Integer.toString(j), j);
+ }
+ map.forEach(consumer);
+ }
+ }
+ }
+
+ @Test
+ public void testForEach_Large() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ BiConsumer<String, Integer> consumer = (s, i) -> {
+ };
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ ArrayMap<String, Integer> map = new ArrayMap<>();
+ for (int j = 0; j < SET_SIZE_LARGE; j++) {
+ map.put(Integer.toString(j), j);
+ }
+ map.forEach(consumer);
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
index b24bf42be632..c299e9986adf 100644
--- a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
@@ -26,6 +26,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.function.Consumer;
import java.util.function.Predicate;
@RunWith(AndroidJUnit4.class)
@@ -39,6 +40,38 @@ public class ArraySetPerfTest {
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@Test
+ public void testForEach_Small() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Consumer<Integer> consumer = (i) -> {
+ };
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ ArraySet<Integer> set = new ArraySet<>();
+ for (int j = 0; j < SET_SIZE_SMALL; j++) {
+ set.add(j);
+ }
+ set.forEach(consumer);
+ }
+ }
+ }
+
+ @Test
+ public void testForEach_Large() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ Consumer<Integer> consumer = (i) -> {
+ };
+ while (state.keepRunning()) {
+ for (int i = 0; i < NUM_ITERATIONS; ++i) {
+ ArraySet<Integer> set = new ArraySet<>();
+ for (int j = 0; j < SET_SIZE_LARGE; j++) {
+ set.add(j);
+ }
+ set.forEach(consumer);
+ }
+ }
+ }
+
+ @Test
public void testValueAt_InBounds() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
ArraySet<Integer> set = new ArraySet<>();
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
index ab3c50b209e6..a1383e620616 100644
--- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
+++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java
@@ -24,8 +24,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertTrue;
-
import android.annotation.UiThread;
import android.app.Activity;
import android.content.ComponentName;
@@ -39,6 +37,7 @@ import android.perftests.utils.PerfManualStatusReporter;
import android.perftests.utils.TraceMarkParser;
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -53,6 +52,7 @@ import androidx.annotation.Nullable;
import androidx.test.filters.LargeTest;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
import junit.framework.Assert;
@@ -185,7 +185,6 @@ public class ImePerfTest extends ImePerfTestBase
public static class BaselineIme extends InputMethodService {
public static final int HEIGHT_DP = 100;
- private static int sPid;
@Override
public View onCreateInputView() {
@@ -196,14 +195,10 @@ public class ImePerfTest extends ImePerfTestBase
view.setPadding(0, 0, 0, 0);
view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height));
inner.setBackgroundColor(0xff01fe10); // green
- sPid = Process.myPid();
+ Log.v(TAG, "onCreateInputView");
return view;
}
- static int getPid() {
- return sPid;
- }
-
static ComponentName getName(Context context) {
return new ComponentName(context, BaselineIme.class);
}
@@ -281,9 +276,16 @@ public class ImePerfTest extends ImePerfTestBase
}
private void killBaselineIme() {
- assertTrue("PID of test and IME can't be same",
- Process.myPid() != BaselineIme.getPid());
- Process.killProcess(BaselineIme.getPid());
+ // pidof returns a space separated list of numeric PIDs.
+ String result = SystemUtil.runShellCommand(
+ "pidof com.android.perftests.inputmethod:BaselineIME");
+ for (String pid : result.trim().split(" ")) {
+ // The output may be empty if there is no process with the name.
+ if (TextUtils.isEmpty(pid)) {
+ continue;
+ }
+ Process.killProcess(Integer.parseInt(pid));
+ }
}
private void testShowOrHideImeWarm(final boolean show) throws Throwable {
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
index 8e342f3389fc..bec3cc9272c3 100644
--- a/apct-tests/perftests/multiuser/AndroidTest.xml
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -29,7 +29,7 @@
<option name="push-file" key="trace_config_multi_user.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
<!--Install the content provider automatically when we push some file in sdcard folder.-->
<!--Needed to avoid the installation during the test suite.-->
- <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" />
+ <option name="push-file" key="trace_config_multi_user.textproto" value="/sdcard/sample.textproto" />
</target_preparer>
<!-- Needed for pulling the collected trace config on to the host -->
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index b1c42a92af9c..9971cd42fbb2 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -26,6 +26,7 @@ import android.app.IStopUserCallback;
import android.app.UserSwitchObserver;
import android.app.WaitResult;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -142,20 +143,25 @@ public class UserLifecycleTests {
}
}
+ /** Tests creating a new user. */
@Test
public void createUser() {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
+ /** Tests creating and starting a new user. */
@Test
public void createAndStartUser() throws RemoteException {
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
@@ -166,12 +172,14 @@ public class UserLifecycleTests {
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
/**
+ * Tests starting an uninitialized user.
* Measures the time until ACTION_USER_STARTED is received.
*/
@Test
@@ -181,18 +189,21 @@ public class UserLifecycleTests {
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mIam.startUserInBackground(userId);
waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
/**
+ * Tests starting & unlocking an uninitialized user.
* Measures the time until unlock listener is triggered and user is unlocked.
*/
@Test
@@ -200,35 +211,40 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
// Waits for UserState.mUnlockProgress.finish().
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
+ /** Tests switching to an uninitialized user. */
@Test
public void switchUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
}
}
- /** Tests switching to an already-created, but no-longer-running, user. */
+ /** Tests switching to a previously-started, but no-longer-running, user. */
@Test
public void switchUser_stopped() throws RemoteException {
while (mRunner.keepRunning()) {
@@ -237,6 +253,7 @@ public class UserLifecycleTests {
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, testUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(testUser);
@@ -244,49 +261,59 @@ public class UserLifecycleTests {
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
}
}
- /** Tests switching to an already-created already-running non-owner user. */
+ /** Tests switching to an already-created already-running non-owner background user. */
@Test
public void switchUser_running() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
switchUser(testUser);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(testUser);
mRunner.resumeTiming();
}
}
+ /** Tests stopping a background user. */
@Test
public void stopUser() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createUserNoFlags();
- final CountDownLatch latch = new CountDownLatch(1);
- registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(1);
+ registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch1, userId);
+ registerMediaBroadcastReceiver(latch2, userId);
mIam.startUserInBackground(userId);
- waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch);
+ waitForLatch("Failed to achieve ACTION_USER_STARTED for user " + userId, latch1);
+ waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, latch2);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, false);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
+ /** Tests reaching LOOKED_BOOT_COMPLETE when switching to uninitialized user. */
@Test
public void lockedBootCompleted() throws RemoteException {
while (mRunner.keepRunning()) {
@@ -295,25 +322,31 @@ public class UserLifecycleTests {
final int userId = createUserNoFlags();
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(null, latch, userId);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(userId);
waitForLatch("Failed to achieve onLockedBootComplete for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
switchUserNoCheck(startUser);
removeUser(userId);
mRunner.resumeTiming();
}
}
+ /** Tests stopping an ephemeral foreground user. */
@Test
public void ephemeralUserStopped() throws RemoteException {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+ final CountDownLatch prelatch = new CountDownLatch(1);
+ registerMediaBroadcastReceiver(prelatch, userId);
switchUser(userId);
+ waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, prelatch);
final CountDownLatch latch = new CountDownLatch(1);
InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
@Override
@@ -326,12 +359,14 @@ public class UserLifecycleTests {
}, new IntentFilter(Intent.ACTION_USER_STOPPED));
final CountDownLatch switchLatch = new CountDownLatch(1);
registerUserSwitchObserver(switchLatch, null, startUser);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
mAm.switchUser(startUser);
waitForLatch("Failed to achieve ACTION_USER_STOPPED for user " + userId, latch);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
try {
switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
} catch (InterruptedException e) {
@@ -348,16 +383,18 @@ public class UserLifecycleTests {
assumeTrue(mHasManagedUserFeature);
while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
final int userId = createManagedProfile();
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
mRunner.resumeTiming();
}
}
- /** Tests starting (unlocking) a newly-created profile. */
+ /** Tests starting (unlocking) an uninitialized profile. */
@Test
public void managedProfileUnlock() {
assumeTrue(mHasManagedUserFeature);
@@ -365,17 +402,19 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
- /** Tests starting (unlocking) an already-created, but no-longer-running, profile. */
+ /** Tests starting (unlocking) a previously-started, but no-longer-running, profile. */
@Test
public void managedProfileUnlock_stopped() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -386,18 +425,20 @@ public class UserLifecycleTests {
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
startUserInBackgroundAndWaitForUnlock(userId);
stopUser(userId, true);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
/**
- * Tests starting (unlocking) and launching an already-installed app in a newly-created profile.
+ * Tests starting (unlocking) & launching an already-installed app in an uninitialized profile.
*/
@Test
public void managedProfileUnlockAndLaunchApp() throws RemoteException {
@@ -408,12 +449,14 @@ public class UserLifecycleTests {
final int userId = createManagedProfile();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -421,7 +464,7 @@ public class UserLifecycleTests {
/**
* Tests starting (unlocking) and launching a previously-launched app
- * in an already-created, but no-longer-running, profile.
+ * in a previously-started, but no-longer-running, profile.
* A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
* {@link #managedProfileUnlock_stopped}}.
*/
@@ -438,18 +481,20 @@ public class UserLifecycleTests {
startApp(userId, DUMMY_PACKAGE_NAME);
stopUser(userId, true);
SystemClock.sleep(1_000); // 1 second cool-down before re-starting profile.
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
}
- /** Tests installing a pre-existing app in a newly-created profile. */
+ /** Tests installing a pre-existing app in an uninitialized profile. */
@Test
public void managedProfileInstall() throws RemoteException {
assumeTrue(mHasManagedUserFeature);
@@ -457,11 +502,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -478,6 +525,7 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
final int userId = createManagedProfile();
@@ -486,6 +534,7 @@ public class UserLifecycleTests {
startApp(userId, DUMMY_PACKAGE_NAME);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -499,12 +548,17 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ final CountDownLatch prelatch = new CountDownLatch(1);
+ registerMediaBroadcastReceiver(prelatch, userId);
startUserInBackgroundAndWaitForUnlock(userId);
+ waitForLatch("Failed to achieve ACTION_MEDIA_MOUNTED for user " + userId, prelatch);
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
stopUser(userId, true);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -523,11 +577,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -546,11 +602,13 @@ public class UserLifecycleTests {
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
final int userId = createManagedProfile();
+ Log.i(TAG, "Starting timer");
mRunner.resumeTiming();
startUserInBackgroundAndWaitForUnlock(userId);
mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
removeUser(userId);
mRunner.resumeTiming();
}
@@ -649,9 +707,12 @@ public class UserLifecycleTests {
// First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
final int testUser = createUserNoFlags();
final CountDownLatch latch1 = new CountDownLatch(1);
+ final CountDownLatch latch2 = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
+ registerMediaBroadcastReceiver(latch2, testUser);
mAm.switchUser(testUser);
waitForLatch("Failed to achieve initial ACTION_USER_UNLOCKED for user " + testUser, latch1);
+ waitForLatch("Failed to achieve initial ACTION_MEDIA_MOUNTED for user " + testUser, latch2);
// Second, switch back to origUser, waiting merely for switchUser() to finish
switchUser(origUser);
@@ -734,6 +795,37 @@ public class UserLifecycleTests {
}, UserHandle.of(userId), new IntentFilter(action), null, null);
}
+ /**
+ * Register for a broadcast to indicate that Storage has processed the given user.
+ * Without this as part of setup, for tests dealing with already-switched users, Storage may not
+ * have finished, making the resulting processing inconsistent.
+ *
+ * Strictly speaking, the receiver should always be unregistered afterwards, but we don't
+ * necessarily bother since receivers from failed tests will be removed on test uninstallation.
+ */
+ private void registerMediaBroadcastReceiver(final CountDownLatch latch, final int userId) {
+ final String action = Intent.ACTION_MEDIA_MOUNTED;
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(action);
+ filter.addDataScheme(ContentResolver.SCHEME_FILE);
+
+ final Context context = InstrumentationRegistry.getContext();
+ context.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String data = intent.getDataString();
+ if (action.equals(intent.getAction())) {
+ Log.d(TAG, "Received ACTION_MEDIA_MOUNTED with " + data);
+ if (data != null && data.contains("/" + userId)) {
+ latch.countDown();
+ context.unregisterReceiver(this);
+ }
+ }
+ }
+ }, UserHandle.of(userId), filter, null, null);
+ }
+
private class ProgressWaiter extends IProgressListener.Stub {
private final CountDownLatch mFinishedLatch = new CountDownLatch(1);
diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
index 14a3f8f9245b..93b06e84e130 100644
--- a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
+++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
@@ -80,21 +80,21 @@ data_sources {
atrace_apps: "*"
atrace_categories: "am"
+ atrace_categories: "binder_driver"
atrace_categories: "bionic"
- atrace_categories: "camera"
- atrace_categories: "wm"
atrace_categories: "dalvik"
- atrace_categories: "sched"
- atrace_categories: "freq"
- atrace_categories: "gfx"
- atrace_categories: "view"
- atrace_categories: "webview"
atrace_categories: "input"
- atrace_categories: "hal"
- atrace_categories: "binder_driver"
+ atrace_categories: "pm"
+ atrace_categories: "res"
+ atrace_categories: "rro"
+ atrace_categories: "ss"
+ atrace_categories: "view"
+ atrace_categories: "wm"
+
+ atrace_categories: "freq"
+ atrace_categories: "sched"
atrace_categories: "sync"
atrace_categories: "workq"
- atrace_categories: "res"
}
}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index 0e76488d9642..fc70219d2aeb 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -20,6 +20,7 @@ android_test {
"androidx.annotation_annotation",
"apct-perftests-utils",
"collector-device-lib-platform",
+ "cts-install-lib-java",
],
libs: ["android.test.base"],
diff --git a/apct-tests/perftests/packagemanager/AndroidManifest.xml b/apct-tests/perftests/packagemanager/AndroidManifest.xml
index 4bcd557695a3..3b9431f1b97a 100644
--- a/apct-tests/perftests/packagemanager/AndroidManifest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidManifest.xml
@@ -17,6 +17,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.perftests.packagemanager">
+ <!-- prevent test application from being obscured because of package visibility -->
+ <queries>
+ <package android:name="com.android.cts.install.lib.testapp.A" />
+ <package android:name="com.android.cts.install.lib.testapp.B" />
+ <package android:name="com.android.cts.install.lib.testapp.C" />
+ </queries>
<permission android:name="com.android.perftests.packagemanager.TestPermission" />
<uses-permission android:name="com.android.perftests.packagemanager.TestPermission" />
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
index 4903510fa502..c9d45a6bda74 100644
--- a/apct-tests/perftests/packagemanager/AndroidTest.xml
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -130,6 +130,10 @@
<option name="instrumentation-arg" key="perfetto_config_file"
value="trace_config.textproto"/>
+ <!--
+ PackageInstallerBenchmark will break for 5 minutes time out so it changes to 10 minutes
+ -->
+ <option name="test-timeout" value="600000" />
</test>
diff --git a/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java b/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java
new file mode 100644
index 000000000000..3b4f72b2e50a
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/src/android/content/pm/PackageInstallerBenchmark.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.HandlerThread;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class PackageInstallerBenchmark {
+ private static final String TAG = "PackageInstallerBenchmark";
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ /**
+ * This rule adopts the Shell process permissions, needed because INSTALL_PACKAGES
+ * and DELETE_PACKAGES are privileged permission.
+ */
+ @Rule
+ public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES);
+
+ private static class SessionCallback extends PackageInstaller.SessionCallback {
+ private final List<Integer> mExpectedSessions;
+ private final CountDownLatch mCountDownLatch;
+ private final boolean mExpectedSuccess;
+
+ SessionCallback(boolean expectedSuccess, List<Integer> expectedSessions,
+ @NonNull CountDownLatch countDownLatch) {
+ mExpectedSuccess = expectedSuccess;
+ mCountDownLatch = countDownLatch;
+ mExpectedSessions = expectedSessions;
+ }
+
+ @Override
+ public void onCreated(int sessionId) { }
+
+ @Override
+ public void onBadgingChanged(int sessionId) { }
+
+ @Override
+ public void onActiveChanged(int sessionId, boolean active) { }
+
+ @Override
+ public void onProgressChanged(int sessionId, float progress) { }
+
+ @Override
+ public void onFinished(int sessionId, boolean success) {
+ if (success == mExpectedSuccess && mExpectedSessions.contains(sessionId)) {
+ mCountDownLatch.countDown();
+ }
+ }
+ }
+
+ private CountDownLatch mCountDownLatch;
+ private SessionCallback mSessionCallback;
+ private PackageInstaller mPackageInstaller;
+ private Install mInstall;
+ private HandlerThread mHandlerThread;
+ private List<PackageInstaller.Session> mExpectedSessions;
+ private List<Integer> mExpectedSessionIds;
+ final LocalIntentSender mLocalIntentSender = new LocalIntentSender();
+ private IntentSender mIntentSender;
+
+ @Before
+ public void setUp() throws IOException {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mPackageInstaller = context.getPackageManager().getPackageInstaller();
+ mHandlerThread = new HandlerThread("PackageInstallerBenchmark");
+ mHandlerThread.start();
+
+ mIntentSender = mLocalIntentSender.getIntentSender();
+ }
+
+ @After
+ public void tearDown() throws InterruptedException {
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ context.unregisterReceiver(mLocalIntentSender);
+
+ uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
+ mHandlerThread.quitSafely();
+ }
+
+ private List<PackageInstaller.Session> createSinglePackageSessions(
+ BenchmarkState state, boolean expectedResult, TestApp...testApps)
+ throws IOException, InterruptedException {
+ state.pauseTiming();
+ uninstall(false /* stop at fail */, testApps);
+
+ mExpectedSessions = new ArrayList<>();
+ mExpectedSessionIds = new ArrayList<>();
+ for (TestApp testApp : testApps) {
+ mInstall = Install.single(testApp);
+ final int expectedSessionId = mInstall.createSession();
+ PackageInstaller.Session session =
+ InstallUtils.openPackageInstallerSession(expectedSessionId);
+ Log.d(TAG, "createNewSession: session expectedSessionId = " + expectedSessionId);
+ mExpectedSessions.add(session);
+ mExpectedSessionIds.add(expectedSessionId);
+ }
+
+ mCountDownLatch = new CountDownLatch(mExpectedSessions.size());
+ mSessionCallback = new SessionCallback(expectedResult, mExpectedSessionIds,
+ mCountDownLatch);
+ mPackageInstaller.registerSessionCallback(mSessionCallback,
+ mHandlerThread.getThreadHandler());
+ state.resumeTiming();
+ return mExpectedSessions;
+ }
+
+ private List<PackageInstaller.Session> createMultiplePackageSessions(BenchmarkState state,
+ boolean expectedSuccess, List<TestApp[]> testAppsList)
+ throws IOException, InterruptedException {
+ state.pauseTiming();
+ mExpectedSessions = new ArrayList<>();
+ mExpectedSessionIds = new ArrayList<>();
+ for (TestApp[] testApps : testAppsList) {
+ uninstall(false /* stop at fail */, testApps);
+
+ mInstall = Install.multi(testApps);
+ final int expectedSessionId = mInstall.createSession();
+ PackageInstaller.Session session =
+ InstallUtils.openPackageInstallerSession(expectedSessionId);
+ mExpectedSessions.add(session);
+ mExpectedSessionIds.add(expectedSessionId);
+ }
+
+ mCountDownLatch = new CountDownLatch(mExpectedSessions.size());
+ mSessionCallback = new SessionCallback(expectedSuccess, mExpectedSessionIds,
+ mCountDownLatch);
+ mPackageInstaller.registerSessionCallback(mSessionCallback,
+ mHandlerThread.getThreadHandler());
+ state.resumeTiming();
+ return mExpectedSessions;
+ }
+
+ private void uninstall(boolean stopAtFail, TestApp...testApps) throws InterruptedException {
+ String[] packageNames = new String[testApps.length];
+ for (int i = 0; i < testApps.length; i++) {
+ packageNames[i] = testApps[i].getPackageName();
+ }
+ uninstall(stopAtFail, packageNames);
+ }
+
+ private void uninstall(boolean stopAtFail, String...packageNames) throws InterruptedException {
+ LocalIntentSender localIntentSender = new LocalIntentSender();
+ IntentSender intentSender = localIntentSender.getIntentSender();
+ for (String packageName : packageNames) {
+ try {
+ mPackageInstaller.uninstall(packageName, intentSender);
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
+ Intent intent = localIntentSender.getResult();
+ if (stopAtFail) {
+ InstallUtils.assertStatusSuccess(intent);
+ }
+ }
+
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ context.unregisterReceiver(localIntentSender);
+ }
+
+ private void uninstallSession(BenchmarkState state, String...packageNames)
+ throws InterruptedException {
+ state.pauseTiming();
+ uninstall(true /* stop at fail */, packageNames);
+ mPackageInstaller.unregisterSessionCallback(mSessionCallback);
+ state.resumeTiming();
+ }
+
+ @Test(timeout = 600_000L)
+ public void commit_aSingleApkSession_untilFinishBenchmark() throws Exception {
+ uninstall(false /* stop at fail */, TestApp.A);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ List<PackageInstaller.Session> sessions =
+ createSinglePackageSessions(state, true, TestApp.A1);
+
+ for (PackageInstaller.Session session : sessions) {
+ session.commit(mIntentSender);
+ }
+ mCountDownLatch.await(1, TimeUnit.MINUTES);
+
+ uninstallSession(state, TestApp.A);
+ }
+ }
+
+ @Test(timeout = 600_000L)
+ public void commit_threeSingleApkSessions_untilFinishBenchmark() throws Exception {
+ uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ List<PackageInstaller.Session> sessions = createSinglePackageSessions(
+ state, true, TestApp.A1, TestApp.B1, TestApp.C1);
+
+ for (PackageInstaller.Session session : sessions) {
+ session.commit(mIntentSender);
+ }
+ mCountDownLatch.await(1, TimeUnit.MINUTES);
+
+ uninstallSession(state, TestApp.A, TestApp.B, TestApp.C);
+ }
+ }
+
+ @Test(timeout = 600_000L)
+ public void commit_aMultiplePackagesSession_untilFinishBenchmark()
+ throws IOException, InterruptedException {
+ uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final List<TestApp[]> multiPackageApps = new ArrayList<>();
+ multiPackageApps.add(new TestApp[] {TestApp.A1, TestApp.B1, TestApp.C1});
+
+ while (state.keepRunning()) {
+ List<PackageInstaller.Session> sessions = createMultiplePackageSessions(
+ state, true, multiPackageApps);
+
+ for (PackageInstaller.Session session : sessions) {
+ session.commit(mIntentSender);
+ }
+ mCountDownLatch.await(1, TimeUnit.MINUTES);
+
+ uninstallSession(state, TestApp.A, TestApp.B, TestApp.C);
+ }
+ }
+
+ @Test(timeout = 600_000L)
+ public void commit_threeMultiplePackageSessions_untilFinishBenchmark()
+ throws Exception {
+ uninstall(false /* stop at fail */, TestApp.A, TestApp.B, TestApp.C);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final List<TestApp[]> multiPackageApps = new ArrayList<>();
+ multiPackageApps.add(new TestApp[] {TestApp.A1});
+ multiPackageApps.add(new TestApp[] {TestApp.B1});
+ multiPackageApps.add(new TestApp[] {TestApp.C1});
+
+ while (state.keepRunning()) {
+ List<PackageInstaller.Session> sessions = createMultiplePackageSessions(
+ state, true, multiPackageApps);
+
+ for (PackageInstaller.Session session : sessions) {
+ session.commit(mIntentSender);
+ }
+ mCountDownLatch.await(1, TimeUnit.MINUTES);
+
+ uninstallSession(state, TestApp.A, TestApp.B, TestApp.C);
+ }
+ }
+
+ @Test(timeout = 600_000L)
+ public void commit_aMultipleApksSession_untilFinishBenchmark()
+ throws IOException, InterruptedException {
+ uninstall(false /* stop at fail */, TestApp.A);
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ List<PackageInstaller.Session> sessions = createSinglePackageSessions(
+ state, true, TestApp.ASplit1);
+
+ for (PackageInstaller.Session session : sessions) {
+ session.commit(mIntentSender);
+ }
+ mCountDownLatch.await(1, TimeUnit.MINUTES);
+
+ uninstallSession(state, TestApp.A);
+ }
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index e1928615211a..73bff08c626d 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -266,9 +266,9 @@ public final class BenchmarkState {
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
Log.i(TAG, key + summaryLine());
Bundle status = new Bundle();
- status.putLong(key + "_median", median());
- status.putLong(key + "_mean", mean());
- status.putLong(key + "_min", min());
+ status.putLong(key + "_median (ns)", median());
+ status.putLong(key + "_mean (ns)", mean());
+ status.putLong(key + "_min (ns)", min());
status.putLong(key + "_standardDeviation", standardDeviation());
instrumentation.sendStatus(Activity.RESULT_OK, status);
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
index fe2b1f6443b3..ebd8d86b1eed 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -306,27 +306,28 @@ public final class ManualBenchmarkState {
private void fillStatus(Bundle status, String key, Stats stats) {
if (shouldReport(StatsReport.FLAG_ITERATION)) {
- status.putLong(key + "_iteration", stats.getSize());
+ status.putLong(key + "_iteration (ns)", stats.getSize());
}
if (shouldReport(StatsReport.FLAG_MEDIAN)) {
- status.putLong(key + "_median", stats.getMedian());
+ status.putLong(key + "_median (ns)", stats.getMedian());
}
if (shouldReport(StatsReport.FLAG_MEAN)) {
- status.putLong(key + "_mean", Math.round(stats.getMean()));
+ status.putLong(key + "_mean (ns)", Math.round(stats.getMean()));
}
if (shouldReport(StatsReport.FLAG_MIN)) {
- status.putLong(key + "_min", stats.getMin());
+ status.putLong(key + "_min (ns)", stats.getMin());
}
if (shouldReport(StatsReport.FLAG_MAX)) {
- status.putLong(key + "_max", stats.getMax());
+ status.putLong(key + "_max (ns)", stats.getMax());
}
if (mStatsReportPercentiles != null) {
for (int percentile : mStatsReportPercentiles) {
- status.putLong(key + "_percentile" + percentile, stats.getPercentile(percentile));
+ status.putLong(key + "_percentile" + percentile + " (ns)",
+ stats.getPercentile(percentile));
}
}
if (shouldReport(StatsReport.FLAG_STDDEV)) {
- status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation()));
+ status.putLong(key + "_stddev (ns)", Math.round(stats.getStandardDeviation()));
}
if (shouldReport(StatsReport.FLAG_COEFFICIENT_VAR)) {
status.putLong(key + "_cv",
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index a2dc1c29f026..452bb0ab5909 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -29,6 +29,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -82,7 +83,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
private static class TestWindow extends BaseIWindow {
final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
- final InsetsState mRequestedVisibility = new InsetsState();
+ final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
final InsetsState mOutInsetsState = new InsetsState();
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
@@ -102,7 +103,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
long startTime = SystemClock.elapsedRealtimeNanos();
session.addToDisplay(this, mLayoutParams, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mRequestedVisibility, inputChannel,
+ Display.DEFAULT_DISPLAY, mRequestedVisibilities, inputChannel,
mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
state.addExtraResult("add", elapsedTimeNsOfAdd);
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 db23a6dc3047..c33d5ecc5d16 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -332,17 +332,20 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
for (int i = 0; i < schemaBundles.size(); i++) {
schemas.add(new AppSearchSchema(schemaBundles.get(i)));
@@ -359,7 +362,7 @@ public class AppSearchManagerService extends SystemService {
}
schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
}
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
packageName,
databaseName,
@@ -418,15 +421,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
GetSchemaResponse response =
instance.getAppSearchImpl().getSchema(packageName, databaseName);
invokeCallbackOnResult(
@@ -450,15 +456,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
List<String> namespaces =
instance.getAppSearchImpl().getNamespaces(packageName, databaseName);
invokeCallbackOnResult(
@@ -485,20 +494,23 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
for (int i = 0; i < documentBundles.size(); i++) {
GenericDocument document = new GenericDocument(documentBundles.get(i));
try {
@@ -571,20 +583,23 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
@@ -652,18 +667,21 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
packageName,
databaseName,
@@ -718,18 +736,21 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
boolean callerHasSystemAccess =
instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName);
@@ -783,17 +804,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
- // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
- // opened it
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
SearchResultPage searchResultPage =
instance.getAppSearchImpl().getNextPage(packageName, nextPageToken);
invokeCallbackOnResult(
@@ -812,15 +834,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
instance.getAppSearchImpl().invalidateNextPageToken(packageName, nextPageToken);
} catch (Throwable t) {
Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -846,15 +871,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
// we don't need to append the file. The file is always brand new.
try (DataOutputStream outputStream = new DataOutputStream(
new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
@@ -895,15 +923,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
GenericDocument document;
ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
@@ -957,15 +988,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
if (systemUsage
&& !instance.getVisibilityStore()
@@ -1004,20 +1038,23 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
@@ -1090,18 +1127,21 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
instance.getAppSearchImpl().removeByQuery(
packageName,
databaseName,
@@ -1154,15 +1194,18 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ mAppSearchUserInstanceManager.getUserInstance(targetUser);
StorageInfo storageInfo = instance.getAppSearchImpl()
.getStorageInfoForDatabase(packageName, databaseName);
Bundle storageInfoBundle = storageInfo.getBundle();
@@ -1184,18 +1227,21 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
- instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
+ instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
++operationSuccessCount;
} catch (Throwable t) {
@@ -1236,7 +1282,6 @@ public class AppSearchManagerService extends SystemService {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
- UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1244,12 +1289,18 @@ public class AppSearchManagerService extends SystemService {
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
- Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
- verifyUserUnlocked(callingUser);
- verifyCallingPackage(userContext, callingUser, callingUid, packageName);
- verifyNotInstantApp(userContext, packageName);
+ verifyCaller(callingUid, packageName);
+
+ // Obtain the user where the client wants to run the operations in. This should
+ // end up being the same as userHandle, assuming it is not a special user and
+ // the client is allowed to run operations in that user.
+ UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ verifyUserUnlocked(targetUser);
+
+ Context targetUserContext = mContext.createContextAsUser(targetUser,
+ /*flags=*/ 0);
instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
- userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR));
+ targetUserContext, targetUser, AppSearchConfig.getInstance(EXECUTOR));
++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
@@ -1278,29 +1329,6 @@ public class AppSearchManagerService extends SystemService {
});
}
- private void verifyCallingPackage(
- @NonNull Context userContext,
- @NonNull UserHandle actualCallingUser,
- int actualCallingUid,
- @NonNull String claimedCallingPackage) {
- Objects.requireNonNull(actualCallingUser);
- Objects.requireNonNull(claimedCallingPackage);
-
- int claimedCallingUid = PackageUtil.getPackageUid(
- userContext, claimedCallingPackage);
- if (claimedCallingUid == INVALID_UID) {
- throw new SecurityException(
- "Specified calling package [" + claimedCallingPackage + "] not found");
- }
- if (claimedCallingUid != actualCallingUid) {
- throw new SecurityException(
- "Specified calling package ["
- + claimedCallingPackage
- + "] does not match the calling uid "
- + actualCallingUid);
- }
- }
-
/** Invokes the {@link IAppSearchResultCallback} with the result. */
private void invokeCallbackOnResult(
IAppSearchResultCallback callback, AppSearchResult<?> result) {
@@ -1354,33 +1382,72 @@ public class AppSearchManagerService extends SystemService {
/**
* Helper for dealing with incoming user arguments to system service calls.
*
- * @param requestedUser The user which the caller is requesting to execute as.
+ * @param targetUserHandle The user which the caller is requesting to execute as.
* @param callingUid The actual uid of the caller as determined by Binder.
* @return the user handle that the call should run as. Will always be a concrete user.
*/
@NonNull
- private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) {
- UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
- if (callingUser.equals(requestedUser)) {
- return requestedUser;
+ private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingUid) {
+ UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+ if (callingUserHandle.equals(targetUserHandle)) {
+ return targetUserHandle;
}
// Duplicates UserController#ensureNotSpecialUser
- if (requestedUser.getIdentifier() < 0) {
+ if (targetUserHandle.getIdentifier() < 0) {
throw new IllegalArgumentException(
- "Call does not support special user " + requestedUser);
+ "Call does not support special user " + targetUserHandle);
}
throw new SecurityException(
- "Requested user, " + requestedUser + ", is not the same as the calling user, "
- + callingUser + ".");
+ "Requested user, " + targetUserHandle + ", is not the same as the calling user, "
+ + callingUserHandle + ".");
+ }
+
+ /**
+ * Verify various aspects of the calling user.
+ *
+ * @param callingUid Uid of the caller, usually retrieved from Binder for authenticity.
+ * @param claimedCallingPackage Package name the caller claims to be.
+ */
+ private void verifyCaller(int callingUid, @NonNull String claimedCallingPackage) {
+ // Obtain the user where the client is running in. Note that this could be different from
+ // the userHandle where the client wants to run the AppSearch operation in.
+ UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+ Context callingUserContext = mContext.createContextAsUser(callingUserHandle,
+ /*flags=*/ 0);
+
+ verifyCallingPackage(callingUserContext, callingUid, claimedCallingPackage);
+ verifyNotInstantApp(callingUserContext, claimedCallingPackage);
+ }
+
+ /**
+ * Check that the caller's supposed package name matches the uid making the call.
+ *
+ * @throws SecurityException if the package name and uid don't match.
+ */
+ private void verifyCallingPackage(
+ @NonNull Context actualCallingUserContext,
+ int actualCallingUid,
+ @NonNull String claimedCallingPackage) {
+ int claimedCallingUid = PackageUtil.getPackageUid(
+ actualCallingUserContext, claimedCallingPackage);
+ if (claimedCallingUid == INVALID_UID) {
+ throw new SecurityException(
+ "Specified calling package [" + claimedCallingPackage + "] not found");
+ }
+ if (claimedCallingUid != actualCallingUid) {
+ throw new SecurityException(
+ "Specified calling package ["
+ + claimedCallingPackage
+ + "] does not match the calling uid "
+ + actualCallingUid);
+ }
}
/**
- * Helper for ensuring instant apps can't make calls to AppSearch.
+ * Ensure instant apps can't make calls to AppSearch.
*
- * @param userContext Context of the user making the call.
- * @param packageName Package name of the caller.
* @throws SecurityException if the caller is an instant app.
*/
private void verifyNotInstantApp(@NonNull Context userContext, @NonNull String packageName) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 144536ec2644..646a0279c722 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -324,6 +324,7 @@ public class JobInfo implements Parcelable {
private final NetworkRequest networkRequest;
private final long networkDownloadBytes;
private final long networkUploadBytes;
+ private final long minimumNetworkChunkBytes;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
private final boolean isPeriodic;
@@ -515,6 +516,17 @@ public class JobInfo implements Parcelable {
}
/**
+ * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+ *
+ * @return Smallest piece of data that cannot be easily paused and resumed, or
+ * {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+ * @see Builder#setMinimumNetworkChunkBytes(long)
+ */
+ public @BytesLong long getMinimumNetworkChunkBytes() {
+ return minimumNetworkChunkBytes;
+ }
+
+ /**
* Set for a job that does not recur periodically, to specify a delay after which the job
* will be eligible for execution. This value is not set if the job recurs periodically.
* @see JobInfo.Builder#setMinimumLatency(long)
@@ -679,6 +691,9 @@ public class JobInfo implements Parcelable {
if (networkUploadBytes != j.networkUploadBytes) {
return false;
}
+ if (minimumNetworkChunkBytes != j.minimumNetworkChunkBytes) {
+ return false;
+ }
if (minLatencyMillis != j.minLatencyMillis) {
return false;
}
@@ -741,6 +756,7 @@ public class JobInfo implements Parcelable {
}
hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
+ hashCode = 31 * hashCode + Long.hashCode(minimumNetworkChunkBytes);
hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -777,6 +793,7 @@ public class JobInfo implements Parcelable {
}
networkDownloadBytes = in.readLong();
networkUploadBytes = in.readLong();
+ minimumNetworkChunkBytes = in.readLong();
minLatencyMillis = in.readLong();
maxExecutionDelayMillis = in.readLong();
isPeriodic = in.readInt() == 1;
@@ -807,6 +824,7 @@ public class JobInfo implements Parcelable {
networkRequest = b.mNetworkRequest;
networkDownloadBytes = b.mNetworkDownloadBytes;
networkUploadBytes = b.mNetworkUploadBytes;
+ minimumNetworkChunkBytes = b.mMinimumNetworkChunkBytes;
minLatencyMillis = b.mMinLatencyMillis;
maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
isPeriodic = b.mIsPeriodic;
@@ -851,6 +869,7 @@ public class JobInfo implements Parcelable {
}
out.writeLong(networkDownloadBytes);
out.writeLong(networkUploadBytes);
+ out.writeLong(minimumNetworkChunkBytes);
out.writeLong(minLatencyMillis);
out.writeLong(maxExecutionDelayMillis);
out.writeInt(isPeriodic ? 1 : 0);
@@ -986,6 +1005,7 @@ public class JobInfo implements Parcelable {
private NetworkRequest mNetworkRequest;
private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+ private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN;
private ArrayList<TriggerContentUri> mTriggerContentUris;
private long mTriggerContentUpdateDelay = -1;
private long mTriggerContentMaxDelay = -1;
@@ -1038,6 +1058,7 @@ public class JobInfo implements Parcelable {
mNetworkRequest = job.getRequiredNetwork();
mNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
mNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
mTriggerContentUris = job.getTriggerContentUris() != null
? new ArrayList<>(Arrays.asList(job.getTriggerContentUris())) : null;
mTriggerContentUpdateDelay = job.getTriggerContentUpdateDelay();
@@ -1256,6 +1277,39 @@ public class JobInfo implements Parcelable {
}
/**
+ * Set the minimum size of non-resumable network traffic this job requires, in bytes. When
+ * the upload or download can be easily paused and resumed, use this to set the smallest
+ * size that must be transmitted between start and stop events to be considered successful.
+ * If the transfer cannot be paused and resumed, then this should be the sum of the values
+ * provided to {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)}.
+ *
+ * <p>
+ * Apps are encouraged to provide values that are as accurate as possible since JobScheduler
+ * will try to run the job at a time when at least the minimum chunk can be transmitted to
+ * reduce the amount of repetitive data that's transferred. Jobs that cannot provide
+ * reasonable estimates should use the sentinel value {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
+ *
+ * <p>
+ * The values provided here only reflect the minimum non-resumable traffic that will be
+ * performed by the base job; if you're using {@link JobWorkItem} then
+ * you also need to define the network traffic used by each work item
+ * when constructing them.
+ *
+ * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and
+ * resumed, in bytes.
+ * @see JobInfo#getMinimumNetworkChunkBytes()
+ * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long)
+ */
+ @NonNull
+ public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) {
+ if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ mMinimumNetworkChunkBytes = chunkSizeBytes;
+ return this;
+ }
+
+ /**
* Specify that to run this job, the device must be charging (or be a
* non-battery-powered device connected to permanent power, such as Android TV
* devices). This defaults to {@code false}.
@@ -1647,12 +1701,29 @@ public class JobInfo implements Parcelable {
/**
* @hide
*/
- public void enforceValidity() {
- // Check that network estimates require network type
- if ((networkDownloadBytes > 0 || networkUploadBytes > 0) && networkRequest == null) {
+ public final void enforceValidity() {
+ // Check that network estimates require network type and are reasonable values.
+ if ((networkDownloadBytes > 0 || networkUploadBytes > 0 || minimumNetworkChunkBytes > 0)
+ && networkRequest == null) {
throw new IllegalArgumentException(
"Can't provide estimated network usage without requiring a network");
}
+ final long estimatedTransfer;
+ if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = networkDownloadBytes;
+ } else {
+ estimatedTransfer = networkUploadBytes
+ + (networkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : networkDownloadBytes);
+ }
+ if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN
+ && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && minimumNetworkChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
+ if (minimumNetworkChunkBytes != NETWORK_BYTES_UNKNOWN && minimumNetworkChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
// Check that a deadline was not set on a periodic job.
if (isPeriodic) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 32655c7c5ed1..acc661e64a2e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -44,6 +44,9 @@ import java.lang.annotation.RetentionPolicy;
public class JobParameters implements Parcelable {
/** @hide */
+ public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
+
+ /** @hide */
public static final int INTERNAL_STOP_REASON_CANCELED =
JobProtoEnums.INTERNAL_STOP_REASON_CANCELLED; // 0.
/** @hide */
@@ -106,6 +109,7 @@ public class JobParameters implements Parcelable {
* @hide
*/
public static final int[] JOB_STOP_REASON_CODES = {
+ INTERNAL_STOP_REASON_UNKNOWN,
INTERNAL_STOP_REASON_CANCELED,
INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
INTERNAL_STOP_REASON_PREEMPT,
@@ -269,7 +273,7 @@ public class JobParameters implements Parcelable {
private final Network network;
private int mStopReason = STOP_REASON_UNDEFINED;
- private int mInternalStopReason; // Default value is REASON_CANCELED
+ private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
private String debugStopReason; // Human readable stop reason for debugging.
/** @hide */
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
index 6e4a5a0c5784..7b287d5f9d15 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
@@ -19,6 +19,7 @@ package android.app.job;
import android.annotation.SystemApi;
import android.app.JobSchedulerImpl;
import android.app.SystemServiceRegistry;
+import android.app.tare.EconomyManager;
import android.content.Context;
import android.os.DeviceIdleManager;
import android.os.IDeviceIdleController;
@@ -56,5 +57,7 @@ public class JobSchedulerFrameworkInitializer {
SystemServiceRegistry.registerContextAwareService(
Context.POWER_EXEMPTION_SERVICE, PowerExemptionManager.class,
PowerExemptionManager::new);
+ SystemServiceRegistry.registerStaticService(
+ Context.RESOURCE_ECONOMY_SERVICE, EconomyManager.class, EconomyManager::new);
}
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
index 0c45cbf6dc11..372f9faacd5a 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobWorkItem.java
@@ -19,6 +19,7 @@ package android.app.job;
import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
import android.annotation.BytesLong;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
@@ -33,8 +34,9 @@ import android.os.Parcelable;
final public class JobWorkItem implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final Intent mIntent;
- final long mNetworkDownloadBytes;
- final long mNetworkUploadBytes;
+ private final long mNetworkDownloadBytes;
+ private final long mNetworkUploadBytes;
+ private final long mMinimumChunkBytes;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
int mDeliveryCount;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -49,9 +51,7 @@ final public class JobWorkItem implements Parcelable {
* @param intent The general Intent describing this work.
*/
public JobWorkItem(Intent intent) {
- mIntent = intent;
- mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
- mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+ this(intent, NETWORK_BYTES_UNKNOWN, NETWORK_BYTES_UNKNOWN);
}
/**
@@ -68,9 +68,45 @@ final public class JobWorkItem implements Parcelable {
* uploaded by this job work item, in bytes.
*/
public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
+ this(intent, downloadBytes, uploadBytes, NETWORK_BYTES_UNKNOWN);
+ }
+
+ /**
+ * Create a new piece of work, which can be submitted to
+ * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+ * <p>
+ * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+ * details about how to estimate network traffic.
+ *
+ * @param intent The general Intent describing this work.
+ * @param downloadBytes The estimated size of network traffic that will be
+ * downloaded by this job work item, in bytes.
+ * @param uploadBytes The estimated size of network traffic that will be
+ * uploaded by this job work item, in bytes.
+ * @param minimumChunkBytes The smallest piece of data that cannot be easily paused and
+ * resumed, in bytes.
+ */
+ public JobWorkItem(@Nullable Intent intent, @BytesLong long downloadBytes,
+ @BytesLong long uploadBytes, @BytesLong long minimumChunkBytes) {
+ if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && minimumChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ final long estimatedTransfer;
+ if (uploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = downloadBytes;
+ } else {
+ estimatedTransfer = uploadBytes
+ + (downloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : downloadBytes);
+ }
+ if (minimumChunkBytes != NETWORK_BYTES_UNKNOWN && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && minimumChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
mIntent = intent;
mNetworkDownloadBytes = downloadBytes;
mNetworkUploadBytes = uploadBytes;
+ mMinimumChunkBytes = minimumChunkBytes;
}
/**
@@ -103,6 +139,16 @@ final public class JobWorkItem implements Parcelable {
}
/**
+ * Return the smallest piece of data that cannot be easily paused and resumed, in bytes.
+ *
+ * @return Smallest piece of data that cannot be easily paused and resumed, or
+ * {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+ */
+ public @BytesLong long getMinimumNetworkChunkBytes() {
+ return mMinimumChunkBytes;
+ }
+
+ /**
* Return the count of the number of times this work item has been delivered
* to the job. The value will be > 1 if it has been redelivered because the job
* was stopped or crashed while it had previously been delivered but before the
@@ -161,6 +207,10 @@ final public class JobWorkItem implements Parcelable {
sb.append(" uploadBytes=");
sb.append(mNetworkUploadBytes);
}
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN) {
+ sb.append(" minimumChunkBytes=");
+ sb.append(mMinimumChunkBytes);
+ }
if (mDeliveryCount != 0) {
sb.append(" dcount=");
sb.append(mDeliveryCount);
@@ -169,6 +219,28 @@ final public class JobWorkItem implements Parcelable {
return sb.toString();
}
+ /**
+ * @hide
+ */
+ public void enforceValidity() {
+ final long estimatedTransfer;
+ if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+ estimatedTransfer = mNetworkDownloadBytes;
+ } else {
+ estimatedTransfer = mNetworkUploadBytes
+ + (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN ? 0 : mNetworkDownloadBytes);
+ }
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN
+ && estimatedTransfer != NETWORK_BYTES_UNKNOWN
+ && mMinimumChunkBytes > estimatedTransfer) {
+ throw new IllegalArgumentException(
+ "Minimum chunk size can't be greater than estimated network usage");
+ }
+ if (mMinimumChunkBytes != NETWORK_BYTES_UNKNOWN && mMinimumChunkBytes <= 0) {
+ throw new IllegalArgumentException("Minimum chunk size must be positive");
+ }
+ }
+
public int describeContents() {
return 0;
}
@@ -182,6 +254,7 @@ final public class JobWorkItem implements Parcelable {
}
out.writeLong(mNetworkDownloadBytes);
out.writeLong(mNetworkUploadBytes);
+ out.writeLong(mMinimumChunkBytes);
out.writeInt(mDeliveryCount);
out.writeInt(mWorkId);
}
@@ -206,6 +279,7 @@ final public class JobWorkItem implements Parcelable {
}
mNetworkDownloadBytes = in.readLong();
mNetworkUploadBytes = in.readLong();
+ mMinimumChunkBytes = in.readLong();
mDeliveryCount = in.readInt();
mWorkId = in.readInt();
}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
new file mode 100644
index 000000000000..8c8d2bfe16e3
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Provides access to the resource economy service.
+ *
+ * @hide
+ */
+@SystemService(Context.RESOURCE_ECONOMY_SERVICE)
+public class EconomyManager {
+ // Keys for AlarmManager TARE factors
+ /** @hide */
+ public static final String KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED =
+ "am_min_satiated_balance_exempted";
+ /** @hide */
+ public static final String KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP =
+ "am_min_satiated_balance_headless_system_app";
+ /** @hide */
+ public static final String KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP =
+ "am_min_satiated_balance_other_app";
+ /** @hide */
+ public static final String KEY_AM_MAX_SATIATED_BALANCE = "am_max_satiated_balance";
+ /** @hide */
+ public static final String KEY_AM_MAX_CIRCULATION = "am_max_circulation";
+ // TODO: Add AlarmManager modifier keys
+ /** @hide */
+ public static final String KEY_AM_REWARD_TOP_ACTIVITY_INSTANT =
+ "am_reward_top_activity_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_TOP_ACTIVITY_ONGOING =
+ "am_reward_top_activity_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_TOP_ACTIVITY_MAX = "am_reward_top_activity_max";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT =
+ "am_reward_notification_seen_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING =
+ "am_reward_notification_seen_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_MAX =
+ "am_reward_notification_seen_max";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT =
+ "am_reward_notification_seen_within_15_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING =
+ "am_reward_notification_seen_within_15_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX =
+ "am_reward_notification_seen_within_15_max";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT =
+ "am_reward_notification_interaction_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING =
+ "am_reward_notification_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX =
+ "am_reward_notification_interaction_max";
+ /** @hide */
+ public static final String KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT =
+ "am_reward_widget_interaction_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING =
+ "am_reward_widget_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_WIDGET_INTERACTION_MAX =
+ "am_reward_widget_interaction_max";
+ /** @hide */
+ public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT =
+ "am_reward_other_user_interaction_instant";
+ /** @hide */
+ public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING =
+ "am_reward_other_user_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX =
+ "am_reward_other_user_interaction_max";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP =
+ "am_action_alarm_allow_while_idle_exact_wakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP =
+ "am_action_alarm_allow_while_idle_inexact_wakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP =
+ "am_action_alarm_exact_wakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP =
+ "am_action_alarm_inexact_wakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP =
+ "am_action_alarm_allow_while_idle_exact_nonwakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP =
+ "am_action_alarm_exact_nonwakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP =
+ "am_action_alarm_allow_while_idle_inexact_nonwakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP =
+ "am_action_alarm_inexact_nonwakeup_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP =
+ "am_action_alarm_alarmclock_ctp";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE =
+ "am_action_alarm_allow_while_idle_exact_wakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE =
+ "am_action_alarm_allow_while_idle_inexact_wakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE =
+ "am_action_alarm_exact_wakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE =
+ "am_action_alarm_inexact_wakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE =
+ "am_action_alarm_allow_while_idle_exact_nonwakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE =
+ "am_action_alarm_exact_nonwakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE =
+ "am_action_alarm_allow_while_idle_inexact_nonwakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE =
+ "am_action_alarm_inexact_nonwakeup_base_price";
+ /** @hide */
+ public static final String KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE =
+ "am_action_alarm_alarmclock_base_price";
+
+// Keys for JobScheduler TARE factors
+ /** @hide */
+ public static final String KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED =
+ "js_min_satiated_balance_exempted";
+ /** @hide */
+ public static final String KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP =
+ "js_min_satiated_balance_headless_system_app";
+ /** @hide */
+ public static final String KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP =
+ "js_min_satiated_balance_other_app";
+ /** @hide */
+ public static final String KEY_JS_MAX_SATIATED_BALANCE =
+ "js_max_satiated_balance";
+ /** @hide */
+ public static final String KEY_JS_MAX_CIRCULATION = "js_max_circulation";
+ // TODO: Add JobScheduler modifier keys
+ /** @hide */
+ public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
+ "js_reward_top_activity_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_TOP_ACTIVITY_ONGOING =
+ "js_reward_top_activity_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_TOP_ACTIVITY_MAX =
+ "js_reward_top_activity_max";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT =
+ "js_reward_notification_seen_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING =
+ "js_reward_notification_seen_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_SEEN_MAX =
+ "js_reward_notification_seen_max";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT =
+ "js_reward_notification_interaction_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING =
+ "js_reward_notification_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX =
+ "js_reward_notification_interaction_max";
+ /** @hide */
+ public static final String KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT =
+ "js_reward_widget_interaction_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING =
+ "js_reward_widget_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_WIDGET_INTERACTION_MAX =
+ "js_reward_widget_interaction_max";
+ /** @hide */
+ public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT =
+ "js_reward_other_user_interaction_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING =
+ "js_reward_other_user_interaction_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX =
+ "js_reward_other_user_interaction_max";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MAX_START_CTP = "js_action_job_max_start_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MAX_RUNNING_CTP = "js_action_job_max_running_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_HIGH_START_CTP = "js_action_job_high_start_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP =
+ "js_action_job_high_running_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_DEFAULT_START_CTP =
+ "js_action_job_default_start_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP =
+ "js_action_job_default_running_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_LOW_START_CTP = "js_action_job_low_start_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_LOW_RUNNING_CTP = "js_action_job_low_running_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MIN_START_CTP = "js_action_job_min_start_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MIN_RUNNING_CTP = "js_action_job_min_running_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP =
+ "js_action_job_timeout_penalty_ctp";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE =
+ "js_action_job_max_start_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE =
+ "js_action_job_max_running_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE =
+ "js_action_job_high_start_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE =
+ "js_action_job_high_running_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE =
+ "js_action_job_default_start_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE =
+ "js_action_job_default_running_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE =
+ "js_action_job_low_start_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE =
+ "js_action_job_low_running_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE =
+ "js_action_job_min_start_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE =
+ "js_action_job_min_running_base_price";
+ /** @hide */
+ public static final String KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE =
+ "js_action_job_timeout_penalty_base_price";
+
+ // Default values AlarmManager factors
+ /** @hide */
+ public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 200;
+ /** @hide */
+ public static final int DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP = 160;
+ /** @hide */
+ public static final int DEFAULT_AM_MAX_SATIATED_BALANCE = 1440;
+ /** @hide */
+ public static final int DEFAULT_AM_MAX_CIRCULATION = 52000;
+ // TODO: add AlarmManager modifier default values
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT = 0;
+ /** @hide */
+ public static final float DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING = 0.01f;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX = 60;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT = 5;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT = 5;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT = 10;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT = 10;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX = 500;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP = 1;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP = 1;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP = 1;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP = 1;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP = 5;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE = 5;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE = 4;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE = 4;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE = 3;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE = 2;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE =
+ 2;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE = 1;
+ /** @hide */
+ public static final int DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE = 10;
+
+ // Default values JobScheduler factors
+ // TODO: add time_since_usage variable to min satiated balance factors
+ /** @hide */
+ public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED = 50000;
+ /** @hide */
+ public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP = 10000;
+ /** @hide */
+ public static final int DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP = 2000;
+ /** @hide */
+ public static final int DEFAULT_JS_MAX_SATIATED_BALANCE = 60000;
+ /** @hide */
+ public static final int DEFAULT_JS_MAX_CIRCULATION = 691200;
+ // TODO: add JobScheduler modifier default values
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT = 0;
+ /** @hide */
+ public static final float DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING = 0.5f;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX = 15000;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT = 1;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX = 10;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT = 5;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX = 5000;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT = 10;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX = 5000;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT = 10;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING = 0;
+ /** @hide */
+ public static final int DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX = 5000;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MAX_START_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_HIGH_START_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_LOW_START_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MIN_START_CTP = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP = 30;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE = 10;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE = 5;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE = 8;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE = 4;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE = 6;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE = 3;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE = 4;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE = 2;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE = 1;
+ /** @hide */
+ public static final int DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE = 60;
+}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl b/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl
new file mode 100644
index 000000000000..bb150118ebb1
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/IEconomyManager.aidl
@@ -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 android.app.tare;
+
+ /**
+ * IPC interface that supports the app-facing {@link #EconomyManager} api.
+ * {@hide}
+ */
+interface IEconomyManager {
+}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/OWNERS b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
new file mode 100644
index 000000000000..217a5edff08b
--- /dev/null
+++ b/apex/jobscheduler/framework/java/android/app/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS \ No newline at end of file
diff --git a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
index b0b9abccd229..fc27a794952c 100644
--- a/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
+++ b/apex/jobscheduler/framework/java/com/android/server/AppStateTracker.java
@@ -25,19 +25,25 @@ public interface AppStateTracker {
String TAG = "AppStateTracker";
/**
- * Register a {@link ServiceStateListener} to listen for forced-app-standby changes that should
- * affect services.
+ * Register a {@link BackgroundRestrictedAppListener} to listen for background restricted mode
+ * changes that should affect services etc.
*/
- void addServiceStateListener(@NonNull ServiceStateListener listener);
+ void addBackgroundRestrictedAppListener(@NonNull BackgroundRestrictedAppListener listener);
/**
- * A listener to listen to forced-app-standby changes that should affect services.
+ * @return {code true} if the given UID/package has been in background restricted mode,
+ * it does NOT include the case where the "force app background restricted" is enabled.
*/
- interface ServiceStateListener {
+ boolean isAppBackgroundRestricted(int uid, @NonNull String packageName);
+
+ /**
+ * A listener to listen to background restricted mode changes that should affect services etc.
+ */
+ interface BackgroundRestrictedAppListener {
/**
- * Called when an app goes into forced app standby and its foreground
- * services need to be removed from that state.
+ * Called when an app goes in/out of background restricted mode.
*/
- void stopForegroundServicesForUidPackage(int uid, String packageName);
+ void updateBackgroundRestrictedForUidPackage(int uid, String packageName,
+ boolean restricted);
}
}
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 8c06338560bf..968c6e5d9cf6 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -71,6 +71,13 @@ public interface AppStandbyInternal {
long getTimeSinceLastJobRun(String packageName, int userId);
+ /**
+ * Returns the time (in milliseconds) since the app was last interacted with by the user.
+ * This can be larger than the current elapsedRealtime, in case it happened before boot or
+ * a really large value if the app was never interacted with.
+ */
+ long getTimeSinceLastUsedByUser(String packageName, int userId);
+
void onUserRemoved(int userId);
void addListener(AppIdleStateChangeListener listener);
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index c332a598c30b..d0a155dbca26 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -60,8 +60,10 @@ import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Class to keep track of the information related to "force app standby", which includes:
@@ -160,16 +162,34 @@ public class AppStateTrackerImpl implements AppStateTracker {
@GuardedBy("mLock")
boolean mForcedAppStandbyEnabled;
+ /**
+ * A lock-free set of (uid, packageName) pairs in background restricted mode.
+ *
+ * <p>
+ * It's bascially shadowing the {@link #mRunAnyRestrictedPackages} together with
+ * the {@link #mForcedAppStandbyEnabled} - mutations on them would result in copy-on-write.
+ * </p>
+ */
+ volatile Set<Pair<Integer, String>> mBackgroundRestrictedUidPackages = Collections.emptySet();
+
@Override
- public void addServiceStateListener(@NonNull ServiceStateListener listener) {
+ public void addBackgroundRestrictedAppListener(
+ @NonNull BackgroundRestrictedAppListener listener) {
addListener(new Listener() {
@Override
- public void stopForegroundServicesForUidPackage(int uid, String packageName) {
- listener.stopForegroundServicesForUidPackage(uid, packageName);
+ public void updateBackgroundRestrictedForUidPackage(int uid, String packageName,
+ boolean restricted) {
+ listener.updateBackgroundRestrictedForUidPackage(uid, packageName, restricted);
}
});
}
+ @Override
+ public boolean isAppBackgroundRestricted(int uid, @NonNull String packageName) {
+ final Set<Pair<Integer, String>> bgRestrictedUidPkgs = mBackgroundRestrictedUidPackages;
+ return bgRestrictedUidPkgs.contains(Pair.create(uid, packageName));
+ }
+
interface Stats {
int UID_FG_STATE_CHANGED = 0;
int UID_ACTIVE_STATE_CHANGED = 1;
@@ -233,6 +253,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
return;
}
mForcedAppStandbyEnabled = enabled;
+ updateBackgroundRestrictedUidPackagesLocked();
if (DEBUG) {
Slog.d(TAG, "Forced app standby feature flag changed: "
+ mForcedAppStandbyEnabled);
@@ -277,7 +298,11 @@ public class AppStateTrackerImpl implements AppStateTracker {
if (!sender.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)) {
Slog.v(TAG, "Package " + packageName + "/" + uid
+ " toggled into fg service restriction");
- stopForegroundServicesForUidPackage(uid, packageName);
+ updateBackgroundRestrictedForUidPackage(uid, packageName, true);
+ } else {
+ Slog.v(TAG, "Package " + packageName + "/" + uid
+ + " toggled out of fg service restriction");
+ updateBackgroundRestrictedForUidPackage(uid, packageName, false);
}
}
@@ -366,10 +391,10 @@ public class AppStateTrackerImpl implements AppStateTracker {
}
/**
- * Called when an app goes into forced app standby and its foreground
- * services need to be removed from that state.
+ * Called when an app goes in/out of background restricted mode.
*/
- public void stopForegroundServicesForUidPackage(int uid, String packageName) {
+ public void updateBackgroundRestrictedForUidPackage(int uid, String packageName,
+ boolean restricted) {
}
/**
@@ -438,9 +463,12 @@ public class AppStateTrackerImpl implements AppStateTracker {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
// No need to notify for state change as all the alarms and jobs should be
// removed too.
- mExemptedBucketPackages.remove(userId, pkgName);
- mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName));
- mActiveUids.delete(uid);
+ synchronized (mLock) {
+ mExemptedBucketPackages.remove(userId, pkgName);
+ mRunAnyRestrictedPackages.remove(Pair.create(uid, pkgName));
+ updateBackgroundRestrictedUidPackagesLocked();
+ mActiveUids.delete(uid);
+ }
}
break;
}
@@ -580,6 +608,28 @@ public class AppStateTrackerImpl implements AppStateTracker {
}
}
}
+ updateBackgroundRestrictedUidPackagesLocked();
+ }
+
+ /**
+ * Update the {@link #mBackgroundRestrictedUidPackages} upon mutations on
+ * {@link #mRunAnyRestrictedPackages} or {@link #mForcedAppStandbyEnabled}.
+ */
+ @GuardedBy("mLock")
+ private void updateBackgroundRestrictedUidPackagesLocked() {
+ if (!mForcedAppStandbyEnabled) {
+ mBackgroundRestrictedUidPackages = Collections.emptySet();
+ return;
+ }
+ if (mForceAllAppsStandby) {
+ mBackgroundRestrictedUidPackages = null;
+ return;
+ }
+ Set<Pair<Integer, String>> fasUidPkgs = new ArraySet<>();
+ for (int i = 0, size = mRunAnyRestrictedPackages.size(); i < size; i++) {
+ fasUidPkgs.add(mRunAnyRestrictedPackages.valueAt(i));
+ }
+ mBackgroundRestrictedUidPackages = Collections.unmodifiableSet(fasUidPkgs);
}
private void updateForceAllAppStandbyState() {
@@ -645,6 +695,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
} else {
mRunAnyRestrictedPackages.removeAt(index);
}
+ updateBackgroundRestrictedUidPackagesLocked();
return true;
}
@@ -966,6 +1017,7 @@ public class AppStateTrackerImpl implements AppStateTracker {
mRunAnyRestrictedPackages.removeAt(i);
}
}
+ updateBackgroundRestrictedUidPackagesLocked();
cleanUpArrayForUser(mActiveUids, removedUserId);
mExemptedBucketPackages.remove(removedUserId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 9eb7bb7149ef..19aed4970569 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2150,7 +2150,7 @@ public class DeviceIdleController extends SystemService
private LocationManager mLocationManager;
Injector(Context ctx) {
- mContext = ctx;
+ mContext = ctx.createAttributionContext(TAG);
}
AlarmManager getAlarmManager() {
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 5a13a849a352..143c0f1ffdf5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -804,28 +804,42 @@ class JobConcurrencyManager {
@WorkType final int workType) {
final List<StateController> controllers = mService.mControllers;
final int numControllers = controllers.size();
- for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).prepareForExecutionLocked(jobStatus);
- }
- final PackageStats packageStats =
- getPkgStatsLocked(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
- packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
- if (!worker.executeRunnableJob(jobStatus, workType)) {
- Slog.e(TAG, "Error executing " + jobStatus);
- mWorkCountTracker.onStagedJobFailed(workType);
+ final PowerManager.WakeLock wl =
+ mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, jobStatus.getTag());
+ wl.setWorkSource(mService.deriveWorkSource(
+ jobStatus.getSourceUid(), jobStatus.getSourcePackageName()));
+ wl.setReferenceCounted(false);
+ // Since the quota controller will start counting from the time prepareForExecutionLocked()
+ // is called, hold a wakelock to make sure the CPU doesn't suspend between that call and
+ // when the service actually starts.
+ wl.acquire();
+ try {
for (int ic = 0; ic < numControllers; ic++) {
- controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ controllers.get(ic).prepareForExecutionLocked(jobStatus);
}
- } else {
- mRunningJobs.add(jobStatus);
- mWorkCountTracker.onJobStarted(workType);
- packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
- mActivePkgStats.add(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), packageStats);
- }
- final List<JobStatus> pendingJobs = mService.mPendingJobs;
- if (pendingJobs.remove(jobStatus)) {
- mService.mJobPackageTracker.noteNonpending(jobStatus);
+ final PackageStats packageStats = getPkgStatsLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
+ packageStats.adjustStagedCount(false, jobStatus.shouldTreatAsExpeditedJob());
+ if (!worker.executeRunnableJob(jobStatus, workType)) {
+ Slog.e(TAG, "Error executing " + jobStatus);
+ mWorkCountTracker.onStagedJobFailed(workType);
+ for (int ic = 0; ic < numControllers; ic++) {
+ controllers.get(ic).unprepareFromExecutionLocked(jobStatus);
+ }
+ } else {
+ mRunningJobs.add(jobStatus);
+ mWorkCountTracker.onJobStarted(workType);
+ packageStats.adjustRunningCount(true, jobStatus.shouldTreatAsExpeditedJob());
+ mActivePkgStats.add(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ packageStats);
+ }
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (pendingJobs.remove(jobStatus)) {
+ mService.mJobPackageTracker.noteNonpending(jobStatus);
+ }
+ } finally {
+ wl.release();
}
}
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 591e8ba859fc..26237c41f5dc 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -40,6 +40,7 @@ import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -50,6 +51,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
@@ -66,6 +68,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -106,6 +109,7 @@ import com.android.server.job.controllers.QuotaController;
import com.android.server.job.controllers.RestrictingController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
+import com.android.server.job.controllers.TareController;
import com.android.server.job.controllers.TimeController;
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;
@@ -143,6 +147,7 @@ import java.util.function.Predicate;
*
* Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
* Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
+ *
* @hide
*/
public class JobSchedulerService extends com.android.server.SystemService
@@ -201,7 +206,7 @@ public class JobSchedulerService extends com.android.server.SystemService
};
@VisibleForTesting
- public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
+ public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return SystemClock.elapsedRealtime();
@@ -226,6 +231,7 @@ public class JobSchedulerService extends com.android.server.SystemService
static final int MSG_UID_GONE = 5;
static final int MSG_UID_ACTIVE = 6;
static final int MSG_UID_IDLE = 7;
+ static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
/**
* Track Services that have currently active or pending jobs. The index is provided by
@@ -248,6 +254,8 @@ public class JobSchedulerService extends com.android.server.SystemService
private final DeviceIdleJobsController mDeviceIdleJobsController;
/** Needed to get remaining quota time. */
private final QuotaController mQuotaController;
+ /** Needed to get max execution time and expedited-job allowance. */
+ private final TareController mTareController;
/**
* List of restrictions.
* Note: do not add to or remove from this list at runtime except in the constructor, because we
@@ -315,7 +323,7 @@ public class JobSchedulerService extends com.android.server.SystemService
/**
* Which uids are currently performing backups, so we shouldn't allow their jobs to run.
*/
- final SparseIntArray mBackingUpUids = new SparseIntArray();
+ private final SparseBooleanArray mBackingUpUids = new SparseBooleanArray();
/**
* Cache of debuggable app status.
@@ -325,6 +333,10 @@ public class JobSchedulerService extends com.android.server.SystemService
/** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+ /** List of jobs whose controller state has changed since the last time we evaluated the job. */
+ @GuardedBy("mLock")
+ private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
+
/**
* Named indices into standby bucket arrays, for clarity in referring to
* specific buckets' bookkeeping.
@@ -338,15 +350,41 @@ public class JobSchedulerService extends com.android.server.SystemService
// (ScheduledJobStateChanged and JobStatusDumpProto).
public static final int RESTRICTED_INDEX = 5;
- private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener {
+ private class ConstantsObserver extends ContentObserver
+ implements DeviceConfig.OnPropertiesChangedListener {
+ private final ContentResolver mContentResolver;
+
+ ConstantsObserver(Handler handler, Context context) {
+ super(handler);
+ mContentResolver = context.getContentResolver();
+ }
+
public void start() {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
JobSchedulerBackgroundThread.getExecutor(), this);
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
// Load all the constants.
+ synchronized (mLock) {
+ mConstants.updateSettingsConstantsLocked(mContentResolver);
+ }
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
}
@Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ if (mConstants.updateSettingsConstantsLocked(mContentResolver)) {
+ for (int controller = 0; controller < mControllers.size(); controller++) {
+ final StateController sc = mControllers.get(controller);
+ sc.onConstantsUpdatedLocked();
+ }
+ onControllerStateChanged(null);
+ }
+ }
+ }
+
+ @Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
boolean apiQuotaScheduleUpdated = false;
boolean concurrencyUpdated = false;
@@ -476,6 +514,7 @@ public class JobSchedulerService extends com.android.server.SystemService
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;
+ private static final boolean DEFAULT_USE_TARE_POLICY = false;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -553,6 +592,11 @@ public class JobSchedulerService extends com.android.server.SystemService
*/
public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
+ /**
+ * If true, use TARE policy for job limiting. If false, use quotas.
+ */
+ public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY;
+
private void updateBatchingConstantsLocked() {
MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -601,7 +645,7 @@ public class JobSchedulerService extends com.android.server.SystemService
KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
+ KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
@@ -631,6 +675,17 @@ public class JobSchedulerService extends com.android.server.SystemService
DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
}
+ private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) {
+ boolean changed = false;
+ final boolean isTareEnabled = Settings.Global.getInt(contentResolver,
+ Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+ if (USE_TARE_POLICY != isTareEnabled) {
+ USE_TARE_POLICY = isTareEnabled;
+ changed = true;
+ }
+ return changed;
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println("Settings:");
pw.increaseIndent();
@@ -659,6 +714,8 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
.println();
+ pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println();
+
pw.decreaseIndent();
}
@@ -810,9 +867,10 @@ public class JobSchedulerService extends com.android.server.SystemService
try {
final int userId = UserHandle.getUserId(pkgUid);
IPackageManager pm = AppGlobals.getPackageManager();
- final int state = pm.getApplicationEnabledSetting(pkgName, userId);
+ final int state =
+ pm.getApplicationEnabledSetting(pkgName, userId);
if (state == COMPONENT_ENABLED_STATE_DISABLED
- || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
if (DEBUG) {
Slog.d(TAG, "Removing jobs for package " + pkgName
+ " in user " + userId);
@@ -828,7 +886,7 @@ public class JobSchedulerService extends com.android.server.SystemService
"app disabled");
}
}
- } catch (RemoteException|IllegalArgumentException e) {
+ } catch (RemoteException | IllegalArgumentException e) {
/*
* IllegalArgumentException means that the package doesn't exist.
* This arises when PACKAGE_CHANGED broadcast delivery has lagged
@@ -864,16 +922,15 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
} else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (DEBUG) {
- Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
}
synchronized (mLock) {
- mUidToPackageCache.remove(uidRemoved);
+ mUidToPackageCache.remove(pkgUid);
// There's no guarantee that the process has been stopped by the time we
// get here, but since this is generally a user-initiated action, it should
// be fine to just put USER instead of UNINSTALL or DISABLED.
- cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+ cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
for (int c = 0; c < mControllers.size(); ++c) {
@@ -982,8 +1039,18 @@ public class JobSchedulerService extends com.android.server.SystemService
return mConstants;
}
- public boolean isChainedAttributionEnabled() {
- return WorkSource.isChainedBatteryAttributionEnabled(getContext());
+ @NonNull
+ public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
+ if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+ WorkSource ws = new WorkSource();
+ ws.createWorkChain()
+ .addNode(sourceUid, sourcePackageName)
+ .addNode(Process.SYSTEM_UID, "JobScheduler");
+ return ws;
+ } else {
+ return sourcePackageName == null
+ ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName);
+ }
}
@Nullable
@@ -1114,9 +1181,12 @@ public class JobSchedulerService extends com.android.server.SystemService
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
// Return failure early if expedited job quota used up.
- if (jobStatus.isRequestedExpeditedJob()
- && !mQuotaController.isWithinEJQuotaLocked(jobStatus)) {
- return JobScheduler.RESULT_FAILURE;
+ if (jobStatus.isRequestedExpeditedJob()) {
+ if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus))
+ || (!mConstants.USE_TARE_POLICY
+ && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) {
+ return JobScheduler.RESULT_FAILURE;
+ }
}
// Give exemption if the source is in the foreground just now.
@@ -1131,7 +1201,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
Slog.w(TAG, "Too many jobs for uid " + uId);
throw new IllegalStateException("Apps may not schedule more than "
- + MAX_JOBS_PER_APP + " distinct jobs");
+ + MAX_JOBS_PER_APP + " distinct jobs");
}
}
@@ -1458,7 +1528,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mHandler = new JobHandler(context.getMainLooper());
mConstants = new Constants();
- mConstantsObserver = new ConstantsObserver();
+ mConstantsObserver = new ConstantsObserver(mHandler, context);
mJobSchedulerStub = new JobSchedulerStub();
mConcurrencyManager = new JobConcurrencyManager(this);
@@ -1503,6 +1573,9 @@ public class JobSchedulerService extends com.android.server.SystemService
new QuotaController(this, backgroundJobsController, connectivityController);
mControllers.add(mQuotaController);
mControllers.add(new ComponentController(this));
+ mTareController =
+ new TareController(this, backgroundJobsController, connectivityController);
+ mControllers.add(mTareController);
mRestrictiveControllers = new ArrayList<>();
mRestrictiveControllers.add(mBatteryController);
@@ -1667,6 +1740,7 @@ public class JobSchedulerService extends com.android.server.SystemService
/**
* Called when we want to remove a JobStatus object that we've finished executing.
+ *
* @return true if the job was removed.
*/
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
@@ -1677,7 +1751,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
if (removed && mReadyToRock) {
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
}
@@ -1698,17 +1772,29 @@ public class JobSchedulerService extends com.android.server.SystemService
return false;
}
+ /** Return {@code true} if the specified job is currently executing. */
+ @GuardedBy("mLock")
+ public boolean isCurrentlyRunningLocked(JobStatus job) {
+ return mConcurrencyManager.isJobRunningLocked(job);
+ }
+
+ private void noteJobPending(JobStatus job) {
+ mJobPackageTracker.notePending(job);
+ }
+
void noteJobsPending(List<JobStatus> jobs) {
for (int i = jobs.size() - 1; i >= 0; i--) {
- JobStatus job = jobs.get(i);
- mJobPackageTracker.notePending(job);
+ noteJobPending(jobs.get(i));
}
}
+ private void noteJobNonPending(JobStatus job) {
+ mJobPackageTracker.noteNonpending(job);
+ }
+
void noteJobsNonpending(List<JobStatus> jobs) {
for (int i = jobs.size() - 1; i >= 0; i--) {
- JobStatus job = jobs.get(i);
- mJobPackageTracker.noteNonpending(job);
+ noteJobNonPending(jobs.get(i));
}
}
@@ -1721,7 +1807,6 @@ public class JobSchedulerService extends com.android.server.SystemService
* @param failureToReschedule Provided job status that we will reschedule.
* @return A newly instantiated JobStatus with the same constraints as the last job except
* with adjusted timing constraints.
- *
* @see #maybeQueueReadyJobsForExecutionLocked
*/
@VisibleForTesting
@@ -1763,7 +1848,7 @@ public class JobSchedulerService extends com.android.server.SystemService
newJob.setOriginalLatestRunTimeElapsed(
failureToReschedule.getOriginalLatestRunTimeElapsed());
}
- for (int ic=0; ic<mControllers.size(); ic++) {
+ for (int ic = 0; ic < mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
}
@@ -1949,13 +2034,19 @@ public class JobSchedulerService extends com.android.server.SystemService
// StateChangedListener implementations.
/**
- * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
- * some controller's state has changed, so as to run through the list of jobs and start/stop
- * any that are eligible.
+ * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} to run
+ * through a list of jobs and start/stop any whose status has changed.
*/
@Override
- public void onControllerStateChanged() {
- mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
+ if (changedJobs == null) {
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ } else if (changedJobs.size() > 0) {
+ synchronized (mLock) {
+ mChangedJobList.addAll(changedJobs);
+ }
+ mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
+ }
}
@Override
@@ -1987,6 +2078,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobPackageTracker.notePending(js);
addOrderedItem(mPendingJobs, js, mPendingJobComparator);
}
+ mChangedJobList.remove(js);
} else {
Slog.e(TAG, "Given null job to check individually");
}
@@ -1995,7 +2087,6 @@ public class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "MSG_CHECK_JOB");
}
- removeMessages(MSG_CHECK_JOB);
if (mReportedActive) {
// if jobs are currently being run, queue all ready jobs for execution.
queueReadyJobsForExecutionLocked();
@@ -2010,6 +2101,12 @@ public class JobSchedulerService extends com.android.server.SystemService
}
queueReadyJobsForExecutionLocked();
break;
+ case MSG_CHECK_CHANGED_JOB_LIST:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CHECK_CHANGED_JOB_LIST");
+ }
+ checkChangedJobListLocked();
+ break;
case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
@@ -2097,11 +2194,8 @@ public class JobSchedulerService extends com.android.server.SystemService
continue;
}
if (!running.isReady()) {
- // If a restricted job doesn't have dynamic constraints satisfied, assume that's
- // the reason the job is being stopped, instead of because of other constraints
- // not being satisfied.
if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
- && !running.areDynamicConstraintsSatisfied()) {
+ && running.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
serviceContext.cancelExecutingJobLocked(
running.getStopReason(),
JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
@@ -2139,6 +2233,11 @@ public class JobSchedulerService extends com.android.server.SystemService
// 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);
+ // MSG_CHECK_CHANGED_JOB_LIST is a weaker form of _GREEDY. Since we're checking and queueing
+ // all ready jobs, we don't need to keep any MSG_CHECK_CHANGED_JOB_LIST messages in the
+ // queue.
+ mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+ mChangedJobList.clear();
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
@@ -2201,17 +2300,21 @@ public class JobSchedulerService extends com.android.server.SystemService
reset();
}
- // Functor method invoked for each job via JobStore.forEachJob()
@Override
public void accept(JobStatus job) {
- if (isReadyToBeExecutedLocked(job)) {
+ final boolean isRunning = isCurrentlyRunningLocked(job);
+ if (isReadyToBeExecutedLocked(job, false)) {
if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(),
job.getJob().getService().getPackageName())) {
Slog.w(TAG, "Aborting job " + job.getUid() + ":"
+ job.getJob().toString() + " -- package not allowed to start");
- mHandler.obtainMessage(MSG_STOP_JOB,
- JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
- .sendToTarget();
+ if (isRunning) {
+ mHandler.obtainMessage(MSG_STOP_JOB,
+ JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
+ .sendToTarget();
+ } else if (mPendingJobs.remove(job)) {
+ noteJobNonPending(job);
+ }
return;
}
@@ -2244,8 +2347,41 @@ public class JobSchedulerService extends com.android.server.SystemService
} else {
unbatchedCount++;
}
- runnableJobs.add(job);
+ if (!isRunning) {
+ runnableJobs.add(job);
+ }
} else {
+ if (isRunning) {
+ final int internalStopReason;
+ final String debugReason;
+ if (!job.isReady()) {
+ if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
+ && job.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
+ internalStopReason =
+ JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET;
+ debugReason = "cancelled due to restricted bucket";
+ } else {
+ internalStopReason =
+ JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED;
+ debugReason = "cancelled due to unsatisfied constraints";
+ }
+ } else {
+ final JobRestriction restriction = checkIfRestricted(job);
+ if (restriction != null) {
+ internalStopReason = restriction.getInternalReason();
+ debugReason = "restricted due to "
+ + JobParameters.getInternalReasonCodeDescription(
+ internalStopReason);
+ } else {
+ internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN;
+ debugReason = "couldn't figure out why the job should stop running";
+ }
+ }
+ stopJobOnServiceContextLocked(job, job.getStopReason(),
+ internalStopReason, debugReason);
+ } else if (mPendingJobs.remove(job)) {
+ noteJobNonPending(job);
+ }
evaluateControllerStatesLocked(job);
}
}
@@ -2281,10 +2417,16 @@ public class JobSchedulerService extends com.android.server.SystemService
runnableJobs.clear();
}
}
+
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
@GuardedBy("mLock")
private void maybeQueueReadyJobsForExecutionLocked() {
+ mHandler.removeMessages(MSG_CHECK_JOB);
+ // This method will evaluate all jobs, so we don't need to keep any messages for a suubset
+ // of jobs in the queue.
+ mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+ mChangedJobList.clear();
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
noteJobsNonpending(mPendingJobs);
@@ -2294,6 +2436,18 @@ public class JobSchedulerService extends com.android.server.SystemService
mMaybeQueueFunctor.postProcessLocked();
}
+ @GuardedBy("mLock")
+ private void checkChangedJobListLocked() {
+ mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
+ if (DEBUG) {
+ Slog.d(TAG, "Check changed jobs...");
+ }
+
+ mChangedJobList.forEach(mMaybeQueueFunctor);
+ mMaybeQueueFunctor.postProcessLocked();
+ mChangedJobList.clear();
+ }
+
/** Returns true if both the calling and source users for the job are started. */
@GuardedBy("mLock")
public boolean areUsersStartedLocked(final JobStatus job) {
@@ -2340,7 +2494,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobExists = mJobs.containsJob(job);
final boolean userStarted = areUsersStartedLocked(job);
- final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+ final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
@@ -2415,7 +2569,7 @@ public class JobSchedulerService extends com.android.server.SystemService
final boolean jobExists = mJobs.containsJob(job);
final boolean userStarted = areUsersStartedLocked(job);
- final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
+ final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
if (DEBUG) {
Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
@@ -2463,7 +2617,9 @@ public class JobSchedulerService extends com.android.server.SystemService
public long getMaxJobExecutionTimeMs(JobStatus job) {
synchronized (mLock) {
return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
- mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ mConstants.USE_TARE_POLICY
+ ? mTareController.getMaxJobExecutionTimeMsLocked(job)
+ : mQuotaController.getMaxJobExecutionTimeMsLocked(job));
}
}
@@ -2535,7 +2691,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// No need to actually do anything here, since for a full backup the
// activity manager will kill the process which will kill the job (and
// cause it to restart, but now it can't run).
- mBackingUpUids.put(uid, uid);
+ mBackingUpUids.put(uid, true);
}
}
@@ -2673,7 +2829,8 @@ public class JobSchedulerService extends com.android.server.SystemService
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub {
- /** Cache determination of whether a given app can persist jobs
+ /**
+ * Cache determination of whether a given app can persist jobs
* key is uid of the calling app; value is undetermined/true/false
*/
private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
@@ -2791,6 +2948,7 @@ public class JobSchedulerService extends com.android.server.SystemService
throw new NullPointerException("work is null");
}
+ work.enforceValidity();
validateJobFlags(job, uid);
final long ident = Binder.clearCallingIdentity();
@@ -2954,8 +3112,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public List<JobInfo> getStartedJobs() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getStartedJobs() is system internal use only.");
+ throw new SecurityException("getStartedJobs() is system internal use only.");
}
final ArrayList<JobInfo> runningJobs;
@@ -2983,8 +3140,7 @@ public class JobSchedulerService extends com.android.server.SystemService
public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
final int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(
- "getAllJobSnapshots() is system internal use only.");
+ throw new SecurityException("getAllJobSnapshots() is system internal use only.");
}
synchronized (mLock) {
final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
@@ -3046,7 +3202,7 @@ public class JobSchedulerService extends com.android.server.SystemService
synchronized (mLock) {
boolean foundSome = false;
- for (int i=0; i<mActiveServices.size(); i++) {
+ for (int i = 0; i < mActiveServices.size(); i++) {
final JobServiceContext jc = mActiveServices.get(i);
final JobStatus js = jc.getRunningJobLocked();
if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
@@ -3185,7 +3341,7 @@ public class JobSchedulerService extends com.android.server.SystemService
printed = true;
pw.println("source-user-stopped");
}
- if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
+ if (mBackingUpUids.get(js.getSourceUid())) {
if (printed) {
pw.print(" ");
}
@@ -3349,7 +3505,7 @@ public class JobSchedulerService extends com.android.server.SystemService
pw.print(" !active=");
pw.print(!mConcurrencyManager.isJobRunningLocked(job));
pw.print(" !backingup=");
- pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
+ pw.print(!(mBackingUpUids.get(job.getSourceUid())));
pw.print(" comp=");
pw.print(isComponentUsable(job));
pw.println(")");
@@ -3362,7 +3518,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
pw.decreaseIndent();
- for (int i=0; i<mControllers.size(); i++) {
+ for (int i = 0; i < mControllers.size(); i++) {
pw.println();
pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
pw.increaseIndent();
@@ -3371,7 +3527,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
boolean overridePrinted = false;
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
if (!overridePrinted) {
@@ -3438,7 +3594,7 @@ public class JobSchedulerService extends com.android.server.SystemService
boolean pendingPrinted = false;
pw.println("Pending queue:");
pw.increaseIndent();
- for (int i=0; i<mPendingJobs.size(); i++) {
+ for (int i = 0; i < mPendingJobs.size(); i++) {
JobStatus job = mPendingJobs.get(i);
if (!predicate.test(job)) {
continue;
@@ -3588,7 +3744,8 @@ public class JobSchedulerService extends com.android.server.SystemService
continue;
}
- job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
+ job.dump(proto,
+ JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
proto.write(
JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
@@ -3605,7 +3762,7 @@ public class JobSchedulerService extends com.android.server.SystemService
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
mConcurrencyManager.isJobRunningLocked(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
- mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
+ mBackingUpUids.get(job.getSourceUid()));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
isComponentUsable(job));
@@ -3626,7 +3783,7 @@ public class JobSchedulerService extends com.android.server.SystemService
controller.dumpControllerStateLocked(
proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
}
- for (int i=0; i< mUidPriorityOverride.size(); i++) {
+ for (int i = 0; i < mUidPriorityOverride.size(); i++) {
int uid = mUidPriorityOverride.keyAt(i);
if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
@@ -3665,8 +3822,8 @@ public class JobSchedulerService extends com.android.server.SystemService
if (job == null) {
final long ijToken = proto.start(ActiveJob.INACTIVE);
- proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
- nowElapsed - jsc.mStoppedTime);
+ proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
+ nowElapsed - jsc.mStoppedTime);
if (jsc.mStoppedReason != null) {
proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
jsc.mStoppedReason);
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 90baa8e54220..5bdee5e636a9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -42,7 +42,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.WorkSource;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -55,6 +54,9 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
+import com.android.server.tare.EconomicPolicy;
+import com.android.server.tare.EconomyManagerInternal;
+import com.android.server.tare.JobSchedulerEconomicPolicy;
/**
* Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
@@ -108,7 +110,9 @@ public final class JobServiceContext implements ServiceConnection {
private final Context mContext;
private final Object mLock;
private final IBatteryStats mBatteryStats;
+ private final EconomyManagerInternal mEconomyManagerInternal;
private final JobPackageTracker mJobPackageTracker;
+ private final PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
// Execution state.
@@ -211,10 +215,12 @@ public final class JobServiceContext implements ServiceConnection {
mLock = service.getLock();
mService = service;
mBatteryStats = batteryStats;
+ mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
mJobConcurrencyManager = concurrencyManager;
mCompletedListener = service;
+ mPowerManager = mContext.getSystemService(PowerManager.class);
mAvailable = true;
mVerb = VERB_FINISHED;
mPreferredUid = NO_PREFERRED_UID;
@@ -281,6 +287,17 @@ public final class JobServiceContext implements ServiceConnection {
// it was inflated from disk with not-yet-coherent delay/deadline bounds.
job.clearPersistedUtcTimes();
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, job.getTag());
+ mWakeLock.setWorkSource(
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mWakeLock.setReferenceCounted(false);
+ mWakeLock.acquire();
+
+ // Note the start when we try to bind so that the app is charged for some processing
+ // even if binding fails.
+ mEconomyManagerInternal.noteInstantaneousEvent(
+ job.getSourceUserId(), job.getSourcePackageName(),
+ getStartActionId(job), String.valueOf(job.getJobId()));
mVerb = VERB_BINDING;
scheduleOpTimeOutLocked();
final Intent intent = new Intent().setComponent(job.getServiceComponent());
@@ -316,6 +333,7 @@ public final class JobServiceContext implements ServiceConnection {
mRunningCallback = null;
mParams = null;
mExecutionStartTimeElapsed = 0L;
+ mWakeLock.release();
mVerb = VERB_FINISHED;
removeOpTimeOutLocked();
return false;
@@ -342,6 +360,9 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
+ mEconomyManagerInternal.noteOngoingEventStarted(
+ job.getSourceUserId(), job.getSourcePackageName(),
+ getRunningActionId(job), String.valueOf(job.getJobId()));
final String jobPackage = job.getSourcePackageName();
final int jobUserId = job.getSourceUserId();
UsageStatsManagerInternal usageStats =
@@ -355,6 +376,22 @@ public final class JobServiceContext implements ServiceConnection {
}
}
+ @EconomicPolicy.AppAction
+ private static int getStartActionId(@NonNull JobStatus job) {
+ if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START;
+ }
+ return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START;
+ }
+
+ @EconomicPolicy.AppAction
+ private static int getRunningActionId(@NonNull JobStatus job) {
+ if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) {
+ return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
+ }
+ return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
+ }
+
/**
* Used externally to query the running job. Will return null if there is no job running.
*/
@@ -511,42 +548,10 @@ public final class JobServiceContext implements ServiceConnection {
return;
}
this.service = IJobService.Stub.asInterface(service);
- final PowerManager pm =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- runningJob.getTag());
- wl.setWorkSource(deriveWorkSource(runningJob));
- wl.setReferenceCounted(false);
- wl.acquire();
-
- // We use a new wakelock instance per job. In rare cases there is a race between
- // teardown following job completion/cancellation and new job service spin-up
- // such that if we simply assign mWakeLock to be the new instance, we orphan
- // the currently-live lock instead of cleanly replacing it. Watch for this and
- // explicitly fast-forward the release if we're in that situation.
- if (mWakeLock != null) {
- Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
- + " tag=" + mWakeLock.getTag());
- mWakeLock.release();
- }
- mWakeLock = wl;
doServiceBoundLocked();
}
}
- private WorkSource deriveWorkSource(JobStatus runningJob) {
- final int jobUid = runningJob.getSourceUid();
- if (WorkSource.isChainedBatteryAttributionEnabled(mContext)) {
- WorkSource workSource = new WorkSource();
- workSource.createWorkChain()
- .addNode(jobUid, null)
- .addNode(android.os.Process.SYSTEM_UID, "JobScheduler");
- return workSource;
- } else {
- return new WorkSource(jobUid);
- }
- }
-
/** If the client service crashes we reschedule this job and clean up. */
@Override
public void onServiceDisconnected(ComponentName name) {
@@ -555,6 +560,42 @@ public final class JobServiceContext implements ServiceConnection {
}
}
+ @Override
+ public void onBindingDied(ComponentName name) {
+ synchronized (mLock) {
+ if (mRunningJob == null) {
+ Slog.e(TAG, "Binding died for " + name.getPackageName()
+ + " but no running job on this context");
+ } else if (mRunningJob.getServiceComponent().equals(name)) {
+ Slog.e(TAG, "Binding died for "
+ + mRunningJob.getSourceUserId() + ":" + name.getPackageName());
+ } else {
+ Slog.e(TAG, "Binding died for " + name.getPackageName()
+ + " but context is running a different job");
+ }
+ closeAndCleanupJobLocked(true /* needsReschedule */, "binding died");
+ }
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ synchronized (mLock) {
+ if (mRunningJob == null) {
+ Slog.wtf(TAG, "Got null binding for " + name.getPackageName()
+ + " but no running job on this context");
+ } else if (mRunningJob.getServiceComponent().equals(name)) {
+ Slog.wtf(TAG, "Got null binding for "
+ + mRunningJob.getSourceUserId() + ":" + name.getPackageName());
+ } else {
+ Slog.wtf(TAG, "Got null binding for " + name.getPackageName()
+ + " but context is running a different job");
+ }
+ // Don't reschedule the job since returning a null binding is an explicit choice by the
+ // app which breaks things.
+ closeAndCleanupJobLocked(false /* needsReschedule */, "null binding");
+ }
+ }
+
/**
* This class is reused across different clients, and passes itself in as a callback. Check
* whether the client exercising the callback is the client we expect.
@@ -970,6 +1011,15 @@ public final class JobServiceContext implements ServiceConnection {
} catch (RemoteException e) {
// Whatever.
}
+ mEconomyManagerInternal.noteOngoingEventStopped(
+ mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
+ getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId()));
+ if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) {
+ mEconomyManagerInternal.noteInstantaneousEvent(
+ mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
+ JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
+ String.valueOf(mRunningJob.getJobId()));
+ }
if (mWakeLock != null) {
mWakeLock.release();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 7a2840709d15..d1afc8093f40 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -41,13 +41,13 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SystemConfigFileCommitEventLogger;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.BitUtils;
-import com.android.internal.util.FastXmlSerializer;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
@@ -57,13 +57,12 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-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.nio.charset.StandardCharsets;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -133,7 +132,7 @@ public final class JobStore {
@VisibleForTesting
public static JobStore initAndGetForTesting(Context context, File dataDir) {
JobStore jobStoreUnderTest = new JobStore(context, new Object(), dataDir);
- jobStoreUnderTest.clear();
+ jobStoreUnderTest.clearForTesting();
return jobStoreUnderTest;
}
@@ -222,6 +221,14 @@ public final class JobStore {
return replaced;
}
+ /**
+ * The same as above but does not schedule writing. This makes perf benchmarks more stable.
+ */
+ @VisibleForTesting
+ public void addForTesting(JobStatus jobStatus) {
+ mJobSet.add(jobStatus);
+ }
+
boolean containsJob(JobStatus jobStatus) {
return mJobSet.contains(jobStatus);
}
@@ -273,6 +280,14 @@ public final class JobStore {
}
/**
+ * The same as above but does not schedule writing. This makes perf benchmarks more stable.
+ */
+ @VisibleForTesting
+ public void clearForTesting() {
+ mJobSet.clear();
+ }
+
+ /**
* @param userHandle User for whom we are querying the list of jobs.
* @return A list of all the jobs scheduled for the provided user. Never null.
*/
@@ -469,11 +484,9 @@ public final class JobStore {
int numJobs = 0;
int numSystemJobs = 0;
int numSyncJobs = 0;
- try {
- mEventLogger.setStartTime(SystemClock.uptimeMillis());
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(baos, StandardCharsets.UTF_8.name());
+ mEventLogger.setStartTime(SystemClock.uptimeMillis());
+ try (FileOutputStream fos = mJobsFile.startWrite()) {
+ TypedXmlSerializer out = Xml.resolveSerializer(fos);
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -502,9 +515,6 @@ public final class JobStore {
out.endTag(null, "job-info");
out.endDocument();
- // Write out to disk in one fell swoop.
- FileOutputStream fos = mJobsFile.startWrite();
- fos.write(baos.toByteArray());
mJobsFile.finishWrite(fos);
} catch (IOException e) {
if (DEBUG) {
@@ -703,9 +713,8 @@ public final class JobStore {
int numJobs = 0;
int numSystemJobs = 0;
int numSyncJobs = 0;
- try {
- List<JobStatus> jobs;
- FileInputStream fis = mJobsFile.openRead();
+ List<JobStatus> jobs;
+ try (FileInputStream fis = mJobsFile.openRead()) {
synchronized (mLock) {
jobs = readJobMapImpl(fis, rtcGood);
if (jobs != null) {
@@ -726,7 +735,6 @@ public final class JobStore {
}
}
}
- fis.close();
} catch (FileNotFoundException e) {
if (DEBUG) {
Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
@@ -743,10 +751,9 @@ public final class JobStore {
Slog.i(TAG, "Read " + numJobs + " jobs");
}
- private List<JobStatus> readJobMapImpl(FileInputStream fis, boolean rtcIsGood)
+ private List<JobStatus> readJobMapImpl(InputStream fis, boolean rtcIsGood)
throws XmlPullParserException, IOException {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
+ XmlPullParser parser = Xml.resolvePullParser(fis);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.START_TAG &&
diff --git a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
index cb3c43714111..1068bda975fb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java
@@ -17,6 +17,8 @@
package com.android.server.job;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
import com.android.server.job.controllers.JobStatus;
@@ -29,10 +31,10 @@ import java.util.List;
*/
public interface StateChangedListener {
/**
- * Called by the controller to notify the JobManager that it should check on the state of a
- * task.
+ * Called by the controller to notify the JobScheduler that it should check on the state of a
+ * set of jobs. If {@code changedJobs} is null, then all registered jobs will be evaluated.
*/
- public void onControllerStateChanged();
+ void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs);
/**
* Called by the controller to notify the JobManager that regardless of the state of the task,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 31a0853746e2..0ceab352c52f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -21,6 +21,7 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -191,8 +192,8 @@ public final class BackgroundJobsController extends StateController {
));
}
- if (mUpdateJobFunctor.mChanged) {
- mStateChangedListener.onControllerStateChanged();
+ if (mUpdateJobFunctor.mChangedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(mUpdateJobFunctor.mChangedJobs);
}
}
@@ -222,7 +223,7 @@ public final class BackgroundJobsController extends StateController {
private final class UpdateJobFunctor implements Consumer<JobStatus> {
int mActiveState;
- boolean mChanged = false;
+ final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
int mTotalCount = 0;
int mCheckedCount = 0;
long mUpdateTimeElapsed = 0;
@@ -230,7 +231,7 @@ public final class BackgroundJobsController extends StateController {
void prepare(int newActiveState) {
mActiveState = newActiveState;
mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
- mChanged = false;
+ mChangedJobs.clear();
mTotalCount = 0;
mCheckedCount = 0;
}
@@ -240,7 +241,7 @@ public final class BackgroundJobsController extends StateController {
mTotalCount++;
mCheckedCount++;
if (updateSingleJobRestrictionLocked(jobStatus, mUpdateTimeElapsed, mActiveState)) {
- mChanged = true;
+ mChangedJobs.add(jobStatus);
}
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
index 6fd094844cd6..657f4700bd5c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java
@@ -103,14 +103,8 @@ public final class BatteryController extends RestrictingController {
boolean reportChange = false;
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
- boolean previous = ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
- if (previous != stablePower) {
- reportChange = true;
- }
- previous = ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
- if (previous != batteryNotLow) {
- reportChange = true;
- }
+ reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
+ reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
}
if (stablePower || batteryNotLow) {
// If one of our conditions has been satisfied, always schedule any newly ready jobs.
@@ -118,7 +112,7 @@ public final class BatteryController extends RestrictingController {
} else if (reportChange) {
// Otherwise, just let the job scheduler know the state has changed and take care of it
// as it thinks is best.
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(mTrackedTasks);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 12d9c7f52fb5..98a39a65ae6c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -28,6 +28,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -220,24 +221,27 @@ public class ComponentController extends StateController {
private void updateComponentStatesLocked(@NonNull Predicate<JobStatus> filter) {
mComponentStateUpdateFunctor.reset();
mService.getJobStore().forEachJob(filter, mComponentStateUpdateFunctor);
- if (mComponentStateUpdateFunctor.mChanged) {
- mStateChangedListener.onControllerStateChanged();
+ if (mComponentStateUpdateFunctor.mChangedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(
+ mComponentStateUpdateFunctor.mChangedJobs);
}
}
final class ComponentStateUpdateFunctor implements Consumer<JobStatus> {
@GuardedBy("mLock")
- boolean mChanged;
+ final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
@Override
@GuardedBy("mLock")
public void accept(JobStatus jobStatus) {
- mChanged |= updateComponentEnabledStateLocked(jobStatus);
+ if (updateComponentEnabledStateLocked(jobStatus)) {
+ mChangedJobs.add(jobStatus);
+ }
}
@GuardedBy("mLock")
private void reset() {
- mChanged = false;
+ mChangedJobs.clear();
}
}
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 e8065aa535f3..607ed3cd6a35 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
@@ -549,6 +549,47 @@ 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 minimumChunkBytes = jobStatus.getMinimumNetworkChunkBytes();
+ if (minimumChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ final long bandwidthDown = capabilities.getLinkDownstreamBandwidthKbps();
+ // If we don't know the bandwidth, all we can do is hope the job finishes the minimum
+ // chunk in time.
+ if (bandwidthDown > 0) {
+ // Divide by 8 to convert bits to bytes.
+ final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+ / (DataUnit.KIBIBYTES.toBytes(bandwidthDown) / 8));
+ if (estimatedMillis > maxJobExecutionTimeMs) {
+ // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+ Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over "
+ + bandwidthDown + " kbps network would take "
+ + estimatedMillis + "ms and job has "
+ + maxJobExecutionTimeMs + "ms to run; that's insane!");
+ return true;
+ }
+ }
+ final long bandwidthUp = capabilities.getLinkUpstreamBandwidthKbps();
+ // If we don't know the bandwidth, all we can do is hope the job finishes in time.
+ if (bandwidthUp > 0) {
+ // Divide by 8 to convert bits to bytes.
+ final long estimatedMillis = ((minimumChunkBytes * DateUtils.SECOND_IN_MILLIS)
+ / (DataUnit.KIBIBYTES.toBytes(bandwidthUp) / 8));
+ if (estimatedMillis > maxJobExecutionTimeMs) {
+ // If we'd never finish the minimum chunk before the timeout, we'd be insane!
+ Slog.w(TAG, "Minimum chunk " + minimumChunkBytes + " bytes over " + bandwidthUp
+ + " kbps network would take " + estimatedMillis + "ms and job has "
+ + maxJobExecutionTimeMs + "ms to run; that's insane!");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Minimum chunk size isn't defined. Check using the estimated upload/download sizes.
+
if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
&& mChargingTracker.isCharging()) {
// We're charging and on an unmetered network. We don't have to be as conservative about
@@ -557,9 +598,6 @@ public final class ConnectivityController extends RestrictingController implemen
return false;
}
- // 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();
if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
@@ -623,7 +661,8 @@ public final class ConnectivityController extends RestrictingController implemen
NetworkCapabilities capabilities, Constants constants) {
// A restricted job that's out of quota MUST use an unmetered network.
if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
- && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
+ && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)
+ || !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_TARE_WEALTH))) {
final NetworkCapabilities.Builder builder =
copyCapabilities(jobStatus.getJob().getRequiredNetwork());
builder.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -633,23 +672,34 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
- private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
+ private boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
NetworkCapabilities capabilities, Constants constants) {
// Only consider doing this for unrestricted prefetching jobs
if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
return false;
}
+ final long estDownloadBytes = jobStatus.getEstimatedNetworkDownloadBytes();
+ if (estDownloadBytes <= 0) {
+ // Need to at least know the estimated download bytes for a prefetch job.
+ return false;
+ }
// See if we match after relaxing any unmetered request
final NetworkCapabilities.Builder builder =
copyCapabilities(jobStatus.getJob().getRequiredNetwork());
builder.removeCapability(NET_CAPABILITY_NOT_METERED);
- if (builder.build().satisfiedByNetworkCapabilities(capabilities)) {
- // TODO: treat this as "maybe" response; need to check quotas
- return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
- } else {
- return false;
+ if (builder.build().satisfiedByNetworkCapabilities(capabilities)
+ && jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC) {
+ final long opportunisticQuotaBytes =
+ mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ network, NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS);
+ final long estUploadBytes = jobStatus.getEstimatedNetworkUploadBytes();
+ final long estimatedBytes = estDownloadBytes
+ + (estUploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN ? 0 : estUploadBytes);
+ return opportunisticQuotaBytes >= estimatedBytes;
}
+
+ return false;
}
@VisibleForTesting
@@ -796,12 +846,14 @@ public final class ConnectivityController extends RestrictingController implemen
mSortedStats.sort(mUidStatsComparator);
- boolean changed = false;
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
// Iterate in reverse order to remove existing callbacks before adding new ones.
for (int i = mSortedStats.size() - 1; i >= 0; --i) {
UidStats us = mSortedStats.get(i);
if (i >= MAX_NETWORK_CALLBACKS) {
- changed |= unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed);
+ if (unregisterDefaultNetworkCallbackLocked(us.uid, nowElapsed)) {
+ changedJobs.addAll(mTrackedJobs.get(us.uid));
+ }
} else {
UidDefaultNetworkCallback defaultNetworkCallback =
mCurrentDefaultNetworkCallbacks.get(us.uid);
@@ -818,8 +870,8 @@ public final class ConnectivityController extends RestrictingController implemen
}
}
}
- if (changed) {
- mStateChangedListener.onControllerStateChanged();
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
@@ -942,16 +994,23 @@ public final class ConnectivityController extends RestrictingController implemen
*/
@GuardedBy("mLock")
private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
- boolean changed = false;
+ final ArraySet<JobStatus> changedJobs;
if (filterUid == -1) {
+ changedJobs = new ArraySet<>();
for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
- changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork);
+ if (updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork)) {
+ changedJobs.addAll(mTrackedJobs.valueAt(i));
+ }
}
} else {
- changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork);
+ if (updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork)) {
+ changedJobs = mTrackedJobs.get(filterUid);
+ } else {
+ changedJobs = null;
+ }
}
- if (changed) {
- mStateChangedListener.onControllerStateChanged();
+ if (changedJobs != null && changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
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 e64233ff4187..83a756cf1e11 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
@@ -331,7 +331,9 @@ public final class ContentObserverController extends StateController {
// Let the scheduler know that state has changed. This may or may not result in an
// execution.
if (reportChange) {
- mStateChangedListener.onControllerStateChanged();
+ ArraySet<JobStatus> changedJob = new ArraySet<>();
+ changedJob.add(mJobStatus);
+ mStateChangedListener.onControllerStateChanged(changedJob);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 79ef321eaf07..090260cb8eb3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -104,14 +104,15 @@ public final class DeviceIdleJobsController extends StateController {
Slog.d(TAG, "Got temp whitelist "
+ Arrays.toString(mPowerSaveTempWhitelistAppIds));
}
- boolean changed = false;
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
final long nowElapsed = sElapsedRealtimeClock.millis();
for (int i = 0; i < mAllowInIdleJobs.size(); i++) {
- changed |=
- updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed);
+ if (updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed)) {
+ changedJobs.add(mAllowInIdleJobs.valueAt(i));
+ }
}
- if (changed) {
- mStateChangedListener.onControllerStateChanged();
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
break;
@@ -184,8 +185,8 @@ public final class DeviceIdleJobsController extends StateController {
mForegroundUids.put(uid, active);
mDeviceIdleUpdateFunctor.prepare();
mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor);
- if (mDeviceIdleUpdateFunctor.mChanged) {
- mStateChangedListener.onControllerStateChanged();
+ if (mDeviceIdleUpdateFunctor.mChangedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(mDeviceIdleUpdateFunctor.mChangedJobs);
}
}
@@ -283,17 +284,19 @@ public final class DeviceIdleJobsController extends StateController {
}
final class DeviceIdleUpdateFunctor implements Consumer<JobStatus> {
- boolean mChanged;
+ final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
long mUpdateTimeElapsed = 0;
void prepare() {
- mChanged = false;
+ mChangedJobs.clear();
mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
}
@Override
public void accept(JobStatus jobStatus) {
- mChanged |= updateTaskStateLocked(jobStatus, mUpdateTimeElapsed);
+ if (updateTaskStateLocked(jobStatus, mUpdateTimeElapsed)) {
+ mChangedJobs.add(jobStatus);
+ }
}
}
@@ -310,8 +313,9 @@ public final class DeviceIdleJobsController extends StateController {
synchronized (mLock) {
mDeviceIdleUpdateFunctor.prepare();
mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
- if (mDeviceIdleUpdateFunctor.mChanged) {
- mStateChangedListener.onControllerStateChanged();
+ if (mDeviceIdleUpdateFunctor.mChangedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(
+ mDeviceIdleUpdateFunctor.mChangedJobs);
}
}
break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index e26a3c6962d5..a6fae2c28898 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -98,7 +98,7 @@ public final class IdleController extends RestrictingController implements Idlen
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle);
}
}
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(mTrackedTasks);
}
/**
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 95eb220cfeb4..3801885c9781 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
@@ -89,10 +89,10 @@ public final class JobStatus {
static final int CONSTRAINT_TIMING_DELAY = 1<<31;
static final int CONSTRAINT_DEADLINE = 1<<30;
static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
+ static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24; // Implicit constraint
- static final int CONSTRAINT_WITHIN_EXPEDITED_QUOTA = 1 << 23; // Implicit constraint
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
// The following set of dynamic constraints are for specific use cases (as explained in their
@@ -147,9 +147,9 @@ public final class JobStatus {
private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
| CONSTRAINT_DEADLINE
| CONSTRAINT_IDLE
+ | CONSTRAINT_TARE_WEALTH
| CONSTRAINT_TIMING_DELAY
- | CONSTRAINT_WITHIN_QUOTA
- | CONSTRAINT_WITHIN_EXPEDITED_QUOTA;
+ | CONSTRAINT_WITHIN_QUOTA;
// TODO(b/129954980)
private static final boolean STATS_LOG_ENABLED = false;
@@ -391,6 +391,16 @@ public final class JobStatus {
private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+ private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+
+ /**
+ * Whether or not this job is approved to be treated as expedited per quota policy.
+ */
+ private boolean mExpeditedQuotaApproved;
+ /**
+ * Whether or not this job is approved to be treated as expedited per TARE policy.
+ */
+ private boolean mExpeditedTareApproved;
/////// Booleans that track if a job is ready to run. They should be updated whenever dependent
/////// states change.
@@ -417,8 +427,8 @@ public final class JobStatus {
/** The job is within its quota based on its standby bucket. */
private boolean mReadyWithinQuota;
- /** The job is an expedited job with sufficient quota to run as an expedited job. */
- private boolean mReadyWithinExpeditedQuota;
+ /** The job has enough credits to run based on TARE. */
+ private boolean mReadyTareWealth;
/** The job's dynamic requirements have been satisfied. */
private boolean mReadyDynamicSatisfied;
@@ -531,7 +541,7 @@ public final class JobStatus {
mInternalFlags = internalFlags;
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
if (job.getRequiredNetwork() != null) {
// Later, when we check if a given network satisfies the required
@@ -664,7 +674,7 @@ public final class JobStatus {
sourcePackageName, sourceUserId, toShortString()));
}
pendingWork.add(work);
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
}
public JobWorkItem dequeueWorkLocked() {
@@ -677,7 +687,7 @@ public final class JobStatus {
executingWork.add(work);
work.bumpDeliveryCount();
}
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
return work;
}
return null;
@@ -736,7 +746,7 @@ public final class JobStatus {
pendingWork = null;
executingWork = null;
incomingJob.nextPendingWorkId = nextPendingWorkId;
- incomingJob.updateEstimatedNetworkBytesLocked();
+ incomingJob.updateNetworkBytesLocked();
} else {
// We are completely stopping the job... need to clean up work.
ungrantWorkList(pendingWork);
@@ -744,7 +754,7 @@ public final class JobStatus {
ungrantWorkList(executingWork);
executingWork = null;
}
- updateEstimatedNetworkBytesLocked();
+ updateNetworkBytesLocked();
}
public void prepareLocked() {
@@ -944,9 +954,10 @@ public final class JobStatus {
}
}
- private void updateEstimatedNetworkBytesLocked() {
+ private void updateNetworkBytesLocked() {
mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
+ mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
if (pendingWork != null) {
for (int i = 0; i < pendingWork.size(); i++) {
@@ -968,6 +979,12 @@ public final class JobStatus {
mTotalNetworkUploadBytes += uploadBytes;
}
}
+ final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
+ if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
+ mMinimumNetworkChunkBytes = chunkBytes;
+ } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes);
+ }
}
}
}
@@ -980,6 +997,10 @@ public final class JobStatus {
return mTotalNetworkUploadBytes;
}
+ public long getMinimumNetworkChunkBytes() {
+ return mMinimumNetworkChunkBytes;
+ }
+
/** Does this job have any sort of networking constraint? */
public boolean hasConnectivityConstraint() {
// No need to check mDynamicConstraints since connectivity will only be in that list if
@@ -1120,7 +1141,7 @@ public final class JobStatus {
* treated as an expedited job.
*/
public boolean shouldTreatAsExpeditedJob() {
- return mReadyWithinExpeditedQuota && isRequestedExpeditedJob();
+ return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob();
}
/**
@@ -1219,20 +1240,53 @@ public final class JobStatus {
}
/** @return true if the constraint was changed, false otherwise. */
- boolean setExpeditedJobQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
- if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, nowElapsed, state)) {
+ boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) {
+ if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) {
// The constraint was changed. Update the ready flag.
- mReadyWithinExpeditedQuota = state;
- // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
- // Making it also track requested-expedited jobs would add unnecessary hops since the
- // controller would then defer to canRunInDoze. Avoid the hops and just update
- // mReadyNotDozing directly.
- mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
+ mReadyTareWealth = state;
return true;
}
return false;
}
+ /**
+ * Sets whether or not this job is approved to be treated as an expedited job based on quota
+ * policy.
+ *
+ * @return true if the approval bit was changed, false otherwise.
+ */
+ boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) {
+ if (mExpeditedQuotaApproved == state) {
+ return false;
+ }
+ mExpeditedQuotaApproved = state;
+ updateExpeditedDependencies();
+ return true;
+ }
+
+ /**
+ * Sets whether or not this job is approved to be treated as an expedited job based on TARE
+ * policy.
+ *
+ * @return true if the approval bit was changed, false otherwise.
+ */
+ boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) {
+ if (mExpeditedTareApproved == state) {
+ return false;
+ }
+ mExpeditedTareApproved = state;
+ updateExpeditedDependencies();
+ return true;
+ }
+
+ private void updateExpeditedDependencies() {
+ // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
+ // Making it also track requested-expedited jobs would add unnecessary hops since the
+ // controller would then defer to canRunInDoze. Avoid the hops and just update
+ // mReadyNotDozing directly.
+ mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
+ }
+
/** @return true if the state was changed, false otherwise. */
boolean setUidActive(final boolean newActiveState) {
if (newActiveState != uidActive) {
@@ -1333,8 +1387,8 @@ public final class JobStatus {
case CONSTRAINT_DEVICE_NOT_DOZING:
return JobParameters.STOP_REASON_DEVICE_STATE;
+ case CONSTRAINT_TARE_WEALTH:
case CONSTRAINT_WITHIN_QUOTA:
- case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
return JobParameters.STOP_REASON_QUOTA;
// These should never be stop reasons since they can never go from true to false.
@@ -1351,6 +1405,10 @@ public final class JobStatus {
return (satisfiedConstraints&constraint) != 0;
}
+ boolean isExpeditedQuotaApproved() {
+ return mExpeditedQuotaApproved;
+ }
+
boolean clearTrackingController(int which) {
if ((trackingControllers&which) != 0) {
trackingControllers &= ~which;
@@ -1382,6 +1440,11 @@ public final class JobStatus {
Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
constraints &= ~CONSTRAINT_WITHIN_QUOTA;
}
+ if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
+ // Quota should never be used as a dynamic constraint.
+ Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint");
+ constraints &= ~CONSTRAINT_TARE_WEALTH;
+ }
// Connectivity and content trigger are special since they're only valid to add if the
// job has requested network or specific content URIs. Adding these constraints to jobs
@@ -1449,14 +1512,14 @@ public final class JobStatus {
oldValue = mReadyNotDozing;
mReadyNotDozing = value;
break;
+ case CONSTRAINT_TARE_WEALTH:
+ oldValue = mReadyTareWealth;
+ mReadyTareWealth = value;
+ break;
case CONSTRAINT_WITHIN_QUOTA:
oldValue = mReadyWithinQuota;
mReadyWithinQuota = value;
break;
- case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
- oldValue = mReadyWithinExpeditedQuota;
- mReadyWithinExpeditedQuota = value;
- break;
default:
if (value) {
satisfied |= constraint;
@@ -1481,12 +1544,12 @@ public final class JobStatus {
case CONSTRAINT_DEVICE_NOT_DOZING:
mReadyNotDozing = oldValue;
break;
+ case CONSTRAINT_TARE_WEALTH:
+ mReadyTareWealth = oldValue;
+ break;
case CONSTRAINT_WITHIN_QUOTA:
mReadyWithinQuota = oldValue;
break;
- case CONSTRAINT_WITHIN_EXPEDITED_QUOTA:
- mReadyWithinExpeditedQuota = oldValue;
- break;
default:
mReadyDynamicSatisfied = mDynamicConstraints != 0
&& mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
@@ -1501,7 +1564,8 @@ public final class JobStatus {
// sessions (exempt from dynamic restrictions), we need the additional check to ensure
// that NEVER jobs don't run.
// TODO: cleanup quota and standby bucket management so we don't need the additional checks
- if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
+ if (((!mReadyWithinQuota || !mReadyTareWealth)
+ && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
|| getEffectiveStandbyBucket() == NEVER_INDEX) {
return false;
}
@@ -1711,12 +1775,12 @@ public final class JobStatus {
if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
pw.print(" BACKGROUND_NOT_RESTRICTED");
}
+ if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
+ pw.print(" TARE_WEALTH");
+ }
if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
pw.print(" WITHIN_QUOTA");
}
- if ((constraints & CONSTRAINT_WITHIN_EXPEDITED_QUOTA) != 0) {
- pw.print(" WITHIN_EXPEDITED_QUOTA");
- }
if (constraints != 0) {
pw.print(" [0x");
pw.print(Integer.toHexString(constraints));
@@ -1789,9 +1853,6 @@ public final class JobStatus {
if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
}
- if ((constraints & CONSTRAINT_WITHIN_EXPEDITED_QUOTA) != 0) {
- proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_EXPEDITED_JOB_QUOTA);
- }
}
private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
@@ -1942,6 +2003,10 @@ public final class JobStatus {
pw.print("Network upload bytes: ");
pw.println(mTotalNetworkUploadBytes);
}
+ if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
+ pw.print("Minimum network chunk bytes: ");
+ pw.println(mMinimumNetworkChunkBytes);
+ }
if (job.getMinLatencyMillis() != 0) {
pw.print("Minimum latency: ");
TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
@@ -1977,7 +2042,8 @@ public final class JobStatus {
pw.println();
pw.print("Unsatisfied constraints:");
dumpConstraints(pw,
- ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
+ ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH)
+ & ~satisfiedConstraints));
pw.println();
pw.println("Constraint history:");
@@ -2034,8 +2100,10 @@ public final class JobStatus {
pw.print("readyComponentEnabled: ");
pw.println(serviceInfo != null);
if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
- pw.print("readyWithinExpeditedQuota: ");
- pw.print(mReadyWithinExpeditedQuota);
+ pw.print("expeditedQuotaApproved: ");
+ pw.print(mExpeditedQuotaApproved);
+ pw.print(" expeditedTareApproved: ");
+ pw.print(mExpeditedTareApproved);
pw.print(" (started as EJ: ");
pw.print(startedAsExpeditedJob);
pw.println(")");
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 80e9e2d6157f..4ea46ebb752d 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
@@ -372,6 +372,9 @@ public final class QuotaController extends StateController {
private final BackgroundJobsController mBackgroundJobsController;
private final ConnectivityController mConnectivityController;
+ @GuardedBy("mLock")
+ private boolean mIsEnabled;
+
/** How much time each app will have to run jobs within their standby bucket window. */
private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
@@ -590,6 +593,7 @@ public final class QuotaController extends StateController {
mQcConstants = new QcConstants();
mBackgroundJobsController = backgroundJobsController;
mConnectivityController = connectivityController;
+ mIsEnabled = !mConstants.USE_TARE_POLICY;
// Set up the app standby bucketing tracker
AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
@@ -634,11 +638,12 @@ public final class QuotaController extends StateController {
jobs.add(jobStatus);
jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
- setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota);
+ final boolean isWithinEJQuota =
+ jobStatus.isRequestedExpeditedJob() && isWithinEJQuotaLocked(jobStatus);
+ setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota || isWithinEJQuota);
final boolean outOfEJQuota;
if (jobStatus.isRequestedExpeditedJob()) {
- final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
- setExpeditedConstraintSatisfied(jobStatus, nowElapsed, isWithinEJQuota);
+ setExpeditedQuotaApproved(jobStatus, nowElapsed, isWithinEJQuota);
outOfEJQuota = !isWithinEJQuota;
} else {
outOfEJQuota = false;
@@ -836,6 +841,9 @@ public final class QuotaController extends StateController {
/** @return true if the job is within expedited job quota. */
@GuardedBy("mLock")
public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return true;
+ }
if (isQuotaFreeLocked(jobStatus.getEffectiveStandbyBucket())) {
return true;
}
@@ -883,6 +891,9 @@ public final class QuotaController extends StateController {
@VisibleForTesting
boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return true;
+ }
final int standbyBucket = jobStatus.getEffectiveStandbyBucket();
// A job is within quota if one of the following is true:
// 1. it was started while the app was in the TOP state
@@ -909,6 +920,9 @@ public final class QuotaController extends StateController {
@GuardedBy("mLock")
boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
final int standbyBucket) {
+ if (!mIsEnabled) {
+ return true;
+ }
if (standbyBucket == NEVER_INDEX) return false;
if (isQuotaFreeLocked(standbyBucket)) return true;
@@ -1458,7 +1472,8 @@ public final class QuotaController extends StateController {
final ShrinkableDebits quota = getEJDebitsLocked(userId, packageName);
if (transactQuotaLocked(userId, packageName, nowElapsed, quota, credit)
&& maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener
+ .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
}
}
}
@@ -1578,17 +1593,19 @@ public final class QuotaController extends StateController {
}
private void maybeUpdateAllConstraintsLocked() {
- boolean changed = false;
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
final long nowElapsed = sElapsedRealtimeClock.millis();
for (int u = 0; u < mTrackedJobs.numMaps(); ++u) {
final int userId = mTrackedJobs.keyAt(u);
for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) {
final String packageName = mTrackedJobs.keyAt(u, p);
- changed |= maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName);
+ if (maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName)) {
+ changedJobs.addAll(mTrackedJobs.valueAt(u, p));
+ }
}
}
- if (changed) {
- mStateChangedListener.onControllerStateChanged();
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
@@ -1611,6 +1628,8 @@ public final class QuotaController extends StateController {
boolean changed = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
+ final boolean isWithinEJQuota =
+ js.isRequestedExpeditedJob() && isWithinEJQuotaLocked(js);
if (isTopStartedJobLocked(js)) {
// Job was started while the app was in the TOP state so we should allow it to
// finish.
@@ -1620,15 +1639,15 @@ public final class QuotaController extends StateController {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
// individually.
- changed |= setConstraintSatisfied(js, nowElapsed, realInQuota);
+ changed |= setConstraintSatisfied(js, nowElapsed, isWithinEJQuota || realInQuota);
} else {
// This job is somehow exempted. Need to determine its own quota status.
- changed |= setConstraintSatisfied(js, nowElapsed, isWithinQuotaLocked(js));
+ changed |= setConstraintSatisfied(js, nowElapsed,
+ isWithinEJQuota || isWithinQuotaLocked(js));
}
if (js.isRequestedExpeditedJob()) {
- boolean isWithinEJQuota = isWithinEJQuotaLocked(js);
- changed |= setExpeditedConstraintSatisfied(js, nowElapsed, isWithinEJQuota);
+ changed |= setExpeditedQuotaApproved(js, nowElapsed, isWithinEJQuota);
outOfEJQuota |= !isWithinEJQuota;
}
}
@@ -1646,31 +1665,34 @@ public final class QuotaController extends StateController {
private class UidConstraintUpdater implements Consumer<JobStatus> {
private final SparseArrayMap<String, Integer> mToScheduleStartAlarms =
new SparseArrayMap<>();
- public boolean wasJobChanged;
+ public final ArraySet<JobStatus> changedJobs = new ArraySet<>();
long mUpdateTimeElapsed = 0;
void prepare() {
mUpdateTimeElapsed = sElapsedRealtimeClock.millis();
+ changedJobs.clear();
}
@Override
public void accept(JobStatus jobStatus) {
- wasJobChanged |= setConstraintSatisfied(
- jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus));
- final boolean outOfEJQuota;
+ final boolean isWithinEJQuota;
if (jobStatus.isRequestedExpeditedJob()) {
- final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
- wasJobChanged |= setExpeditedConstraintSatisfied(
- jobStatus, mUpdateTimeElapsed, isWithinEJQuota);
- outOfEJQuota = !isWithinEJQuota;
+ isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
} else {
- outOfEJQuota = false;
+ isWithinEJQuota = false;
+ }
+ if (setConstraintSatisfied(jobStatus, mUpdateTimeElapsed,
+ isWithinEJQuota || isWithinQuotaLocked(jobStatus))) {
+ changedJobs.add(jobStatus);
+ }
+ if (setExpeditedQuotaApproved(jobStatus, mUpdateTimeElapsed, isWithinEJQuota)) {
+ changedJobs.add(jobStatus);
}
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
- if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && !outOfEJQuota) {
+ if (isWithinQuotaLocked(userId, packageName, realStandbyBucket) && isWithinEJQuota) {
// TODO(141645789): we probably shouldn't cancel the alarm until we've verified
// that all jobs for the userId-package are within quota.
mInQuotaAlarmListener.removeAlarmLocked(userId, packageName);
@@ -1691,21 +1713,21 @@ public final class QuotaController extends StateController {
}
void reset() {
- wasJobChanged = false;
mToScheduleStartAlarms.clear();
}
}
private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater();
- private boolean maybeUpdateConstraintForUidLocked(final int uid) {
+ @GuardedBy("mLock")
+ @NonNull
+ private ArraySet<JobStatus> maybeUpdateConstraintForUidLocked(final int uid) {
mUpdateUidConstraints.prepare();
mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints);
mUpdateUidConstraints.postProcess();
- boolean changed = mUpdateUidConstraints.wasJobChanged;
mUpdateUidConstraints.reset();
- return changed;
+ return mUpdateUidConstraints.changedJobs;
}
/**
@@ -1819,9 +1841,9 @@ public final class QuotaController extends StateController {
* If the satisfaction changes, this will tell connectivity & background jobs controller to
* also re-evaluate their state.
*/
- private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed,
+ private boolean setExpeditedQuotaApproved(@NonNull JobStatus jobStatus, long nowElapsed,
boolean isWithinQuota) {
- if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(nowElapsed, isWithinQuota)) {
+ if (jobStatus.setExpeditedJobQuotaApproved(nowElapsed, isWithinQuota)) {
mBackgroundJobsController.evaluateStateLocked(jobStatus);
mConnectivityController.evaluateStateLocked(jobStatus);
if (isWithinQuota && jobStatus.isReady()) {
@@ -2320,7 +2342,8 @@ public final class QuotaController extends StateController {
nowElapsed, debits, pendingReward)
&& maybeUpdateConstraintForPkgLocked(nowElapsed,
mPkg.userId, mPkg.packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(
+ mTrackedJobs.get(mPkg.userId, mPkg.packageName));
}
}
break;
@@ -2425,7 +2448,8 @@ public final class QuotaController extends StateController {
}
if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
userId, packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener
+ .onControllerStateChanged(mTrackedJobs.get(userId, packageName));
}
}
if (restrictedChanges.size() > 0) {
@@ -2459,8 +2483,9 @@ public final class QuotaController extends StateController {
t.onStateChangedLocked(nowElapsed, true);
}
}
- if (maybeUpdateConstraintForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
+ final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForUidLocked(uid);
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
}
@@ -2558,7 +2583,8 @@ public final class QuotaController extends StateController {
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
pkg.userId, pkg.packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(
+ mTrackedJobs.get(pkg.userId, pkg.packageName));
}
} else {
// This could potentially happen if an old session phases out while a
@@ -2586,7 +2612,8 @@ public final class QuotaController extends StateController {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
pkg.userId, pkg.packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(
+ mTrackedJobs.get(pkg.userId, pkg.packageName));
}
} else {
// This could potentially happen if an old session phases out while a
@@ -2618,7 +2645,8 @@ public final class QuotaController extends StateController {
}
if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(),
userId, packageName)) {
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(
+ mTrackedJobs.get(userId, packageName));
}
break;
}
@@ -2680,8 +2708,10 @@ public final class QuotaController extends StateController {
}
}
}
- if (maybeUpdateConstraintForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
+ final ArraySet<JobStatus> changedJobs =
+ maybeUpdateConstraintForUidLocked(uid);
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
break;
@@ -2760,8 +2790,10 @@ public final class QuotaController extends StateController {
t.onStateChangedLocked(nowElapsed, false);
}
}
- if (maybeUpdateConstraintForUidLocked(uid)) {
- mStateChangedListener.onControllerStateChanged();
+ final ArraySet<JobStatus> changedJobs =
+ maybeUpdateConstraintForUidLocked(uid);
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
}
}
@@ -2961,7 +2993,8 @@ public final class QuotaController extends StateController {
@Override
public void onConstantsUpdatedLocked() {
- if (mQcConstants.mShouldReevaluateConstraints) {
+ if (mQcConstants.mShouldReevaluateConstraints || mIsEnabled == mConstants.USE_TARE_POLICY) {
+ mIsEnabled = !mConstants.USE_TARE_POLICY;
// Update job bookkeeping out of band.
JobSchedulerBackgroundThread.getHandler().post(() -> {
synchronized (mLock) {
@@ -4101,6 +4134,7 @@ public final class QuotaController extends StateController {
@Override
public void dumpControllerStateLocked(final IndentingPrintWriter pw,
final Predicate<JobStatus> predicate) {
+ pw.println("Is enabled: " + mIsEnabled);
pw.println("Is charging: " + mChargeTracker.isChargingLocked());
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
pw.println();
@@ -4312,7 +4346,7 @@ public final class QuotaController extends StateController {
js.isRequestedExpeditedJob());
proto.write(
StateControllerProto.QuotaController.TrackedJob.IS_WITHIN_FG_JOB_QUOTA,
- js.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ js.isExpeditedQuotaApproved());
proto.end(jsToken);
}
});
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
index f0fc3b00a7bc..3fe8df24fa30 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java
@@ -160,8 +160,8 @@ public abstract class StateController {
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
- public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
- Predicate<JobStatus> predicate);
+ public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
+ Predicate<JobStatus> predicate) {}
/** Dump any internal constants the Controller may have. */
public void dumpConstants(IndentingPrintWriter pw) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
index 867891363912..1ce0a7f6b4c7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java
@@ -93,7 +93,7 @@ public final class StorageController extends StateController {
} else if (reportChange) {
// Let the scheduler know that state has changed. This may or may not result in an
// execution.
- mStateChangedListener.onControllerStateChanged();
+ mStateChangedListener.onControllerStateChanged(mTrackedTasks);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
new file mode 100644
index 000000000000..be3a3ee2921b
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.controllers;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.tare.EconomyManagerInternal;
+import com.android.server.tare.EconomyManagerInternal.ActionBill;
+import com.android.server.tare.JobSchedulerEconomicPolicy;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Controller that interfaces with Tare ({@link EconomyManagerInternal} and manages each job's
+ * ability to run per TARE policies.
+ *
+ * @see JobSchedulerEconomicPolicy
+ */
+public class TareController extends StateController {
+ private static final String TAG = "JobScheduler.TARE";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
+ * eligible to run unless it can pay for a job start and at least some period of execution time.
+ */
+ private static final ActionBill BILL_JOB_START_DEFAULT =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 30_000L)
+ ));
+
+ /**
+ * Bill to use when a default is currently running. We want to track and make sure the app can
+ * continue to pay for 1 more second of execution time. We stop the job when the app can no
+ * longer pay for that time.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_DEFAULT =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, 0, 1_000L)
+ ));
+
+ /**
+ * Bill to use while we're waiting to start a job. If a job isn't running yet, don't consider it
+ * eligible to run unless it can pay for a job start and at least some period of execution time.
+ */
+ private static final ActionBill BILL_JOB_START_EXPEDITED =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0),
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 30_000L)
+ ));
+
+ /**
+ * Bill to use when an EJ is currently running (as an EJ). We want to track and make sure the
+ * app can continue to pay for 1 more second of execution time. We stop the job when the app can
+ * no longer pay for that time.
+ */
+ private static final ActionBill BILL_JOB_RUNNING_EXPEDITED =
+ new ActionBill(List.of(
+ new EconomyManagerInternal.AnticipatedAction(
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1_000L)
+ ));
+
+ private final EconomyManagerInternal mEconomyManagerInternal;
+
+ private final BackgroundJobsController mBackgroundJobsController;
+ private final ConnectivityController mConnectivityController;
+
+ /**
+ * Local cache of the ability of each userId-pkg to afford the various bills we're tracking for
+ * them.
+ */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, ArrayMap<ActionBill, Boolean>> mAffordabilityCache =
+ new SparseArrayMap<>();
+
+ /**
+ * List of all tracked jobs. Out SparseArrayMap is userId-sourcePkg. The inner mapping is the
+ * anticipated actions and all the jobs that are applicable to them.
+ */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, ArrayMap<ActionBill, ArraySet<JobStatus>>>
+ mRegisteredBillsAndJobs = new SparseArrayMap<>();
+
+ private final EconomyManagerInternal.AffordabilityChangeListener mAffordabilityChangeListener =
+ (userId, pkgName, bill, canAfford) -> {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ if (DEBUG) {
+ Slog.d(TAG,
+ userId + ":" + pkgName + " affordability for " + getBillName(bill)
+ + " changed to " + canAfford);
+ }
+ synchronized (mLock) {
+ ArrayMap<ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, pkgName);
+ if (actionAffordability == null) {
+ actionAffordability = new ArrayMap<>();
+ mAffordabilityCache.add(userId, pkgName, actionAffordability);
+ }
+ actionAffordability.put(bill, canAfford);
+
+ final ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap != null) {
+ final ArraySet<JobStatus> jobs = billToJobMap.get(bill);
+ if (jobs != null) {
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
+ for (int i = 0; i < jobs.size(); ++i) {
+ final JobStatus job = jobs.valueAt(i);
+ // Use hasEnoughWealth if canAfford is false in case the job has
+ // other bills it can depend on (eg. EJs being demoted to
+ // regular jobs).
+ if (job.setTareWealthConstraintSatisfied(nowElapsed,
+ canAfford || hasEnoughWealthLocked(job))) {
+ changedJobs.add(job);
+ }
+ if (job.isRequestedExpeditedJob()
+ && setExpeditedTareApproved(job, nowElapsed,
+ canAffordExpeditedBillLocked(job))) {
+ changedJobs.add(job);
+ }
+ }
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
+ }
+ }
+ }
+ }
+ };
+
+ @GuardedBy("mLock")
+ private boolean mIsEnabled;
+
+ public TareController(JobSchedulerService service,
+ @NonNull BackgroundJobsController backgroundJobsController,
+ @NonNull ConnectivityController connectivityController) {
+ super(service);
+ mBackgroundJobsController = backgroundJobsController;
+ mConnectivityController = connectivityController;
+ mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
+ mIsEnabled = mConstants.USE_TARE_POLICY;
+ }
+
+ @Override
+ public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus));
+ setExpeditedTareApproved(jobStatus, nowElapsed,
+ jobStatus.isRequestedExpeditedJob() && canAffordExpeditedBillLocked(jobStatus));
+
+ final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
+ for (int i = 0; i < bills.size(); ++i) {
+ addJobToBillList(jobStatus, bills.valueAt(i));
+ }
+ }
+
+ @Override
+ public void prepareForExecutionLocked(JobStatus jobStatus) {
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap == null) {
+ Slog.e(TAG, "Job is being prepared but doesn't have a pre-existing billToJobMap");
+ } else {
+ for (int i = 0; i < billToJobMap.size(); ++i) {
+ removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
+ }
+ }
+ if (jobStatus.shouldTreatAsExpeditedJob()) {
+ addJobToBillList(jobStatus, BILL_JOB_RUNNING_EXPEDITED);
+ }
+ addJobToBillList(jobStatus, BILL_JOB_RUNNING_DEFAULT);
+ }
+
+ @Override
+ public void unprepareFromExecutionLocked(JobStatus jobStatus) {
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
+ ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap == null) {
+ Slog.e(TAG, "Job was just unprepared but didn't have a pre-existing billToJobMap");
+ } else {
+ for (int i = 0; i < billToJobMap.size(); ++i) {
+ removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
+ }
+ }
+ for (int i = 0; i < bills.size(); ++i) {
+ addJobToBillList(jobStatus, bills.valueAt(i));
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+ boolean forUpdate) {
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap != null) {
+ for (int i = 0; i < billToJobMap.size(); ++i) {
+ removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
+ }
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ public void onConstantsUpdatedLocked() {
+ if (mIsEnabled != mConstants.USE_TARE_POLICY) {
+ mIsEnabled = mConstants.USE_TARE_POLICY;
+ // Update job bookkeeping out of band.
+ JobSchedulerBackgroundThread.getHandler().post(() -> {
+ synchronized (mLock) {
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ mService.getJobStore().forEachJob((jobStatus) -> {
+ if (!mIsEnabled) {
+ jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true);
+ setExpeditedTareApproved(jobStatus, nowElapsed, true);
+ } else {
+ jobStatus.setTareWealthConstraintSatisfied(
+ nowElapsed, hasEnoughWealthLocked(jobStatus));
+ setExpeditedTareApproved(jobStatus, nowElapsed,
+ jobStatus.isRequestedExpeditedJob()
+ && canAffordExpeditedBillLocked(jobStatus));
+ }
+ });
+ }
+ });
+ }
+ }
+
+ @GuardedBy("mLock")
+ public boolean canScheduleEJ(@NonNull JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return true;
+ }
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED);
+ }
+
+ @GuardedBy("mLock")
+ public long getMaxJobExecutionTimeMsLocked(@NonNull JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
+ }
+ if (jobStatus.shouldTreatAsExpeditedJob()) {
+ return mEconomyManagerInternal.getMaxDurationMs(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ BILL_JOB_RUNNING_EXPEDITED);
+ }
+ return mEconomyManagerInternal.getMaxDurationMs(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
+ BILL_JOB_RUNNING_DEFAULT);
+ }
+
+ @GuardedBy("mLock")
+ private void addJobToBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap == null) {
+ billToJobMap = new ArrayMap<>();
+ mRegisteredBillsAndJobs.add(userId, pkgName, billToJobMap);
+ }
+ ArraySet<JobStatus> jobs = billToJobMap.get(bill);
+ if (jobs == null) {
+ jobs = new ArraySet<>();
+ billToJobMap.put(bill, jobs);
+ }
+ if (jobs.add(jobStatus)) {
+ mEconomyManagerInternal.registerAffordabilityChangeListener(userId, pkgName,
+ mAffordabilityChangeListener, bill);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void removeJobFromBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ final ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
+ mRegisteredBillsAndJobs.get(userId, pkgName);
+ if (billToJobMap != null) {
+ final ArraySet<JobStatus> jobs = billToJobMap.get(bill);
+ if (jobs == null || (jobs.remove(jobStatus) && jobs.size() == 0)) {
+ mEconomyManagerInternal.unregisterAffordabilityChangeListener(
+ userId, pkgName, mAffordabilityChangeListener, bill);
+ // Remove the cached value so we don't accidentally use it when the app
+ // schedules a new job.
+ final ArrayMap<ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, pkgName);
+ if (actionAffordability != null) {
+ actionAffordability.remove(bill);
+ }
+ }
+ }
+ }
+
+ @NonNull
+ private ArraySet<ActionBill> getPossibleStartBills(JobStatus jobStatus) {
+ // TODO: factor in network cost when available
+ final ArraySet<ActionBill> bills = new ArraySet<>();
+ if (jobStatus.isRequestedExpeditedJob()) {
+ bills.add(BILL_JOB_START_EXPEDITED);
+ }
+ bills.add(BILL_JOB_START_DEFAULT);
+ return bills;
+ }
+
+ @GuardedBy("mLock")
+ private boolean canAffordBillLocked(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
+ if (!mIsEnabled) {
+ return true;
+ }
+ final int userId = jobStatus.getSourceUserId();
+ final String pkgName = jobStatus.getSourcePackageName();
+ ArrayMap<ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, pkgName);
+ if (actionAffordability == null) {
+ actionAffordability = new ArrayMap<>();
+ mAffordabilityCache.add(userId, pkgName, actionAffordability);
+ }
+
+ if (actionAffordability.containsKey(bill)) {
+ return actionAffordability.get(bill);
+ }
+
+ final boolean canAfford = mEconomyManagerInternal.canPayFor(userId, pkgName, bill);
+ actionAffordability.put(bill, canAfford);
+ return canAfford;
+ }
+
+ @GuardedBy("mLock")
+ private boolean canAffordExpeditedBillLocked(@NonNull JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return true;
+ }
+ if (mService.isCurrentlyRunningLocked(jobStatus)) {
+ return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_EXPEDITED);
+ }
+
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED);
+ }
+
+ @GuardedBy("mLock")
+ private boolean hasEnoughWealthLocked(@NonNull JobStatus jobStatus) {
+ if (!mIsEnabled) {
+ return true;
+ }
+ if (mService.isCurrentlyRunningLocked(jobStatus)) {
+ if (jobStatus.isRequestedExpeditedJob()) {
+ return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_EXPEDITED)
+ || canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_DEFAULT);
+ }
+ return canAffordBillLocked(jobStatus, BILL_JOB_RUNNING_DEFAULT);
+ }
+
+ if (jobStatus.isRequestedExpeditedJob()
+ && canAffordBillLocked(jobStatus, BILL_JOB_START_EXPEDITED)) {
+ return true;
+ }
+
+ return canAffordBillLocked(jobStatus, BILL_JOB_START_DEFAULT);
+ }
+
+ /**
+ * If the satisfaction changes, this will tell connectivity & background jobs controller to
+ * also re-evaluate their state.
+ */
+ private boolean setExpeditedTareApproved(@NonNull JobStatus jobStatus, long nowElapsed,
+ boolean isApproved) {
+ if (jobStatus.setExpeditedJobTareApproved(nowElapsed, isApproved)) {
+ mBackgroundJobsController.evaluateStateLocked(jobStatus);
+ mConnectivityController.evaluateStateLocked(jobStatus);
+ if (isApproved && jobStatus.isReady()) {
+ mStateChangedListener.onRunJobNow(jobStatus);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @NonNull
+ private String getBillName(@NonNull ActionBill bill) {
+ if (bill.equals(BILL_JOB_START_EXPEDITED)) {
+ return "EJ_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_EXPEDITED)) {
+ return "EJ_RUNNING_BILL";
+ }
+ if (bill.equals(BILL_JOB_START_DEFAULT)) {
+ return "DEFAULT_START_BILL";
+ }
+ if (bill.equals(BILL_JOB_RUNNING_DEFAULT)) {
+ return "DEFAULT_RUNNING_BILL";
+ }
+ return "UNKNOWN_BILL (" + bill.toString() + ")";
+ }
+
+ @Override
+ public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) {
+ pw.print("Is enabled: ");
+ pw.println(mIsEnabled);
+
+ pw.println("Affordability cache:");
+ pw.increaseIndent();
+ mAffordabilityCache.forEach((userId, pkgName, billMap) -> {
+ final int numBills = billMap.size();
+ if (numBills > 0) {
+ pw.print(userId);
+ pw.print(":");
+ pw.print(pkgName);
+ pw.println(":");
+
+ pw.increaseIndent();
+ for (int i = 0; i < numBills; ++i) {
+ pw.print(getBillName(billMap.keyAt(i)));
+ pw.print(": ");
+ pw.println(billMap.valueAt(i));
+ }
+ pw.decreaseIndent();
+ }
+ });
+ pw.decreaseIndent();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index e8ebfb53fde8..b6361ce56569 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,12 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -62,8 +61,6 @@ public final class TimeController extends StateController {
private long mNextDelayExpiredElapsedMillis;
private volatile long mLastFiredDelayExpiredElapsedMillis;
- private final boolean mChainedAttributionEnabled;
-
private AlarmManager mAlarmService = null;
/** List of tracked jobs, sorted asc. by deadline */
private final List<JobStatus> mTrackedJobs = new LinkedList<>();
@@ -73,7 +70,6 @@ public final class TimeController extends StateController {
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
- mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
}
/**
@@ -117,7 +113,8 @@ public final class TimeController extends StateController {
it.add(job);
job.setTrackingController(JobStatus.TRACKING_TIME);
- WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
+ WorkSource ws =
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName());
// Only update alarms if the job would be ready with the relevant timing constraint
// satisfied.
@@ -165,7 +162,7 @@ public final class TimeController extends StateController {
} else if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) {
// This job's deadline is earlier than the current set alarm. Update the alarm.
setDeadlineExpiredAlarmLocked(job.getLatestRunTimeElapsed(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
if (job.hasTimingDelayConstraint()
@@ -177,7 +174,7 @@ public final class TimeController extends StateController {
&& wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
// This job's delay is earlier than the current set alarm. Update the alarm.
setDelayExpiredAlarmLocked(job.getEarliestRunTime(),
- deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
+ mService.deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()));
}
}
}
@@ -248,7 +245,7 @@ public final class TimeController extends StateController {
}
}
setDeadlineExpiredAlarmLocked(nextExpiryTime,
- deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
+ mService.deriveWorkSource(nextExpiryUid, nextExpiryPackageName));
}
}
@@ -276,7 +273,7 @@ public final class TimeController extends StateController {
long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
String nextDelayPackageName = null;
- boolean ready = false;
+ final ArraySet<JobStatus> changedJobs = new ArraySet<>();
Iterator<JobStatus> it = mTrackedJobs.iterator();
final long nowElapsedMillis = sElapsedRealtimeClock.millis();
while (it.hasNext()) {
@@ -288,9 +285,7 @@ public final class TimeController extends StateController {
if (canStopTrackingJobLocked(job)) {
it.remove();
}
- if (job.isReady()) {
- ready = true;
- }
+ changedJobs.add(job);
} else {
if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
if (DEBUG) {
@@ -308,23 +303,11 @@ public final class TimeController extends StateController {
}
}
}
- if (ready) {
- mStateChangedListener.onControllerStateChanged();
+ if (changedJobs.size() > 0) {
+ mStateChangedListener.onControllerStateChanged(changedJobs);
}
setDelayExpiredAlarmLocked(nextDelayTime,
- deriveWorkSource(nextDelayUid, nextDelayPackageName));
- }
- }
-
- private WorkSource deriveWorkSource(int uid, @Nullable String packageName) {
- if (mChainedAttributionEnabled) {
- WorkSource ws = new WorkSource();
- ws.createWorkChain()
- .addNode(uid, packageName)
- .addNode(Process.SYSTEM_UID, "JobScheduler");
- return ws;
- } else {
- return packageName == null ? new WorkSource(uid) : new WorkSource(uid, packageName);
+ mService.deriveWorkSource(nextDelayUid, nextDelayPackageName));
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
index 20df3ea2196b..51931792390c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -66,7 +66,8 @@ public abstract class JobRestriction {
public abstract void dumpConstants(IndentingPrintWriter pw);
/** Dump any internal constants the Restriction may have. */
- public abstract void dumpConstants(ProtoOutputStream proto);
+ public void dumpConstants(ProtoOutputStream proto) {
+ }
/** @return reason code for the Restriction. */
@JobParameters.StopReason
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 6d67ee37d8e6..a9b046507f3d 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
@@ -20,16 +20,21 @@ import android.app.job.JobParameters;
import android.os.PowerManager;
import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.IndentingPrintWriter;
-import android.util.proto.ProtoOutputStream;
import com.android.server.job.JobSchedulerService;
-import com.android.server.job.JobSchedulerServiceDumpProto;
import com.android.server.job.controllers.JobStatus;
public class ThermalStatusRestriction extends JobRestriction {
private static final String TAG = "ThermalStatusRestriction";
- private volatile boolean mIsThermalRestricted = false;
+ /** The threshold at which we start restricting non-EJ jobs. */
+ private static final int REGULAR_JOB_THRESHOLD = PowerManager.THERMAL_STATUS_SEVERE;
+ /** The lowest threshold at which we start restricting jobs. */
+ private static final int LOWER_THRESHOLD = REGULAR_JOB_THRESHOLD;
+ /** The threshold at which we start restricting ALL jobs. */
+ private static final int UPPER_THRESHOLD = PowerManager.THERMAL_STATUS_CRITICAL;
+
+ private volatile int mThermalStatus = PowerManager.THERMAL_STATUS_NONE;
private PowerManager mPowerManager;
@@ -47,29 +52,46 @@ public class ThermalStatusRestriction extends JobRestriction {
public void onThermalStatusChanged(int status) {
// This is called on the main thread. Do not do any slow operations in it.
// mService.onControllerStateChanged() will just post a message, which is okay.
- final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
- if (mIsThermalRestricted == shouldBeActive) {
- return;
+
+ // There are three buckets:
+ // 1. Below the lower threshold (we don't care about changes within this bucket)
+ // 2. Between the lower and upper thresholds.
+ // -> We care about transitions across buckets
+ // -> We care about transitions within the middle bucket
+ // 3. Upper the upper threshold (we don't care about changes within this bucket)
+ final boolean significantChange =
+ // Handle transitions within and into the bucket we care about (thus
+ // causing us to change our restrictions).
+ (status >= LOWER_THRESHOLD && status <= UPPER_THRESHOLD)
+ // Take care of transitions from the 2nd or 3rd bucket to the 1st
+ // bucket (thus exiting any restrictions we started enforcing).
+ || (mThermalStatus >= LOWER_THRESHOLD && status < LOWER_THRESHOLD)
+ // Take care of transitions from the 1st or 2nd bucket to the 3rd
+ // bucket (thus resulting in us beginning to enforce the tightest
+ // restrictions).
+ || (mThermalStatus < UPPER_THRESHOLD && status > UPPER_THRESHOLD);
+ mThermalStatus = status;
+ if (significantChange) {
+ mService.onControllerStateChanged(null);
}
- mIsThermalRestricted = shouldBeActive;
- mService.onControllerStateChanged();
}
});
}
@Override
public boolean isJobRestricted(JobStatus job) {
- return mIsThermalRestricted;
+ if (mThermalStatus >= UPPER_THRESHOLD) {
+ return true;
+ }
+ if (mThermalStatus >= REGULAR_JOB_THRESHOLD) {
+ return !job.shouldTreatAsExpeditedJob();
+ }
+ return false;
}
@Override
public void dumpConstants(IndentingPrintWriter pw) {
- pw.print("In thermal throttling?: ");
- pw.println(mIsThermalRestricted);
- }
-
- @Override
- public void dumpConstants(ProtoOutputStream proto) {
- proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
+ pw.print("Thermal status: ");
+ pw.println(mThermalStatus);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
new file mode 100644
index 000000000000..2ecc0ed798d2
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -0,0 +1,1317 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME;
+import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT;
+import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION;
+import static com.android.server.tare.EconomicPolicy.TYPE_ACTION;
+import static com.android.server.tare.EconomicPolicy.TYPE_REWARD;
+import static com.android.server.tare.EconomicPolicy.eventToString;
+import static com.android.server.tare.EconomicPolicy.getEventType;
+import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.usage.AppStandbyInternal;
+
+import libcore.util.EmptyArray;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.PriorityQueue;
+import java.util.function.Consumer;
+
+/**
+ * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with
+ * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or
+ * replenishing balances, and tracking ongoing events.
+ */
+class Agent {
+ private static final String TAG = "TARE-" + Agent.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * The minimum amount of time an app must not have been used by the user before we start
+ * regularly reclaiming ARCs from it.
+ */
+ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
+ /**
+ * The maximum amount of time we'll keep a transaction around for.
+ * For now, only keep transactions we actually have a use for. We can increase it if we want
+ * to use older transactions or provide older transactions to apps.
+ */
+ private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
+ /** The maximum number of transactions to dump per ledger. */
+ private static final int MAX_NUM_TRANSACTION_DUMP = 25;
+
+ private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*";
+ private static final String ALARM_TAG_LEDGER_CLEANUP = "*tare.ledger_cleanup*";
+
+ private final Object mLock;
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final Handler mHandler;
+ private final InternalResourceService mIrs;
+
+ private final AppStandbyInternal mAppStandbyInternal;
+
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, SparseArrayMap<String, OngoingEvent>>
+ mCurrentOngoingEvents = new SparseArrayMap<>();
+
+ /**
+ * Set of {@link ActionAffordabilityNote}s keyed by userId-pkgName.
+ *
+ * Note: it would be nice/better to sort by base price since that doesn't change and simply
+ * look at the change in the "insertion" of what would be affordable, but since CTP
+ * is factored into the final price, the sorting order (by modified price) could be different
+ * and that method wouldn't work >:(
+ */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, ArraySet<ActionAffordabilityNote>>
+ mActionAffordabilityNotes = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private long mCurrentNarcsInCirculation;
+
+ /**
+ * Listener to track and manage when we remove old transactions from ledgers.
+ */
+ @GuardedBy("mLock")
+ private final LedgerCleanupAlarmListener mLedgerCleanupAlarmListener =
+ new LedgerCleanupAlarmListener();
+
+ /**
+ * Listener to track and manage when apps will cross the closest affordability threshold (in
+ * both directions).
+ */
+ @GuardedBy("mLock")
+ private final BalanceThresholdAlarmListener mBalanceThresholdAlarmListener =
+ new BalanceThresholdAlarmListener();
+
+ private static final int MSG_CHECK_BALANCE = 0;
+ private static final int MSG_CLEAN_LEDGER = 1;
+ private static final int MSG_SET_ALARMS = 2;
+
+ Agent(@NonNull InternalResourceService irs,
+ @NonNull CompleteEconomicPolicy completeEconomicPolicy) {
+ mLock = irs.getLock();
+ mIrs = irs;
+ mCompleteEconomicPolicy = completeEconomicPolicy;
+ mHandler = new AgentHandler(TareHandlerThread.get().getLooper());
+ mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ private Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = mLedgers.get(userId, pkgName);
+ if (ledger == null) {
+ // TODO: load from disk
+ ledger = new Ledger();
+ mLedgers.add(userId, pkgName, ledger);
+ }
+ return ledger;
+ }
+
+ private class TotalDeltaCalculator implements Consumer<OngoingEvent> {
+ private Ledger mLedger;
+ private long mNowElapsed;
+ private long mNow;
+ private long mTotal;
+
+ void reset(@NonNull Ledger ledger, long nowElapsed, long now) {
+ mLedger = ledger;
+ mNowElapsed = nowElapsed;
+ mNow = now;
+ mTotal = 0;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ mTotal += getActualDeltaLocked(ongoingEvent, mLedger, mNowElapsed, mNow);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final TotalDeltaCalculator mTotalDeltaCalculator = new TotalDeltaCalculator();
+
+ /** Get an app's current balance, factoring in any currently ongoing events. */
+ @GuardedBy("mLock")
+ long getBalanceLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ long balance = ledger.getCurrentBalance();
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents != null) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = getCurrentTimeMillis();
+ mTotalDeltaCalculator.reset(ledger, nowElapsed, now);
+ ongoingEvents.forEach(mTotalDeltaCalculator);
+ balance += mTotalDeltaCalculator.mTotal;
+ }
+ return balance;
+ }
+
+ /** Returns the total amount of narcs currently allocated to apps. */
+ @GuardedBy("mLock")
+ long getCurrentCirculationLocked() {
+ return mCurrentNarcsInCirculation;
+ }
+
+ @GuardedBy("mLock")
+ void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
+ final int eventId, @Nullable String tag) {
+ if (mIrs.isSystem(userId, pkgName)) {
+ // Events are free for the system. Don't bother recording them.
+ return;
+ }
+
+ final long now = getCurrentTimeMillis();
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+
+ final int eventType = getEventType(eventId);
+ switch (eventType) {
+ case TYPE_ACTION:
+ final long actionCost =
+ mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, eventId, tag, -actionCost), true);
+ break;
+
+ case TYPE_REWARD:
+ final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+ if (reward != null) {
+ final long rewardSum = ledger.get24HourSum(eventId, now);
+ final long rewardVal = Math.max(0,
+ Math.min(reward.maxDailyReward - rewardSum, reward.instantReward));
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, eventId, tag, rewardVal), true);
+ }
+ break;
+
+ default:
+ Slog.w(TAG, "Unsupported event type: " + eventType);
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+
+ @GuardedBy("mLock")
+ void noteOngoingEventLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long startElapsed) {
+ noteOngoingEventLocked(userId, pkgName, eventId, tag, startElapsed, true);
+ }
+
+ @GuardedBy("mLock")
+ private void noteOngoingEventLocked(final int userId, @NonNull final String pkgName,
+ final int eventId, @Nullable String tag, final long startElapsed,
+ final boolean updateBalanceCheck) {
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ ongoingEvents = new SparseArrayMap<>();
+ mCurrentOngoingEvents.add(userId, pkgName, ongoingEvents);
+ }
+ OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+
+ final int eventType = getEventType(eventId);
+ switch (eventType) {
+ case TYPE_ACTION:
+ final long actionCost =
+ mCompleteEconomicPolicy.getCostOfAction(eventId, userId, pkgName);
+
+ if (ongoingEvent == null) {
+ ongoingEvents.add(eventId, tag,
+ new OngoingEvent(eventId, tag, null, startElapsed, -actionCost));
+ } else {
+ ongoingEvent.refCount++;
+ }
+ break;
+
+ case TYPE_REWARD:
+ final EconomicPolicy.Reward reward = mCompleteEconomicPolicy.getReward(eventId);
+ if (reward != null) {
+ if (ongoingEvent == null) {
+ ongoingEvents.add(eventId, tag, new OngoingEvent(
+ eventId, tag, reward, startElapsed, reward.ongoingRewardPerSecond));
+ } else {
+ ongoingEvent.refCount++;
+ }
+ }
+ break;
+
+ default:
+ Slog.w(TAG, "Unsupported event type: " + eventType);
+ }
+
+ if (updateBalanceCheck) {
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onDeviceStateChangedLocked() {
+ final long now = getCurrentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ mCurrentOngoingEvents.forEach((userId, pkgName, ongoingEvents) -> {
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ final boolean[] wasAffordable;
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ wasAffordable = new boolean[size];
+ for (int i = 0; i < size; ++i) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+ final long originalBalance =
+ getLedgerLocked(userId, pkgName).getCurrentBalance();
+ wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
+ }
+ } else {
+ wasAffordable = EmptyArray.BOOLEAN;
+ }
+ ongoingEvents.forEach((ongoingEvent) -> {
+ // Disable balance check & affordability notifications here because we're in the
+ // middle of updating ongoing action costs/prices and sending out notifications
+ // or rescheduling the balance check alarm would be a waste since we'll have to
+ // redo them again after all of our internal state is updated.
+ stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+ ongoingEvent.tag, nowElapsed, now, false, false);
+ noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ nowElapsed, false);
+ });
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ for (int i = 0; i < size; ++i) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+ note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+ final long newBalance = getLedgerLocked(userId, pkgName).getCurrentBalance();
+ final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+ if (wasAffordable[i] != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ });
+ }
+
+ @GuardedBy("mLock")
+ void onAppStatesChangedLocked(final int userId, @NonNull ArraySet<String> pkgNames) {
+ final long now = getCurrentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ for (int i = 0; i < pkgNames.size(); ++i) {
+ final String pkgName = pkgNames.valueAt(i);
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents != null) {
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ final boolean[] wasAffordable;
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ wasAffordable = new boolean[size];
+ for (int n = 0; n < size; ++n) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+ final long originalBalance =
+ getLedgerLocked(userId, pkgName).getCurrentBalance();
+ wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
+ }
+ } else {
+ wasAffordable = EmptyArray.BOOLEAN;
+ }
+ ongoingEvents.forEach((ongoingEvent) -> {
+ // Disable balance check & affordability notifications here because we're in the
+ // middle of updating ongoing action costs/prices and sending out notifications
+ // or rescheduling the balance check alarm would be a waste since we'll have to
+ // redo them again after all of our internal state is updated.
+ stopOngoingActionLocked(userId, pkgName, ongoingEvent.eventId,
+ ongoingEvent.tag, nowElapsed, now, false, false);
+ noteOngoingEventLocked(userId, pkgName, ongoingEvent.eventId, ongoingEvent.tag,
+ nowElapsed, false);
+ });
+ if (actionAffordabilityNotes != null) {
+ final int size = actionAffordabilityNotes.size();
+ for (int n = 0; n < size; ++n) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
+ note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+ final long newBalance =
+ getLedgerLocked(userId, pkgName).getCurrentBalance();
+ final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+ if (wasAffordable[n] != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void stopOngoingActionLocked(final int userId, @NonNull final String pkgName, final int eventId,
+ @Nullable String tag, final long nowElapsed, final long now) {
+ stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now, true, true);
+ }
+
+ /**
+ * @param updateBalanceCheck Whether or not to reschedule the affordability/balance
+ * check alarm.
+ * @param notifyOnAffordabilityChange Whether or not to evaluate the app's ability to afford
+ * registered bills and notify listeners about any changes.
+ */
+ @GuardedBy("mLock")
+ private void stopOngoingActionLocked(final int userId, @NonNull final String pkgName,
+ final int eventId, @Nullable String tag, final long nowElapsed, final long now,
+ final boolean updateBalanceCheck, final boolean notifyOnAffordabilityChange) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ // This may occur if TARE goes from disabled to enabled while an event is already
+ // occurring.
+ Slog.w(TAG, "No ongoing transactions for " + appToString(userId, pkgName));
+ return;
+ }
+ final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag);
+ if (ongoingEvent == null) {
+ // This may occur if TARE goes from disabled to enabled while an event is already
+ // occurring.
+ Slog.w(TAG, "Nonexistent ongoing transaction "
+ + eventToString(eventId) + (tag == null ? "" : ":" + tag)
+ + " for " + appToString(userId, pkgName) + " ended");
+ return;
+ }
+ ongoingEvent.refCount--;
+ if (ongoingEvent.refCount <= 0) {
+ if (!mIrs.isSystem(userId, pkgName)) {
+ final long startElapsed = ongoingEvent.startTimeElapsed;
+ final long startTime = now - (nowElapsed - startElapsed);
+ final long actualDelta =
+ getActualDeltaLocked(ongoingEvent, ledger, nowElapsed, now);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(startTime, now, eventId, tag, actualDelta),
+ notifyOnAffordabilityChange);
+ }
+ ongoingEvents.delete(eventId, tag);
+ }
+ if (updateBalanceCheck) {
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private long getActualDeltaLocked(@NonNull OngoingEvent ongoingEvent, @NonNull Ledger ledger,
+ long nowElapsed, long now) {
+ final long startElapsed = ongoingEvent.startTimeElapsed;
+ final long durationSecs = (nowElapsed - startElapsed) / 1000;
+ final long computedDelta = durationSecs * ongoingEvent.deltaPerSec;
+ if (ongoingEvent.reward == null) {
+ return computedDelta;
+ }
+ final long rewardSum = ledger.get24HourSum(ongoingEvent.eventId, now);
+ return Math.max(0,
+ Math.min(ongoingEvent.reward.maxDailyReward - rewardSum, computedDelta));
+ }
+
+ @GuardedBy("mLock")
+ private void recordTransactionLocked(final int userId, @NonNull final String pkgName,
+ @NonNull Ledger ledger, @NonNull Ledger.Transaction transaction,
+ final boolean notifyOnAffordabilityChange) {
+ if (transaction.delta == 0) {
+ // Skip recording transactions with a delta of 0 to save on space.
+ return;
+ }
+ if (mIrs.isSystem(userId, pkgName)) {
+ Slog.wtfStack(TAG,
+ "Tried to adjust system balance for " + appToString(userId, pkgName));
+ return;
+ }
+ final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
+ final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
+ if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
+ final long newDelta = maxCirculationAllowed - mCurrentNarcsInCirculation;
+ Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+ + eventToString(transaction.eventId)
+ + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for " + appToString(userId, pkgName)
+ + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.eventId, transaction.tag, newDelta);
+ }
+ final long originalBalance = ledger.getCurrentBalance();
+ if (transaction.delta > 0
+ && originalBalance + transaction.delta
+ > mCompleteEconomicPolicy.getMaxSatiatedBalance()) {
+ final long newDelta = mCompleteEconomicPolicy.getMaxSatiatedBalance() - originalBalance;
+ Slog.i(TAG, "Would result in becoming too rich. Decreasing transaction "
+ + eventToString(transaction.eventId)
+ + (transaction.tag == null ? "" : ":" + transaction.tag)
+ + " for " + appToString(userId, pkgName)
+ + " by " + (transaction.delta - newDelta));
+ transaction = new Ledger.Transaction(
+ transaction.startTimeMs, transaction.endTimeMs,
+ transaction.eventId, transaction.tag, newDelta);
+ }
+ ledger.recordTransaction(transaction);
+ mCurrentNarcsInCirculation += transaction.delta;
+ if (!mLedgerCleanupAlarmListener.hasAlarmScheduledLocked(userId, pkgName)) {
+ // The earliest transaction won't change until we clean up the ledger, so no point
+ // continuing to reschedule an existing cleanup.
+ final long cleanupAlarmElapsed = SystemClock.elapsedRealtime() + MAX_TRANSACTION_AGE_MS
+ - (getCurrentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
+ mLedgerCleanupAlarmListener.addAlarmLocked(userId, pkgName, cleanupAlarmElapsed);
+ }
+ // TODO: save changes to disk in a background thread
+ if (notifyOnAffordabilityChange) {
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ if (actionAffordabilityNotes != null) {
+ final long newBalance = ledger.getCurrentBalance();
+ for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+ final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
+ if (note.isCurrentlyAffordable() != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The
+ * reclamation will not reduce an app's balance below its minimum balance as dictated by the
+ * EconomicPolicy.
+ *
+ * @param percentage A value between 0 and 1 to indicate how much of the unused balance should
+ * be reclaimed.
+ */
+ @GuardedBy("mLock")
+ void reclaimUnusedAssetsLocked(double percentage) {
+ final List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final long now = getCurrentTimeMillis();
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
+ final String pkgName = pkgs.get(i).packageName;
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ // AppStandby only counts elapsed time for things like this
+ // TODO: should we use clock time instead?
+ final long timeSinceLastUsedMs =
+ mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId);
+ if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) {
+ // Use a constant floor instead of the scaled floor from the IRS.
+ final long minBalance =
+ mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName);
+ final long curBalance = ledger.getCurrentBalance();
+ long toReclaim = (long) (curBalance * percentage);
+ if (curBalance - toReclaim < minBalance) {
+ toReclaim = curBalance - minBalance;
+ }
+ if (toReclaim > 0) {
+ Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim
+ + " from " + appToString(userId, pkgName));
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(
+ now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim),
+ true);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void distributeBasicIncomeLocked(int batteryLevel) {
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final long now = getCurrentTimeMillis();
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo pkgInfo = pkgs.get(i);
+ final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
+ final String pkgName = pkgInfo.packageName;
+ if (mIrs.isSystem(userId, pkgName)) {
+ // No point allocating ARCs to the system. It can do whatever it wants.
+ continue;
+ }
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
+ final double perc = batteryLevel / 100d;
+ // TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
+ if (ledger.getCurrentBalance() < minBalance) {
+ final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
+ null, (long) (perc * shortfall)), true);
+ }
+ }
+ }
+
+ /** Give each app an initial balance. */
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked() {
+ UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ final int[] userIds = userManagerInternal.getUserIds();
+ for (int userId : userIds) {
+ grantBirthrightsLocked(userId);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightsLocked(final int userId) {
+ PackageManager packageManager = mIrs.getContext().getPackageManager();
+ List<PackageInfo> pkgs = packageManager.getInstalledPackagesAsUser(0, userId);
+ final long maxBirthright =
+ mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
+ final long now = getCurrentTimeMillis();
+
+ for (int i = 0; i < pkgs.size(); ++i) {
+ final PackageInfo packageInfo = pkgs.get(i);
+ final String pkgName = packageInfo.packageName;
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (mIrs.isSystem(userId, pkgName)) {
+ // No point allocating ARCs to the system. It can do whatever it wants.
+ continue;
+ }
+ if (ledger.getCurrentBalance() > 0) {
+ // App already got credits somehow. Move along.
+ Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up");
+ continue;
+ }
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))),
+ true);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void grantBirthrightLocked(final int userId, @NonNull final String pkgName) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() > 0) {
+ Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed");
+ // App already got credits somehow. Move along.
+ return;
+ }
+
+ List<PackageInfo> pkgs = mIrs.getInstalledPackages();
+ final int numPackages = pkgs.size();
+ final long maxBirthright = mIrs.getMaxCirculationLocked() / numPackages;
+ final long now = getCurrentTimeMillis();
+
+ recordTransactionLocked(userId, pkgName, ledger,
+ new Ledger.Transaction(now, now, REGULATION_BIRTHRIGHT, null,
+ Math.min(maxBirthright, mIrs.getMinBalanceLocked(userId, pkgName))), true);
+ }
+
+ @GuardedBy("mLock")
+ void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
+ reclaimAssetsLocked(userId, pkgName);
+ mLedgerCleanupAlarmListener.removeAlarmLocked(userId, pkgName);
+ mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+ }
+
+ /**
+ * Reclaims any ARCs granted to the app, making them available to other apps. Also deletes the
+ * app's ledger and stops any ongoing event tracking.
+ */
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = getLedgerLocked(userId, pkgName);
+ if (ledger.getCurrentBalance() != 0) {
+ mCurrentNarcsInCirculation -= ledger.getCurrentBalance();
+ }
+ // TODO: delete ledger entry from disk
+ mLedgers.delete(userId, pkgName);
+ mCurrentOngoingEvents.delete(userId, pkgName);
+ }
+
+ @GuardedBy("mLock")
+ void onUserRemovedLocked(final int userId, @NonNull final List<String> pkgNames) {
+ reclaimAssetsLocked(userId, pkgNames);
+ mLedgerCleanupAlarmListener.removeAlarmsLocked(userId);
+ mBalanceThresholdAlarmListener.removeAlarmsLocked(userId);
+ }
+
+ @GuardedBy("mLock")
+ private void reclaimAssetsLocked(final int userId, @NonNull final List<String> pkgNames) {
+ for (int i = 0; i < pkgNames.size(); ++i) {
+ reclaimAssetsLocked(userId, pkgNames.get(i));
+ }
+ }
+
+ @VisibleForTesting
+ static class TrendCalculator implements Consumer<OngoingEvent> {
+ static final long WILL_NOT_CROSS_THRESHOLD = -1;
+
+ private long mCurBalance;
+ /**
+ * The maximum change in credits per second towards the upper threshold
+ * {@link #mUpperThreshold}. A value of 0 means the current ongoing events will never
+ * result in the app crossing the upper threshold.
+ */
+ private long mMaxDeltaPerSecToUpperThreshold;
+ /**
+ * The maximum change in credits per second towards the lower threshold
+ * {@link #mLowerThreshold}. A value of 0 means the current ongoing events will never
+ * result in the app crossing the lower threshold.
+ */
+ private long mMaxDeltaPerSecToLowerThreshold;
+ private long mUpperThreshold;
+ private long mLowerThreshold;
+
+ void reset(long curBalance,
+ @Nullable ArraySet<ActionAffordabilityNote> actionAffordabilityNotes) {
+ mCurBalance = curBalance;
+ mMaxDeltaPerSecToUpperThreshold = mMaxDeltaPerSecToLowerThreshold = 0;
+ mUpperThreshold = Long.MIN_VALUE;
+ mLowerThreshold = Long.MAX_VALUE;
+ if (actionAffordabilityNotes != null) {
+ for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+ final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
+ final long price = note.getCachedModifiedPrice();
+ if (price <= mCurBalance) {
+ mLowerThreshold = (mLowerThreshold == Long.MAX_VALUE)
+ ? price : Math.max(mLowerThreshold, price);
+ } else {
+ mUpperThreshold = (mUpperThreshold == Long.MIN_VALUE)
+ ? price : Math.min(mUpperThreshold, price);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the amount of time (in millisecond) it will take for the app to cross the next
+ * lowest action affordability note (compared to its current balance) based on current
+ * ongoing events.
+ * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the lowest
+ * threshold.
+ */
+ long getTimeToCrossLowerThresholdMs() {
+ if (mMaxDeltaPerSecToLowerThreshold == 0) {
+ // Will never cross upper threshold based on current events.
+ return WILL_NOT_CROSS_THRESHOLD;
+ }
+ // deltaPerSec is a negative value, so do threshold-balance to cancel out the negative.
+ final long minSeconds =
+ (mLowerThreshold - mCurBalance) / mMaxDeltaPerSecToLowerThreshold;
+ return minSeconds * 1000;
+ }
+
+ /**
+ * Returns the amount of time (in millisecond) it will take for the app to cross the next
+ * highest action affordability note (compared to its current balance) based on current
+ * ongoing events.
+ * Returns {@link #WILL_NOT_CROSS_THRESHOLD} if the app will never cross the upper
+ * threshold.
+ */
+ long getTimeToCrossUpperThresholdMs() {
+ if (mMaxDeltaPerSecToUpperThreshold == 0) {
+ // Will never cross upper threshold based on current events.
+ return WILL_NOT_CROSS_THRESHOLD;
+ }
+ final long minSeconds =
+ (mUpperThreshold - mCurBalance) / mMaxDeltaPerSecToUpperThreshold;
+ return minSeconds * 1000;
+ }
+
+ @Override
+ public void accept(OngoingEvent ongoingEvent) {
+ if (mCurBalance >= mLowerThreshold && ongoingEvent.deltaPerSec < 0) {
+ mMaxDeltaPerSecToLowerThreshold += ongoingEvent.deltaPerSec;
+ } else if (mCurBalance < mUpperThreshold && ongoingEvent.deltaPerSec > 0) {
+ mMaxDeltaPerSecToUpperThreshold += ongoingEvent.deltaPerSec;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final TrendCalculator mTrendCalculator = new TrendCalculator();
+
+ @GuardedBy("mLock")
+ private void scheduleBalanceCheckLocked(final int userId, @NonNull final String pkgName) {
+ SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+ if (ongoingEvents == null) {
+ // No ongoing transactions. No reason to schedule
+ mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+ return;
+ }
+ mTrendCalculator.reset(
+ getBalanceLocked(userId, pkgName), mActionAffordabilityNotes.get(userId, pkgName));
+ ongoingEvents.forEach(mTrendCalculator);
+ final long lowerTimeMs = mTrendCalculator.getTimeToCrossLowerThresholdMs();
+ final long upperTimeMs = mTrendCalculator.getTimeToCrossUpperThresholdMs();
+ final long timeToThresholdMs;
+ if (lowerTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) {
+ if (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD) {
+ // Will never cross a threshold based on current events.
+ mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
+ return;
+ }
+ timeToThresholdMs = upperTimeMs;
+ } else {
+ timeToThresholdMs = (upperTimeMs == TrendCalculator.WILL_NOT_CROSS_THRESHOLD)
+ ? lowerTimeMs : Math.min(lowerTimeMs, upperTimeMs);
+ }
+ mBalanceThresholdAlarmListener.addAlarmLocked(userId, pkgName,
+ SystemClock.elapsedRealtime() + timeToThresholdMs);
+ }
+
+ @GuardedBy("mLock")
+ void tearDownLocked() {
+ mLedgers.clear();
+ mCurrentNarcsInCirculation = 0;
+ mCurrentOngoingEvents.clear();
+ mBalanceThresholdAlarmListener.dropAllAlarmsLocked();
+ mLedgerCleanupAlarmListener.dropAllAlarmsLocked();
+ }
+
+ @VisibleForTesting
+ static class OngoingEvent {
+ public final long startTimeElapsed;
+ public final int eventId;
+ @Nullable
+ public final String tag;
+ @Nullable
+ public final EconomicPolicy.Reward reward;
+ public final long deltaPerSec;
+ public int refCount;
+
+ OngoingEvent(int eventId, @Nullable String tag,
+ @Nullable EconomicPolicy.Reward reward, long startTimeElapsed, long deltaPerSec) {
+ this.startTimeElapsed = startTimeElapsed;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.reward = reward;
+ this.deltaPerSec = deltaPerSec;
+ refCount = 1;
+ }
+ }
+
+ /**
+ * An {@link AlarmManager.OnAlarmListener} that will queue up all pending alarms and only
+ * schedule one alarm for the earliest alarm.
+ */
+ private abstract class AlarmQueueListener implements AlarmManager.OnAlarmListener {
+ final class Package {
+ public final String packageName;
+ public final int userId;
+
+ Package(int userId, String packageName) {
+ this.userId = userId;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public String toString() {
+ return appToString(userId, packageName);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Package) {
+ Package other = (Package) obj;
+ return userId == other.userId && Objects.equals(packageName, other.packageName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() + userId;
+ }
+ }
+
+ class AlarmQueue extends PriorityQueue<Pair<Package, Long>> {
+ AlarmQueue() {
+ super(1, (o1, o2) -> (int) (o1.second - o2.second));
+ }
+
+ boolean contains(@NonNull Package pkg) {
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (pkg.equals(alarms[i].first)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove any instances of the Package from the queue.
+ *
+ * @return true if an instance was removed, false otherwise.
+ */
+ boolean remove(@NonNull Package pkg) {
+ boolean removed = false;
+ Pair[] alarms = toArray(new Pair[size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ if (pkg.equals(alarms[i].first)) {
+ remove(alarms[i]);
+ removed = true;
+ }
+ }
+ return removed;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private final AlarmQueue mAlarmQueue = new AlarmQueue();
+ private final String mAlarmTag;
+ /** Whether to use an exact alarm or an inexact alarm. */
+ private final boolean mExactAlarm;
+ /** The minimum amount of time between check alarms. */
+ private final long mMinTimeBetweenAlarmsMs;
+ /** The next time the alarm is set to go off, in the elapsed realtime timebase. */
+ @GuardedBy("mLock")
+ private long mTriggerTimeElapsed = 0;
+
+ protected AlarmQueueListener(@NonNull String alarmTag, boolean exactAlarm,
+ long minTimeBetweenAlarmsMs) {
+ mAlarmTag = alarmTag;
+ mExactAlarm = exactAlarm;
+ mMinTimeBetweenAlarmsMs = minTimeBetweenAlarmsMs;
+ }
+
+ @GuardedBy("mLock")
+ boolean hasAlarmScheduledLocked(int userId, @NonNull String pkgName) {
+ final Package pkg = new Package(userId, pkgName);
+ return mAlarmQueue.contains(pkg);
+ }
+
+ @GuardedBy("mLock")
+ void addAlarmLocked(int userId, @NonNull String pkgName, long alarmTimeElapsed) {
+ final Package pkg = new Package(userId, pkgName);
+ mAlarmQueue.remove(pkg);
+ mAlarmQueue.offer(new Pair<>(pkg, alarmTimeElapsed));
+ setNextAlarmLocked();
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmLocked(@NonNull Package pkg) {
+ if (mAlarmQueue.remove(pkg)) {
+ setNextAlarmLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmLocked(int userId, @NonNull String packageName) {
+ removeAlarmLocked(new Package(userId, packageName));
+ }
+
+ @GuardedBy("mLock")
+ void removeAlarmsLocked(int userId) {
+ boolean removed = false;
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = alarms.length - 1; i >= 0; --i) {
+ final Package pkg = (Package) alarms[i].first;
+ if (userId == pkg.userId) {
+ mAlarmQueue.remove(alarms[i]);
+ removed = true;
+ }
+ }
+ if (removed) {
+ setNextAlarmLocked();
+ }
+ }
+
+ /** Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue. */
+ @GuardedBy("mLock")
+ void setNextAlarmLocked() {
+ setNextAlarmLocked(SystemClock.elapsedRealtime());
+ }
+
+ /**
+ * Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue, using
+ * {@code earliestTriggerElapsed} as a floor.
+ */
+ @GuardedBy("mLock")
+ private void setNextAlarmLocked(long earliestTriggerElapsed) {
+ if (mAlarmQueue.size() > 0) {
+ final Pair<Package, Long> alarm = mAlarmQueue.peek();
+ final long nextTriggerTimeElapsed = Math.max(earliestTriggerElapsed, alarm.second);
+ // Only schedule the alarm if one of the following is true:
+ // 1. There isn't one currently scheduled
+ // 2. The new alarm is significantly earlier than the previous alarm. If it's
+ // earlier but not significantly so, then we essentially delay the check for some
+ // apps by up to a minute.
+ // 3. The alarm is after the current alarm.
+ if (mTriggerTimeElapsed == 0
+ || nextTriggerTimeElapsed < mTriggerTimeElapsed - MINUTE_IN_MILLIS
+ || mTriggerTimeElapsed < nextTriggerTimeElapsed) {
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling start alarm at " + nextTriggerTimeElapsed
+ + " for app " + alarm.first);
+ }
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager =
+ mIrs.getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ if (mExactAlarm) {
+ alarmManager.setExact(AlarmManager.ELAPSED_REALTIME,
+ nextTriggerTimeElapsed, mAlarmTag, this, mHandler);
+ } else {
+ alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+ nextTriggerTimeElapsed, mMinTimeBetweenAlarmsMs / 2,
+ mAlarmTag, this, mHandler);
+ }
+ } else {
+ mHandler.sendEmptyMessageDelayed(MSG_SET_ALARMS, 30_000);
+ }
+ });
+ mTriggerTimeElapsed = nextTriggerTimeElapsed;
+ }
+ } else {
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager =
+ mIrs.getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ // This should only be null at boot time. No concerns around not
+ // cancelling if we get null here.
+ alarmManager.cancel(this);
+ }
+ });
+ mTriggerTimeElapsed = 0;
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dropAllAlarmsLocked() {
+ mAlarmQueue.clear();
+ setNextAlarmLocked(0);
+ }
+
+ @GuardedBy("mLock")
+ protected abstract void processExpiredAlarmLocked(int userId, @NonNull String packageName);
+
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ while (mAlarmQueue.size() > 0) {
+ final Pair<Package, Long> alarm = mAlarmQueue.peek();
+ if (alarm.second <= nowElapsed) {
+ processExpiredAlarmLocked(alarm.first.userId, alarm.first.packageName);
+ mAlarmQueue.remove(alarm);
+ } else {
+ break;
+ }
+ }
+ setNextAlarmLocked(nowElapsed + mMinTimeBetweenAlarmsMs);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.print(mAlarmTag);
+ pw.println(" alarms:");
+ pw.increaseIndent();
+
+ if (mAlarmQueue.size() == 0) {
+ pw.println("NOT WAITING");
+ } else {
+ Pair[] alarms = mAlarmQueue.toArray(new Pair[mAlarmQueue.size()]);
+ for (int i = 0; i < alarms.length; ++i) {
+ final Package pkg = (Package) alarms[i].first;
+ pw.print(pkg);
+ pw.print(": ");
+ pw.print(alarms[i].second);
+ pw.println();
+ }
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+
+ /** Clean up old transactions from {@link Ledger}s. */
+ private class LedgerCleanupAlarmListener extends AlarmQueueListener {
+ private LedgerCleanupAlarmListener() {
+ // We don't need to run cleanup too frequently.
+ super(ALARM_TAG_LEDGER_CLEANUP, false, HOUR_IN_MILLIS);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+ mHandler.obtainMessage(MSG_CLEAN_LEDGER, userId, 0, packageName).sendToTarget();
+ }
+ }
+
+ /** Track when apps will cross the closest affordability threshold (in both directions). */
+ private class BalanceThresholdAlarmListener extends AlarmQueueListener {
+ private BalanceThresholdAlarmListener() {
+ super(ALARM_TAG_AFFORDABILITY_CHECK, true, 15_000L);
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
+ mHandler.obtainMessage(MSG_CHECK_BALANCE, userId, 0, packageName).sendToTarget();
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void registerAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName,
+ @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+ @NonNull EconomyManagerInternal.ActionBill bill) {
+ ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ if (actionAffordabilityNotes == null) {
+ actionAffordabilityNotes = new ArraySet<>();
+ mActionAffordabilityNotes.add(userId, pkgName, actionAffordabilityNotes);
+ }
+ final ActionAffordabilityNote note =
+ new ActionAffordabilityNote(bill, listener, mCompleteEconomicPolicy);
+ if (actionAffordabilityNotes.add(note)) {
+ if (!mIrs.isEnabled()) {
+ // When TARE isn't enabled, we always say something is affordable. We also don't
+ // want to silently drop affordability change listeners in case TARE becomes enabled
+ // because then clients will be in an ambiguous state.
+ note.setNewAffordability(true);
+ return;
+ }
+ note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName);
+ note.setNewAffordability(
+ getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice());
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ // Update ongoing alarm
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void unregisterAffordabilityChangeListenerLocked(int userId, @NonNull String pkgName,
+ @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+ @NonNull EconomyManagerInternal.ActionBill bill) {
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ if (actionAffordabilityNotes != null) {
+ final ActionAffordabilityNote note =
+ new ActionAffordabilityNote(bill, listener, mCompleteEconomicPolicy);
+ if (actionAffordabilityNotes.remove(note)) {
+ // Update ongoing alarm
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ }
+
+ static final class ActionAffordabilityNote {
+ private final EconomyManagerInternal.ActionBill mActionBill;
+ private final EconomyManagerInternal.AffordabilityChangeListener mListener;
+ private long mModifiedPrice;
+ private boolean mIsAffordable;
+
+ @VisibleForTesting
+ ActionAffordabilityNote(@NonNull EconomyManagerInternal.ActionBill bill,
+ @NonNull EconomyManagerInternal.AffordabilityChangeListener listener,
+ @NonNull EconomicPolicy economicPolicy) {
+ mActionBill = bill;
+ final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
+ bill.getAnticipatedActions();
+ for (int i = 0; i < anticipatedActions.size(); ++i) {
+ final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
+ final EconomicPolicy.Action action = economicPolicy.getAction(aa.actionId);
+ if (action == null) {
+ throw new IllegalArgumentException("Invalid action id: " + aa.actionId);
+ }
+ }
+ mListener = listener;
+ }
+
+ @NonNull
+ EconomyManagerInternal.ActionBill getActionBill() {
+ return mActionBill;
+ }
+
+ @NonNull
+ EconomyManagerInternal.AffordabilityChangeListener getListener() {
+ return mListener;
+ }
+
+ private long getCachedModifiedPrice() {
+ return mModifiedPrice;
+ }
+
+ @VisibleForTesting
+ long recalculateModifiedPrice(@NonNull EconomicPolicy economicPolicy,
+ int userId, @NonNull String pkgName) {
+ long modifiedPrice = 0;
+ final List<EconomyManagerInternal.AnticipatedAction> anticipatedActions =
+ mActionBill.getAnticipatedActions();
+ for (int i = 0; i < anticipatedActions.size(); ++i) {
+ final EconomyManagerInternal.AnticipatedAction aa = anticipatedActions.get(i);
+
+ final long actionCost =
+ economicPolicy.getCostOfAction(aa.actionId, userId, pkgName);
+ modifiedPrice += actionCost * aa.numInstantaneousCalls
+ + actionCost * (aa.ongoingDurationMs / 1000);
+ }
+ mModifiedPrice = modifiedPrice;
+ return modifiedPrice;
+ }
+
+ boolean isCurrentlyAffordable() {
+ return mIsAffordable;
+ }
+
+ private void setNewAffordability(boolean isAffordable) {
+ mIsAffordable = isAffordable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ActionAffordabilityNote)) return false;
+ ActionAffordabilityNote other = (ActionAffordabilityNote) o;
+ return mActionBill.equals(other.mActionBill)
+ && mListener.equals(other.mListener);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 0;
+ hash = 31 * hash + Objects.hash(mListener);
+ hash = 31 * hash + mActionBill.hashCode();
+ return hash;
+ }
+ }
+
+ private final class AgentHandler extends Handler {
+ AgentHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CHECK_BALANCE: {
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ synchronized (mLock) {
+ final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
+ mActionAffordabilityNotes.get(userId, pkgName);
+ if (actionAffordabilityNotes != null
+ && actionAffordabilityNotes.size() > 0) {
+ final long newBalance = getBalanceLocked(userId, pkgName);
+
+ for (int i = 0; i < actionAffordabilityNotes.size(); ++i) {
+ final ActionAffordabilityNote note =
+ actionAffordabilityNotes.valueAt(i);
+ final boolean isAffordable =
+ newBalance >= note.getCachedModifiedPrice();
+ if (note.isCurrentlyAffordable() != isAffordable) {
+ note.setNewAffordability(isAffordable);
+ mIrs.postAffordabilityChanged(userId, pkgName, note);
+ }
+ }
+ }
+ scheduleBalanceCheckLocked(userId, pkgName);
+ }
+ }
+ break;
+
+ case MSG_CLEAN_LEDGER: {
+ final int userId = msg.arg1;
+ final String pkgName = (String) msg.obj;
+ synchronized (mLock) {
+ final Ledger ledger = getLedgerLocked(userId, pkgName);
+ ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
+ }
+ }
+ break;
+
+ case MSG_SET_ALARMS: {
+ synchronized (mLock) {
+ mLedgerCleanupAlarmListener.setNextAlarmLocked();
+ mBalanceThresholdAlarmListener.setNextAlarmLocked();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.println("Ledgers:");
+ pw.increaseIndent();
+ mLedgers.forEach((userId, pkgName, ledger) -> {
+ pw.print(appToString(userId, pkgName));
+ if (mIrs.isSystem(userId, pkgName)) {
+ pw.print(" (system)");
+ }
+ pw.println();
+ pw.increaseIndent();
+ ledger.dump(pw, MAX_NUM_TRANSACTION_DUMP);
+ pw.decreaseIndent();
+ });
+ pw.decreaseIndent();
+
+ pw.println();
+ mBalanceThresholdAlarmListener.dumpLocked(pw);
+
+ pw.println();
+ mLedgerCleanupAlarmListener.dumpLocked(pw);
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
new file mode 100644
index 000000000000..fc057f7d187f
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP;
+import static android.app.tare.EconomyManager.KEY_AM_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_AM_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * AlarmManager.
+ */
+public class AlarmManagerEconomicPolicy extends EconomicPolicy {
+ private static final String TAG = "TARE- " + AlarmManagerEconomicPolicy.class.getSimpleName();
+
+ public static final int ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 0;
+ public static final int ACTION_ALARM_WAKEUP_EXACT =
+ TYPE_ACTION | POLICY_AM | 1;
+ public static final int ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 2;
+ public static final int ACTION_ALARM_WAKEUP_INEXACT =
+ TYPE_ACTION | POLICY_AM | 3;
+ public static final int ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 4;
+ public static final int ACTION_ALARM_NONWAKEUP_EXACT =
+ TYPE_ACTION | POLICY_AM | 5;
+ public static final int ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE =
+ TYPE_ACTION | POLICY_AM | 6;
+ public static final int ACTION_ALARM_NONWAKEUP_INEXACT =
+ TYPE_ACTION | POLICY_AM | 7;
+ public static final int ACTION_ALARM_CLOCK =
+ TYPE_ACTION | POLICY_AM | 8;
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private long mMinSatiatedBalance;
+ private long mMaxSatiatedBalance;
+ private long mMaxSatiatedCirculation;
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final SettingsObserver mSettingsObserver;
+ private final InternalResourceService mInternalResourceService;
+
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+ AlarmManagerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ mInternalResourceService = irs;
+ mSettingsObserver = new SettingsObserver(TareHandlerThread.getHandler());
+ loadConstants("");
+ }
+
+ @Override
+ void setup() {
+ super.setup();
+ ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ loadConstants(Settings.Global.getString(
+ mInternalResourceService.getContext().getContentResolver(),
+ TARE_ALARM_MANAGER_CONSTANTS));
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: take exemption into account
+ return mMinSatiatedBalance;
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return mMaxSatiatedBalance;
+ }
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return mMaxSatiatedCirculation;
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@AppAction int actionId) {
+ return mActions.get(actionId);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@UtilityReward int rewardId) {
+ return mRewards.get(rewardId);
+ }
+
+ private void loadConstants(String policyValuesString) {
+ mActions.clear();
+ mRewards.clear();
+
+ try {
+ mParser.setString(policyValuesString);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Global setting key incorrect: ", e);
+ }
+
+ mMinSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
+ DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP));
+ mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_AM_MAX_SATIATED_BALANCE,
+ DEFAULT_AM_MAX_SATIATED_BALANCE));
+ mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_AM_MAX_CIRCULATION,
+ DEFAULT_AM_MAX_CIRCULATION));
+
+ final long exactAllowWhileIdleWakeupBasePrice = arcToNarc(
+ mParser.getInt(KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE));
+
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP)),
+ exactAllowWhileIdleWakeupBasePrice));
+ mActions.put(ACTION_ALARM_WAKEUP_EXACT,
+ new Action(ACTION_ALARM_WAKEUP_EXACT,
+ arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP)),
+ arcToNarc(mParser.getInt(KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE))));
+
+ final long inexactAllowWhileIdleWakeupBasePrice =
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE));
+
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP)),
+ inexactAllowWhileIdleWakeupBasePrice));
+ mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
+ new Action(ACTION_ALARM_WAKEUP_INEXACT,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE))));
+
+ final long exactAllowWhileIdleNonWakeupBasePrice =
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
+
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP)),
+ exactAllowWhileIdleNonWakeupBasePrice));
+ mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_EXACT,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE))));
+
+ final long inexactAllowWhileIdleNonWakeupBasePrice =
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE));
+
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP)),
+ inexactAllowWhileIdleNonWakeupBasePrice));
+ mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
+ new Action(ACTION_ALARM_NONWAKEUP_INEXACT,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
+ DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE))));
+ mActions.put(ACTION_ALARM_CLOCK,
+ new Action(ACTION_ALARM_CLOCK,
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
+ DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
+ DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE))));
+
+ mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
+ DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT)),
+ (long) (arcToNarc(1) * mParser.getFloat(KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
+ DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_TOP_ACTIVITY_MAX,
+ DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX))));
+ mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
+ DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT)),
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
+ DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
+ DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX))));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(mParser.getInt(
+ KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
+ DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
+ DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
+ DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX))));
+ mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
+ DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
+ DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
+ DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX))));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(mParser.getInt(KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
+ DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
+ DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(
+ KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
+ DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX))));
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ loadConstants(Settings.Global.getString(
+ mInternalResourceService.getContext().getContentResolver(),
+ TARE_ALARM_MANAGER_CONSTANTS));
+ }
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Actions:");
+ pw.increaseIndent();
+ for (int i = 0; i < mActions.size(); ++i) {
+ dumpAction(pw, mActions.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Rewards:");
+ pw.increaseIndent();
+ for (int i = 0; i < mRewards.size(); ++i) {
+ dumpReward(pw, mRewards.valueAt(i));
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
new file mode 100644
index 000000000000..712e13eb14e2
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.SystemClock;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+
+/** Modifier that makes things free when the device is charging. */
+class ChargingModifier extends Modifier {
+ private static final String TAG = "TARE-" + ChargingModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final ChargingTracker mChargingTracker;
+
+ ChargingModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mChargingTracker = new ChargingTracker();
+ }
+
+ @Override
+ public void setup() {
+ mChargingTracker.startTracking(mIrs.getContext());
+ }
+
+ @Override
+ public void tearDown() {
+ mChargingTracker.stopTracking(mIrs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ return modifyValue(ctp);
+ }
+
+ @Override
+ long getModifiedPrice(long price) {
+ return modifyValue(price);
+ }
+
+ private long modifyValue(long val) {
+ if (mChargingTracker.mCharging) {
+ return 0;
+ }
+ return val;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("charging=");
+ pw.println(mChargingTracker.mCharging);
+ }
+
+ private final class ChargingTracker extends BroadcastReceiver {
+ /**
+ * Track whether we're "charging", where charging means that we're ready to commit to
+ * doing work.
+ */
+ private volatile boolean mCharging;
+
+ public void startTracking(@NonNull Context context) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ mCharging = batteryManager.isCharging();
+ }
+
+ public void stopTracking(@NonNull Context context) {
+ context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (BatteryManager.ACTION_CHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received charging intent, fired @ "
+ + SystemClock.elapsedRealtime());
+ }
+ if (!mCharging) {
+ mCharging = true;
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power.");
+ }
+ if (mCharging) {
+ mCharging = false;
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
new file mode 100644
index 000000000000..dbc7bd6b1f73
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+
+/** Combines all enabled policies into one. */
+public class CompleteEconomicPolicy extends EconomicPolicy {
+ private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
+ /** Lazily populated set of actions covered by this policy. */
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ /** Lazily populated set of rewards covered by this policy. */
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+ private final int[] mCostModifiers;
+ private final long mMaxSatiatedBalance;
+ private final long mMaxSatiatedCirculation;
+
+ CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
+ super(irs);
+ mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
+ mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));
+
+ ArraySet<Integer> costModifiers = new ArraySet<>();
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ final int[] sm = mEnabledEconomicPolicies.valueAt(i).getCostModifiers();
+ for (int s : sm) {
+ costModifiers.add(s);
+ }
+ }
+ mCostModifiers = new int[costModifiers.size()];
+ for (int i = 0; i < costModifiers.size(); ++i) {
+ mCostModifiers[i] = costModifiers.valueAt(i);
+ }
+
+ long max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
+ }
+ mMaxSatiatedBalance = max;
+
+ max = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
+ }
+ mMaxSatiatedCirculation = max;
+ }
+
+ @Override
+ void setup() {
+ super.setup();
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ mEnabledEconomicPolicies.valueAt(i).setup();
+ }
+ }
+
+ @Override
+ public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ long min = 0;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
+ }
+ return min;
+ }
+
+ @Override
+ public long getMaxSatiatedBalance() {
+ return mMaxSatiatedBalance;
+ }
+
+ @Override
+ public long getMaxSatiatedCirculation() {
+ return mMaxSatiatedCirculation;
+ }
+
+ @NonNull
+ @Override
+ public int[] getCostModifiers() {
+ return mCostModifiers == null ? EmptyArray.INT : mCostModifiers;
+ }
+
+ @Nullable
+ @Override
+ public Action getAction(@AppAction int actionId) {
+ if (mActions.contains(actionId)) {
+ return mActions.get(actionId);
+ }
+
+ long ctp = 0, price = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionId);
+ if (a != null) {
+ exists = true;
+ ctp += a.costToProduce;
+ price += a.basePrice;
+ }
+ }
+ final Action action = exists ? new Action(actionId, ctp, price) : null;
+ mActions.put(actionId, action);
+ return action;
+ }
+
+ @Nullable
+ @Override
+ public Reward getReward(@UtilityReward int rewardId) {
+ if (mRewards.contains(rewardId)) {
+ return mRewards.get(rewardId);
+ }
+
+ long instantReward = 0, ongoingReward = 0, maxReward = 0;
+ boolean exists = false;
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardId);
+ if (r != null) {
+ exists = true;
+ instantReward += r.instantReward;
+ ongoingReward += r.ongoingRewardPerSecond;
+ maxReward += r.maxDailyReward;
+ }
+ }
+ final Reward reward = exists
+ ? new Reward(rewardId, instantReward, ongoingReward, maxReward) : null;
+ mRewards.put(rewardId, reward);
+ return reward;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ dumpActiveModifiers(pw);
+
+ pw.println();
+ pw.println(getClass().getSimpleName() + ":");
+ pw.increaseIndent();
+
+ pw.println("Cached actions:");
+ pw.increaseIndent();
+ for (int i = 0; i < mActions.size(); ++i) {
+ dumpAction(pw, mActions.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Cached rewards:");
+ pw.increaseIndent();
+ for (int i = 0; i < mRewards.size(); ++i) {
+ dumpReward(pw, mRewards.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); i++) {
+ final EconomicPolicy economicPolicy = mEnabledEconomicPolicies.valueAt(i);
+ pw.println();
+ pw.print("(Includes) ");
+ pw.println(economicPolicy.getClass().getSimpleName() + ":");
+ pw.increaseIndent();
+ economicPolicy.dump(pw);
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
new file mode 100644
index 000000000000..37b0aa8f4862
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+/** Modifier that makes things more expensive in light and deep doze. */
+class DeviceIdleModifier extends Modifier {
+ private static final String TAG = "TARE-" + DeviceIdleModifier.class.getSimpleName();
+ private static final boolean DEBUG = InternalResourceService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+ private final DeviceIdleTracker mDeviceIdleTracker;
+
+ DeviceIdleModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ mDeviceIdleTracker = new DeviceIdleTracker();
+ }
+
+ @Override
+ public void setup() {
+ mDeviceIdleTracker.startTracking(mIrs.getContext());
+ }
+
+ @Override
+ public void tearDown() {
+ mDeviceIdleTracker.stopTracking(mIrs.getContext());
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mDeviceIdleTracker.mDeviceIdle) {
+ return (long) (1.2 * ctp);
+ }
+ if (mDeviceIdleTracker.mDeviceLightIdle) {
+ return (long) (1.1 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("idle=");
+ pw.println(mDeviceIdleTracker.mDeviceIdle);
+ pw.print("lightIdle=");
+ pw.println(mDeviceIdleTracker.mDeviceLightIdle);
+ }
+
+ private final class DeviceIdleTracker extends BroadcastReceiver {
+
+ private volatile boolean mDeviceIdle;
+ private volatile boolean mDeviceLightIdle;
+
+ DeviceIdleTracker() {
+ }
+
+ void startTracking(@NonNull Context context) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+ context.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ }
+
+ void stopTracking(@NonNull Context context) {
+ context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isDeviceIdleMode()) {
+ mDeviceIdle = mPowerManager.isDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ if (mDeviceIdle != mPowerManager.isLightDeviceIdleMode()) {
+ mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode();
+ mIrs.onDeviceStateChanged();
+ }
+ }
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
new file mode 100644
index 000000000000..d16e378bce9e
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.Modifier.NUM_COST_MODIFIERS;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions.
+ * Policies are defined per participating system service. This allows each service’s EconomicPolicy
+ * to be isolated while allowing the core economic system to scale across policies to achieve a
+ * logical system-wide value system.
+ */
+public abstract class EconomicPolicy {
+ private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName();
+
+ private static final int SHIFT_TYPE = 30;
+ static final int MASK_TYPE = 0b11 << SHIFT_TYPE;
+ static final int TYPE_REGULATION = 0 << SHIFT_TYPE;
+ static final int TYPE_ACTION = 1 << SHIFT_TYPE;
+ static final int TYPE_REWARD = 2 << SHIFT_TYPE;
+
+ private static final int SHIFT_POLICY = 29;
+ static final int MASK_POLICY = 0b1 << SHIFT_POLICY;
+ static final int POLICY_AM = 0 << SHIFT_POLICY;
+ static final int POLICY_JS = 1 << SHIFT_POLICY;
+
+ static final int MASK_EVENT = ~0 - (0b111 << SHIFT_POLICY);
+
+ static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0;
+ static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1;
+ static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2;
+
+ static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0;
+ static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1;
+ static final int REWARD_TOP_ACTIVITY = TYPE_REWARD | 2;
+ static final int REWARD_WIDGET_INTERACTION = TYPE_REWARD | 3;
+ static final int REWARD_OTHER_USER_INTERACTION = TYPE_REWARD | 4;
+
+ @IntDef({
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT,
+ AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START,
+ JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING,
+ JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppAction {
+ }
+
+ @IntDef({
+ TYPE_ACTION,
+ TYPE_REGULATION,
+ TYPE_REWARD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {
+ }
+
+ @IntDef({
+ REWARD_TOP_ACTIVITY,
+ REWARD_NOTIFICATION_SEEN,
+ REWARD_NOTIFICATION_INTERACTION,
+ REWARD_WIDGET_INTERACTION,
+ REWARD_OTHER_USER_INTERACTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UtilityReward {
+ }
+
+ static class Action {
+ /** Unique id (including across policies) for this action. */
+ public final int id;
+ /**
+ * How many ARCs the system says it takes to perform this action.
+ */
+ public final long costToProduce;
+ /**
+ * The base price to perform this action. If this is
+ * less than the {@link #costToProduce}, then the system should not perform
+ * the action unless a modifier lowers the cost to produce.
+ */
+ public final long basePrice;
+
+ Action(int id, long costToProduce, long basePrice) {
+ this.id = id;
+ this.costToProduce = costToProduce;
+ this.basePrice = basePrice;
+ }
+ }
+
+ static class Reward {
+ /** Unique id (including across policies) for this reward. */
+ @UtilityReward
+ public final int id;
+ public final long instantReward;
+ /** Reward credited per second of ongoing activity. */
+ public final long ongoingRewardPerSecond;
+ /** The maximum amount an app can earn from this reward within a 24 hour period. */
+ public final long maxDailyReward;
+
+ Reward(int id, long instantReward, long ongoingReward, long maxDailyReward) {
+ this.id = id;
+ this.instantReward = instantReward;
+ this.ongoingRewardPerSecond = ongoingReward;
+ this.maxDailyReward = maxDailyReward;
+ }
+ }
+
+ private static final Modifier[] COST_MODIFIER_BY_INDEX = new Modifier[NUM_COST_MODIFIERS];
+
+ EconomicPolicy(@NonNull InternalResourceService irs) {
+ for (int mId : getCostModifiers()) {
+ initModifier(mId, irs);
+ }
+ }
+
+ @CallSuper
+ void setup() {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.setup();
+ }
+ }
+ }
+
+ @CallSuper
+ void tearDown() {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.tearDown();
+ }
+ }
+ }
+
+ /**
+ * Returns the minimum suggested balance an app should have when the device is at 100% battery.
+ * This takes into account any exemptions the app may have.
+ */
+ abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName);
+
+ /**
+ * Returns the maximum balance an app should have when the device is at 100% battery. This
+ * exists to ensure that no single app accumulate all available resources and increases fairness
+ * for all apps.
+ */
+ abstract long getMaxSatiatedBalance();
+
+ /**
+ * Returns the maximum number of narcs that should be in circulation at once when the device is
+ * at 100% battery.
+ */
+ abstract long getMaxSatiatedCirculation();
+
+ /** Return the set of modifiers that should apply to this policy's costs. */
+ @NonNull
+ abstract int[] getCostModifiers();
+
+ @Nullable
+ abstract Action getAction(@AppAction int actionId);
+
+ @Nullable
+ abstract Reward getReward(@UtilityReward int rewardId);
+
+ void dump(IndentingPrintWriter pw) {
+ }
+
+ final long getCostOfAction(int actionId, int userId, @NonNull String pkgName) {
+ final Action action = getAction(actionId);
+ if (action == null) {
+ return 0;
+ }
+ long ctp = action.costToProduce;
+ long price = action.basePrice;
+ final int[] costModifiers = getCostModifiers();
+ boolean useProcessStatePriceDeterminant = false;
+ for (int costModifier : costModifiers) {
+ if (costModifier == COST_MODIFIER_PROCESS_STATE) {
+ useProcessStatePriceDeterminant = true;
+ } else {
+ final Modifier modifier = getModifier(costModifier);
+ ctp = modifier.getModifiedCostToProduce(ctp);
+ price = modifier.getModifiedPrice(price);
+ }
+ }
+ // ProcessStateModifier needs to be done last.
+ if (useProcessStatePriceDeterminant) {
+ ProcessStateModifier processStateModifier =
+ (ProcessStateModifier) getModifier(COST_MODIFIER_PROCESS_STATE);
+ price = processStateModifier.getModifiedPrice(userId, pkgName, ctp, price);
+ }
+ return price;
+ }
+
+ private static void initModifier(@Modifier.CostModifier final int modifierId,
+ @NonNull InternalResourceService irs) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ switch (modifierId) {
+ case COST_MODIFIER_CHARGING:
+ modifier = new ChargingModifier(irs);
+ break;
+ case COST_MODIFIER_DEVICE_IDLE:
+ modifier = new DeviceIdleModifier(irs);
+ break;
+ case COST_MODIFIER_POWER_SAVE_MODE:
+ modifier = new PowerSaveModeModifier(irs);
+ break;
+ case COST_MODIFIER_PROCESS_STATE:
+ modifier = new ProcessStateModifier(irs);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ COST_MODIFIER_BY_INDEX[modifierId] = modifier;
+ }
+ }
+
+ @NonNull
+ private static Modifier getModifier(@Modifier.CostModifier final int modifierId) {
+ if (modifierId < 0 || modifierId >= COST_MODIFIER_BY_INDEX.length) {
+ throw new IllegalArgumentException("Invalid modifier id " + modifierId);
+ }
+ final Modifier modifier = COST_MODIFIER_BY_INDEX[modifierId];
+ if (modifier == null) {
+ throw new IllegalStateException(
+ "Modifier #" + modifierId + " was never initialized");
+ }
+ return modifier;
+ }
+
+ @EventType
+ static int getEventType(int eventId) {
+ return eventId & MASK_TYPE;
+ }
+
+ @NonNull
+ static String eventToString(int eventId) {
+ switch (eventId & MASK_TYPE) {
+ case TYPE_ACTION:
+ return actionToString(eventId);
+
+ case TYPE_REGULATION:
+ return regulationToString(eventId);
+
+ case TYPE_REWARD:
+ return rewardToString(eventId);
+
+ default:
+ return "UNKNOWN_EVENT:" + Integer.toHexString(eventId);
+ }
+ }
+
+ @NonNull
+ static String actionToString(int eventId) {
+ switch (eventId & MASK_POLICY) {
+ case POLICY_AM:
+ switch (eventId) {
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT:
+ return "ALARM_WAKEUP_EXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT:
+ return "ALARM_WAKEUP_INEXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT:
+ return "ALARM_NONWAKEUP_EXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE:
+ return "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT:
+ return "ALARM_NONWAKEUP_INEXACT";
+ case AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK:
+ return "ALARM_CLOCK";
+ }
+ break;
+
+ case POLICY_JS:
+ switch (eventId) {
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START:
+ return "JOB_MAX_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING:
+ return "JOB_MAX_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START:
+ return "JOB_HIGH_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING:
+ return "JOB_HIGH_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START:
+ return "JOB_DEFAULT_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING:
+ return "JOB_DEFAULT_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START:
+ return "JOB_LOW_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING:
+ return "JOB_LOW_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START:
+ return "JOB_MIN_START";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING:
+ return "JOB_MIN_RUNNING";
+ case JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT:
+ return "JOB_TIMEOUT";
+ }
+ break;
+ }
+ return "UNKNOWN_ACTION:" + Integer.toHexString(eventId);
+ }
+
+ @NonNull
+ static String regulationToString(int eventId) {
+ switch (eventId) {
+ case REGULATION_BASIC_INCOME:
+ return "BASIC_INCOME";
+ case REGULATION_BIRTHRIGHT:
+ return "BIRTHRIGHT";
+ case REGULATION_WEALTH_RECLAMATION:
+ return "WEALTH_RECLAMATION";
+ }
+ return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId);
+ }
+
+ @NonNull
+ static String rewardToString(int eventId) {
+ switch (eventId) {
+ case REWARD_TOP_ACTIVITY:
+ return "REWARD_TOP_ACTIVITY";
+ case REWARD_NOTIFICATION_SEEN:
+ return "REWARD_NOTIFICATION_SEEN";
+ case REWARD_NOTIFICATION_INTERACTION:
+ return "REWARD_NOTIFICATION_INTERACTION";
+ case REWARD_WIDGET_INTERACTION:
+ return "REWARD_WIDGET_INTERACTION";
+ case REWARD_OTHER_USER_INTERACTION:
+ return "REWARD_OTHER_USER_INTERACTION";
+ }
+ return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
+ }
+
+ protected static void dumpActiveModifiers(IndentingPrintWriter pw) {
+ for (int i = 0; i < NUM_COST_MODIFIERS; ++i) {
+ pw.print("Modifier ");
+ pw.println(i);
+ pw.increaseIndent();
+
+ Modifier modifier = COST_MODIFIER_BY_INDEX[i];
+ if (modifier != null) {
+ modifier.dump(pw);
+ } else {
+ pw.println("NOT ACTIVE");
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+
+ protected static void dumpAction(IndentingPrintWriter pw, @NonNull Action action) {
+ pw.print(actionToString(action.id));
+ pw.print(": ");
+ pw.print("ctp=");
+ pw.print(narcToString(action.costToProduce));
+ pw.print(", basePrice=");
+ pw.print(narcToString(action.basePrice));
+ pw.println();
+ }
+
+ protected static void dumpReward(IndentingPrintWriter pw, @NonNull Reward reward) {
+ pw.print(rewardToString(reward.id));
+ pw.print(": ");
+ pw.print("instant=");
+ pw.print(narcToString(reward.instantReward));
+ pw.print(", ongoing/sec=");
+ pw.print(narcToString(reward.ongoingRewardPerSecond));
+ pw.print(", maxDaily=");
+ pw.print(narcToString(reward.maxDailyReward));
+ pw.println();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java
new file mode 100644
index 000000000000..29aa94631d68
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.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.server.tare;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Interface for the system server to deal with the resource economy subsystem.
+ *
+ * @hide
+ */
+public interface EconomyManagerInternal {
+ /**
+ * Used to indicate a future action an app is expected to take.
+ */
+ final class AnticipatedAction {
+ public final int actionId;
+ public final int numInstantaneousCalls;
+ public final long ongoingDurationMs;
+ private final int mHashCode;
+
+ /**
+ * @param actionId The expected action
+ * @param numInstantaneousCalls How many instantaneous times the action will be performed
+ * @param ongoingDurationMs An estimate of how long the ongoing event will go on for
+ */
+ public AnticipatedAction(@EconomicPolicy.AppAction int actionId,
+ int numInstantaneousCalls, long ongoingDurationMs) {
+ this.actionId = actionId;
+ this.numInstantaneousCalls = numInstantaneousCalls;
+ this.ongoingDurationMs = ongoingDurationMs;
+
+ int hash = 0;
+ hash = 31 * hash + actionId;
+ hash = 31 * hash + numInstantaneousCalls;
+ hash = 31 * hash + (int) (ongoingDurationMs ^ (ongoingDurationMs >>> 32));
+ mHashCode = hash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AnticipatedAction that = (AnticipatedAction) o;
+ return actionId == that.actionId
+ && numInstantaneousCalls == that.numInstantaneousCalls
+ && ongoingDurationMs == that.ongoingDurationMs;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+ }
+
+ /**
+ * A collection of {@link AnticipatedAction}s that will be performed together.
+ */
+ final class ActionBill {
+ private static final Comparator<AnticipatedAction>
+ sAnticipatedActionComparator = Comparator.comparingInt(aa -> aa.actionId);
+
+ private final List<AnticipatedAction> mAnticipatedActions;
+ private final int mHashCode;
+
+ public ActionBill(@NonNull List<AnticipatedAction> anticipatedActions) {
+ List<AnticipatedAction> actions = new ArrayList<>(anticipatedActions);
+ actions.sort(sAnticipatedActionComparator);
+ mAnticipatedActions = Collections.unmodifiableList(actions);
+
+ int hash = 0;
+ for (int i = 0; i < mAnticipatedActions.size(); ++i) {
+ hash = 31 * hash + mAnticipatedActions.get(i).hashCode();
+ }
+ mHashCode = hash;
+ }
+
+ List<AnticipatedAction> getAnticipatedActions() {
+ return mAnticipatedActions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ActionBill that = (ActionBill) o;
+ return mAnticipatedActions.equals(that.mAnticipatedActions);
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+ }
+
+ /** Listener for when an app's ability to afford a bill changes. */
+ interface AffordabilityChangeListener {
+ void onAffordabilityChanged(int userId, @NonNull String pkgName, @NonNull ActionBill bill,
+ boolean canAfford);
+ }
+
+ /**
+ * Return {@code true} if the app is able to pay for the anticipated actions.
+ */
+ boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
+
+ /**
+ * Returns the maximum duration (in milliseconds) that the specified app can afford the bill,
+ * based on current prices.
+ */
+ long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill);
+
+ /**
+ * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the
+ * indicated bill changes.
+ */
+ void registerAffordabilityChangeListener(int userId, @NonNull String pkgName,
+ @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
+
+ /**
+ * Unregister a {@link AffordabilityChangeListener} from being notified of any changes to an
+ * app's ability to afford the specified bill.
+ */
+ void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName,
+ @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill);
+
+ /**
+ * Note that an instantaneous event has occurred. The event must be specified in one of the
+ * EconomicPolicies.
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event is starting. The event must be specified in one of the
+ * EconomicPolicies. You must always call
+ * {@link #noteOngoingEventStopped(int, String, int, String)} to end the event. Ongoing
+ * events will be separated and grouped by event-tag combinations. There must be an equal
+ * number of start() and stop() calls for the same event-tag combination in order for the
+ * tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+
+ /**
+ * Note that a long-running event has stopped. The event must be specified in one of the
+ * EconomicPolicies. Ongoing events are separated and grouped by event-tag combinations.
+ * There must be an equal number of start() and stop() calls for the same event-tag combination
+ * in order for the tracking to finally stop (ie. ongoing events are ref-counted).
+ *
+ * @param tag An optional tag that can be used to differentiate the same event for the same app.
+ */
+ void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
new file mode 100644
index 000000000000..653822943095
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.appToString;
+import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.tare.IEconomyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManagerInternal;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.SparseSetArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DumpUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when
+ * appropriate, and reclaiming ARCs at the right times. The IRS deals with the high level details
+ * while the {@link Agent} deals with the nitty-gritty details.
+ *
+ * Note on locking: Any function with the suffix 'Locked' needs to lock on {@link #mLock}.
+ *
+ * @hide
+ */
+public class InternalResourceService extends SystemService {
+ public static final String TAG = "TARE-IRS";
+ public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG);
+
+ static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS;
+ /** How much of an app's unused wealth should be reclaimed periodically. */
+ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f;
+
+ /** Global local for all resource economy state. */
+ private final Object mLock = new Object();
+
+ private final Handler mHandler;
+ private final BatteryManagerInternal mBatteryManagerInternal;
+ private final PackageManager mPackageManager;
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ private final Agent mAgent;
+ private final CompleteEconomicPolicy mCompleteEconomicPolicy;
+ private final ConfigObserver mConfigObserver;
+ private final EconomyManagerStub mEconomyManagerStub;
+
+ @NonNull
+ @GuardedBy("mLock")
+ private List<PackageInfo> mPkgCache = new ArrayList<>();
+
+ /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
+ @GuardedBy("mLock")
+ private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
+
+ /** Cached mapping of userId+package to their UIDs (for all users) */
+ @GuardedBy("mPackageToUidCache")
+ private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+
+ private volatile boolean mIsEnabled;
+ private volatile int mBootPhase;
+ // In the range [0,100] to represent 0% to 100% battery.
+ @GuardedBy("mLock")
+ private int mCurrentBatteryLevel;
+ // TODO: load from disk
+ @GuardedBy("mLock")
+ private long mLastUnusedReclamationTime;
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Nullable
+ private String getPackageName(Intent intent) {
+ Uri uri = intent.getData();
+ return uri != null ? uri.getSchemeSpecificPart() : null;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_LEVEL_CHANGED:
+ onBatteryLevelChanged();
+ break;
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageRemoved(pkgUid, pkgName);
+ }
+ break;
+ case Intent.ACTION_PACKAGE_ADDED: {
+ if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ onPackageAdded(pkgUid, pkgName);
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_RESTARTED: {
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final int userId = UserHandle.getUserId(pkgUid);
+ onPackageForceStopped(userId, pkgName);
+ }
+ break;
+ case Intent.ACTION_USER_ADDED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserAdded(userId);
+ }
+ break;
+ case Intent.ACTION_USER_REMOVED: {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ onUserRemoved(userId);
+ }
+ break;
+ }
+ }
+ };
+
+ private final UsageStatsManagerInternal.UsageEventListener mSurveillanceAgent =
+ new UsageStatsManagerInternal.UsageEventListener() {
+ /**
+ * Callback to inform listeners of a new event.
+ */
+ @Override
+ public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) {
+ mHandler.obtainMessage(MSG_PROCESS_USAGE_EVENT, userId, 0, event)
+ .sendToTarget();
+ }
+ };
+
+ private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener =
+ new AlarmManager.OnAlarmListener() {
+ @Override
+ public void onAlarm() {
+ synchronized (mLock) {
+ mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE);
+ mLastUnusedReclamationTime = getCurrentTimeMillis();
+ scheduleUnusedWealthReclamationLocked();
+ }
+ }
+ };
+
+ private static final int MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER = 0;
+ private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1;
+ private static final int MSG_PROCESS_USAGE_EVENT = 2;
+ private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*";
+ private static final String KEY_PKG = "pkg";
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public InternalResourceService(Context context) {
+ super(context);
+
+ mHandler = new IrsHandler(TareHandlerThread.get().getLooper());
+ mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+ mPackageManager = context.getPackageManager();
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mEconomyManagerStub = new EconomyManagerStub();
+ mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
+ mAgent = new Agent(this, mCompleteEconomicPolicy);
+
+ mConfigObserver = new ConfigObserver(mHandler, context);
+
+ publishLocalService(EconomyManagerInternal.class, new LocalService());
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.RESOURCE_ECONOMY_SERVICE, mEconomyManagerStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ mBootPhase = phase;
+
+ if (PHASE_SYSTEM_SERVICES_READY == phase) {
+ mConfigObserver.start();
+ setupEverything();
+ }
+ }
+
+ @NonNull
+ Object getLock() {
+ return mLock;
+ }
+
+ @NonNull
+ List<PackageInfo> getInstalledPackages() {
+ synchronized (mLock) {
+ return mPkgCache;
+ }
+ }
+
+ @GuardedBy("mLock")
+ long getMaxCirculationLocked() {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMaxSatiatedCirculation() / 100;
+ }
+
+ @GuardedBy("mLock")
+ long getMinBalanceLocked(final int userId, @NonNull final String pkgName) {
+ return mCurrentBatteryLevel * mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName)
+ / 100;
+ }
+
+ int getUid(final int userId, @NonNull final String pkgName) {
+ synchronized (mPackageToUidCache) {
+ Integer uid = mPackageToUidCache.get(userId, pkgName);
+ if (uid == null) {
+ uid = mPackageManagerInternal.getPackageUid(pkgName, 0, userId);
+ mPackageToUidCache.add(userId, pkgName, uid);
+ }
+ return uid;
+ }
+ }
+
+ boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ boolean isSystem(final int userId, @NonNull String pkgName) {
+ if ("android".equals(pkgName)) {
+ return true;
+ }
+ return UserHandle.isCore(getUid(userId, pkgName));
+ }
+
+ void onBatteryLevelChanged() {
+ synchronized (mLock) {
+ final int newBatteryLevel = getCurrentBatteryLevel();
+ if (newBatteryLevel > mCurrentBatteryLevel) {
+ mAgent.distributeBasicIncomeLocked(newBatteryLevel);
+ }
+ mCurrentBatteryLevel = newBatteryLevel;
+ }
+ }
+
+ void onDeviceStateChanged() {
+ synchronized (mLock) {
+ mAgent.onDeviceStateChangedLocked();
+ }
+ }
+
+ void onPackageAdded(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = mPackageManager.getPackageInfoAsUser(pkgName, 0, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "PM couldn't find newly added package: " + pkgName);
+ return;
+ }
+ synchronized (mPackageToUidCache) {
+ mPackageToUidCache.add(userId, pkgName, uid);
+ }
+ synchronized (mLock) {
+ mPkgCache.add(packageInfo);
+ mUidToPackageCache.add(uid, pkgName);
+ // TODO: only do this when the user first launches the app (app leaves stopped state)
+ mAgent.grantBirthrightLocked(userId, pkgName);
+ }
+ }
+
+ void onPackageForceStopped(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ // TODO: reduce ARC count by some amount
+ }
+ }
+
+ void onPackageRemoved(final int uid, @NonNull final String pkgName) {
+ final int userId = UserHandle.getUserId(uid);
+ synchronized (mPackageToUidCache) {
+ mPackageToUidCache.delete(userId, pkgName);
+ }
+ synchronized (mLock) {
+ mUidToPackageCache.remove(uid, pkgName);
+ for (int i = 0; i < mPkgCache.size(); ++i) {
+ PackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId
+ && pkgName.equals(pkgInfo.packageName)) {
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ mAgent.onPackageRemovedLocked(userId, pkgName);
+ }
+ }
+
+ void onUidStateChanged(final int uid) {
+ synchronized (mLock) {
+ final ArraySet<String> pkgNames = getPackagesForUidLocked(uid);
+ if (pkgNames == null) {
+ Slog.e(TAG, "Don't have packages for uid " + uid);
+ } else {
+ mAgent.onAppStatesChangedLocked(UserHandle.getUserId(uid), pkgNames);
+ }
+ }
+ }
+
+ void onUserAdded(final int userId) {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ mAgent.grantBirthrightsLocked(userId);
+ }
+ }
+
+ void onUserRemoved(final int userId) {
+ synchronized (mLock) {
+ ArrayList<String> removedPkgs = new ArrayList<>();
+ for (int i = mPkgCache.size() - 1; i >= 0; --i) {
+ PackageInfo pkgInfo = mPkgCache.get(i);
+ if (UserHandle.getUserId(pkgInfo.applicationInfo.uid) == userId) {
+ removedPkgs.add(pkgInfo.packageName);
+ mUidToPackageCache.remove(pkgInfo.applicationInfo.uid);
+ mPkgCache.remove(i);
+ break;
+ }
+ }
+ loadInstalledPackageListLocked();
+ mAgent.onUserRemovedLocked(userId, removedPkgs);
+ }
+ }
+
+ void postAffordabilityChanged(final int userId, @NonNull final String pkgName,
+ @NonNull Agent.ActionAffordabilityNote affordabilityNote) {
+ if (DEBUG) {
+ Slog.d(TAG, userId + ":" + pkgName + " affordability changed to "
+ + affordabilityNote.isCurrentlyAffordable());
+ }
+ Message msg = mHandler.obtainMessage(
+ MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER, userId, 0, affordabilityNote);
+ Bundle data = new Bundle();
+ data.putString(KEY_PKG, pkgName);
+ msg.setData(data);
+ msg.sendToTarget();
+ }
+
+ @GuardedBy("mLock")
+ private void processUsageEventLocked(final int userId, @NonNull UsageEvents.Event event) {
+ if (!mIsEnabled) {
+ return;
+ }
+ final String pkgName = event.getPackageName();
+ if (DEBUG) {
+ Slog.d(TAG, "Processing event " + event.getEventType()
+ + " for " + appToString(userId, pkgName));
+ }
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ switch (event.getEventType()) {
+ case UsageEvents.Event.ACTIVITY_RESUMED:
+ mAgent.noteOngoingEventLocked(userId, pkgName,
+ EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed);
+ break;
+ case UsageEvents.Event.ACTIVITY_PAUSED:
+ case UsageEvents.Event.ACTIVITY_STOPPED:
+ case UsageEvents.Event.ACTIVITY_DESTROYED:
+ final long now = getCurrentTimeMillis();
+ mAgent.stopOngoingActionLocked(userId, pkgName,
+ EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed, now);
+ break;
+ case UsageEvents.Event.USER_INTERACTION:
+ case UsageEvents.Event.CHOOSER_ACTION:
+ mAgent.noteInstantaneousEventLocked(userId, pkgName,
+ EconomicPolicy.REWARD_OTHER_USER_INTERACTION, null);
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case UsageEvents.Event.NOTIFICATION_SEEN:
+ mAgent.noteInstantaneousEventLocked(userId, pkgName,
+ EconomicPolicy.REWARD_NOTIFICATION_SEEN, null);
+ break;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleUnusedWealthReclamationLocked() {
+ final long now = getCurrentTimeMillis();
+ final long nextReclamationTime =
+ Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000);
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + (nextReclamationTime - now),
+ 30 * MINUTE_IN_MILLIS,
+ ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler);
+ } else {
+ mHandler.sendEmptyMessageDelayed(
+ MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, 30_000);
+ }
+ });
+ }
+
+ private int getCurrentBatteryLevel() {
+ return mBatteryManagerInternal.getBatteryLevel();
+ }
+
+ @Nullable
+ @GuardedBy("mLock")
+ private ArraySet<String> getPackagesForUidLocked(final int uid) {
+ ArraySet<String> packages = mUidToPackageCache.get(uid);
+ if (packages == null) {
+ final String[] pkgs = mPackageManager.getPackagesForUid(uid);
+ if (pkgs != null) {
+ for (String pkg : pkgs) {
+ mUidToPackageCache.add(uid, pkg);
+ }
+ packages = mUidToPackageCache.get(uid);
+ }
+ }
+ return packages;
+ }
+
+ @GuardedBy("mLock")
+ private void loadInstalledPackageListLocked() {
+ mPkgCache = mPackageManager.getInstalledPackages(0);
+ }
+
+ private void registerListeners() {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED);
+ getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+
+ final IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ pkgFilter.addDataScheme("package");
+ getContext()
+ .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null);
+
+ final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
+ getContext()
+ .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+
+ UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class);
+ usmi.registerListener(mSurveillanceAgent);
+ }
+
+ /** Perform long-running and/or heavy setup work. This should be called off the main thread. */
+ private void setupHeavyWork() {
+ synchronized (mLock) {
+ loadInstalledPackageListLocked();
+ // TODO: base on if we have anything persisted
+ final boolean isFirstSetup = true;
+ if (isFirstSetup) {
+ mAgent.grantBirthrightsLocked();
+ }
+ }
+ }
+
+ private void setupEverything() {
+ if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ registerListeners();
+ mCurrentBatteryLevel = getCurrentBatteryLevel();
+ mHandler.post(this::setupHeavyWork);
+ scheduleUnusedWealthReclamationLocked();
+ mCompleteEconomicPolicy.setup();
+ }
+ }
+
+ private void tearDownEverything() {
+ if (mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ mAgent.tearDownLocked();
+ mCompleteEconomicPolicy.tearDown();
+ mHandler.post(() -> {
+ // Never call out to AlarmManager with the lock held. This sits below AM.
+ AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class);
+ if (alarmManager != null) {
+ alarmManager.cancel(mUnusedWealthReclamationListener);
+ }
+ });
+ mPkgCache.clear();
+ mUidToPackageCache.clear();
+ getContext().unregisterReceiver(mBroadcastReceiver);
+ UsageStatsManagerInternal usmi =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ usmi.unregisterListener(mSurveillanceAgent);
+ }
+ synchronized (mPackageToUidCache) {
+ mPackageToUidCache.clear();
+ }
+ }
+
+ private final class IrsHandler extends Handler {
+ IrsHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_NOTIFY_AFFORDABILITY_CHANGE_LISTENER: {
+ Bundle data = msg.getData();
+ final int userId = msg.arg1;
+ final String pkgName = data.getString(KEY_PKG);
+ final Agent.ActionAffordabilityNote affordabilityNote =
+ (Agent.ActionAffordabilityNote) msg.obj;
+ final EconomyManagerInternal.AffordabilityChangeListener listener =
+ affordabilityNote.getListener();
+ listener.onAffordabilityChanged(userId, pkgName,
+ affordabilityNote.getActionBill(),
+ affordabilityNote.isCurrentlyAffordable());
+ }
+ break;
+
+ case MSG_PROCESS_USAGE_EVENT: {
+ final int userId = msg.arg1;
+ final UsageEvents.Event event = (UsageEvents.Event) msg.obj;
+ synchronized (mLock) {
+ processUsageEventLocked(userId, event);
+ }
+ }
+ break;
+
+ case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: {
+ removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT);
+ synchronized (mLock) {
+ scheduleUnusedWealthReclamationLocked();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Binder stub trampoline implementation
+ */
+ final class EconomyManagerStub extends IEconomyManager.Stub {
+ /**
+ * "dumpsys" infrastructure
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
+
+ if (!ArrayUtils.isEmpty(args)) {
+ String arg = args[0];
+ if ("-h".equals(arg) || "--help".equals(arg)) {
+ dumpHelp(pw);
+ return;
+ } else if ("-a".equals(arg)) {
+ // -a is passed when dumping a bug report so we have to acknowledge the
+ // argument. However, we currently don't do anything differently for bug
+ // reports.
+ } else if (arg.length() > 0 && arg.charAt(0) == '-') {
+ pw.println("Unknown option: " + arg);
+ return;
+ }
+ }
+
+ final long identityToken = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(new IndentingPrintWriter(pw, " "));
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ }
+
+ private final class LocalService implements EconomyManagerInternal {
+ /**
+ * Use an extremely large value to indicate that an app can pay for a bill indefinitely.
+ * The value set here should be large/long enough that there's no reasonable expectation
+ * of a device operating uninterrupted (or in the exact same state) for that period of time.
+ * We intentionally don't use Long.MAX_VALUE to avoid potential overflow if a client
+ * doesn't check the value and just immediately adds it to the current time.
+ */
+ private static final long FOREVER_MS = 27 * 365 * 24 * HOUR_IN_MILLIS;
+
+ @Override
+ public void registerAffordabilityChangeListener(int userId, @NonNull String pkgName,
+ @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
+ if (isSystem(userId, pkgName)) {
+ // The system's affordability never changes.
+ return;
+ }
+ synchronized (mLock) {
+ mAgent.registerAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
+ }
+ }
+
+ @Override
+ public void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName,
+ @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill) {
+ if (isSystem(userId, pkgName)) {
+ // The system's affordability never changes.
+ return;
+ }
+ synchronized (mLock) {
+ mAgent.unregisterAffordabilityChangeListenerLocked(userId, pkgName, listener, bill);
+ }
+ }
+
+ @Override
+ public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) {
+ if (!mIsEnabled) {
+ return true;
+ }
+ if (isSystem(userId, pkgName)) {
+ // The government, I mean the system, can create ARCs as it needs to in order to
+ // operate.
+ return true;
+ }
+ // TODO: take temp-allowlist into consideration
+ long requiredBalance = 0;
+ final List<EconomyManagerInternal.AnticipatedAction> projectedActions =
+ bill.getAnticipatedActions();
+ for (int i = 0; i < projectedActions.size(); ++i) {
+ AnticipatedAction action = projectedActions.get(i);
+ final long cost =
+ mCompleteEconomicPolicy.getCostOfAction(action.actionId, userId, pkgName);
+ requiredBalance += cost * action.numInstantaneousCalls
+ + cost * (action.ongoingDurationMs / 1000);
+ }
+ synchronized (mLock) {
+ return mAgent.getBalanceLocked(userId, pkgName) >= requiredBalance;
+ }
+ }
+
+ @Override
+ public long getMaxDurationMs(int userId, @NonNull String pkgName,
+ @NonNull ActionBill bill) {
+ if (!mIsEnabled) {
+ return FOREVER_MS;
+ }
+ if (isSystem(userId, pkgName)) {
+ return FOREVER_MS;
+ }
+ long totalCostPerSecond = 0;
+ final List<EconomyManagerInternal.AnticipatedAction> projectedActions =
+ bill.getAnticipatedActions();
+ for (int i = 0; i < projectedActions.size(); ++i) {
+ AnticipatedAction action = projectedActions.get(i);
+ final long cost =
+ mCompleteEconomicPolicy.getCostOfAction(action.actionId, userId, pkgName);
+ totalCostPerSecond += cost;
+ }
+ if (totalCostPerSecond == 0) {
+ return FOREVER_MS;
+ }
+ synchronized (mLock) {
+ return mAgent.getBalanceLocked(userId, pkgName) * 1000 / totalCostPerSecond;
+ }
+ }
+
+ @Override
+ public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ if (!mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag);
+ }
+ }
+
+ @Override
+ public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ if (!mIsEnabled) {
+ return;
+ }
+ synchronized (mLock) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed);
+ }
+ }
+
+ @Override
+ public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId,
+ @Nullable String tag) {
+ if (!mIsEnabled) {
+ return;
+ }
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long now = getCurrentTimeMillis();
+ synchronized (mLock) {
+ mAgent.stopOngoingActionLocked(userId, pkgName, eventId, tag, nowElapsed, now);
+ }
+ }
+ }
+
+ private class ConfigObserver extends ContentObserver {
+ private final ContentResolver mContentResolver;
+
+ ConfigObserver(Handler handler, Context context) {
+ super(handler);
+ mContentResolver = context.getContentResolver();
+ }
+
+ public void start() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
+ updateConfig();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateConfig();
+ }
+
+ private void updateConfig() {
+ final boolean isTareEnabled = Settings.Global.getInt(mContentResolver,
+ Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
+ if (mIsEnabled != isTareEnabled) {
+ mIsEnabled = isTareEnabled;
+ if (mIsEnabled) {
+ setupEverything();
+ } else {
+ tearDownEverything();
+ }
+ }
+ }
+ }
+
+ private static void dumpHelp(PrintWriter pw) {
+ pw.println("Resource Economy (economy) dump options:");
+ pw.println(" [-h|--help] [package] ...");
+ pw.println(" -h | --help: print this help");
+ pw.println(" [package] is an optional package name to limit the output to.");
+ }
+
+ private void dumpInternal(final IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.print("Is enabled: ");
+ pw.println(mIsEnabled);
+
+ pw.print("Current battery level: ");
+ pw.println(mCurrentBatteryLevel);
+
+ final long maxCircluation = getMaxCirculationLocked();
+ pw.print("Max circulation (current/satiated): ");
+ pw.print(narcToString(maxCircluation));
+ pw.print("/");
+ pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
+
+ final long currentCirculation = mAgent.getCurrentCirculationLocked();
+ pw.print("Current GDP: ");
+ pw.print(narcToString(currentCirculation));
+ pw.print(" (");
+ pw.print(String.format("%.2f", 100f * currentCirculation / maxCircluation));
+ pw.println("% of current max)");
+
+ pw.println();
+ mCompleteEconomicPolicy.dump(pw);
+
+ pw.println();
+ mAgent.dumpLocked(pw);
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
new file mode 100644
index 000000000000..c332f2f53b08
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE;
+import static android.app.tare.EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP;
+import static android.app.tare.EconomyManager.KEY_JS_MAX_CIRCULATION;
+import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING;
+import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+
+import static com.android.server.tare.Modifier.COST_MODIFIER_CHARGING;
+import static com.android.server.tare.Modifier.COST_MODIFIER_DEVICE_IDLE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_POWER_SAVE_MODE;
+import static com.android.server.tare.Modifier.COST_MODIFIER_PROCESS_STATE;
+import static com.android.server.tare.TareUtils.arcToNarc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+
+/**
+ * Policy defining pricing information and daily ARC requirements and suggestions for
+ * JobScheduler.
+ */
+public class JobSchedulerEconomicPolicy extends EconomicPolicy {
+ private static final String TAG = "TARE- " + JobSchedulerEconomicPolicy.class.getSimpleName();
+
+ public static final int ACTION_JOB_MAX_START = TYPE_ACTION | POLICY_JS | 0;
+ public static final int ACTION_JOB_MAX_RUNNING = TYPE_ACTION | POLICY_JS | 1;
+ public static final int ACTION_JOB_HIGH_START = TYPE_ACTION | POLICY_JS | 2;
+ public static final int ACTION_JOB_HIGH_RUNNING = TYPE_ACTION | POLICY_JS | 3;
+ public static final int ACTION_JOB_DEFAULT_START = TYPE_ACTION | POLICY_JS | 4;
+ public static final int ACTION_JOB_DEFAULT_RUNNING = TYPE_ACTION | POLICY_JS | 5;
+ public static final int ACTION_JOB_LOW_START = TYPE_ACTION | POLICY_JS | 6;
+ public static final int ACTION_JOB_LOW_RUNNING = TYPE_ACTION | POLICY_JS | 7;
+ public static final int ACTION_JOB_MIN_START = TYPE_ACTION | POLICY_JS | 8;
+ public static final int ACTION_JOB_MIN_RUNNING = TYPE_ACTION | POLICY_JS | 9;
+ public static final int ACTION_JOB_TIMEOUT = TYPE_ACTION | POLICY_JS | 10;
+
+ private static final int[] COST_MODIFIERS = new int[]{
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE
+ };
+
+ private long mMinSatiatedBalance;
+ private long mMaxSatiatedBalance;
+ private long mMaxSatiatedCirculation;
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final SettingsObserver mSettingsObserver;
+ private final InternalResourceService mInternalResourceService;
+
+ private final SparseArray<Action> mActions = new SparseArray<>();
+ private final SparseArray<Reward> mRewards = new SparseArray<>();
+
+ JobSchedulerEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ mInternalResourceService = irs;
+ mSettingsObserver = new SettingsObserver(TareHandlerThread.getHandler());
+ loadConstants("");
+ }
+
+ @Override
+ void setup() {
+ super.setup();
+ ContentResolver resolver = mInternalResourceService.getContext().getContentResolver();
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ loadConstants(Settings.Global.getString(
+ mInternalResourceService.getContext().getContentResolver(),
+ TARE_JOB_SCHEDULER_CONSTANTS));
+ }
+
+ @Override
+ long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
+ // TODO: incorporate time since usage
+ return mMinSatiatedBalance;
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return mMaxSatiatedBalance;
+ }
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return mMaxSatiatedCirculation;
+ }
+
+ @NonNull
+ @Override
+ int[] getCostModifiers() {
+ return COST_MODIFIERS;
+ }
+
+ @Nullable
+ @Override
+ Action getAction(@AppAction int actionId) {
+ return mActions.get(actionId);
+ }
+
+ @Nullable
+ @Override
+ Reward getReward(@UtilityReward int rewardId) {
+ return mRewards.get(rewardId);
+ }
+
+ private void loadConstants(String policyValuesString) {
+ mActions.clear();
+ mRewards.clear();
+
+ try {
+ mParser.setString(policyValuesString);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Global setting key incorrect: ", e);
+ }
+
+ mMinSatiatedBalance = arcToNarc(
+ mParser.getInt(KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
+ DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP));
+ mMaxSatiatedBalance = arcToNarc(mParser.getInt(KEY_JS_MAX_SATIATED_BALANCE,
+ DEFAULT_JS_MAX_SATIATED_BALANCE));
+ mMaxSatiatedCirculation = arcToNarc(mParser.getInt(KEY_JS_MAX_CIRCULATION,
+ DEFAULT_JS_MAX_CIRCULATION));
+
+ mActions.put(ACTION_JOB_MAX_START, new Action(ACTION_JOB_MAX_START,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_CTP,
+ DEFAULT_JS_ACTION_JOB_MAX_START_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE))));
+ mActions.put(ACTION_JOB_MAX_RUNNING, new Action(ACTION_JOB_MAX_RUNNING,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
+ DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE))));
+ mActions.put(ACTION_JOB_HIGH_START, new Action(ACTION_JOB_HIGH_START,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_CTP,
+ DEFAULT_JS_ACTION_JOB_HIGH_START_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE))));
+ mActions.put(ACTION_JOB_HIGH_RUNNING, new Action(ACTION_JOB_HIGH_RUNNING,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
+ DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE))));
+ mActions.put(ACTION_JOB_DEFAULT_START, new Action(ACTION_JOB_DEFAULT_START,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
+ DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE))));
+ mActions.put(ACTION_JOB_DEFAULT_RUNNING, new Action(ACTION_JOB_DEFAULT_RUNNING,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
+ DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE))));
+ mActions.put(ACTION_JOB_LOW_START, new Action(ACTION_JOB_LOW_START,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_CTP,
+ DEFAULT_JS_ACTION_JOB_LOW_START_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE))));
+ mActions.put(ACTION_JOB_LOW_RUNNING, new Action(ACTION_JOB_LOW_RUNNING,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
+ DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE))));
+ mActions.put(ACTION_JOB_MIN_START, new Action(ACTION_JOB_MIN_START,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_CTP,
+ DEFAULT_JS_ACTION_JOB_MIN_START_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE))));
+ mActions.put(ACTION_JOB_MIN_RUNNING, new Action(ACTION_JOB_MIN_RUNNING,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
+ DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE))));
+ mActions.put(ACTION_JOB_TIMEOUT, new Action(ACTION_JOB_TIMEOUT,
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
+ DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP)),
+ arcToNarc(mParser.getInt(KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
+ DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE))));
+
+ mRewards.put(REWARD_TOP_ACTIVITY, new Reward(REWARD_TOP_ACTIVITY,
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
+ DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT)),
+ (long) (arcToNarc(1) * mParser.getFloat(KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
+ DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_TOP_ACTIVITY_MAX,
+ DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX))));
+ mRewards.put(REWARD_NOTIFICATION_SEEN, new Reward(REWARD_NOTIFICATION_SEEN,
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
+ DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT)),
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
+ DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
+ DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX))));
+ mRewards.put(REWARD_NOTIFICATION_INTERACTION,
+ new Reward(REWARD_NOTIFICATION_INTERACTION,
+ arcToNarc(mParser.getInt(
+ KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
+ DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(
+ KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
+ DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(
+ KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
+ DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX))));
+ mRewards.put(REWARD_WIDGET_INTERACTION, new Reward(REWARD_WIDGET_INTERACTION,
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
+ DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
+ DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
+ DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX))));
+ mRewards.put(REWARD_OTHER_USER_INTERACTION,
+ new Reward(REWARD_OTHER_USER_INTERACTION,
+ arcToNarc(mParser.getInt(KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
+ DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT)),
+ arcToNarc(mParser.getInt(
+ KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
+ DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING)),
+ arcToNarc(mParser.getInt(
+ KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
+ DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX))));
+ }
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ loadConstants(Settings.Global.getString(
+ mInternalResourceService.getContext().getContentResolver(),
+ TARE_JOB_SCHEDULER_CONSTANTS));
+ }
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Actions:");
+ pw.increaseIndent();
+ for (int i = 0; i < mActions.size(); ++i) {
+ dumpAction(pw, mActions.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Rewards:");
+ pw.increaseIndent();
+ for (int i = 0; i < mRewards.size(); ++i) {
+ dumpReward(pw, mRewards.valueAt(i));
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
new file mode 100644
index 000000000000..f2b78c0cd3e5
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.dumpTime;
+import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+import static com.android.server.tare.TareUtils.narcToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.IndentingPrintWriter;
+import android.util.SparseLongArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Ledger to track the last recorded balance and recent activities of an app.
+ */
+class Ledger {
+ static class Transaction {
+ public final long startTimeMs;
+ public final long endTimeMs;
+ public final int eventId;
+ @Nullable
+ public final String tag;
+ public final long delta;
+
+ Transaction(long startTimeMs, long endTimeMs,
+ int eventId, @Nullable String tag, long delta) {
+ this.startTimeMs = startTimeMs;
+ this.endTimeMs = endTimeMs;
+ this.eventId = eventId;
+ this.tag = tag;
+ this.delta = delta;
+ }
+ }
+
+ /** Last saved balance. This doesn't take currently ongoing events into account. */
+ private long mCurrentBalance = 0;
+ private final List<Transaction> mTransactions = new ArrayList<>();
+ private final SparseLongArray mCumulativeDeltaPerReason = new SparseLongArray();
+ private long mEarliestSumTime;
+
+ Ledger() {
+ }
+
+ long getCurrentBalance() {
+ return mCurrentBalance;
+ }
+
+ @Nullable
+ Transaction getEarliestTransaction() {
+ if (mTransactions.size() > 0) {
+ return mTransactions.get(0);
+ }
+ return null;
+ }
+
+ void recordTransaction(@NonNull Transaction transaction) {
+ mTransactions.add(transaction);
+ mCurrentBalance += transaction.delta;
+
+ final long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+ mCumulativeDeltaPerReason.put(transaction.eventId, sum + transaction.delta);
+ mEarliestSumTime = Math.min(mEarliestSumTime, transaction.startTimeMs);
+ }
+
+ long get24HourSum(int eventId, final long now) {
+ final long windowStartTime = now - 24 * HOUR_IN_MILLIS;
+ if (mEarliestSumTime < windowStartTime) {
+ // Need to redo sums
+ mCumulativeDeltaPerReason.clear();
+ for (int i = mTransactions.size() - 1; i >= 0; --i) {
+ final Transaction transaction = mTransactions.get(i);
+ if (transaction.endTimeMs <= windowStartTime) {
+ break;
+ }
+ long sum = mCumulativeDeltaPerReason.get(transaction.eventId);
+ if (transaction.startTimeMs >= windowStartTime) {
+ sum += transaction.delta;
+ } else {
+ // Pro-rate durationed deltas. Intentionally floor the result.
+ sum += (long) (1.0 * (transaction.endTimeMs - windowStartTime)
+ * transaction.delta)
+ / (transaction.endTimeMs - transaction.startTimeMs);
+ }
+ mCumulativeDeltaPerReason.put(transaction.eventId, sum);
+ }
+ mEarliestSumTime = windowStartTime;
+ }
+ return mCumulativeDeltaPerReason.get(eventId);
+ }
+
+ /** Deletes transactions that are older than {@code minAgeMs}. */
+ void removeOldTransactions(long minAgeMs) {
+ final long cutoff = getCurrentTimeMillis() - minAgeMs;
+ while (mTransactions.size() > 0 && mTransactions.get(0).endTimeMs <= cutoff) {
+ mTransactions.remove(0);
+ }
+ }
+
+ void dump(IndentingPrintWriter pw, int numRecentTransactions) {
+ pw.print("Current balance", narcToString(getCurrentBalance())).println();
+
+ final int size = mTransactions.size();
+ for (int i = Math.max(0, size - numRecentTransactions); i < size; ++i) {
+ final Transaction transaction = mTransactions.get(i);
+
+ dumpTime(pw, transaction.startTimeMs);
+ pw.print("--");
+ dumpTime(pw, transaction.endTimeMs);
+ pw.print(": ");
+ pw.print(EconomicPolicy.eventToString(transaction.eventId));
+ pw.print(" --> ");
+ pw.println(narcToString(transaction.delta));
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
new file mode 100644
index 000000000000..311b6cb7910f
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.IntDef;
+import android.util.IndentingPrintWriter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base class of a modifier that can affect end pricing.
+ */
+abstract class Modifier {
+ static final int COST_MODIFIER_CHARGING = 0;
+ static final int COST_MODIFIER_DEVICE_IDLE = 1;
+ static final int COST_MODIFIER_POWER_SAVE_MODE = 2;
+ static final int COST_MODIFIER_PROCESS_STATE = 3;
+ static final int NUM_COST_MODIFIERS = COST_MODIFIER_PROCESS_STATE + 1;
+
+ @IntDef({
+ COST_MODIFIER_CHARGING,
+ COST_MODIFIER_DEVICE_IDLE,
+ COST_MODIFIER_POWER_SAVE_MODE,
+ COST_MODIFIER_PROCESS_STATE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CostModifier {
+ }
+
+ /**
+ * Returns a modified cost to produce based on the modifier's state.
+ *
+ * @param ctp Current cost to produce
+ */
+ long getModifiedCostToProduce(long ctp) {
+ return ctp;
+ }
+
+ /**
+ * Returns a modified price based on the modifier's state.
+ *
+ * @param price Current price
+ */
+ long getModifiedPrice(long price) {
+ return price;
+ }
+
+ void setup() {
+ }
+
+ void tearDown() {
+ }
+
+ abstract void dump(IndentingPrintWriter pw);
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/OWNERS b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
new file mode 100644
index 000000000000..96ec75f39e4e
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
@@ -0,0 +1,5 @@
+dplotnikov@google.com
+kwekua@google.com
+mwachens@google.com
+suprabh@google.com
+yamasani@google.com \ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
new file mode 100644
index 000000000000..764a3a8b1ed9
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.os.PowerManager;
+import android.util.IndentingPrintWriter;
+
+/** Modifier that makes things more expensive in adaptive and full battery saver are active. */
+class PowerSaveModeModifier extends Modifier {
+ private final InternalResourceService mIrs;
+ private final PowerManager mPowerManager;
+
+ PowerSaveModeModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ mPowerManager = irs.getContext().getSystemService(PowerManager.class);
+ }
+
+ @Override
+ long getModifiedCostToProduce(long ctp) {
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.5 * ctp);
+ }
+ // TODO: get adaptive power save mode
+ if (mPowerManager.isPowerSaveMode()) {
+ return (long) (1.25 * ctp);
+ }
+ return ctp;
+ }
+
+ @Override
+ void dump(IndentingPrintWriter pw) {
+ pw.print("power save=");
+ pw.println(mPowerManager.isPowerSaveMode());
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
new file mode 100644
index 000000000000..67a3dc67e569
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.IUidObserver;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Modifier that makes things more cheaper based on an app's process state. */
+class ProcessStateModifier extends Modifier {
+ private static final String TAG = "TARE-" + ProcessStateModifier.class.getSimpleName();
+
+ private static final int PROC_STATE_BUCKET_NONE = 0;
+ private static final int PROC_STATE_BUCKET_TOP = 1;
+ private static final int PROC_STATE_BUCKET_FGS = 2;
+ private static final int PROC_STATE_BUCKET_BFGS = 3;
+ private static final int PROC_STATE_BUCKET_BG = 4;
+
+ @IntDef(prefix = {"PROC_STATE_BUCKET_"}, value = {
+ PROC_STATE_BUCKET_NONE,
+ PROC_STATE_BUCKET_TOP,
+ PROC_STATE_BUCKET_FGS,
+ PROC_STATE_BUCKET_BFGS,
+ PROC_STATE_BUCKET_BG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProcStateBucket {
+ }
+
+ private final Object mLock = new Object();
+ private final InternalResourceService mIrs;
+
+ /** Cached mapping of userId+package to their UIDs (for all users) */
+ private final SparseArrayMap<String, Integer> mPackageToUidCache = new SparseArrayMap<>();
+
+ @GuardedBy("mLock")
+ private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
+
+ private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ final int newBucket = getProcStateBucket(procState);
+ synchronized (mLock) {
+ final int curBucket = mUidProcStateBucketCache.get(uid);
+ if (curBucket != newBucket) {
+ mUidProcStateBucketCache.put(uid, newBucket);
+ }
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ synchronized (mLock) {
+ if (mUidProcStateBucketCache.indexOfKey(uid) < 0) {
+ Slog.e(TAG, "UID " + uid + " marked gone but wasn't in cache.");
+ return;
+ }
+ mUidProcStateBucketCache.delete(uid);
+ notifyStateChangedLocked(uid);
+ }
+ }
+
+ @Override
+ public void onUidActive(int uid) {
+ }
+
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+ };
+
+ ProcessStateModifier(@NonNull InternalResourceService irs) {
+ super();
+ mIrs = irs;
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void setup() {
+ try {
+ ActivityManager.getService().registerUidObserver(mUidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void tearDown() {
+ try {
+ ActivityManager.getService().unregisterUidObserver(mUidObserver);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+ mPackageToUidCache.clear();
+ mUidProcStateBucketCache.clear();
+ }
+
+ /**
+ * Get the final modified price based on an app's process state.
+ *
+ * @param ctp Cost to produce. @see EconomicPolicy.Action#costToProduce
+ * @param price Current price
+ */
+ long getModifiedPrice(final int userId, @NonNull final String pkgName,
+ final long ctp, final long price) {
+ final int procState;
+ synchronized (mLock) {
+ procState = mUidProcStateBucketCache.get(
+ mIrs.getUid(userId, pkgName), PROC_STATE_BUCKET_NONE);
+ }
+ switch (procState) {
+ case PROC_STATE_BUCKET_TOP:
+ return 0;
+ case PROC_STATE_BUCKET_FGS:
+ // Can't get notification priority. Just use CTP for now.
+ return ctp;
+ case PROC_STATE_BUCKET_BFGS:
+ return (long) (ctp + .5 * (price - ctp));
+ case PROC_STATE_BUCKET_BG:
+ default:
+ return price;
+ }
+ }
+
+ @Override
+ @GuardedBy("mLock")
+ void dump(IndentingPrintWriter pw) {
+ pw.print("Proc state bucket cache = ");
+ pw.println(mUidProcStateBucketCache);
+ }
+
+ @ProcStateBucket
+ private int getProcStateBucket(int procState) {
+ if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+ return PROC_STATE_BUCKET_TOP;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_FGS;
+ }
+ if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ return PROC_STATE_BUCKET_BFGS;
+ }
+ return PROC_STATE_BUCKET_BG;
+ }
+
+ @GuardedBy("mLock")
+ private void notifyStateChangedLocked(final int uid) {
+ // Never call out to the IRS with the local lock held.
+ TareHandlerThread.getHandler().post(() -> mIrs.onUidStateChanged(uid));
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
new file mode 100644
index 000000000000..73b00b6cfa24
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"}
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.tare"}
+ ]
+ }
+ ]
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
new file mode 100644
index 000000000000..5b2b6607ba14
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tare;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Trace;
+
+/**
+ * Singleton thread for all of TARE.
+ *
+ * @see com.android.internal.os.BackgroundThread
+ */
+final class TareHandlerThread extends HandlerThread {
+
+ private static TareHandlerThread sInstance;
+ private static Handler sHandler;
+
+ private TareHandlerThread() {
+ super("tare");
+ }
+
+ private static void ensureThreadLocked() {
+ if (sInstance == null) {
+ sInstance = new TareHandlerThread();
+ sInstance.start();
+ final Looper looper = sInstance.getLooper();
+ looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
+ sHandler = new Handler(sInstance.getLooper());
+ }
+ }
+
+ static TareHandlerThread get() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sInstance;
+ }
+
+ /** Returns the singleton handler for TareHandlerThread. */
+ public static Handler getHandler() {
+ synchronized (TareHandlerThread.class) {
+ ensureThreadLocked();
+ }
+ return sHandler;
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
new file mode 100644
index 000000000000..78508d4129ef
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/tare/TareUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.text.SimpleDateFormat;
+import java.time.Clock;
+
+class TareUtils {
+ private static final long NARC_IN_ARC = 1_000_000_000L;
+
+ @SuppressLint("SimpleDateFormat")
+ private static final SimpleDateFormat sDumpDateFormat =
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+ @VisibleForTesting
+ static Clock sSystemClock = Clock.systemUTC();
+
+ static long arcToNarc(int arcs) {
+ return arcs * NARC_IN_ARC;
+ }
+
+ static void dumpTime(IndentingPrintWriter pw, long time) {
+ pw.print(sDumpDateFormat.format(time));
+ }
+
+ static long getCurrentTimeMillis() {
+ return sSystemClock.millis();
+ }
+
+ static int narcToArc(long narcs) {
+ return (int) (narcs / NARC_IN_ARC);
+ }
+
+ @NonNull
+ static String narcToString(long narcs) {
+ if (narcs == 0) {
+ return "0 ARCs";
+ }
+ final long sub = Math.abs(narcs) % NARC_IN_ARC;
+ final long arcs = narcToArc(narcs);
+ if (arcs == 0) {
+ return sub == 1
+ ? sub + " narc"
+ : sub + " narcs";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(arcs);
+ if (sub > 0) {
+ sb.append(".").append(sub / (NARC_IN_ARC / 1000));
+ }
+ sb.append(" ARC");
+ if (arcs != 1 || sub > 0) {
+ sb.append("s");
+ }
+ return sb.toString();
+ }
+
+ /** Returns a standardized format for printing userId+pkgName combinations. */
+ @NonNull
+ static String appToString(int userId, String pkgName) {
+ return "<" + userId + ">" + pkgName;
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index d532e20a0158..187422bf1970 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -462,6 +462,17 @@ public class AppIdleHistory {
return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime;
}
+ public long getTimeSinceLastUsedByUser(String packageName, int userId, long elapsedRealtime) {
+ ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
+ AppUsageHistory appUsageHistory =
+ getPackageHistory(userHistory, packageName, elapsedRealtime, false);
+ if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE
+ || appUsageHistory.lastUsedByUserElapsedTime == 0) {
+ return Long.MAX_VALUE;
+ }
+ return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime;
+ }
+
public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 4b081d258fd4..2fa10f08e9ae 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1082,6 +1082,14 @@ public class AppStandbyController
}
@Override
+ public long getTimeSinceLastUsedByUser(String packageName, int userId) {
+ final long elapsedRealtime = mInjector.elapsedRealtime();
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.getTimeSinceLastUsedByUser(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ @Override
public void onUserRemoved(int userId) {
synchronized (mAppIdleLock) {
mAppIdleHistory.onUserRemoved(userId);
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c908787472..3d2191410413 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsMediaParserTestCases"
},
{
- "name": "CtsMediaParserHostSideTestCases"
+ "name": "CtsMediaParserHostTestCases"
}
]
}
diff --git a/apex/media/framework/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
index 26f4568fa7e5..7e71591d05fb 100644
--- a/apex/media/framework/java/android/media/Session2Command.java
+++ b/apex/media/framework/java/android/media/Session2Command.java
@@ -37,9 +37,8 @@ import java.util.Objects;
* If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
* {@link #getCustomAction()} shouldn't be {@code null}.
* <p>
- * Refer to the
- * <a href="{@docRoot}reference/androidx/media2/SessionCommand2.html">AndroidX SessionCommand</a>
- * class for the list of valid commands.
+ * Refer to the <a href="{@docRoot}reference/androidx/media2/session/SessionCommand.html">
+ * AndroidX SessionCommand</a> class for the list of valid commands.
*/
public final class Session2Command implements Parcelable {
/**
diff --git a/api/Android.bp b/api/Android.bp
index 2ea180ebf598..66b6dbafcc85 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -90,6 +90,7 @@ genrule {
":framework-sdkextensions{.public.api.txt}",
":framework-statsd{.public.api.txt}",
":framework-tethering{.public.api.txt}",
+ ":framework-uwb{.public.api.txt}",
":framework-wifi{.public.api.txt}",
":i18n.module.public.api{.public.api.txt}",
":non-updatable-current.txt",
@@ -149,6 +150,7 @@ genrule {
":framework-sdkextensions{.public.stubs.source}",
":framework-statsd{.public.stubs.source}",
":framework-tethering{.public.stubs.source}",
+ ":framework-uwb{.public.stubs.source}",
":framework-wifi{.public.stubs.source}",
":i18n.module.public.api{.public.stubs.source}",
],
@@ -175,6 +177,7 @@ genrule {
":framework-sdkextensions{.public.removed-api.txt}",
":framework-statsd{.public.removed-api.txt}",
":framework-tethering{.public.removed-api.txt}",
+ ":framework-uwb{.public.removed-api.txt}",
":framework-wifi{.public.removed-api.txt}",
":i18n.module.public.api{.public.removed-api.txt}",
":non-updatable-removed.txt",
@@ -215,6 +218,7 @@ genrule {
":framework-sdkextensions{.system.api.txt}",
":framework-statsd{.system.api.txt}",
":framework-tethering{.system.api.txt}",
+ ":framework-uwb{.system.api.txt}",
":framework-wifi{.system.api.txt}",
":non-updatable-system-current.txt",
],
@@ -273,6 +277,7 @@ genrule {
":framework-sdkextensions{.system.removed-api.txt}",
":framework-statsd{.system.removed-api.txt}",
":framework-tethering{.system.removed-api.txt}",
+ ":framework-uwb{.system.removed-api.txt}",
":framework-wifi{.system.removed-api.txt}",
":non-updatable-system-removed.txt",
],
@@ -313,6 +318,7 @@ genrule {
":framework-sdkextensions{.module-lib.api.txt}",
":framework-statsd{.module-lib.api.txt}",
":framework-tethering{.module-lib.api.txt}",
+ ":framework-uwb{.module-lib.api.txt}",
":framework-wifi{.module-lib.api.txt}",
":non-updatable-module-lib-current.txt",
],
@@ -373,6 +379,7 @@ genrule {
":framework-sdkextensions{.module-lib.removed-api.txt}",
":framework-statsd{.module-lib.removed-api.txt}",
":framework-tethering{.module-lib.removed-api.txt}",
+ ":framework-uwb{.module-lib.removed-api.txt}",
":framework-wifi{.module-lib.removed-api.txt}",
":non-updatable-module-lib-removed.txt",
],
@@ -491,6 +498,7 @@ genrule {
":framework-sdkextensions.stubs{.jar}",
":framework-statsd.stubs{.jar}",
":framework-tethering.stubs{.jar}",
+ ":framework-uwb.stubs{.jar}",
":framework-wifi.stubs{.jar}",
":i18n.module.public.api.stubs{.jar}",
],
diff --git a/boot/Android.bp b/boot/Android.bp
index e8d88a531b5b..049c8026faa9 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -76,6 +76,10 @@ platform_bootclasspath {
module: "com.android.mediaprovider-bootclasspath-fragment",
},
{
+ apex: "com.android.nearby",
+ module: "com.android.nearby-bootclasspath-fragment",
+ },
+ {
apex: "com.android.os.statsd",
module: "com.android.os.statsd-bootclasspath-fragment",
},
@@ -96,6 +100,10 @@ platform_bootclasspath {
module: "com.android.tethering-bootclasspath-fragment",
},
{
+ apex: "com.android.uwb",
+ module: "com.android.uwb-bootclasspath-fragment",
+ },
+ {
apex: "com.android.wifi",
module: "com.android.wifi-bootclasspath-fragment",
},
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index b2b66c27f795..3534624a58a2 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -71,7 +71,7 @@ cc_library_shared {
"libui",
"libjnigraphics",
"libEGL",
- "libGLESv1_CM",
+ "libGLESv2",
"libgui",
],
}
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3109c5c1e075..7c81f0942f78 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -52,9 +52,8 @@
#include <gui/DisplayEventReceiver.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
#include <EGL/eglext.h>
#include "BootAnimation.h"
@@ -108,6 +107,82 @@ static const char PROGRESS_PROP_NAME[] = "service.bootanim.progress";
static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
static constexpr size_t TEXT_POS_LEN_MAX = 16;
+static const int DYNAMIC_COLOR_COUNT = 4;
+static const char U_TEXTURE[] = "uTexture";
+static const char U_FADE[] = "uFade";
+static const char U_CROP_AREA[] = "uCropArea";
+static const char U_START_COLOR_PREFIX[] = "uStartColor";
+static const char U_END_COLOR_PREFIX[] = "uEndColor";
+static const char U_COLOR_PROGRESS[] = "uColorProgress";
+static const char A_UV[] = "aUv";
+static const char A_POSITION[] = "aPosition";
+static const char VERTEX_SHADER_SOURCE[] = R"(
+ precision mediump float;
+ attribute vec4 aPosition;
+ attribute highp vec2 aUv;
+ varying highp vec2 vUv;
+ void main() {
+ gl_Position = aPosition;
+ vUv = aUv;
+ })";
+static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
+ precision mediump float;
+ uniform sampler2D uTexture;
+ uniform float uFade;
+ uniform float uColorProgress;
+ uniform vec4 uStartColor0;
+ uniform vec4 uStartColor1;
+ uniform vec4 uStartColor2;
+ uniform vec4 uStartColor3;
+ uniform vec4 uEndColor0;
+ uniform vec4 uEndColor1;
+ uniform vec4 uEndColor2;
+ uniform vec4 uEndColor3;
+ varying highp vec2 vUv;
+ void main() {
+ vec4 mask = texture2D(uTexture, vUv);
+ vec4 color = mask.r * mix(uStartColor0, uEndColor0, uColorProgress)
+ + mask.g * mix(uStartColor1, uEndColor1, uColorProgress)
+ + mask.b * mix(uStartColor2, uEndColor2, uColorProgress)
+ + mask.a * mix(uStartColor3, uEndColor3, uColorProgress);
+ gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+ })";
+static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
+ precision mediump float;
+ uniform sampler2D uTexture;
+ uniform float uFade;
+ varying highp vec2 vUv;
+ void main() {
+ vec4 color = texture2D(uTexture, vUv);
+ gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+ })";
+static const char TEXT_FRAG_SHADER_SOURCE[] = R"(
+ precision mediump float;
+ uniform sampler2D uTexture;
+ uniform vec4 uCropArea;
+ varying highp vec2 vUv;
+ void main() {
+ vec2 uv = vec2(mix(uCropArea.x, uCropArea.z, vUv.x),
+ mix(uCropArea.y, uCropArea.w, vUv.y));
+ gl_FragColor = texture2D(uTexture, uv);
+ })";
+
+static GLfloat quadPositions[] = {
+ -0.5f, -0.5f,
+ +0.5f, -0.5f,
+ +0.5f, +0.5f,
+ +0.5f, +0.5f,
+ -0.5f, +0.5f,
+ -0.5f, -0.5f
+};
+static GLfloat quadUVs[] = {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 0.0f,
+ 0.0f, 0.0f,
+ 0.0f, 1.0f
+};
// ---------------------------------------------------------------------------
@@ -163,7 +238,8 @@ void BootAnimation::binderDied(const wp<IBinder>&) {
requestExit();
}
-static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo) {
+static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo,
+ bool premultiplyAlpha) {
AImageDecoder* decoder = nullptr;
AImageDecoder_createFromBuffer(encodedData, dataLength, &decoder);
if (!decoder) {
@@ -177,6 +253,10 @@ static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitm
outInfo->stride = AImageDecoder_getMinimumStride(decoder);
outInfo->flags = 0;
+ if (!premultiplyAlpha) {
+ AImageDecoder_setUnpremultipliedRequired(decoder, true);
+ }
+
const size_t size = outInfo->stride * outInfo->height;
void* pixels = malloc(size);
int result = AImageDecoder_decodeImage(decoder, pixels, outInfo->stride, size);
@@ -190,13 +270,14 @@ static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitm
}
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
- const char* name) {
+ const char* name, bool premultiplyAlpha) {
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
if (asset == nullptr)
return NO_INIT;
AndroidBitmapInfo bitmapInfo;
- void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo);
+ void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo,
+ premultiplyAlpha);
auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
asset->close();
@@ -209,7 +290,6 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
const int w = bitmapInfo.width;
const int h = bitmapInfo.height;
- GLint crop[4] = { 0, h, w, -h };
texture->w = w;
texture->h = h;
@@ -237,18 +317,19 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
break;
}
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return NO_ERROR;
}
-status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
+status_t BootAnimation::initTexture(FileMap* map, int* width, int* height,
+ bool premultiplyAlpha) {
AndroidBitmapInfo bitmapInfo;
- void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo);
+ void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo,
+ premultiplyAlpha);
auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
// FileMap memory is never released until application exit.
@@ -263,7 +344,6 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
const int w = bitmapInfo.width;
const int h = bitmapInfo.height;
- GLint crop[4] = { 0, h, w, -h };
int tw = 1 << (31 - __builtin_clz(w));
int th = 1 << (31 - __builtin_clz(h));
if (tw < w) tw <<= 1;
@@ -297,7 +377,10 @@ status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
break;
}
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
*width = w;
*height = h;
@@ -448,16 +531,15 @@ status_t BootAnimation::readyToRun() {
// In the case of multi-display, boot animation shows on the specified displays
// in addition to the primary display
- auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- constexpr uint32_t LAYER_STACK = 0;
- for (auto id : physicalDisplayIds) {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ for (const auto id : physicalDisplayIds) {
if (std::find(ids.begin(), ids.end(), id) != ids.end()) {
- sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(id);
- if (token != nullptr)
- t.setDisplayLayerStack(token, LAYER_STACK);
+ if (const auto token = SurfaceComposerClient::getPhysicalDisplayToken(id)) {
+ t.setDisplayLayerStack(token, ui::DEFAULT_LAYER_STACK);
+ }
}
}
- t.setLayerStack(control, LAYER_STACK);
+ t.setLayerStack(control, ui::DEFAULT_LAYER_STACK);
}
t.setLayer(control, 0x40000000)
@@ -470,7 +552,9 @@ status_t BootAnimation::readyToRun() {
eglInitialize(display, nullptr, nullptr);
EGLConfig config = getEglConfig(display);
EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
- EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
+ // Initialize egl context with client version number 2.0.
+ EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+ EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes);
EGLint w, h;
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
@@ -503,11 +587,6 @@ status_t BootAnimation::readyToRun() {
void BootAnimation::projectSceneToWindow() {
glViewport(0, 0, mWidth, mHeight);
glScissor(0, 0, mWidth, mHeight);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
}
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
@@ -600,8 +679,71 @@ void BootAnimation::findBootAnimationFile() {
}
}
+GLuint compileShader(GLenum shaderType, const GLchar *source) {
+ GLuint shader = glCreateShader(shaderType);
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+ GLint isCompiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+ if (isCompiled == GL_FALSE) {
+ SLOGE("Compile shader failed. Shader type: %d", shaderType);
+ return 0;
+ }
+ return shader;
+}
+
+GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
+ GLuint program = glCreateProgram();
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, fragmentShader);
+ glLinkProgram(program);
+ GLint isLinked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
+ if (isLinked == GL_FALSE) {
+ SLOGE("Linking shader failed. Shader handles: vert %d, frag %d",
+ vertexShader, fragmentShader);
+ return 0;
+ }
+ return program;
+}
+
+void BootAnimation::initShaders() {
+ bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled;
+ GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
+ GLuint imageFragmentShader =
+ compileShader(GL_FRAGMENT_SHADER, dynamicColoringEnabled
+ ? (const GLchar *)IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE
+ : (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
+ GLuint textFragmentShader =
+ compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE);
+
+ // Initialize image shader.
+ mImageShader = linkShader(vertexShader, imageFragmentShader);
+ GLint positionLocation = glGetAttribLocation(mImageShader, A_POSITION);
+ GLint uvLocation = glGetAttribLocation(mImageShader, A_UV);
+ mImageTextureLocation = glGetUniformLocation(mImageShader, U_TEXTURE);
+ mImageFadeLocation = glGetUniformLocation(mImageShader, U_FADE);
+ glEnableVertexAttribArray(positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions);
+ glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+ glEnableVertexAttribArray(uvLocation);
+
+ // Initialize text shader.
+ mTextShader = linkShader(vertexShader, textFragmentShader);
+ positionLocation = glGetAttribLocation(mTextShader, A_POSITION);
+ uvLocation = glGetAttribLocation(mTextShader, A_UV);
+ mTextTextureLocation = glGetUniformLocation(mTextShader, U_TEXTURE);
+ mTextCropAreaLocation = glGetUniformLocation(mTextShader, U_CROP_AREA);
+ glEnableVertexAttribArray(positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, quadPositions);
+ glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+ glEnableVertexAttribArray(uvLocation);
+}
+
bool BootAnimation::threadLoop() {
bool result;
+ initShaders();
+
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
@@ -623,6 +765,8 @@ bool BootAnimation::threadLoop() {
}
bool BootAnimation::android() {
+ glActiveTexture(GL_TEXTURE0);
+
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
@@ -631,19 +775,14 @@ bool BootAnimation::android() {
mCallbacks->init({});
// clear screen
- glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(mDisplay, mSurface);
- glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
// Blend state
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
const nsecs_t startTime = systemTime();
do {
@@ -666,12 +805,12 @@ bool BootAnimation::android() {
glEnable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
- glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h);
- glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
+ drawTexturedQuad(x, yc, mAndroid[1].w, mAndroid[1].h);
+ drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h);
glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
- glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
+ drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h);
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
if (res == EGL_FALSE)
@@ -766,6 +905,20 @@ static bool parseColor(const char str[7], float color[3]) {
return true;
}
+// Parse a color represented as a signed decimal int string.
+// E.g. "-2757722" (whose hex 2's complement is 0xFFD5EBA6).
+// If the input color string is empty, set color with values in defaultColor.
+static void parseColorDecimalString(const std::string& colorString,
+ float color[3], float defaultColor[3]) {
+ if (colorString == "") {
+ memcpy(color, defaultColor, sizeof(float) * 3);
+ return;
+ }
+ int colorInt = atoi(colorString.c_str());
+ color[0] = ((float)((colorInt >> 16) & 0xFF)) / 0xFF; // r
+ color[1] = ((float)((colorInt >> 8) & 0xFF)) / 0xFF; // g
+ color[2] = ((float)(colorInt & 0xFF)) / 0xFF; // b
+}
static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
ZipEntryRO entry = zip->findEntryByName(name);
@@ -798,10 +951,10 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
status = initTexture(font->map, &font->texture.w, &font->texture.h);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
} else if (fallback != nullptr) {
status = initTexture(&font->texture, mAssets, fallback);
} else {
@@ -816,40 +969,11 @@ status_t BootAnimation::initFont(Font* font, const char* fallback) {
return status;
}
-void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth,
- const int frameHeight, const Animation::Part& part,
- const int fadedFramesCount) {
- glEnable(GL_BLEND);
- glEnableClientState(GL_VERTEX_ARRAY);
- glDisable(GL_TEXTURE_2D);
- // avoid creating a hole due to mixing result alpha with GL_REPLACE texture
- glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
-
- const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount;
- glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha);
-
- const float frameStartX = static_cast<float>(frameLeft);
- const float frameStartY = static_cast<float>(frameBottom);
- const float frameEndX = frameStartX + frameWidth;
- const float frameEndY = frameStartY + frameHeight;
- const GLfloat frameRect[] = {
- frameStartX, frameStartY,
- frameEndX, frameStartY,
- frameEndX, frameEndY,
- frameStartX, frameEndY
- };
- glVertexPointer(2, GL_FLOAT, 0, frameRect);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_TEXTURE_2D);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisable(GL_BLEND);
-}
-
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
glEnable(GL_BLEND); // Allow us to draw on top of the animation
glBindTexture(GL_TEXTURE_2D, font.texture.name);
+ glUseProgram(mTextShader);
+ glUniform1i(mTextTextureLocation, 0);
const int len = strlen(str);
const int strWidth = font.char_width * len;
@@ -865,8 +989,6 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int*
*y = mHeight + *y - font.char_height;
}
- int cropRect[4] = { 0, 0, font.char_width, -font.char_height };
-
for (int i = 0; i < len; i++) {
char c = str[i];
@@ -878,13 +1000,13 @@ void BootAnimation::drawText(const char* str, const Font& font, bool bold, int*
const int charPos = (c - FONT_BEGIN_CHAR); // Position in the list of valid characters
const int row = charPos / FONT_NUM_COLS;
const int col = charPos % FONT_NUM_COLS;
- cropRect[0] = col * font.char_width; // Left of column
- cropRect[1] = row * font.char_height * 2; // Top of row
- // Move down to bottom of regular (one char_heigh) or bold (two char_heigh) line
- cropRect[1] += bold ? 2 * font.char_height : font.char_height;
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
-
- glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height);
+ // Bold fonts are expected in the second half of each row.
+ float v0 = (row + (bold ? 0.5f : 0.0f)) / FONT_NUM_ROWS;
+ float u0 = ((float)col) / FONT_NUM_COLS;
+ float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2;
+ float u1 = u0 + 1.0f / FONT_NUM_COLS;
+ glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1);
+ drawTexturedQuad(*x, *y, font.char_width, font.char_height);
*x += font.char_width;
}
@@ -938,6 +1060,8 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
return false;
}
char const* s = desString.string();
+ std::string dynamicColoringPartName = "";
+ bool postDynamicColoring = false;
// Parse the description file
for (;;) {
@@ -956,7 +1080,13 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
+ char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX];
char pathType;
+ // start colors default to black if unspecified
+ char start_color_0[7] = "000000";
+ char start_color_1[7] = "000000";
+ char start_color_2[7] = "000000";
+ char start_color_3[7] = "000000";
int nextReadPos;
@@ -971,6 +1101,15 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
} else {
animation.progressEnabled = false;
}
+ } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s",
+ dynamicColoringPartNameBuffer,
+ start_color_0, start_color_1, start_color_2, start_color_3)) {
+ animation.dynamicColoringEnabled = true;
+ parseColor(start_color_0, animation.startColors[0]);
+ parseColor(start_color_1, animation.startColors[1]);
+ parseColor(start_color_2, animation.startColors[2]);
+ parseColor(start_color_3, animation.startColors[3]);
+ dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
} else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
&pathType, &count, &pause, path, &nextReadPos) >= 4) {
if (pathType == 'f') {
@@ -983,6 +1122,16 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) {
// "clockPos1=%s, clockPos2=%s",
// pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
Animation::Part part;
+ if (path == dynamicColoringPartName) {
+ // Part is specified to use dynamic coloring.
+ part.useDynamicColoring = true;
+ part.postDynamicColoring = false;
+ postDynamicColoring = true;
+ } else {
+ // Part does not use dynamic coloring.
+ part.useDynamicColoring = false;
+ part.postDynamicColoring = postDynamicColoring;
+ }
part.playUntilComplete = pathType == 'c';
part.framesToFadeCount = framesToFadeCount;
part.count = count;
@@ -1166,19 +1315,16 @@ bool BootAnimation::movie() {
// Blend required to draw time on top of animation frames.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
bool clockFontInitialized = false;
if (mClockEnabled) {
clockFontInitialized =
@@ -1193,6 +1339,10 @@ bool BootAnimation::movie() {
mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
}
+ if (mAnimation != nullptr && mAnimation->dynamicColoringEnabled) {
+ initDynamicColors();
+ }
+
playAnimation(*mAnimation);
if (mTimeCheckThread != nullptr) {
@@ -1218,6 +1368,55 @@ bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part,
(lastDisplayedProgress == 0 || lastDisplayedProgress == 100);
}
+// Linear mapping from range <a1, a2> to range <b1, b2>
+float mapLinear(float x, float a1, float a2, float b1, float b2) {
+ return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+}
+
+void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) {
+ // Map coordinates from screen space to world space.
+ float x0 = mapLinear(xStart, 0, mWidth, -1, 1);
+ float y0 = mapLinear(yStart, 0, mHeight, -1, 1);
+ float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1);
+ float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1);
+ // Update quad vertex positions.
+ quadPositions[0] = x0;
+ quadPositions[1] = y0;
+ quadPositions[2] = x1;
+ quadPositions[3] = y0;
+ quadPositions[4] = x1;
+ quadPositions[5] = y1;
+ quadPositions[6] = x1;
+ quadPositions[7] = y1;
+ quadPositions[8] = x0;
+ quadPositions[9] = y1;
+ quadPositions[10] = x0;
+ quadPositions[11] = y0;
+ glDrawArrays(GL_TRIANGLES, 0,
+ sizeof(quadPositions) / sizeof(quadPositions[0]) / 2);
+}
+
+void BootAnimation::initDynamicColors() {
+ for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+ parseColorDecimalString(
+ android::base::GetProperty("persist.bootanim.color" + std::to_string(i + 1), ""),
+ mAnimation->endColors[i], mAnimation->startColors[i]);
+ }
+ glUseProgram(mImageShader);
+ SLOGI("[BootAnimation] Dynamically coloring boot animation.");
+ for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+ float *startColor = mAnimation->startColors[i];
+ float *endColor = mAnimation->endColors[i];
+ glUniform4f(glGetUniformLocation(mImageShader,
+ (U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
+ startColor[0], startColor[1], startColor[2], 1 /* alpha */);
+ glUniform4f(glGetUniformLocation(mImageShader,
+ (U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
+ endColor[0], endColor[1], endColor[2], 1 /* alpha */);
+ }
+ mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
+}
+
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -1230,7 +1429,6 @@ bool BootAnimation::playAnimation(const Animation& animation) {
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
- glBindTexture(GL_TEXTURE_2D, 0);
// Handle animation package
if (part.animation != nullptr) {
@@ -1261,6 +1459,14 @@ bool BootAnimation::playAnimation(const Animation& animation) {
for (size_t j=0 ; j<fcount ; j++) {
if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
+ // Color progress is
+ // - the normalized animation progress between [0, 1] for the dynamic coloring part,
+ // - 0 for parts that come before,
+ // - 1 for parts that come after.
+ float colorProgress = part.useDynamicColoring
+ ? (float)j / fcount
+ : (part.postDynamicColoring ? 1 : 0);
+
processDisplayEvents();
const int animationX = (mWidth - animation.width) / 2;
@@ -1272,44 +1478,38 @@ bool BootAnimation::playAnimation(const Animation& animation) {
if (r > 0) {
glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
- if (part.count != 1) {
- glGenTextures(1, &frame.tid);
- glBindTexture(GL_TEXTURE_2D, frame.tid);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- }
+ glGenTextures(1, &frame.tid);
+ glBindTexture(GL_TEXTURE_2D, frame.tid);
int w, h;
- initTexture(frame.map, &w, &h);
+ // Set decoding option to alpha unpremultiplied so that the R, G, B channels
+ // of transparent pixels are preserved.
+ initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);
}
const int xc = animationX + frame.trimX;
const int yc = animationY + frame.trimY;
- Region clearReg(Rect(mWidth, mHeight));
- clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
- if (!clearReg.isEmpty()) {
- Region::const_iterator head(clearReg.begin());
- Region::const_iterator tail(clearReg.end());
- glEnable(GL_SCISSOR_TEST);
- while (head != tail) {
- const Rect& r2(*head++);
- glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
- glClear(GL_COLOR_BUFFER_BIT);
- }
- glDisable(GL_SCISSOR_TEST);
- }
+ glClear(GL_COLOR_BUFFER_BIT);
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
const int frameDrawY = mHeight - (yc + frame.trimHeight);
- glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
+ float fade = 0;
// if the part hasn't been stopped yet then continue fading if necessary
if (exitPending() && part.hasFadingPhase()) {
- fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
- ++fadedFramesCount);
+ fade = static_cast<float>(++fadedFramesCount) / part.framesToFadeCount;
if (fadedFramesCount >= part.framesToFadeCount) {
fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
}
}
+ glUseProgram(mImageShader);
+ glUniform1i(mImageTextureLocation, 0);
+ glUniform1f(mImageFadeLocation, fade);
+ if (animation.dynamicColoringEnabled) {
+ glUniform1f(mImageColorProgressLocation, colorProgress);
+ }
+ glEnable(GL_BLEND);
+ drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight);
+ glDisable(GL_BLEND);
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index f8a31c6d8790..0e29621f0326 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -31,7 +31,7 @@
#include <binder/IBinder.h>
#include <EGL/egl.h>
-#include <GLES/gl.h>
+#include <GLES2/gl2.h>
namespace android {
@@ -53,7 +53,7 @@ public:
};
struct Font {
- FileMap* map;
+ FileMap* map = nullptr;
Texture texture;
int char_width;
int char_height;
@@ -62,7 +62,7 @@ public:
struct Animation {
struct Frame {
String8 name;
- FileMap* map;
+ FileMap* map = nullptr;
int trimX;
int trimY;
int trimWidth;
@@ -90,6 +90,10 @@ public:
uint8_t* audioData;
int audioLength;
Animation* animation;
+ // Controls if dynamic coloring is enabled for this part.
+ bool useDynamicColoring = false;
+ // Defines if this part is played after the dynamic coloring part.
+ bool postDynamicColoring = false;
bool hasFadingPhase() const {
return !playUntilComplete && framesToFadeCount > 0;
@@ -105,6 +109,10 @@ public:
ZipFileRO* zip;
Font clockFont;
Font progressFont;
+ // Controls if dynamic coloring is enabled for the whole animation.
+ bool dynamicColoringEnabled = false;
+ float startColors[4][3]; // Start colors of dynamic color transition.
+ float endColors[4][3]; // End colors of dynamic color transition.
};
// All callbacks will be called from this class's internal thread.
@@ -163,9 +171,12 @@ private:
int displayEventCallback(int fd, int events, void* data);
void processDisplayEvents();
- status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
- status_t initTexture(FileMap* map, int* width, int* height);
+ status_t initTexture(Texture* texture, AssetManager& asset, const char* name,
+ bool premultiplyAlpha = true);
+ status_t initTexture(FileMap* map, int* width, int* height,
+ bool premultiplyAlpha = true);
status_t initFont(Font* font, const char* fallback);
+ void initShaders();
bool android();
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
@@ -173,6 +184,7 @@ private:
void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
const Animation::Part& part, int fadedFramesCount);
+ void drawTexturedQuad(float xStart, float yStart, float width, float height);
bool validClock(const Animation::Part& part);
Animation* loadAnimation(const String8&);
bool playAnimation(const Animation&);
@@ -192,6 +204,7 @@ private:
void checkExit();
void handleViewport(nsecs_t timestep);
+ void initDynamicColors();
sp<SurfaceComposerClient> mSession;
AssetManager mAssets;
@@ -218,6 +231,13 @@ private:
sp<TimeCheckThread> mTimeCheckThread = nullptr;
sp<Callbacks> mCallbacks;
Animation* mAnimation = nullptr;
+ GLuint mImageShader;
+ GLuint mTextShader;
+ GLuint mImageFadeLocation;
+ GLuint mImageTextureLocation;
+ GLuint mTextCropAreaLocation;
+ GLuint mTextTextureLocation;
+ GLuint mImageColorProgressLocation;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 1678053c48d9..64814c8a25e2 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -31,6 +31,9 @@ The first line defines the general parameters of the animation:
+ The percentage will be displayed with an x-coordinate of 'c', and a
y-coordinate set to 1/3 of the animation height.
+Next, provide an optional line for dynamic coloring attributes, should dynamic coloring be used.
+See the dyanmic coloring section for format details. Skip if you don't use dynamic coloring.
+
It is followed by a number of rows of the form:
TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]]
@@ -140,3 +143,35 @@ Some animations benefit from being reduced to 256 colors:
Note that the ZIP archive is not actually compressed! The PNG files are already as compressed
as they can reasonably get, and there is unlikely to be any redundancy between files.
+
+### Dynamic coloring
+
+Dynamic coloring is a render mode that draws the boot animation using a color transition.
+In this mode, instead of directly rendering the PNG images, it treats the R, G, B, A channels
+of input images as area masks of dynamic colors, which interpolates between start and end colors
+based on animation progression.
+
+To enable it, add the following line as the second line of desc.txt:
+
+ dynamic_colors PATH #RGBHEX0 #RGBHEX1 #RGBHEX2 #RGBHEX3
+
+ * **PATH:** file path of the part to apply dynamic color transition to.
+ Any part before this part will be rendered in the start colors.
+ Any part after will be rendered in the end colors.
+ * **RGBHEX1:** the first start color (masked by the R channel), specified as `#RRGGBB`.
+ * **RGBHEX2:** the second start color (masked by the G channel), specified as `#RRGGBB`.
+ * **RGBHEX3:** the thrid start color (masked by the B channel), specified as `#RRGGBB`.
+ * **RGBHEX4:** the forth start color (masked by the A channel), specified as `#RRGGBB`.
+
+The end colors will be read from the following system properties:
+
+ * persist.bootanim.color1
+ * persist.bootanim.color2
+ * persist.bootanim.color3
+ * persist.bootanim.color4
+
+When missing, the end colors will default to the start colors, effectively producing no color
+transition.
+
+Prepare your PNG images so that the R, G, B, A channels indicates the areas to draw color1,
+color2, color3 and color4 respectively.
diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS
new file mode 100644
index 000000000000..b6fb007bea52
--- /dev/null
+++ b/cmds/bootanimation/OWNERS
@@ -0,0 +1,3 @@
+dupin@google.com
+shanh@google.com
+jreck@google.com
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index de1bcaea079c..cc455898f99e 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -35,8 +35,10 @@ cc_defaults {
tidy_checks_as_errors: [
"modernize-*",
"-modernize-avoid-c-arrays",
+ "-modernize-concat-nested-namespaces",
"-modernize-pass-by-value",
"-modernize-replace-disallow-copy-and-assign-macro",
+ "-modernize-return-braced-init-list",
"-modernize-use-equals-default",
"-modernize-use-nodiscard",
"-modernize-use-override",
@@ -54,9 +56,6 @@ cc_defaults {
"-readability-redundant-access-specifiers",
"-readability-uppercase-literal-suffix",
],
- tidy_flags: [
- "-system-headers",
- ],
}
cc_library {
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index a55b41b83c93..1c8259746189 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -96,7 +96,7 @@ TEST_F(Idmap2BinaryTests, Create) {
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
struct stat st;
ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
@@ -123,7 +123,7 @@ TEST_F(Idmap2BinaryTests, Dump) {
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -131,24 +131,24 @@ TEST_F(Idmap2BinaryTests, Dump) {
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
- ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
+ ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
R::overlay::integer::int1)),
std::string::npos)
- << result->stdout;
- ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
+ << result->stdout_str;
+ ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
R::overlay::string::str1)),
std::string::npos)
- << result->stdout;
- ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
+ << result->stdout_str;
+ ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
R::overlay::string::str3)),
std::string::npos)
- << result->stdout;
- ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
+ << result->stdout_str;
+ ASSERT_NE(result->stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
R::overlay::string::str4)),
std::string::npos)
- << result->stdout;
+ << result->stdout_str;
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -157,8 +157,8 @@ TEST_F(Idmap2BinaryTests, Dump) {
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("00000000: 504d4449 magic"), std::string::npos);
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
+ ASSERT_NE(result->stdout_str.find("00000000: 504d4449 magic"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -184,7 +184,7 @@ TEST_F(Idmap2BinaryTests, Lookup) {
"--idmap-path", GetIdmapPath()});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -194,9 +194,9 @@ TEST_F(Idmap2BinaryTests, Lookup) {
"--resid", StringPrintf("0x%08x", R::target::string::str1)});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
- ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
+ ASSERT_NE(result->stdout_str.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout_str.find("overlay-1-sv"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -206,9 +206,9 @@ TEST_F(Idmap2BinaryTests, Lookup) {
"--resid", "test.target:string/str1"});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
- ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
+ ASSERT_NE(result->stdout_str.find("overlay-1"), std::string::npos);
+ ASSERT_EQ(result->stdout_str.find("overlay-1-sv"), std::string::npos);
// clang-format off
result = ExecuteBinary({"idmap2",
@@ -218,8 +218,8 @@ TEST_F(Idmap2BinaryTests, Lookup) {
"--resid", "test.target:string/str1"});
// clang-format on
ASSERT_THAT(result, NotNull());
- ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
- ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
+ ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr_str;
+ ASSERT_NE(result->stdout_str.find("overlay-1-sv"), std::string::npos);
unlink(GetIdmapPath().c_str());
}
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index d4da5e554591..7e6a521ca46c 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -45,13 +45,13 @@ using namespace android;
#define COLORSPACE_SRGB 1
#define COLORSPACE_DISPLAY_P3 2
-static void usage(const char* pname, PhysicalDisplayId displayId)
+static void usage(const char* pname, DisplayId displayId)
{
fprintf(stderr,
"usage: %s [-hp] [-d display-id] [FILENAME]\n"
" -h: this message\n"
" -p: save the file as a png.\n"
- " -d: specify the physical display ID to capture (default: %s)\n"
+ " -d: specify the display ID to capture (default: %s)\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
"If FILENAME ends with .png it will be saved as a png.\n"
"If FILENAME is not given, the results will be printed to stdout.\n",
@@ -121,9 +121,9 @@ static status_t notifyMediaScanner(const char* fileName) {
int main(int argc, char** argv)
{
- std::optional<PhysicalDisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
+ std::optional<DisplayId> displayId = SurfaceComposerClient::getInternalDisplayId();
if (!displayId) {
- fprintf(stderr, "Failed to get token for internal display\n");
+ fprintf(stderr, "Failed to get ID for internal display\n");
return 1;
}
@@ -136,7 +136,11 @@ int main(int argc, char** argv)
png = true;
break;
case 'd':
- displayId = PhysicalDisplayId(atoll(optarg));
+ displayId = DisplayId::fromValue(atoll(optarg));
+ if (!displayId) {
+ fprintf(stderr, "Invalid display ID\n");
+ return 1;
+ }
break;
case '?':
case 'h':
@@ -182,7 +186,7 @@ int main(int argc, char** argv)
ProcessState::self()->startThreadPool();
sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- status_t result = ScreenshotClient::captureDisplay(displayId->value, captureListener);
+ status_t result = ScreenshotClient::captureDisplay(*displayId, captureListener);
if (result != NO_ERROR) {
close(fd);
return 1;
diff --git a/config/OWNERS b/config/OWNERS
index 0691dbc4dcb0..c0778f88637f 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,8 +1,8 @@
include /ZYGOTE_OWNERS
# art-team@ manages the boot image profiles
-per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file boot-* = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file dirty-image-objects = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file generate-preloaded-classes.sh = calin@google.com, ngeoffray@google.com, vmarko@google.com
+per-file preloaded-classes* = calin@google.com, ngeoffray@google.com, vmarko@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 1de47b548a5c..e4e5a1acc420 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1291,6 +1291,7 @@ package android {
field public static final int shareInterpolator = 16843195; // 0x10101bb
field @Deprecated public static final int sharedUserId = 16842763; // 0x101000b
field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int sharedUserMaxSdkVersion;
field public static final int shell = 16844180; // 0x1010594
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
field public static final int shortcutId = 16844072; // 0x1010528
@@ -6652,7 +6653,7 @@ package android.app {
method public boolean onUnbind(android.content.Intent);
method public final void startForeground(int, android.app.Notification);
method public final void startForeground(int, @NonNull android.app.Notification, int);
- method public final void stopForeground(boolean);
+ method @Deprecated public final void stopForeground(boolean);
method public final void stopForeground(int);
method public final void stopSelf();
method public final void stopSelf(int);
@@ -6980,7 +6981,7 @@ package android.app {
}
public static interface WallpaperManager.OnColorsChangedListener {
- method public void onColorsChanged(android.app.WallpaperColors, int);
+ method public void onColorsChanged(@Nullable android.app.WallpaperColors, int);
}
public interface ZygotePreload {
@@ -7455,7 +7456,7 @@ package android.app.admin {
field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
field public static final int KEYGUARD_DISABLE_IRIS = 256; // 0x100
- field public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
+ field @Deprecated public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
@@ -7937,6 +7938,7 @@ package android.app.job {
method public static final long getMinFlexMillis();
method public long getMinLatencyMillis();
method public static final long getMinPeriodMillis();
+ method public long getMinimumNetworkChunkBytes();
method @Deprecated public int getNetworkType();
method @Nullable public android.net.NetworkRequest getRequiredNetwork();
method @NonNull public android.content.ComponentName getService();
@@ -7979,6 +7981,7 @@ package android.app.job {
method public android.app.job.JobInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
method @Deprecated public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
method public android.app.job.JobInfo.Builder setMinimumLatency(long);
+ method @NonNull public android.app.job.JobInfo.Builder setMinimumNetworkChunkBytes(long);
method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
@@ -8071,11 +8074,13 @@ package android.app.job {
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.content.Intent, long, long);
+ ctor public JobWorkItem(@Nullable android.content.Intent, long, long, long);
method public int describeContents();
method public int getDeliveryCount();
method public long getEstimatedNetworkDownloadBytes();
method public long getEstimatedNetworkUploadBytes();
method public android.content.Intent getIntent();
+ method public long getMinimumNetworkChunkBytes();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
}
@@ -9120,6 +9125,7 @@ package android.bluetooth {
field public static final int GATT_CONNECTION_CONGESTED = 143; // 0x8f
field public static final int GATT_FAILURE = 257; // 0x101
field public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5; // 0x5
+ field public static final int GATT_INSUFFICIENT_AUTHORIZATION = 8; // 0x8
field public static final int GATT_INSUFFICIENT_ENCRYPTION = 15; // 0xf
field public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 13; // 0xd
field public static final int GATT_INVALID_OFFSET = 7; // 0x7
@@ -9429,6 +9435,7 @@ package android.bluetooth {
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getGroupId(@NonNull android.bluetooth.BluetoothDevice);
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
}
@@ -9453,6 +9460,7 @@ package android.bluetooth {
field @Deprecated public static final int HEALTH = 3; // 0x3
field public static final int HEARING_AID = 21; // 0x15
field public static final int HID_DEVICE = 19; // 0x13
+ field public static final int LE_AUDIO = 22; // 0x16
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
field public static final int STATE_CONNECTING = 1; // 0x1
@@ -11175,11 +11183,13 @@ package android.content {
field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ field public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
field public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
field public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+ field public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
field public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
field public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
field public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
@@ -11372,6 +11382,8 @@ package android.content {
method public final void addDataScheme(String);
method public final void addDataSchemeSpecificPart(String, int);
method public final void addDataType(String) throws android.content.IntentFilter.MalformedMimeTypeException;
+ method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicate();
+ method @NonNull public java.util.function.Predicate<android.content.Intent> asPredicateWithTypeResolution(@NonNull android.content.ContentResolver);
method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
method public final java.util.Iterator<java.lang.String> categoriesIterator();
method public final int countActions();
@@ -12545,6 +12557,7 @@ package android.content.pm {
method public abstract int getInstantAppCookieMaxBytes();
method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.Intent getLaunchIntentForPackage(@NonNull String);
+ method @NonNull public android.content.IntentSender getLaunchIntentSenderForPackage(@NonNull String);
method @Nullable public abstract android.content.Intent getLeanbackLaunchIntentForPackage(@NonNull String);
method @NonNull public java.util.Set<java.lang.String> getMimeGroup(@NonNull String);
method @NonNull public android.content.pm.ModuleInfo getModuleInfo(@NonNull String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -12622,6 +12635,7 @@ package android.content.pm {
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setApplicationEnabledSetting(@NonNull String, int, int);
method @RequiresPermission(value="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS", conditional=true) public boolean setAutoRevokeWhitelisted(@NonNull String, boolean);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public abstract void setComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
+ method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setComponentEnabledSettings(@NonNull java.util.List<android.content.pm.PackageManager.ComponentEnabledSetting>);
method public abstract void setInstallerPackageName(@NonNull String, @Nullable String);
method public void setMimeGroup(@NonNull String, @NonNull java.util.Set<java.lang.String>);
method public abstract void updateInstantAppCookie(@Nullable byte[]);
@@ -12818,6 +12832,16 @@ package android.content.pm {
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
}
+ public static final class PackageManager.ComponentEnabledSetting implements android.os.Parcelable {
+ ctor public PackageManager.ComponentEnabledSetting(@NonNull android.content.ComponentName, int, int);
+ method public int describeContents();
+ method @Nullable public android.content.ComponentName getComponentName();
+ method public int getEnabledFlags();
+ method public int getEnabledState();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.ComponentEnabledSetting> CREATOR;
+ }
+
public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
ctor public PackageManager.NameNotFoundException();
ctor public PackageManager.NameNotFoundException(String);
@@ -14959,7 +14983,7 @@ package android.graphics {
method public void drawTextRun(@NonNull android.graphics.text.MeasuredText, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
method public void drawVertices(@NonNull android.graphics.Canvas.VertexMode, int, @NonNull float[], int, @Nullable float[], int, @Nullable int[], int, @Nullable short[], int, int, @NonNull android.graphics.Paint);
method public void enableZ();
- method public boolean getClipBounds(@Nullable android.graphics.Rect);
+ method public boolean getClipBounds(@NonNull android.graphics.Rect);
method @NonNull public final android.graphics.Rect getClipBounds();
method public int getDensity();
method @Nullable public android.graphics.DrawFilter getDrawFilter();
@@ -15254,9 +15278,11 @@ package android.graphics {
method public void clearContent();
method @NonNull public android.graphics.HardwareRenderer.FrameRenderRequest createRenderRequest();
method public void destroy();
+ method public static boolean isDrawingEnabled();
method public boolean isOpaque();
method public void notifyFramePending();
method public void setContentRoot(@Nullable android.graphics.RenderNode);
+ method public static void setDrawingEnabled(boolean);
method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
method public void setLightSourceGeometry(float, float, float, float);
method public void setName(@NonNull String);
@@ -20178,8 +20204,10 @@ package android.media {
method public int getAllowedCapturePolicy();
method public int getContentType();
method public int getFlags();
+ method public int getSpatializationBehavior();
method public int getUsage();
method public int getVolumeControlStream();
+ method public boolean isContentSpatialized();
method public void writeToParcel(android.os.Parcel, int);
field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1
field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3
@@ -20193,6 +20221,8 @@ package android.media {
field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
field public static final int FLAG_HW_AV_SYNC = 16; // 0x10
field @Deprecated public static final int FLAG_LOW_LATENCY = 256; // 0x100
+ field public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0; // 0x0
+ field public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1; // 0x1
field public static final int USAGE_ALARM = 4; // 0x4
field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
@@ -20219,7 +20249,9 @@ package android.media {
method public android.media.AudioAttributes.Builder setContentType(int);
method public android.media.AudioAttributes.Builder setFlags(int);
method @NonNull public android.media.AudioAttributes.Builder setHapticChannelsMuted(boolean);
+ method @NonNull public android.media.AudioAttributes.Builder setIsContentSpatialized(boolean);
method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
+ method @NonNull public android.media.AudioAttributes.Builder setSpatializationBehavior(int);
method public android.media.AudioAttributes.Builder setUsage(int);
}
@@ -20336,24 +20368,45 @@ package android.media {
field public static final int CHANNEL_IN_Y_AXIS = 4096; // 0x1000
field public static final int CHANNEL_IN_Z_AXIS = 8192; // 0x2000
field public static final int CHANNEL_OUT_5POINT1 = 252; // 0xfc
+ field public static final int CHANNEL_OUT_5POINT1POINT2 = 3145980; // 0x3000fc
+ field public static final int CHANNEL_OUT_5POINT1POINT4 = 737532; // 0xb40fc
field @Deprecated public static final int CHANNEL_OUT_7POINT1 = 1020; // 0x3fc
+ field public static final int CHANNEL_OUT_7POINT1POINT2 = 3152124; // 0x3018fc
+ field public static final int CHANNEL_OUT_7POINT1POINT4 = 743676; // 0xb58fc
field public static final int CHANNEL_OUT_7POINT1_SURROUND = 6396; // 0x18fc
+ field public static final int CHANNEL_OUT_9POINT1POINT4 = 202070268; // 0xc0b58fc
+ field public static final int CHANNEL_OUT_9POINT1POINT6 = 205215996; // 0xc3b58fc
field public static final int CHANNEL_OUT_BACK_CENTER = 1024; // 0x400
field public static final int CHANNEL_OUT_BACK_LEFT = 64; // 0x40
field public static final int CHANNEL_OUT_BACK_RIGHT = 128; // 0x80
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 8388608; // 0x800000
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 4194304; // 0x400000
+ field public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 16777216; // 0x1000000
field public static final int CHANNEL_OUT_DEFAULT = 1; // 0x1
field public static final int CHANNEL_OUT_FRONT_CENTER = 16; // 0x10
field public static final int CHANNEL_OUT_FRONT_LEFT = 4; // 0x4
field public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 256; // 0x100
field public static final int CHANNEL_OUT_FRONT_RIGHT = 8; // 0x8
field public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 512; // 0x200
+ field public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 67108864; // 0x4000000
+ field public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 134217728; // 0x8000000
field public static final int CHANNEL_OUT_LOW_FREQUENCY = 32; // 0x20
+ field public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 33554432; // 0x2000000
field public static final int CHANNEL_OUT_MONO = 4; // 0x4
field public static final int CHANNEL_OUT_QUAD = 204; // 0xcc
field public static final int CHANNEL_OUT_SIDE_LEFT = 2048; // 0x800
field public static final int CHANNEL_OUT_SIDE_RIGHT = 4096; // 0x1000
field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
+ field public static final int CHANNEL_OUT_TOP_BACK_CENTER = 262144; // 0x40000
+ field public static final int CHANNEL_OUT_TOP_BACK_LEFT = 131072; // 0x20000
+ field public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 524288; // 0x80000
+ field public static final int CHANNEL_OUT_TOP_CENTER = 8192; // 0x2000
+ field public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 32768; // 0x8000
+ field public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 16384; // 0x4000
+ field public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 65536; // 0x10000
+ field public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 1048576; // 0x100000
+ field public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 2097152; // 0x200000
field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
field public static final int ENCODING_AAC_ELD = 15; // 0xf
field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
@@ -20423,6 +20476,7 @@ package android.media {
method public String getProperty(String);
method public int getRingerMode();
method @Deprecated public int getRouting(int);
+ method @Nullable public android.media.Spatializer getSpatializer();
method public int getStreamMaxVolume(int);
method public int getStreamMinVolume(int);
method public int getStreamVolume(int);
@@ -23819,6 +23873,17 @@ package android.media {
method public void onLoadComplete(android.media.SoundPool, int, int);
}
+ public class Spatializer {
+ method public void addOnSpatializerEnabledChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
+ method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
+ method public boolean isEnabled();
+ method public void removeOnSpatializerEnabledChangedListener(@NonNull android.media.Spatializer.OnSpatializerEnabledChangedListener);
+ }
+
+ public static interface Spatializer.OnSpatializerEnabledChangedListener {
+ method public void onSpatializerEnabledChanged(boolean);
+ }
+
public final class SubtitleData {
ctor public SubtitleData(int, long, long, @NonNull byte[]);
method @NonNull public byte[] getData();
@@ -25232,13 +25297,17 @@ package android.media.session {
public final class MediaSessionManager {
method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @Nullable android.os.Handler);
+ method public void addOnMediaKeyEventSessionChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener, @NonNull android.os.Handler);
method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessions(@Nullable android.content.ComponentName);
+ method @Nullable public android.media.session.MediaSession.Token getMediaKeyEventSession();
+ method @NonNull public String getMediaKeyEventSessionPackageName();
method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo);
method @Deprecated public void notifySession2Created(@NonNull android.media.Session2Token);
method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+ method public void removeOnMediaKeyEventSessionChangedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
}
@@ -25246,6 +25315,10 @@ package android.media.session {
method public void onActiveSessionsChanged(@Nullable java.util.List<android.media.session.MediaController>);
}
+ public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
+ method public void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token);
+ }
+
public static interface MediaSessionManager.OnSession2TokensChangedListener {
method public void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>);
}
@@ -30797,6 +30870,7 @@ package android.os {
field public static final int Q = 29; // 0x1d
field public static final int R = 30; // 0x1e
field public static final int S = 31; // 0x1f
+ field public static final int TIRAMISU = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -31070,8 +31144,8 @@ package android.os {
ctor public Environment();
method public static java.io.File getDataDirectory();
method public static java.io.File getDownloadCacheDirectory();
- method @Deprecated public static java.io.File getExternalStorageDirectory();
- method @Deprecated public static java.io.File getExternalStoragePublicDirectory(String);
+ method public static java.io.File getExternalStorageDirectory();
+ method public static java.io.File getExternalStoragePublicDirectory(String);
method public static String getExternalStorageState();
method public static String getExternalStorageState(java.io.File);
method @NonNull public static java.io.File getRootDirectory();
@@ -31641,6 +31715,7 @@ package android.os {
method public boolean isDeviceIdleMode();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
+ method public boolean isLightDeviceIdleMode();
method public boolean isPowerSaveMode();
method public boolean isRebootingUserspaceSupported();
method @Deprecated public boolean isScreenOn();
@@ -31651,6 +31726,7 @@ package android.os {
method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+ field public static final String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED = "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
field public static final String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field @Deprecated public static final int FULL_WAKE_LOCK = 26; // 0x1a
field public static final int LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2; // 0x2
@@ -31790,6 +31866,7 @@ package android.os {
method public void close();
method @NonNull public static android.os.SharedMemory create(@Nullable String, int) throws android.system.ErrnoException;
method public int describeContents();
+ method @NonNull public static android.os.SharedMemory fromFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int getSize();
method @NonNull public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
method @NonNull public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
@@ -34789,6 +34866,7 @@ package android.provider {
}
public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
+ field public static final String ACTION_SET_DEFAULT_ACCOUNT = "android.provider.action.SET_DEFAULT_ACCOUNT";
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/setting";
field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/setting";
field public static final android.net.Uri CONTENT_URI;
@@ -39649,6 +39727,7 @@ package android.telecom {
method public final void cancelCall();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onPlaceCall(@NonNull android.net.Uri, @NonNull android.telecom.PhoneAccountHandle, boolean);
+ method public void onRedirectionTimeout();
method public final boolean onUnbind(@NonNull android.content.Intent);
method public final void placeCallUnmodified();
method public final void redirectCall(@NonNull android.net.Uri, @NonNull android.telecom.PhoneAccountHandle, boolean);
@@ -45958,6 +46037,7 @@ package android.util {
method public boolean contains(Object);
method public boolean containsAll(java.util.Collection<?>);
method public void ensureCapacity(int);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public int indexOf(Object);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
@@ -51019,6 +51099,7 @@ package android.view.accessibility {
method public CharSequence getClassName();
method public CharSequence getContentDescription();
method public int getCurrentItemIndex();
+ method public int getDisplayId();
method public int getFromIndex();
method public int getItemCount();
method public int getMaxScrollX();
@@ -51435,6 +51516,7 @@ package android.view.autofill {
public final class AutofillManager {
method public void cancel();
+ method public void clearAutofillRequestCallback();
method public void commit();
method public void disableAutofillServices();
method @Nullable public android.content.ComponentName getAutofillServiceComponentName();
@@ -51460,6 +51542,7 @@ package android.view.autofill {
method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
method public void requestAutofill(@NonNull android.view.View);
method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
+ method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
method public void setUserData(@Nullable android.service.autofill.UserData);
method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
@@ -51478,6 +51561,10 @@ package android.view.autofill {
field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
}
+ public interface AutofillRequestCallback {
+ method public void onFillRequest(@Nullable android.view.inputmethod.InlineSuggestionsRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback);
+ }
+
public final class AutofillValue implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutofillValue forDate(long);
@@ -51848,10 +51935,12 @@ package android.view.inputmethod {
ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setClientSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlineTooltipPresentationSpec(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setServiceSupported(boolean);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
@@ -51888,7 +51977,7 @@ package android.view.inputmethod {
method public boolean finishComposingText();
method public int getCursorCapsMode(int);
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
- method public android.os.Handler getHandler();
+ method @Nullable public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
method @Nullable public default android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e48a1da7b6a7..aa0f61e3e3dc 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,14 @@ package android.hardware.usb {
}
+package android.location {
+
+ public class LocationManager {
+ method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull android.location.Location);
+ }
+
+}
+
package android.media {
public class AudioManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d73aa67ed1a..6727031f7a1c 100644..100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -924,6 +924,7 @@ package android.app.admin {
}
public class DevicePolicyManager {
+ method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
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(anyOf={android.Manifest.permission.MANAGE_USERS, "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -956,7 +957,7 @@ package android.app.admin {
field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
field @RequiresPermission(android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION) public static final String ACTION_RESET_PROTECTION_POLICY_CHANGED = "android.app.action.RESET_PROTECTION_POLICY_CHANGED";
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 @Deprecated 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 @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";
@@ -972,6 +973,7 @@ package android.app.admin {
field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1
field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4
+ field public static final int PROVISIONING_TRIGGER_NFC = 5; // 0x5
field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3
field public static final int PROVISIONING_TRIGGER_QR_CODE = 2; // 0x2
field public static final int PROVISIONING_TRIGGER_UNSPECIFIED = 0; // 0x0
@@ -1967,6 +1969,7 @@ package android.bluetooth {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean canBondWithoutDialog();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean fetchUuidsWithSdp(int);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public byte[] getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getSimAccessPermission();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isConnected();
@@ -2087,6 +2090,7 @@ package android.bluetooth {
field public static final int PBAP_CLIENT = 17; // 0x11
field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
+ field public static final int VOLUME_CONTROL = 23; // 0x17
}
public final class BluetoothStatusCodes {
@@ -2104,6 +2108,7 @@ package android.bluetooth {
field @NonNull public static final android.os.ParcelUuid BASE_UUID;
field @NonNull public static final android.os.ParcelUuid BNEP;
field @NonNull public static final android.os.ParcelUuid DIP;
+ field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL;
field @NonNull public static final android.os.ParcelUuid HEARING_AID;
field @NonNull public static final android.os.ParcelUuid HFP;
field @NonNull public static final android.os.ParcelUuid HFP_AG;
@@ -2114,6 +2119,7 @@ package android.bluetooth {
field @NonNull public static final android.os.ParcelUuid LE_AUDIO;
field @NonNull public static final android.os.ParcelUuid MAP;
field @NonNull public static final android.os.ParcelUuid MAS;
+ field @NonNull public static final android.os.ParcelUuid MEDIA_CONTROL;
field @NonNull public static final android.os.ParcelUuid MNS;
field @NonNull public static final android.os.ParcelUuid NAP;
field @NonNull public static final android.os.ParcelUuid OBEX_OBJECT_PUSH;
@@ -2127,6 +2133,16 @@ package android.bluetooth {
field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
}
+ public final class BluetoothVolumeControl implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(@Nullable android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+ }
+
public final class BufferConstraint implements android.os.Parcelable {
ctor public BufferConstraint(int, int, int);
method public int describeContents();
@@ -2202,17 +2218,17 @@ package android.bluetooth.le {
public final class BluetoothLeScanner {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
}
- public final class ResultStorageDescriptor implements android.os.Parcelable {
- ctor public ResultStorageDescriptor(int, int, int);
- method public int describeContents();
- method public int getLength();
- method public int getOffset();
- method public int getType();
- method public void writeToParcel(android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
+ @Deprecated public final class ResultStorageDescriptor implements android.os.Parcelable {
+ ctor @Deprecated public ResultStorageDescriptor(int, int, int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public int getLength();
+ method @Deprecated public int getOffset();
+ method @Deprecated public int getType();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.ResultStorageDescriptor> CREATOR;
}
public final class ScanFilter implements android.os.Parcelable {
@@ -2236,10 +2252,10 @@ package android.bluetooth.le {
method public android.bluetooth.le.ScanSettings.Builder setScanResultType(int);
}
- public final class TruncatedFilter {
- ctor public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
- method public android.bluetooth.le.ScanFilter getFilter();
- method public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
+ @Deprecated public final class TruncatedFilter {
+ ctor @Deprecated public TruncatedFilter(android.bluetooth.le.ScanFilter, java.util.List<android.bluetooth.le.ResultStorageDescriptor>);
+ method @Deprecated public android.bluetooth.le.ScanFilter getFilter();
+ method @Deprecated public java.util.List<android.bluetooth.le.ResultStorageDescriptor> getStorageDescriptors();
}
}
@@ -2339,6 +2355,7 @@ package android.content {
field public static final String TETHERING_SERVICE = "tethering";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
+ field public static final String UWB_SERVICE = "uwb";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -2421,6 +2438,7 @@ package android.content {
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
+ field public static final String EXTRA_SHOWING_ATTRIBUTION = "android.intent.extra.SHOWING_ATTRIBUTION";
field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
@@ -2818,6 +2836,9 @@ package android.content.pm {
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
field public static final int RESTRICTION_NONE = 0; // 0x0
+ field public static final int ROLLBACK_DATA_POLICY_RESTORE = 0; // 0x0
+ field public static final int ROLLBACK_DATA_POLICY_RETAIN = 2; // 0x2
+ field public static final int ROLLBACK_DATA_POLICY_WIPE = 1; // 0x1
field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN = 0; // 0x0
field public static final int SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_VISIBLE = 1; // 0x1
field public static final int SYSTEM_APP_STATE_INSTALLED = 2; // 0x2
@@ -2848,7 +2869,7 @@ package android.content.pm {
field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000
field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000
field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000
- field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000
+ field @Deprecated 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
@@ -3262,11 +3283,13 @@ package android.hardware.display {
public final class DisplayManager {
method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
+ method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfigurationForDisplay(@NonNull String);
method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
method public android.util.Pair<float[],float[]> getMinimumBrightnessCurve();
method public android.graphics.Point getStableDisplaySize();
method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+ method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
}
@@ -3297,7 +3320,9 @@ package android.hardware.hdmi {
method @Nullable public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerControlMode();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public String getPowerStateChangeOnActiveSourceLost();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getRoutingControl();
method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
+ method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioControl();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getSystemAudioModeMuting();
method @Nullable public android.hardware.hdmi.HdmiTvClient getTvClient();
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public int getTvSendStandbyOnSleep();
@@ -3313,7 +3338,9 @@ package android.hardware.hdmi {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecVolumeControlEnabled(int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerControlMode(@NonNull String);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setPowerStateChangeOnActiveSourceLost(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setRoutingControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
+ method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioControl(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setSystemAudioModeMuting(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvSendStandbyOnSleep(@NonNull int);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setTvWakeOnOneTouchPlay(@NonNull int);
@@ -3323,6 +3350,8 @@ package android.hardware.hdmi {
field public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
field public static final String CEC_SETTING_NAME_POWER_CONTROL_MODE = "power_control_mode";
field public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST = "power_state_change_on_active_source_lost";
+ field public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL = "system_audio_control";
field public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING = "system_audio_mode_muting";
field public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
field public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY = "tv_wake_on_one_touch_play";
@@ -3380,6 +3409,7 @@ package android.hardware.hdmi {
field public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
field public static final String POWER_CONTROL_MODE_NONE = "none";
field public static final String POWER_CONTROL_MODE_TV = "to_tv";
+ field public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE = "none";
field public static final String POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW = "standby_now";
field public static final int POWER_STATUS_ON = 0; // 0x0
@@ -3395,6 +3425,10 @@ package android.hardware.hdmi {
field public static final int RESULT_SUCCESS = 0; // 0x0
field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
field public static final int RESULT_TIMEOUT = 1; // 0x1
+ field public static final int ROUTING_CONTROL_DISABLED = 0; // 0x0
+ field public static final int ROUTING_CONTROL_ENABLED = 1; // 0x1
+ field public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0; // 0x0
+ field public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1; // 0x1
field public static final int SYSTEM_AUDIO_MODE_MUTING_DISABLED = 0; // 0x0
field public static final int SYSTEM_AUDIO_MODE_MUTING_ENABLED = 1; // 0x1
field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3
@@ -3608,7 +3642,7 @@ package android.hardware.hdmi {
public final class HdmiTvClient extends android.hardware.hdmi.HdmiClient {
method public void clearTimerRecording(int, int, android.hardware.hdmi.HdmiTimerRecordSources.TimerRecordSource);
method public void deviceSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
- method public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getDeviceList();
+ method @Deprecated public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getDeviceList();
method public int getDeviceType();
method public void portSelect(int, @NonNull android.hardware.hdmi.HdmiTvClient.SelectCallback);
method public void sendMhlVendorCommand(int, int, int, byte[]);
@@ -3660,7 +3694,7 @@ package android.hardware.location {
public class ContextHubClient implements java.io.Closeable {
method public void close();
method @NonNull public android.hardware.location.ContextHubInfo getAttachedHub();
- method @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessageToNanoApp(@NonNull android.hardware.location.NanoAppMessage);
}
public class ContextHubClientCallback {
@@ -3711,26 +3745,26 @@ package android.hardware.location {
}
public final class ContextHubManager {
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
- method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int[] getContextHubHandles();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
- method @Deprecated @Nullable @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.location.ContextHubClientCallback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback, @NonNull java.util.concurrent.Executor);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.ContextHubClientCallback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@Nullable android.content.Context, @NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubClient createClient(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.app.PendingIntent, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] findNanoAppOnHub(int, @NonNull android.hardware.location.NanoAppFilter);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int[] getContextHubHandles();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubInfo getContextHubInfo(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public java.util.List<android.hardware.location.ContextHubInfo> getContextHubs();
+ method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.NanoAppInstanceInfo getNanoAppInstanceInfo(int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int loadNanoApp(int, @NonNull android.hardware.location.NanoApp);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> loadNanoApp(@NonNull android.hardware.location.ContextHubInfo, @NonNull android.hardware.location.NanoAppBinary);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.util.List<android.hardware.location.NanoAppState>> queryNanoApps(@NonNull android.hardware.location.ContextHubInfo);
method @Deprecated public int registerCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
method @Deprecated public int registerCallback(android.hardware.location.ContextHubManager.Callback, android.os.Handler);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public int unloadNanoApp(int);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_CONTEXT_HUB}) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int sendMessage(int, int, @NonNull android.hardware.location.ContextHubMessage);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public int unloadNanoApp(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(@NonNull android.hardware.location.ContextHubInfo, long);
method @Deprecated public int unregisterCallback(@NonNull android.hardware.location.ContextHubManager.Callback);
field public static final int AUTHORIZATION_DENIED = 0; // 0x0
field public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; // 0x1
@@ -4909,6 +4943,7 @@ package android.location {
public final class LastLocationRequest implements android.os.Parcelable {
method public int describeContents();
+ method public boolean isAdasGnssBypass();
method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -4919,6 +4954,7 @@ package android.location {
ctor public LastLocationRequest.Builder();
ctor public LastLocationRequest.Builder(@NonNull android.location.LastLocationRequest);
method @NonNull public android.location.LastLocationRequest build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setAdasGnssBypass(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LastLocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LastLocationRequest.Builder setLocationSettingsIgnored(boolean);
}
@@ -4938,6 +4974,7 @@ package android.location {
method @Deprecated public int getGnssBatchSize();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String, @NonNull android.location.LastLocationRequest);
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void injectGnssMeasurementCorrections(@NonNull android.location.GnssMeasurementCorrections);
+ method public boolean isAdasGnssLocationEnabled();
method public boolean isExtraLocationControllerPackageEnabled();
method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -4949,11 +4986,14 @@ package android.location {
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);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAdasGnssLocationEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackage(@Nullable String);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setExtraLocationControllerPackageEnabled(boolean);
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);
+ field public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED = "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+ field public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
}
public final class LocationRequest implements android.os.Parcelable {
@@ -4969,6 +5009,7 @@ package android.location {
method @Deprecated @NonNull public String getProvider();
method @Deprecated public float getSmallestDisplacement();
method @NonNull public android.os.WorkSource getWorkSource();
+ method public boolean isAdasGnssBypass();
method public boolean isHiddenFromAppOps();
method public boolean isLocationSettingsIgnored();
method public boolean isLowPower();
@@ -4994,6 +5035,7 @@ package android.location {
}
public static final class LocationRequest.Builder {
+ method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setAdasGnssBypass(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
@@ -5370,6 +5412,12 @@ package android.media {
field public static final android.media.RouteDiscoveryPreference EMPTY;
}
+ public class Spatializer {
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ }
+
}
package android.media.audiofx {
@@ -5401,6 +5449,9 @@ package android.media.audiopolicy {
}
public class AudioMixingRule {
+ method public int getTargetMixRole();
+ field public static final int MIX_ROLE_INJECTOR = 1; // 0x1
+ field public static final int MIX_ROLE_PLAYERS = 0; // 0x0
field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2
field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1
field public static final int RULE_MATCH_UID = 4; // 0x4
@@ -5415,6 +5466,7 @@ package android.media.audiopolicy {
method public android.media.audiopolicy.AudioMixingRule build();
method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException;
method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder setTargetMixRole(int);
}
public class AudioPolicy {
@@ -5547,11 +5599,7 @@ package android.media.session {
public final class MediaSessionManager {
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventDispatchedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener);
- method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void addOnMediaKeyEventSessionChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
- method @Nullable @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public android.media.session.MediaSession.Token getMediaKeyEventSession();
- method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public String getMediaKeyEventSessionPackageName();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventDispatchedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventDispatchedListener);
- method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void removeOnMediaKeyEventSessionChangedListener(@NonNull android.media.session.MediaSessionManager.OnMediaKeyEventSessionChangedListener);
method @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, @Nullable android.os.Handler);
method @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, @Nullable android.os.Handler);
}
@@ -5560,10 +5608,6 @@ package android.media.session {
method public void onMediaKeyEventDispatched(@NonNull android.view.KeyEvent, @NonNull String, @Nullable android.media.session.MediaSession.Token);
}
- public static interface MediaSessionManager.OnMediaKeyEventSessionChangedListener {
- method public void onMediaKeyEventSessionChanged(@NonNull String, @Nullable android.media.session.MediaSession.Token);
- }
-
public static interface MediaSessionManager.OnMediaKeyListener {
method public boolean onMediaKey(android.view.KeyEvent);
}
@@ -5984,6 +6028,7 @@ package android.media.tv.tuner {
method public static int getTunerVersion();
field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000
field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
+ field public static final int TUNER_VERSION_2_0 = 131072; // 0x20000
field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
}
@@ -6992,13 +7037,13 @@ package android.media.tv.tuner.frontend {
field public static final long FEC_23_36 = 268435456L; // 0x10000000L
field public static final long FEC_25_36 = 536870912L; // 0x20000000L
field public static final long FEC_26_45 = 1073741824L; // 0x40000000L
- field public static final long FEC_28_45 = -2147483648L; // 0xffffffff80000000L
- field public static final long FEC_29_45 = 1L; // 0x1L
+ field public static final long FEC_28_45 = 2147483648L; // 0x80000000L
+ field public static final long FEC_29_45 = 4294967296L; // 0x100000000L
field public static final long FEC_2_3 = 32L; // 0x20L
field public static final long FEC_2_5 = 64L; // 0x40L
field public static final long FEC_2_9 = 128L; // 0x80L
- field public static final long FEC_31_45 = 2L; // 0x2L
- field public static final long FEC_32_45 = 4L; // 0x4L
+ field public static final long FEC_31_45 = 8589934592L; // 0x200000000L
+ field public static final long FEC_32_45 = 17179869184L; // 0x400000000L
field public static final long FEC_3_4 = 256L; // 0x100L
field public static final long FEC_3_5 = 512L; // 0x200L
field public static final long FEC_4_15 = 2048L; // 0x800L
@@ -7006,7 +7051,7 @@ package android.media.tv.tuner.frontend {
field public static final long FEC_5_6 = 4096L; // 0x1000L
field public static final long FEC_5_9 = 8192L; // 0x2000L
field public static final long FEC_6_7 = 16384L; // 0x4000L
- field public static final long FEC_77_90 = 8L; // 0x8L
+ field public static final long FEC_77_90 = 34359738368L; // 0x800000000L
field public static final long FEC_7_15 = 131072L; // 0x20000L
field public static final long FEC_7_8 = 32768L; // 0x8000L
field public static final long FEC_7_9 = 65536L; // 0x10000L
@@ -8059,8 +8104,12 @@ package android.os {
}
public final class BatteryStatsManager {
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
- method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanReset();
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanResults(@NonNull android.os.WorkSource, int);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStarted(@NonNull android.os.WorkSource, boolean);
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportBleScanStopped(@NonNull android.os.WorkSource, boolean);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
@@ -8548,8 +8597,10 @@ package android.os {
method public boolean bind(android.os.UpdateEngineCallback);
method public void cancel();
method @WorkerThread public int cleanupAppliedPayload();
+ method public void resetShouldSwitchSlotOnReboot();
method public void resetStatus();
method public void resume();
+ method public void setShouldSwitchSlotOnReboot(@NonNull String);
method public void suspend();
method public boolean unbind();
method public boolean verifyPayloadMetadata(String);
@@ -9053,6 +9104,7 @@ package android.provider {
public final class DeviceConfig {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
@@ -9083,6 +9135,7 @@ package android.provider {
field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
+ field public static final String NAMESPACE_LMKD_NATIVE = "lmkd_native";
field public static final String NAMESPACE_LOCATION = "location";
field public static final String NAMESPACE_MEDIA = "media";
field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
@@ -9105,6 +9158,8 @@ package android.provider {
field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
+ field public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT = "surface_flinger_native_boot";
+ field public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
field public static final String NAMESPACE_SYSTEMUI = "systemui";
field public static final String NAMESPACE_SYSTEM_TIME = "system_time";
field public static final String NAMESPACE_TELEPHONY = "telephony";
@@ -14220,148 +14275,6 @@ package android.util {
}
-package android.uwb {
-
- public final class AngleMeasurement implements android.os.Parcelable {
- ctor public AngleMeasurement(@FloatRange(from=-3.141592653589793, to=3.141592653589793) double, @FloatRange(from=0.0, to=3.141592653589793) double, @FloatRange(from=0.0, to=1.0) double);
- method public int describeContents();
- method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
- method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
- method @FloatRange(from=-3.141592653589793, to=3.141592653589793) public double getRadians();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
- }
-
- public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.uwb.AngleMeasurement getAltitude();
- method @NonNull public android.uwb.AngleMeasurement getAzimuth();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleOfArrivalMeasurement> CREATOR;
- }
-
- public static final class AngleOfArrivalMeasurement.Builder {
- ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
- method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
- method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
- }
-
- public final class DistanceMeasurement implements android.os.Parcelable {
- method public int describeContents();
- method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
- method @FloatRange(from=0.0) public double getErrorMeters();
- method public double getMeters();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
- }
-
- public static final class DistanceMeasurement.Builder {
- ctor public DistanceMeasurement.Builder();
- method @NonNull public android.uwb.DistanceMeasurement build();
- method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(@FloatRange(from=0.0, to=1.0) double);
- method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(@FloatRange(from=0.0) double);
- method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
- }
-
- public final class RangingMeasurement implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.uwb.AngleOfArrivalMeasurement getAngleOfArrivalMeasurement();
- method @Nullable public android.uwb.DistanceMeasurement getDistanceMeasurement();
- method public long getElapsedRealtimeNanos();
- method @NonNull public android.uwb.UwbAddress getRemoteDeviceAddress();
- method public int getStatus();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingMeasurement> CREATOR;
- field public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; // 0x1
- field public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; // 0xffffffff
- field public static final int RANGING_STATUS_SUCCESS = 0; // 0x0
- }
-
- public static final class RangingMeasurement.Builder {
- ctor public RangingMeasurement.Builder();
- method @NonNull public android.uwb.RangingMeasurement build();
- method @NonNull public android.uwb.RangingMeasurement.Builder setAngleOfArrivalMeasurement(@NonNull android.uwb.AngleOfArrivalMeasurement);
- method @NonNull public android.uwb.RangingMeasurement.Builder setDistanceMeasurement(@NonNull android.uwb.DistanceMeasurement);
- method @NonNull public android.uwb.RangingMeasurement.Builder setElapsedRealtimeNanos(long);
- method @NonNull public android.uwb.RangingMeasurement.Builder setRemoteDeviceAddress(@NonNull android.uwb.UwbAddress);
- method @NonNull public android.uwb.RangingMeasurement.Builder setStatus(int);
- }
-
- public final class RangingReport implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<android.uwb.RangingMeasurement> getMeasurements();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingReport> CREATOR;
- }
-
- public static final class RangingReport.Builder {
- ctor public RangingReport.Builder();
- method @NonNull public android.uwb.RangingReport.Builder addMeasurement(@NonNull android.uwb.RangingMeasurement);
- method @NonNull public android.uwb.RangingReport.Builder addMeasurements(@NonNull java.util.List<android.uwb.RangingMeasurement>);
- method @NonNull public android.uwb.RangingReport build();
- }
-
- public final class RangingSession implements java.lang.AutoCloseable {
- 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 {
- method public void onClosed(int, @NonNull android.os.PersistableBundle);
- method public void onOpenFailed(int, @NonNull android.os.PersistableBundle);
- method public void onOpened(@NonNull android.uwb.RangingSession);
- method public void onReconfigureFailed(int, @NonNull android.os.PersistableBundle);
- method public void onReconfigured(@NonNull android.os.PersistableBundle);
- method public void onReportReceived(@NonNull android.uwb.RangingReport);
- method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
- method public void onStarted(@NonNull android.os.PersistableBundle);
- method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
- method public void onStopped(int, @NonNull android.os.PersistableBundle);
- field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
- field public static final int REASON_GENERIC_ERROR = 4; // 0x4
- field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
- field public static final int REASON_MAX_SESSIONS_REACHED = 5; // 0x5
- field public static final int REASON_PROTOCOL_SPECIFIC_ERROR = 7; // 0x7
- field public static final int REASON_REMOTE_REQUEST = 2; // 0x2
- field public static final int REASON_SYSTEM_POLICY = 6; // 0x6
- field public static final int REASON_UNKNOWN = 0; // 0x0
- }
-
- public final class UwbAddress implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public static android.uwb.UwbAddress fromBytes(@NonNull byte[]);
- method public int size();
- method @NonNull public byte[] toBytes();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.uwb.UwbAddress> CREATOR;
- field public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; // 0x8
- field public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; // 0x2
- }
-
- public final class UwbManager {
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
- method @NonNull @RequiresPermission(allOf={android.Manifest.permission.UWB_PRIVILEGED, android.Manifest.permission.UWB_RANGING}) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
- }
-
- public static interface UwbManager.AdapterStateCallback {
- method public void onStateChanged(int, int);
- field public static final int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; // 0x1
- field public static final int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; // 0x4
- field public static final int STATE_CHANGED_REASON_SESSION_STARTED = 0; // 0x0
- field public static final int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; // 0x3
- field public static final int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; // 0x2
- field public static final int STATE_DISABLED = 0; // 0x0
- field public static final int STATE_ENABLED_ACTIVE = 2; // 0x2
- field public static final int STATE_ENABLED_INACTIVE = 1; // 0x1
- }
-
-}
-
package android.view {
public abstract class Window {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ea6d0cecfd73..f951df15ca5b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1281,6 +1281,12 @@ package android.hardware.soundtrigger {
package android.inputmethodservice {
+ public abstract class AbstractInputMethodService extends android.window.WindowProviderService implements android.view.KeyEvent.Callback {
+ method public final int getInitialDisplayId();
+ method @Nullable public final android.os.Bundle getWindowContextOptions();
+ method public final int getWindowType();
+ }
+
@UiContext public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
field public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // 0x94fa793L
}
@@ -1397,6 +1403,7 @@ package android.location {
package android.media {
public final class AudioAttributes implements android.os.Parcelable {
+ method public static int[] getSdkUsages();
method @NonNull public static String usageToXsdString(int);
method public static int xsdStringToUsage(@NonNull String);
}
@@ -1421,7 +1428,9 @@ package android.media {
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int abandonAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String);
method @Nullable public static android.media.AudioDeviceInfo getDeviceInfoFromType(int);
method @IntRange(from=0) @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public long getFadeOutDurationOnFocusLossMillis(@NonNull android.media.AudioAttributes);
+ method public static final int[] getPublicStreamTypes();
method @NonNull public java.util.List<java.lang.Integer> getReportedSurroundFormats();
+ method public int getStreamMinVolumeInt(int);
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Boolean> getSurroundFormats();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE}) public boolean isFullVolumeDevice();
@@ -1446,6 +1455,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static float getMasterBalance();
method public static final int getNumStreamTypes();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) public static int setMasterBalance(float);
+ method @NonNull public static String streamToString(int);
field public static final int DEVICE_ROLE_DISABLED = 2; // 0x2
field public static final int DEVICE_ROLE_NONE = 0; // 0x0
field public static final int DEVICE_ROLE_PREFERRED = 1; // 0x1
@@ -1543,6 +1553,13 @@ package android.media.audiopolicy {
method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean);
}
+ public final class AudioProductStrategy implements android.os.Parcelable {
+ method @NonNull public static android.media.AudioAttributes getDefaultAttributes();
+ method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method public int getVolumeGroupIdForLegacyStreamType(int);
+ }
+
}
package android.media.metrics {
@@ -2121,6 +2138,7 @@ package android.provider {
public final class DeviceConfig {
field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
field public static final String NAMESPACE_ANDROID = "android";
+ field public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
field public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
field public static final String NAMESPACE_DEVICE_IDLE = "device_idle";
field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
@@ -2834,6 +2852,7 @@ package android.view {
method @Nullable public final android.os.IBinder getWindowContextToken();
method public final void setWindowContextToken(@NonNull android.os.IBinder);
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
+ field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
field public CharSequence accessibilityTitle;
field public int privateFlags;
@@ -2865,6 +2884,10 @@ package android.view.accessibility {
method public long getAccessibilityIdForRegion(@NonNull android.graphics.Region);
}
+ public class AccessibilityRecord {
+ method public void setDisplayId(int);
+ }
+
public final class AccessibilityWindowInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
}
@@ -3194,6 +3217,59 @@ package android.window {
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR;
}
+ public final class TaskFragmentAppearedInfo implements android.os.Parcelable {
+ method @NonNull public android.view.SurfaceControl getLeash();
+ method @NonNull public android.window.TaskFragmentInfo getTaskFragmentInfo();
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentAppearedInfo> CREATOR;
+ }
+
+ public final class TaskFragmentCreationParams implements android.os.Parcelable {
+ method @NonNull public android.os.IBinder getFragmentToken();
+ method @NonNull public android.graphics.Rect getInitialBounds();
+ method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizer();
+ method @NonNull public android.os.IBinder getOwnerToken();
+ method public int getWindowingMode();
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentCreationParams> CREATOR;
+ }
+
+ public static final class TaskFragmentCreationParams.Builder {
+ ctor public TaskFragmentCreationParams.Builder(@NonNull android.window.TaskFragmentOrganizerToken, @NonNull android.os.IBinder, @NonNull android.os.IBinder);
+ method @NonNull public android.window.TaskFragmentCreationParams build();
+ method @NonNull public android.window.TaskFragmentCreationParams.Builder setInitialBounds(@NonNull android.graphics.Rect);
+ method @NonNull public android.window.TaskFragmentCreationParams.Builder setWindowingMode(int);
+ }
+
+ public final class TaskFragmentInfo implements android.os.Parcelable {
+ method public boolean equalsForTaskFragmentOrganizer(@Nullable android.window.TaskFragmentInfo);
+ method @NonNull public java.util.List<android.os.IBinder> getActivities();
+ method @NonNull public android.content.res.Configuration getConfiguration();
+ method @NonNull public android.os.IBinder getFragmentToken();
+ method @NonNull public android.graphics.Point getPositionInParent();
+ method @NonNull public android.window.WindowContainerToken getToken();
+ method public int getWindowingMode();
+ method public boolean hasRunningActivity();
+ method public boolean isEmpty();
+ method public boolean isVisible();
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentInfo> CREATOR;
+ }
+
+ public class TaskFragmentOrganizer extends android.window.WindowOrganizer {
+ ctor public TaskFragmentOrganizer(@NonNull java.util.concurrent.Executor);
+ method @NonNull public java.util.concurrent.Executor getExecutor();
+ method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizerToken();
+ method public void onTaskFragmentAppeared(@NonNull android.window.TaskFragmentAppearedInfo);
+ method public void onTaskFragmentError(@NonNull android.os.IBinder, @NonNull Throwable);
+ method public void onTaskFragmentInfoChanged(@NonNull android.window.TaskFragmentInfo);
+ method public void onTaskFragmentParentInfoChanged(@NonNull android.os.IBinder, @NonNull android.content.res.Configuration);
+ method public void onTaskFragmentVanished(@NonNull android.window.TaskFragmentInfo);
+ method @CallSuper public void registerOrganizer();
+ method @CallSuper public void unregisterOrganizer();
+ }
+
+ public final class TaskFragmentOrganizerToken implements android.os.Parcelable {
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
+ }
+
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
method @BinderThread public void addStartingWindow(@NonNull android.window.StartingWindowInfo, @NonNull android.os.IBinder);
@@ -3222,9 +3298,13 @@ package android.window {
public final class WindowContainerTransaction implements android.os.Parcelable {
ctor public WindowContainerTransaction();
+ method @NonNull public android.window.WindowContainerTransaction createTaskFragment(@NonNull android.window.TaskFragmentCreationParams);
+ method @NonNull public android.window.WindowContainerTransaction deleteTaskFragment(@NonNull android.window.WindowContainerToken);
method public int describeContents();
method @NonNull public android.window.WindowContainerTransaction reorder(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction reparent(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, boolean);
+ method @NonNull public android.window.WindowContainerTransaction reparentActivityToTaskFragment(@NonNull android.os.IBinder, @NonNull android.os.IBinder);
+ method @NonNull public android.window.WindowContainerTransaction reparentChildren(@NonNull android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
@@ -3232,12 +3312,14 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);
+ method @NonNull public android.window.WindowContainerTransaction setErrorCallbackToken(@NonNull android.os.IBinder);
method @NonNull public android.window.WindowContainerTransaction setFocusable(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setHidden(@NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setLaunchRoot(@NonNull android.window.WindowContainerToken, @Nullable int[], @Nullable int[]);
method @NonNull public android.window.WindowContainerTransaction setScreenSizeDp(@NonNull android.window.WindowContainerToken, int, int);
method @NonNull public android.window.WindowContainerTransaction setSmallestScreenWidthDp(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setWindowingMode(@NonNull android.window.WindowContainerToken, int);
+ method @NonNull public android.window.WindowContainerTransaction startActivityInTaskFragment(@NonNull android.os.IBinder, @NonNull android.os.IBinder, @NonNull android.content.Intent, @Nullable android.os.Bundle);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.WindowContainerTransaction> CREATOR;
}
@@ -3249,14 +3331,15 @@ package android.window {
public class WindowOrganizer {
ctor public WindowOrganizer();
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
+ method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
+ method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
}
@UiContext public abstract class WindowProviderService extends android.app.Service {
ctor public WindowProviderService();
method public final void attachToWindowToken(@NonNull android.os.IBinder);
- method @Nullable public android.os.Bundle getWindowContextOptions();
+ method @NonNull public int getInitialDisplayId();
+ method @CallSuper @Nullable public android.os.Bundle getWindowContextOptions();
method public abstract int getWindowType();
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 5409165542f4..e3690e55f4da 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -71,6 +71,10 @@ ActionValue: android.telephony.mbms.vendor.VendorUtils#EXTRA_TEMP_LIST:
+AllUpper: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Constant field names must be named with only upper case characters: `android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes`, should be `S_DEFAULT_ATTRIBUTES`?
+
+
ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
@@ -537,6 +541,8 @@ InterfaceConstant: android.telecom.PhoneAccountSuggestionService#SERVICE_INTERFA
+InternalField: android.media.audiopolicy.AudioProductStrategy#sDefaultAttributes:
+ Internal field sDefaultAttributes must not be exposed
InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
@@ -1045,8 +1051,14 @@ MissingNullability: android.location.LocationManager#getTestProviderCurrentReque
MissingNullability: android.location.LocationRequest#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioAttributes#SDK_USAGES:
+ Missing nullability on field `SDK_USAGES` in class `class android.media.AudioAttributes`
+MissingNullability: android.media.AudioAttributes#getSdkUsages():
+ Missing nullability on method `getSdkUsages` return
MissingNullability: android.media.AudioFocusInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+MissingNullability: android.media.AudioManager#getPublicStreamTypes():
+ Missing nullability on method `getPublicStreamTypes` return
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #3:
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String) parameter #4:
@@ -1063,6 +1075,8 @@ MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConf
MissingNullability: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #6:
+MissingNullability: android.media.AudioSystem#streamToString(int):
+ Missing nullability on method `streamToString` return
MissingNullability: android.media.PlaybackParams#setAudioStretchMode(int):
MissingNullability: android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL:
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 26c83eeca508..427bbd82f561 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -315,6 +315,27 @@ aidl_interface {
backend: {
rust: {
enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt", // for virtualizationservice
+ ],
+ },
+ },
+}
+
+aidl_interface {
+ name: "android.debug_aidl",
+ unstable: true,
+ srcs: [
+ "android/debug/AdbTransportType.aidl",
+ "android/debug/FingerprintAndPairDevice.aidl",
+ "android/debug/IAdbCallback.aidl",
+ "android/debug/IAdbManager.aidl",
+ "android/debug/PairDevice.aidl",
+ ],
+ backend: {
+ cpp: {
+ enabled: true,
},
},
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index e91209c1a273..f47995ca0efe 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,8 @@
package android.accessibilityservice;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
@@ -27,6 +29,7 @@ import android.annotation.TestApi;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.Bitmap;
@@ -36,6 +39,7 @@ import android.graphics.Region;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -102,14 +106,14 @@ import java.util.function.Consumer;
* An accessibility is declared as any other service in an AndroidManifest.xml, but it
* must do two things:
* <ul>
- * <ol>
+ * <li>
* Specify that it handles the "android.accessibilityservice.AccessibilityService"
* {@link android.content.Intent}.
- * </ol>
- * <ol>
+ * </li>
+ * <li>
* Request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission to
* ensure that only the system can bind to it.
- * </ol>
+ * </li>
* </ul>
* If either of these items is missing, the system will ignore the accessibility service.
* Following is an example declaration:
@@ -961,30 +965,31 @@ public abstract class AccessibilityService extends Service {
}
}
+ @NonNull
@Override
public Context createDisplayContext(Display display) {
- final Context context = super.createDisplayContext(display);
- final int displayId = display.getDisplayId();
- setDefaultTokenInternal(context, displayId);
- return context;
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
}
- private void setDefaultTokenInternal(Context context, int displayId) {
- final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
- final IAccessibilityServiceConnection connection =
- AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
- IBinder token = null;
- if (connection != null) {
- synchronized (mLock) {
- try {
- token = connection.getOverlayWindowToken(displayId);
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Failed to get window token", re);
- re.rethrowFromSystemServer();
- }
- }
- wm.setDefaultToken(token);
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
}
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
}
/**
@@ -2069,6 +2074,10 @@ public abstract class AccessibilityService extends Service {
if (WINDOW_SERVICE.equals(name)) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) getBaseContext().getSystemService(name);
+ final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+ // Set e default token obtained from the connection to ensure client could use
+ // accessibility overlay.
+ wm.setDefaultToken(mWindowToken);
}
return mWindowManager;
}
@@ -2177,8 +2186,10 @@ public abstract class AccessibilityService extends Service {
// The client may have already obtained the window manager, so
// update the default token on whatever manager we gave them.
- final WindowManagerImpl wm = (WindowManagerImpl) getSystemService(WINDOW_SERVICE);
- wm.setDefaultToken(windowToken);
+ if (mWindowManager != null) {
+ final WindowManagerImpl wm = (WindowManagerImpl) mWindowManager;
+ wm.setDefaultToken(mWindowToken);
+ }
}
@Override
@@ -2400,6 +2411,14 @@ public abstract class AccessibilityService extends Service {
if (connection != null) {
AccessibilityInteractionClient.getInstance(mContext).addConnection(
mConnectionId, connection);
+ if (mContext != null) {
+ try {
+ connection.setAttributionTag(mContext.getAttributionTag());
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting attributionTag", re);
+ re.rethrowFromSystemServer();
+ }
+ }
mCallback.init(mConnectionId, windowToken);
mCallback.onServiceConnected();
} else {
@@ -2675,4 +2694,58 @@ public abstract class AccessibilityService extends Service {
}
}
}
+
+ private static class AccessibilityContext extends ContextWrapper {
+ private final int mConnectionId;
+
+ private AccessibilityContext(Context base, int connectionId) {
+ super(base);
+ mConnectionId = connectionId;
+ setDefaultTokenInternal(this, getDisplayId());
+ }
+
+ @NonNull
+ @Override
+ public Context createDisplayContext(Display display) {
+ return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(int type, @Nullable Bundle options) {
+ final Context context = super.createWindowContext(type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ @NonNull
+ @Override
+ public Context createWindowContext(@NonNull Display display, int type,
+ @Nullable Bundle options) {
+ final Context context = super.createWindowContext(display, type, options);
+ if (type != TYPE_ACCESSIBILITY_OVERLAY) {
+ return context;
+ }
+ return new AccessibilityContext(context, mConnectionId);
+ }
+
+ private void setDefaultTokenInternal(Context context, int displayId) {
+ final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
+ WINDOW_SERVICE);
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getConnection(mConnectionId);
+ IBinder token = null;
+ if (connection != null) {
+ try {
+ token = connection.getOverlayWindowToken(displayId);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to get window token", re);
+ re.rethrowFromSystemServer();
+ }
+ wm.setDefaultToken(token);
+ }
+ }
+ }
}
diff --git a/core/java/android/accessibilityservice/AccessibilityTrace.java b/core/java/android/accessibilityservice/AccessibilityTrace.java
new file mode 100644
index 000000000000..f28015ab4685
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityTrace.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.accessibilityservice;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface to log accessibility trace.
+ *
+ * @hide
+ */
+public interface AccessibilityTrace {
+ String NAME_ACCESSIBILITY_SERVICE_CONNECTION = "IAccessibilityServiceConnection";
+ String NAME_ACCESSIBILITY_SERVICE_CLIENT = "IAccessibilityServiceClient";
+ String NAME_ACCESSIBILITY_MANAGER = "IAccessibilityManager";
+ String NAME_ACCESSIBILITY_MANAGER_CLIENT = "IAccessibilityManagerClient";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION = "IAccessibilityInteractionConnection";
+ String NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK =
+ "IAccessibilityInteractionConnectionCallback";
+ String NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = "IRemoteMagnificationAnimationCallback";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION = "IWindowMagnificationConnection";
+ String NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = "IWindowMagnificationConnectionCallback";
+ String NAME_WINDOW_MANAGER_INTERNAL = "WindowManagerInternal";
+ String NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = "WindowsForAccessibilityCallback";
+ String NAME_MAGNIFICATION_CALLBACK = "MagnificationCallbacks";
+ String NAME_INPUT_FILTER = "InputFilter";
+ String NAME_GESTURE = "Gesture";
+ String NAME_ACCESSIBILITY_SERVICE = "AccessibilityService";
+ String NAME_PACKAGE_BROADCAST_RECEIVER = "PMBroadcastReceiver";
+ String NAME_USER_BROADCAST_RECEIVER = "UserBroadcastReceiver";
+ String NAME_FINGERPRINT = "FingerprintGesture";
+ String NAME_ACCESSIBILITY_INTERACTION_CLIENT = "AccessibilityInteractionClient";
+
+ String NAME_ALL_LOGGINGS = "AllLoggings";
+ String NAME_NONE = "None";
+
+ long FLAGS_ACCESSIBILITY_SERVICE_CONNECTION = 0x0000000000000001L;
+ long FLAGS_ACCESSIBILITY_SERVICE_CLIENT = 0x0000000000000002L;
+ long FLAGS_ACCESSIBILITY_MANAGER = 0x0000000000000004L;
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT = 0x0000000000000008L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION = 0x0000000000000010L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK = 0x0000000000000020L;
+ long FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK = 0x0000000000000040L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION = 0x0000000000000080L;
+ long FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK = 0x0000000000000100L;
+ long FLAGS_WINDOW_MANAGER_INTERNAL = 0x0000000000000200L;
+ long FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK = 0x0000000000000400L;
+ long FLAGS_MAGNIFICATION_CALLBACK = 0x0000000000000800L;
+ long FLAGS_INPUT_FILTER = 0x0000000000001000L;
+ long FLAGS_GESTURE = 0x0000000000002000L;
+ long FLAGS_ACCESSIBILITY_SERVICE = 0x0000000000004000L;
+ long FLAGS_PACKAGE_BROADCAST_RECEIVER = 0x0000000000008000L;
+ long FLAGS_USER_BROADCAST_RECEIVER = 0x0000000000010000L;
+ long FLAGS_FINGERPRINT = 0x0000000000020000L;
+ long FLAGS_ACCESSIBILITY_INTERACTION_CLIENT = 0x0000000000040000L;
+
+ long FLAGS_LOGGING_NONE = 0x0000000000000000L;
+ long FLAGS_LOGGING_ALL = 0xFFFFFFFFFFFFFFFFL;
+
+ long FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES = FLAGS_ACCESSIBILITY_INTERACTION_CLIENT
+ | FLAGS_ACCESSIBILITY_SERVICE
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION
+ | FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
+ Map<String, Long> sNamesToFlags = Map.ofEntries(
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CONNECTION, FLAGS_ACCESSIBILITY_SERVICE_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE_CLIENT, FLAGS_ACCESSIBILITY_SERVICE_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER, FLAGS_ACCESSIBILITY_MANAGER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_MANAGER_CLIENT, FLAGS_ACCESSIBILITY_MANAGER_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK,
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION, FLAGS_WINDOW_MAGNIFICATION_CONNECTION),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOW_MANAGER_INTERNAL, FLAGS_WINDOW_MANAGER_INTERNAL),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_MAGNIFICATION_CALLBACK, FLAGS_MAGNIFICATION_CALLBACK),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_INPUT_FILTER, FLAGS_INPUT_FILTER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_GESTURE, FLAGS_GESTURE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_SERVICE, FLAGS_ACCESSIBILITY_SERVICE),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_PACKAGE_BROADCAST_RECEIVER, FLAGS_PACKAGE_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_USER_BROADCAST_RECEIVER, FLAGS_USER_BROADCAST_RECEIVER),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_FINGERPRINT, FLAGS_FINGERPRINT),
+ new AbstractMap.SimpleEntry<String, Long>(
+ NAME_ACCESSIBILITY_INTERACTION_CLIENT, FLAGS_ACCESSIBILITY_INTERACTION_CLIENT),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_NONE, FLAGS_LOGGING_NONE),
+ new AbstractMap.SimpleEntry<String, Long>(NAME_ALL_LOGGINGS, FLAGS_LOGGING_ALL));
+
+ /**
+ * Get the flags of the logging types by the given names.
+ * The names list contains logging type names in lower case.
+ */
+ static long getLoggingFlagsFromNames(List<String> names) {
+ long types = FLAGS_LOGGING_NONE;
+ for (String name : names) {
+ long flag = sNamesToFlags.get(name);
+ types |= flag;
+ }
+ return types;
+ }
+
+ /**
+ * Get the list of the names of logging types by the given flags.
+ */
+ static List<String> getNamesOfLoggingTypes(long flags) {
+ List<String> list = new ArrayList<String>();
+
+ for (Map.Entry<String, Long> entry : sNamesToFlags.entrySet()) {
+ if ((entry.getValue() & flags) != FLAGS_LOGGING_NONE) {
+ list.add(entry.getKey());
+ }
+ }
+
+ return list;
+ }
+
+ /**
+ * Whether the trace is enabled for any logging type.
+ */
+ boolean isA11yTracingEnabled();
+
+ /**
+ * Whether the trace is enabled for any of the given logging type.
+ */
+ boolean isA11yTracingEnabledForTypes(long typeIdFlags);
+
+ /**
+ * Get trace state to be sent to AccessibilityManager.
+ */
+ int getTraceStateForAccessibilityManagerClientState();
+
+ /**
+ * Start tracing for the given logging types.
+ */
+ void startTrace(long flagss);
+
+ /**
+ * Stop tracing.
+ */
+ void stopTrace();
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to search through the
+ * tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ */
+ void logTrace(String where, long loggingFlags);
+
+ /**
+ * Log one trace entry.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ */
+ void logTrace(String where, long loggingFlags, String callingParams);
+
+ /**
+ * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
+ * make screen content related requests use this API to log entry when receive callback.
+ * @param timestamp The timestamp when a callback is received.
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param loggingFlags Flags to identify which logging types this entry belongs to. This
+ * can be used to filter the log entries when generating tracing file.
+ * @param callingParams The parameters for the callback.
+ * @param processId The process id of the calling component.
+ * @param threadId The threadId of the calling component.
+ * @param callingUid The calling uid of the callback.
+ * @param callStack The call stack of the callback.
+ * @param ignoreStackElements ignore these call stack element
+ */
+ void logTrace(long timestamp, String where, long loggingFlags, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreStackElements);
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 923b6f41414a..6c360e50a7bd 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -39,6 +39,8 @@ interface IAccessibilityServiceConnection {
void setServiceInfo(in AccessibilityServiceInfo info);
+ void setAttributionTag(in String attributionTag);
+
String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -118,6 +120,6 @@ interface IAccessibilityServiceConnection {
void setFocusAppearance(int strokeWidth, int color);
- oneway void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
+ oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
}
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 7b57442fb580..3807b503ce70 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -88,7 +88,8 @@ import java.util.Arrays;
* {@link AccountManager#KEY_INTENT}.
* <p>
* The activity needs to return the final result when it is complete so the Intent should contain
- * the {@link AccountAuthenticatorResponse} as {@link AccountManager#KEY_ACCOUNT_MANAGER_RESPONSE}.
+ * the {@link AccountAuthenticatorResponse} as
+ * {@link AccountManager#KEY_ACCOUNT_AUTHENTICATOR_RESPONSE}.
* The activity must then call {@link AccountAuthenticatorResponse#onResult} or
* {@link AccountAuthenticatorResponse#onError} when it is complete.
* <li> If the authenticator cannot synchronously process the request and return a result then it
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 0d6a07938e95..e6cdcc0ee742 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -31,6 +31,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import java.util.Objects;
import java.util.Set;
/**
@@ -86,6 +87,12 @@ public class Account implements Parcelable {
if (TextUtils.isEmpty(type)) {
throw new IllegalArgumentException("the type must not be empty: " + type);
}
+ if (name.length() > 200) {
+ throw new IllegalArgumentException("account name is longer than 200 characters");
+ }
+ if (type.length() > 200) {
+ throw new IllegalArgumentException("account type is longer than 200 characters");
+ }
this.name = name;
this.type = type;
this.accessId = accessId;
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 79fb86365b74..2bbf280277ff 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -27,6 +27,7 @@ import android.annotation.Size;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
@@ -349,6 +350,7 @@ public class AccountManager {
private static final class UserIdPackage
{
+ @UserIdInt
public int userId;
public String packageName;
@@ -379,7 +381,8 @@ public class AccountManager {
}
PropertyInvalidatedCache<UserIdPackage, Account[]> mAccountsForUserCache =
- new PropertyInvalidatedCache<UserIdPackage, Account[]>(CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
+ new PropertyInvalidatedCache<UserIdPackage, Account[]>(
+ CACHE_ACCOUNTS_DATA_SIZE, CACHE_KEY_ACCOUNTS_DATA_PROPERTY) {
@Override
protected Account[] recompute(UserIdPackage userAndPackage) {
try {
@@ -389,6 +392,10 @@ public class AccountManager {
}
}
@Override
+ protected boolean bypass(UserIdPackage query) {
+ return query.userId < 0;
+ }
+ @Override
protected boolean debugCompareQueryResults(Account[] l, Account[] r) {
if (l == r) {
return true;
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 377edc65281f..a3a7b0ccbc2b 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -105,12 +105,15 @@ interface IAccountManager {
String statusToken);
/* Returns Map<String, Integer> from package name to visibility with all values stored for given account */
+ @SuppressWarnings(value = {"untyped-collection"})
Map getPackagesAndVisibilityForAccount(in Account account);
+ @SuppressWarnings(value = {"untyped-collection"})
boolean addAccountExplicitlyWithVisibility(in Account account, String password, in Bundle extras,
in Map visibility, in String opPackageName);
boolean setAccountVisibility(in Account a, in String packageName, int newVisibility);
int getAccountVisibility(in Account a, in String packageName);
/* Type may be null returns Map <Account, Integer>*/
+ @SuppressWarnings(value = {"untyped-collection"})
Map getAccountsAndVisibilityForPackage(in String packageName, in String accountType);
void registerAccountListener(in String[] accountTypes, String opPackageName);
diff --git a/core/java/android/accounts/OWNERS b/core/java/android/accounts/OWNERS
index 8dcc04a27af6..6ad9d924f171 100644
--- a/core/java/android/accounts/OWNERS
+++ b/core/java/android/accounts/OWNERS
@@ -1,9 +1,4 @@
-carlosvaldivia@google.com
+jcivelli@google.com
dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@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 db5dcc5c264b..96476a5055ef 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -61,7 +61,6 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
@@ -126,11 +125,9 @@ import android.view.Window.WindowControllerCallback;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.view.autofill.AutofillClientController;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillClient;
-import android.view.autofill.AutofillPopupWindow;
-import android.view.autofill.IAutofillWindowPresenter;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
@@ -159,7 +156,6 @@ 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;
import java.util.HashMap;
import java.util.List;
@@ -740,7 +736,7 @@ public class Activity extends ContextThemeWrapper
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback,
- AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
+ ContentCaptureManager.ContentCaptureClient {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
@@ -766,9 +762,7 @@ public class Activity extends ContextThemeWrapper
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final String FRAGMENTS_TAG = "android:fragments";
- private static final String LAST_AUTOFILL_ID = "android:lastAutofillId";
- private static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
@@ -778,7 +772,6 @@ public class Activity extends ContextThemeWrapper
"android:hasCurrentPermissionsRequest";
private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
- private static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
private static final int LOG_AM_ON_CREATE_CALLED = 30057;
@@ -851,9 +844,6 @@ public class Activity extends ContextThemeWrapper
private SearchManager mSearchManager;
private MenuInflater mMenuInflater;
- /** The autofill manager. Always access via {@link #getAutofillManager()}. */
- @Nullable private AutofillManager mAutofillManager;
-
/** The content capture manager. Access via {@link #getContentCaptureManager()}. */
@Nullable private ContentCaptureManager mContentCaptureManager;
@@ -953,13 +943,8 @@ public class Activity extends ContextThemeWrapper
private boolean mHasCurrentPermissionsRequest;
- private boolean mAutoFillResetNeeded;
- private boolean mAutoFillIgnoreFirstResumePause;
-
- /** The last autofill id that was returned from {@link #getNextAutofillId()} */
- private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
-
- private AutofillPopupWindow mAutofillPopupWindow;
+ /** The autofill client controller. Always access via {@link #getAutofillClientController()}. */
+ private AutofillClientController mAutofillClientController;
/** @hide */
boolean mEnterAnimationComplete;
@@ -1140,19 +1125,6 @@ public class Activity extends ContextThemeWrapper
}
/**
- * (Creates, sets and) returns the autofill manager
- *
- * @return The autofill manager
- */
- @NonNull private AutofillManager getAutofillManager() {
- if (mAutofillManager == null) {
- mAutofillManager = getSystemService(AutofillManager.class);
- }
-
- return mAutofillManager;
- }
-
- /**
* (Creates, sets, and ) returns the content capture manager
*
* @return The content capture manager
@@ -1250,7 +1222,7 @@ public class Activity extends ContextThemeWrapper
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
if (newBase != null) {
- newBase.setAutofillClient(this);
+ newBase.setAutofillClient(getAutofillClient());
newBase.setContentCaptureOptions(getContentCaptureOptions());
}
}
@@ -1258,7 +1230,14 @@ public class Activity extends ContextThemeWrapper
/** @hide */
@Override
public final AutofillClient getAutofillClient() {
- return this;
+ return getAutofillClientController();
+ }
+
+ private AutofillClientController getAutofillClientController() {
+ if (mAutofillClientController == null) {
+ mAutofillClientController = new AutofillClientController(this);
+ }
+ return mAutofillClientController;
}
/** @hide */
@@ -1539,6 +1518,17 @@ public class Activity extends ContextThemeWrapper
getApplication().dispatchActivityPostDestroyed(this);
}
+ private void dispatchActivityConfigurationChanged() {
+ getApplication().dispatchActivityConfigurationChanged(this);
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((Application.ActivityLifecycleCallbacks) callbacks[i])
+ .onActivityConfigurationChanged(this);
+ }
+ }
+ }
+
private Object[] collectActivityLifecycleCallbacks() {
Object[] callbacks = null;
synchronized (mActivityLifecycleCallbacks) {
@@ -1590,14 +1580,9 @@ public class Activity extends ContextThemeWrapper
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
}
}
- if (savedInstanceState != null) {
- mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
- mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID,
- View.LAST_APP_AUTOFILL_ID);
- if (mAutoFillResetNeeded) {
- getAutofillManager().onCreate(savedInstanceState);
- }
+ if (savedInstanceState != null) {
+ getAutofillClientController().onActivityCreated(savedInstanceState);
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
@@ -1877,9 +1862,7 @@ public class Activity extends ContextThemeWrapper
dispatchActivityStarted();
- if (mAutoFillResetNeeded) {
- getAutofillManager().onVisibleForAutofill();
- }
+ getAutofillClientController().onActivityStarted();
}
/**
@@ -1949,22 +1932,7 @@ public class Activity extends ContextThemeWrapper
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
dispatchActivityResumed();
mActivityTransitionState.onResume(this);
- enableAutofillCompatibilityIfNeeded();
- if (mAutoFillResetNeeded) {
- if (!mAutoFillIgnoreFirstResumePause) {
- View focus = getCurrentFocus();
- if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
- // TODO(b/148815880): Bring up keyboard if resumed from inline authentication.
- // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
- // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
- // window visibility after recreation is INVISIBLE in onResume() and next frame
- // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
- // So we cannot call View.notifyEnterOrExited() which will do nothing
- // when View.isVisibleToUser() is false.
- getAutofillManager().notifyViewEntered(focus);
- }
- }
- }
+ getAutofillClientController().onActivityResumed();
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME);
@@ -2045,32 +2013,16 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Gets the next autofill ID.
+ * Returns the next autofill ID that is unique in the activity
*
* <p>All IDs will be bigger than {@link View#LAST_APP_AUTOFILL_ID}. All IDs returned
* will be unique.
*
- * @return A ID that is unique in the activity
- *
* {@hide}
*/
@Override
public int getNextAutofillId() {
- if (mLastAutofillId == Integer.MAX_VALUE - 1) {
- mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
- }
-
- mLastAutofillId++;
-
- return mLastAutofillId;
- }
-
- /**
- * @hide
- */
- @Override
- public AutofillId autofillClientGetNextAutofillId() {
- return new AutofillId(getNextAutofillId());
+ return getAutofillClientController().getNextAutofillId();
}
/**
@@ -2273,15 +2225,11 @@ public class Activity extends ContextThemeWrapper
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
- outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
- if (mAutoFillResetNeeded) {
- outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
- getAutofillManager().onSaveInstanceState(outState);
- }
+ getAutofillClientController().onSaveInstanceState(outState);
dispatchActivitySaveInstanceState(outState);
}
@@ -2380,19 +2328,7 @@ public class Activity extends ContextThemeWrapper
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
dispatchActivityPaused();
- if (mAutoFillResetNeeded) {
- if (!mAutoFillIgnoreFirstResumePause) {
- if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
- View focus = getCurrentFocus();
- if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
- getAutofillManager().notifyViewExited(focus);
- }
- } else {
- // reset after first pause()
- if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this);
- mAutoFillIgnoreFirstResumePause = false;
- }
- }
+ getAutofillClientController().onActivityPaused();
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
mCalled = true;
@@ -2623,14 +2559,7 @@ public class Activity extends ContextThemeWrapper
mTranslucentCallback = null;
mCalled = true;
- if (mAutoFillResetNeeded) {
- // If stopped without changing the configurations, the response should expire.
- getAutofillManager().onInvisibleForAutofill(!mChangingConfigurations);
- } else if (mIntent != null
- && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
- && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
- restoreAutofillSaveUi();
- }
+ getAutofillClientController().onActivityStopped(mIntent, mChangingConfigurations);
mEnterAnimationComplete = false;
}
@@ -2667,9 +2596,7 @@ public class Activity extends ContextThemeWrapper
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
mCalled = true;
- if (isFinishing() && mAutoFillResetNeeded) {
- getAutofillManager().onActivityFinishing();
- }
+ getAutofillClientController().onActivityDestroyed();
// dismiss any dialogs we are managing.
if (mManagedDialogs != null) {
@@ -3028,6 +2955,8 @@ public class Activity extends ContextThemeWrapper
// view changes from above.
mActionBar.onConfigurationChanged(newConfig);
}
+
+ dispatchActivityConfigurationChanged();
}
/**
@@ -3908,11 +3837,7 @@ public class Activity extends ContextThemeWrapper
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.
- if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
- restoreAutofillSaveUi();
- }
+ getAutofillClientController().onActivityBackPressed(mIntent);
}
/**
@@ -5602,6 +5527,18 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startIntentSenderForResult} but taking {@code who} as an additional identifier.
+ *
+ * @hide
+ */
+ public void startIntentSenderForResult(IntentSender intent, String who, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, Bundle options)
+ throws IntentSender.SendIntentException {
+ startIntentSenderForResultInner(intent, who, requestCode, fillInIntent, flagsMask,
+ flagsValues, options);
+ }
+
+ /**
* Like {@link #startActivityForResult(Intent, int)}, but allowing you
* to use a IntentSender to describe the activity to be started. If
* the IntentSender is for an activity, that activity will be started
@@ -5643,7 +5580,10 @@ public class Activity extends ContextThemeWrapper
}
}
- private void startIntentSenderForResultInner(IntentSender intent, String who, int requestCode,
+ /**
+ * @hide
+ */
+ public void startIntentSenderForResultInner(IntentSender intent, String who, int requestCode,
Intent fillInIntent, int flagsMask, int flagsValues,
@Nullable Bundle options)
throws IntentSender.SendIntentException {
@@ -5725,21 +5665,7 @@ public class Activity extends ContextThemeWrapper
*/
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
- if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
- && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
- if (TextUtils.equals(getPackageName(),
- intent.resolveActivity(getPackageManager()).getPackageName())) {
- // Apply Autofill restore mechanism on the started activity by startActivity()
- final IBinder token =
- mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
- // Remove restore ability from current activity
- mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
- mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY);
- // Put restore token
- intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
- intent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);
- }
- }
+ getAutofillClientController().onStartActivity(intent, mIntent);
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
@@ -6471,24 +6397,7 @@ public class Activity extends ContextThemeWrapper
mParent.finishFromChild(this);
}
- // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
- // be restored now.
- if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
- restoreAutofillSaveUi();
- }
- }
-
- /**
- * Restores Autofill Save UI
- */
- private void restoreAutofillSaveUi() {
- final IBinder token =
- mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
- // Make only restore Autofill once
- mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
- mIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY);
- getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
- token);
+ getAutofillClientController().onActivityFinish(mIntent);
}
/**
@@ -6805,12 +6714,6 @@ public class Activity extends ContextThemeWrapper
/** @hide */
@Override
- public final ComponentName autofillClientGetComponentName() {
- return getComponentName();
- }
-
- /** @hide */
- @Override
public final ComponentName contentCaptureClientGetComponentName() {
return getComponentName();
}
@@ -7134,12 +7037,6 @@ public class Activity extends ContextThemeWrapper
}
}
- /** @hide */
- @Override
- public final void autofillClientRunOnUiThread(Runnable action) {
- runOnUiThread(action);
- }
-
/**
* Standard implementation of
* {@link android.view.LayoutInflater.Factory#onCreateView} used when
@@ -7198,7 +7095,7 @@ public class Activity extends ContextThemeWrapper
// Handle special cases
switch (args[0]) {
case "--autofill":
- dumpAutofillManager(prefix, writer);
+ getAutofillClientController().dumpAutofillManager(prefix, writer);
return;
case "--contentcapture":
dumpContentCaptureManager(prefix, writer);
@@ -7244,24 +7141,13 @@ public class Activity extends ContextThemeWrapper
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
- dumpAutofillManager(prefix, writer);
+ getAutofillClientController().dumpAutofillManager(prefix, writer);
dumpContentCaptureManager(prefix, writer);
dumpUiTranslation(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
}
- void dumpAutofillManager(String prefix, PrintWriter writer) {
- final AutofillManager afm = getAutofillManager();
- if (afm != null) {
- afm.dump(prefix, writer);
- writer.print(prefix); writer.print("Autofill Compat Mode: ");
- writer.println(isAutofillCompatibilityEnabled());
- } else {
- writer.print(prefix); writer.println("No AutofillManager");
- }
- }
-
void dumpContentCaptureManager(String prefix, PrintWriter writer) {
final ContentCaptureManager cm = getContentCaptureManager();
if (cm != null) {
@@ -7992,19 +7878,10 @@ public class Activity extends ContextThemeWrapper
mWindow.setPreferMinimalPostProcessing(
(info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);
- setAutofillOptions(application.getAutofillOptions());
+ getAutofillClientController().onActivityAttached(application);
setContentCaptureOptions(application.getContentCaptureOptions());
}
- private void enableAutofillCompatibilityIfNeeded() {
- if (isAutofillCompatibilityEnabled()) {
- final AutofillManager afm = getSystemService(AutofillManager.class);
- if (afm != null) {
- afm.enableCompatibilityMode();
- }
- }
- }
-
/** @hide */
@UnsupportedAppUsage
public final IBinder getActivityToken() {
@@ -8176,15 +8053,7 @@ public class Activity extends ContextThemeWrapper
mLastNonConfigurationInstances = null;
- if (mAutoFillResetNeeded) {
- // When Activity is destroyed in paused state, and relaunch activity, there will be
- // extra onResume and onPause event, ignore the first onResume and onPause.
- // see ActivityThread.handleRelaunchActivity()
- mAutoFillIgnoreFirstResumePause = followedByPause;
- if (mAutoFillIgnoreFirstResumePause && DEBUG_LIFECYCLE) {
- Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
- }
- }
+ getAutofillClientController().onActivityPerformResume(followedByPause);
mCalled = false;
// mResumed is set by the instrumentation
@@ -8196,7 +8065,7 @@ public class Activity extends ContextThemeWrapper
" did not call through to super.onResume()");
}
- // invisible activities must be finished before onResume() completes
+ // invisible activities must be finished before onResume) completes
if (!mVisibleFromClient && !mFinished) {
Log.w(TAG, "An activity without a UI must call finish() before onResume() completes");
if (getApplicationInfo().targetSdkVersion
@@ -8400,9 +8269,8 @@ public class Activity extends ContextThemeWrapper
return;
}
}
- } else if (who.startsWith(AUTO_FILL_AUTH_WHO_PREFIX)) {
- Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
- getAutofillManager().onAuthenticationResult(requestCode, resultData, getCurrentFocus());
+ } else if (who.startsWith(AutofillClientController.AUTO_FILL_AUTH_WHO_PREFIX)) {
+ getAutofillClientController().onDispatchActivityResult(requestCode, resultCode, data);
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
@@ -8539,184 +8407,11 @@ public class Activity extends ContextThemeWrapper
fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- /** @hide */
- @Override
- public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
- Intent fillInIntent, boolean authenticateInline) {
- try {
- startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
- authenticationId, fillInIntent, 0, 0, null);
- } catch (IntentSender.SendIntentException e) {
- Log.e(TAG, "authenticate() failed for intent:" + intent, e);
- }
- }
-
- /** @hide */
- @Override
- public final void autofillClientResetableStateAvailable() {
- mAutoFillResetNeeded = true;
- }
-
- /** @hide */
- @Override
- public final boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width,
- int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) {
- final boolean wasShowing;
-
- if (mAutofillPopupWindow == null) {
- wasShowing = false;
- mAutofillPopupWindow = new AutofillPopupWindow(presenter);
- } else {
- wasShowing = mAutofillPopupWindow.isShowing();
- }
- mAutofillPopupWindow.update(anchor, 0, 0, width, height, anchorBounds);
-
- return !wasShowing && mAutofillPopupWindow.isShowing();
- }
-
- /** @hide */
- @Override
- public final void autofillClientDispatchUnhandledKey(@NonNull View anchor,
- @NonNull KeyEvent keyEvent) {
- ViewRootImpl rootImpl = anchor.getViewRootImpl();
- if (rootImpl != null) {
- // dont care if anchorView is current focus, for example a custom view may only receive
- // touchEvent, not focusable but can still trigger autofill window. The Key handling
- // might be inside parent of the custom view.
- rootImpl.dispatchKeyFromAutofill(keyEvent);
- }
- }
-
- /** @hide */
- @Override
- public final boolean autofillClientRequestHideFillUi() {
- if (mAutofillPopupWindow == null) {
- return false;
- }
- mAutofillPopupWindow.dismiss();
- mAutofillPopupWindow = null;
- return true;
- }
-
- /** @hide */
- @Override
- public final boolean autofillClientIsFillUiShowing() {
- return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
- }
-
- /** @hide */
- @Override
- @NonNull
- public final View[] autofillClientFindViewsByAutofillIdTraversal(
- @NonNull AutofillId[] autofillId) {
- final View[] views = new View[autofillId.length];
- final ArrayList<ViewRootImpl> roots =
- WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
-
- for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
- final View rootView = roots.get(rootNum).getView();
-
- if (rootView != null) {
- final int viewCount = autofillId.length;
- for (int viewNum = 0; viewNum < viewCount; viewNum++) {
- if (views[viewNum] == null) {
- views[viewNum] = rootView.findViewByAutofillIdTraversal(
- autofillId[viewNum].getViewId());
- }
- }
- }
- }
-
- return views;
- }
-
- /** @hide */
- @Nullable
- public View findViewByAutofillIdTraversal(@NonNull AutofillId autofillId) {
- final ArrayList<ViewRootImpl> roots =
- WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
- for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
- final View rootView = roots.get(rootNum).getView();
-
- if (rootView != null) {
- final View view = rootView.findViewByAutofillIdTraversal(autofillId.getViewId());
- if (view != null) {
- return view;
- }
- }
- }
- return null;
- }
-
- /** @hide */
- @Override
- @Nullable
- public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
- return findViewByAutofillIdTraversal(autofillId);
- }
-
- /** @hide */
- @Override
- public final @NonNull boolean[] autofillClientGetViewVisibility(
- @NonNull AutofillId[] autofillIds) {
- final int autofillIdCount = autofillIds.length;
- final boolean[] visible = new boolean[autofillIdCount];
- for (int i = 0; i < autofillIdCount; i++) {
- final AutofillId autofillId = autofillIds[i];
- final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
- if (view != null) {
- if (!autofillId.isVirtualInt()) {
- visible[i] = view.isVisibleToUser();
- } else {
- visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildIntId());
- }
- }
- }
- if (android.view.autofill.Helper.sVerbose) {
- Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible));
- }
- return visible;
- }
-
- /** @hide */
- public final @Nullable View autofillClientFindViewByAccessibilityIdTraversal(int viewId,
- int windowId) {
- final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance()
- .getRootViews(getActivityToken());
- for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
- final View rootView = roots.get(rootNum).getView();
- if (rootView != null && rootView.getAccessibilityWindowId() == windowId) {
- final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
- if (view != null) {
- return view;
- }
- }
- }
- return null;
- }
-
- /** @hide */
- @Override
- public final @Nullable IBinder autofillClientGetActivityToken() {
- return getActivityToken();
- }
-
- /** @hide */
- @Override
- public final boolean autofillClientIsVisibleForAutofill() {
- return !mStopped;
- }
-
- /** @hide */
- @Override
- public final boolean autofillClientIsCompatibilityModeEnabled() {
- return isAutofillCompatibilityEnabled();
- }
-
- /** @hide */
- @Override
- public final boolean isDisablingEnterExitEventForAutofill() {
- return mAutoFillIgnoreFirstResumePause || !mResumed;
+ /**
+ * @hide
+ */
+ public final boolean isVisibleForAutofill() {
+ return mStopped;
}
/**
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index bd4386885dd6..2efdf51612c7 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
@@ -205,6 +206,18 @@ public class ActivityClient {
}
}
+ /**
+ * Returns the activity token below in the same task if it belongs to the same process.
+ */
+ @Nullable
+ public IBinder getActivityTokenBelow(IBinder activityToken) {
+ try {
+ return getActivityClientController().getActivityTokenBelow(activityToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
ComponentName getCallingActivity(IBinder token) {
try {
return getActivityClientController().getCallingActivity(token);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4376d225e676..b29349ee47de 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2835,7 +2835,13 @@ public class ActivityManager {
* Returns a list of any processes that are currently in an error condition. The result
* will be null if all processes are running properly at this time.
*
- * @return Returns a list of ProcessErrorStateInfo records, or null if there are no
+ * <p>As of {@link android.os.Build.VERSION_CODES#TIRAMISU Android TIRAMISU}, for regular apps
+ * this method will only return {@link ProcessErrorStateInfo} records for the processes running
+ * as the caller's uid, unless the caller has the permission
+ * {@link android.Manifest.permission#DUMP}.
+ * </p>
+ *
+ * @return Returns a list of {@link ProcessErrorStateInfo} records, or null if there are no
* current error conditions (it will not return an empty list). This list ordering is not
* specified.
*/
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0d68df48c316..b80c4b4d7f88 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -36,6 +36,7 @@ import android.os.PowerExemptionManager.TempAllowListType;
import android.os.TransactionTooLargeException;
import android.os.WorkSource;
import android.util.ArraySet;
+import android.util.Pair;
import java.util.ArrayList;
import java.util.List;
@@ -84,6 +85,18 @@ public abstract class ActivityManagerInternal {
public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
/**
+ * Returns profile information in free form string in two separate strings.
+ * See AppProfiler for the output format.
+ * The output can only be used for human consumption. The format may change
+ * in the future.
+ * Do not call it frequently.
+ * @param time uptime for the cpu state
+ * @param lines lines of the cpu state should be returned
+ * @return a pair of Strings. The first is the current cpu load, the second is the cpu state.
+ */
+ public abstract Pair<String, String> getAppProfileStatsForDebugging(long time, int lines);
+
+ /**
* Verify that calling app has access to the given provider.
*/
public abstract String checkContentProviderAccess(String authority, @UserIdInt int userId);
@@ -377,7 +390,7 @@ public abstract class ActivityManagerInternal {
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
@UserIdInt int userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken);
+ @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList);
public abstract ComponentName startServiceInPackage(int uid, Intent service,
String resolvedType, boolean fgRequired, String callingPackage,
@@ -412,6 +425,13 @@ public abstract class ActivityManagerInternal {
public abstract void inputDispatchingResumed(int pid);
/**
+ * User tapped "wait" in the ANR dialog - reschedule the dialog to be shown again at a later
+ * time.
+ * @param data AppNotRespondingDialog.Data object
+ */
+ public abstract void rescheduleAnrDialog(Object data);
+
+ /**
* Sends {@link android.content.Intent#ACTION_CONFIGURATION_CHANGED} with all the appropriate
* flags.
*/
@@ -646,4 +666,9 @@ public abstract class ActivityManagerInternal {
*/
@Nullable
public abstract List<Integer> getIsolatedProcesses(int uid);
+
+ /** @see ActivityManagerService#sendIntentSender */
+ public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
+ Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 8e1f263ebf03..76f873185267 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -215,6 +215,14 @@ public class ActivityOptions {
"android.activity.launchRootTaskToken";
/**
+ * The {@link com.android.server.wm.TaskFragment} token the activity should be launched into.
+ * @see #setLaunchTaskFragmentToken(IBinder)
+ * @hide
+ */
+ public static final String KEY_LAUNCH_TASK_FRAGMENT_TOKEN =
+ "android.activity.launchTaskFragmentToken";
+
+ /**
* The windowing mode the activity should be launched into.
* @hide
*/
@@ -396,6 +404,7 @@ public class ActivityOptions {
private int mCallerDisplayId = INVALID_DISPLAY;
private WindowContainerToken mLaunchTaskDisplayArea;
private WindowContainerToken mLaunchRootTask;
+ private IBinder mLaunchTaskFragmentToken;
@WindowConfiguration.WindowingMode
private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
@WindowConfiguration.ActivityType
@@ -1138,6 +1147,7 @@ public class ActivityOptions {
mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN);
mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN);
+ mLaunchTaskFragmentToken = opts.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
@@ -1473,6 +1483,17 @@ public class ActivityOptions {
}
/** @hide */
+ public IBinder getLaunchTaskFragmentToken() {
+ return mLaunchTaskFragmentToken;
+ }
+
+ /** @hide */
+ public ActivityOptions setLaunchTaskFragmentToken(IBinder taskFragmentToken) {
+ mLaunchTaskFragmentToken = taskFragmentToken;
+ return this;
+ }
+
+ /** @hide */
public int getLaunchWindowingMode() {
return mLaunchWindowingMode;
}
@@ -1882,6 +1903,9 @@ public class ActivityOptions {
if (mLaunchRootTask != null) {
b.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, mLaunchRootTask);
}
+ if (mLaunchTaskFragmentToken != null) {
+ b.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, mLaunchTaskFragmentToken);
+ }
if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
}
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4a7fcd232ce9..a83662592513 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -476,6 +476,19 @@ public class ActivityTaskManager {
}
/**
+ * Detaches the navigation bar from the app it was attached to during a transition.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS)
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ try {
+ getService().detachNavigationBarFromApp(transition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Information you can retrieve about a root task in the system.
* @hide
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0c64c86daf3e..bff1e57c9f22 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -18,7 +18,6 @@ package android.app;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ConfigurationController.createNewConfigAndUpdateIfNotNull;
-import static android.app.ConfigurationController.freeTextLayoutCachesIfNeeded;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
@@ -31,6 +30,10 @@ import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.view.Display.INVALID_DISPLAY;
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
@@ -88,10 +91,8 @@ import android.database.sqlite.SQLiteDebug.DbStats;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
-import android.graphics.Rect;
import android.graphics.Typeface;
import android.hardware.display.DisplayManagerGlobal;
-import android.inputmethodservice.InputMethodService;
import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer;
import android.media.MediaServiceManager;
@@ -183,6 +184,7 @@ import android.webkit.WebView;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
import android.window.SplashScreenView;
+import android.window.WindowProviderService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -528,6 +530,9 @@ public final class ActivityThread extends ClientTransactionHandler
// A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
// used without security checks
public IBinder shareableActivityToken;
+ // The token of the initial TaskFragment that embedded this activity. Do not rely on it
+ // after creation because the activity could be reparented.
+ @Nullable public IBinder mInitialTaskFragmentToken;
int ident;
@UnsupportedAppUsage
Intent intent;
@@ -621,7 +626,8 @@ public final class ActivityThread extends ClientTransactionHandler
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
- IBinder shareableActivityToken, boolean launchedFromBubble) {
+ IBinder shareableActivityToken, boolean launchedFromBubble,
+ IBinder initialTaskFragmentToken) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -643,6 +649,7 @@ public final class ActivityThread extends ClientTransactionHandler
mActivityOptions = activityOptions;
mPendingFixedRotationAdjustments = fixedRotationAdjustments;
mLaunchedFromBubble = launchedFromBubble;
+ mInitialTaskFragmentToken = initialTaskFragmentToken;
init();
}
@@ -1098,18 +1105,17 @@ public final class ActivityThread extends ClientTransactionHandler
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
- CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
+ CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
SharedMemory serializedSystemFontMap) {
if (services != null) {
if (false) {
// Test code to make sure the app could see the passed-in services.
- for (Object oname : services.keySet()) {
- if (services.get(oname) == null) {
+ for (String name : services.keySet()) {
+ if (services.get(name) == null) {
continue; // AM just passed in a null service.
}
- String name = (String) oname;
// See b/79378449 about the following exemption.
switch (name) {
@@ -3561,6 +3567,13 @@ public final class ActivityThread extends ClientTransactionHandler
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
+ // updatePendingActivityConfiguration() reads from mActivities to update
+ // ActivityClientRecord which runs in a different thread. Protect modifications to
+ // mActivities to avoid race.
+ synchronized (mResourcesManager) {
+ mActivities.put(r.token, r);
+ }
+
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config =
@@ -3622,13 +3635,6 @@ public final class ActivityThread extends ClientTransactionHandler
}
r.setState(ON_CREATE);
- // updatePendingActivityConfiguration() reads from mActivities to update
- // ActivityClientRecord which runs in a different thread. Protect modifications to
- // mActivities to avoid race.
- synchronized (mResourcesManager) {
- mActivities.put(r.token, r);
- }
-
} catch (SuperNotCalledException e) {
throw e;
@@ -5439,6 +5445,12 @@ public final class ActivityThread extends ClientTransactionHandler
// behave properly when activity is relaunching.
r.window.clearContentView();
} else {
+ final ViewRootImpl viewRoot = v.getViewRootImpl();
+ if (viewRoot != null) {
+ // Clear the callback to avoid the destroyed activity from receiving
+ // configuration changes that are no longer effective.
+ viewRoot.setActivityConfigCallback(null);
+ }
wm.removeViewImmediate(v);
}
}
@@ -5762,7 +5774,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities) {
+ public ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
ArrayList<ComponentCallbacks2> callbacks
= new ArrayList<ComponentCallbacks2>();
@@ -5771,7 +5783,7 @@ public final class ActivityThread extends ClientTransactionHandler
for (int i=0; i<NAPP; i++) {
callbacks.add(mAllApplications.get(i));
}
- if (includeActivities) {
+ if (includeUiContexts) {
for (int i = mActivities.size() - 1; i >= 0; i--) {
final Activity a = mActivities.valueAt(i).activity;
if (a != null && !a.mFinished) {
@@ -5781,11 +5793,12 @@ public final class ActivityThread extends ClientTransactionHandler
}
final int NSVC = mServices.size();
for (int i=0; i<NSVC; i++) {
- final ComponentCallbacks2 serviceComp = mServices.valueAt(i);
- if (serviceComp instanceof InputMethodService) {
- mHasImeComponent = true;
+ final Service service = mServices.valueAt(i);
+ // If {@code includeUiContext} is set to false, WindowProviderService should not be
+ // collected because WindowProviderService is a UI Context.
+ if (includeUiContexts || !(service instanceof WindowProviderService)) {
+ callbacks.add(service);
}
- callbacks.add(serviceComp);
}
}
synchronized (mProviderMap) {
@@ -5840,35 +5853,25 @@ public final class ActivityThread extends ClientTransactionHandler
// change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
handleWindowingModeChangeIfNeeded(activity, newConfig);
- final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
- boolean shouldReportChange = false;
- if (activity.mCurrentConfig == null) {
- shouldReportChange = true;
- } else {
- // If the new config is the same as the config this Activity is already running with and
- // the override config also didn't change, then don't bother calling
- // onConfigurationChanged.
- // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
- // ResourcesImpl constructions.
- int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
- final ActivityClientRecord cr = getActivityClient(activityToken);
- diff = SizeConfigurationBuckets.filterDiff(diff, activity.mCurrentConfig, newConfig,
- cr != null ? cr.mSizeConfigurations : null);
-
- if (diff == 0) {
- if (!shouldUpdateWindowMetricsBounds(activity.mCurrentConfig, newConfig)
- && !movedToDifferentDisplay
- && mResourcesManager.isSameResourcesOverrideConfig(
- activityToken, amOverrideConfig)) {
- // Nothing significant, don't proceed with updating and reporting.
- return null;
- }
- } else if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
+ final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(),
+ displayId);
+ final ActivityClientRecord r = mActivities.get(activityToken);
+ final int diff = diffPublicWithSizeBuckets(activity.mCurrentConfig,
+ newConfig, r != null ? r.mSizeConfigurations : null);
+ final boolean hasPublicConfigChange = diff != 0;
+ // TODO(b/173090263): Use diff instead after the improvement of AssetManager and
+ // ResourcesImpl constructions.
+ final boolean shouldUpdateResources = hasPublicConfigChange
+ || shouldUpdateResources(activityToken, activity.mCurrentConfig, newConfig,
+ amOverrideConfig, movedToDifferentDisplay, hasPublicConfigChange);
+ final boolean shouldReportChange = hasPublicConfigChange
// If this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged. Otherwise, report to the activity for the
// changes.
- shouldReportChange = true;
- }
+ && (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0;
+ // Nothing significant, don't proceed with updating and reporting.
+ if (!shouldUpdateResources) {
+ return null;
}
// Propagate the configuration change to ResourcesManager and Activity.
@@ -5919,26 +5922,6 @@ public final class ActivityThread extends ClientTransactionHandler
return configToReport;
}
- // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
- // constructions.
- /**
- * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
- * should be updated.
- *
- * @see WindowManager#getCurrentWindowMetrics()
- * @see WindowManager#getMaximumWindowMetrics()
- */
- private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
- @NonNull Configuration newConfig) {
- final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
- final Rect newBounds = newConfig.windowConfiguration.getBounds();
-
- final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
- final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
-
- return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
- }
-
public final void applyConfigurationToResources(Configuration config) {
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResources(config, null);
@@ -6082,7 +6065,8 @@ public final class ActivityThread extends ClientTransactionHandler
// display.
displayId = r.activity.getDisplayId();
}
- final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
+ final boolean movedToDifferentDisplay = isDifferentDisplay(
+ r.activity.getDisplayId(), displayId);
if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
&& !movedToDifferentDisplay) {
if (DEBUG_CONFIGURATION) {
@@ -6118,14 +6102,6 @@ public final class ActivityThread extends ClientTransactionHandler
mSomeActivitiesChanged = true;
}
- /**
- * Checks if the display id of activity is different from the given one. Note that
- * {@link Display#INVALID_DISPLAY} means no difference.
- */
- private static boolean isDifferentDisplay(@NonNull Activity activity, int displayId) {
- return displayId != INVALID_DISPLAY && displayId != activity.getDisplayId();
- }
-
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
try {
@@ -6305,7 +6281,7 @@ public final class ActivityThread extends ClientTransactionHandler
final void handleLowMemory() {
final ArrayList<ComponentCallbacks2> callbacks =
- collectComponentCallbacks(true /* includeActivities */);
+ collectComponentCallbacks(true /* includeUiContexts */);
final int N = callbacks.size();
for (int i=0; i<N; i++) {
@@ -6338,7 +6314,7 @@ public final class ActivityThread extends ClientTransactionHandler
}
final ArrayList<ComponentCallbacks2> callbacks =
- collectComponentCallbacks(true /* includeActivities */);
+ collectComponentCallbacks(true /* includeUiContexts */);
final int N = callbacks.size();
for (int i = 0; i < N; i++) {
@@ -7566,12 +7542,6 @@ public final class ActivityThread extends ClientTransactionHandler
ViewRootImpl.ConfigChangedCallback configChangedCallback = (Configuration globalConfig) -> {
synchronized (mResourcesManager) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && mHasImeComponent) {
- Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "
- + "config=" + globalConfig);
- }
-
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResources(globalConfig,
@@ -7926,11 +7896,6 @@ public final class ActivityThread extends ClientTransactionHandler
return mDensityCompatMode;
}
- @Override
- public boolean hasImeComponent() {
- return mHasImeComponent;
- }
-
// ------------------ Regular JNI ------------------------
private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd);
diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java
index d91933c0f817..bc698f657305 100644
--- a/core/java/android/app/ActivityThreadInternal.java
+++ b/core/java/android/app/ActivityThreadInternal.java
@@ -32,11 +32,9 @@ interface ActivityThreadInternal {
boolean isInDensityCompatMode();
- boolean hasImeComponent();
-
boolean isCachedProcessState();
Application getApplication();
- ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeActivities);
+ ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts);
}
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 618eda8c84e8..7f849ef00d7d 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -205,6 +205,13 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
*/
default void onActivityPostDestroyed(@NonNull Activity activity) {
}
+
+ /**
+ * Called when the Activity configuration was changed.
+ * @hide
+ */
+ default void onActivityConfigurationChanged(@NonNull Activity activity) {
+ }
}
/**
@@ -554,6 +561,16 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
}
}
+ /* package */ void dispatchActivityConfigurationChanged(@NonNull Activity activity) {
+ Object[] callbacks = collectActivityLifecycleCallbacks();
+ if (callbacks != null) {
+ for (int i = 0; i < callbacks.length; i++) {
+ ((ActivityLifecycleCallbacks) callbacks[i]).onActivityConfigurationChanged(
+ activity);
+ }
+ }
+ }
+
@UnsupportedAppUsage
private Object[] collectActivityLifecycleCallbacks() {
Object[] callbacks = null;
@@ -613,7 +630,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
if (android.view.autofill.Helper.sVerbose) {
Log.v(TAG, "getAutofillClient(): found activity for " + this + ": " + activity);
}
- return activity;
+ return activity.getAutofillClient();
}
}
if (android.view.autofill.Helper.sVerbose) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd2c12cb4b6f..57de588f3884 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -317,6 +317,16 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ try {
+ return mPM.getLaunchIntentSenderForPackage(packageName, mContext.getPackageName(),
+ mContext.getAttributionTag(), getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int[] getPackageGids(String packageName) throws NameNotFoundException {
return getPackageGids(packageName, 0);
}
@@ -1047,7 +1057,7 @@ public class ApplicationPackageManager extends PackageManager {
throws NameNotFoundException {
try {
int uid = mPM.getUidForSharedUser(sharedUserName);
- if(uid != -1) {
+ if (uid != Process.INVALID_UID) {
return uid;
}
} catch (RemoteException e) {
@@ -2797,6 +2807,15 @@ public class ApplicationPackageManager extends PackageManager {
}
@Override
+ public void setComponentEnabledSettings(List<ComponentEnabledSetting> settings) {
+ try {
+ mPM.setComponentEnabledSettings(settings, getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
public int getComponentEnabledSetting(ComponentName componentName) {
try {
return mPM.getComponentEnabledSetting(componentName, getUserId());
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index f79e0780ecae..8637e31eb122 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -17,24 +17,20 @@
package android.app;
import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentCallbacks2;
import android.content.Context;
-import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.HardwareRenderer;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
import android.os.LocaleList;
import android.os.Trace;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.WindowManagerGlobal;
@@ -169,12 +165,7 @@ class ConfigurationController {
mPendingConfiguration = null;
}
- final boolean hasIme = mActivityThread.hasImeComponent();
if (config == null) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && hasIme) {
- Log.w(TAG, "handleConfigurationChanged for IME app but config is null");
- }
return;
}
@@ -205,12 +196,6 @@ class ConfigurationController {
mConfiguration = new Configuration();
}
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && hasIme) {
- Log.w(TAG, "handleConfigurationChanged for IME app but config seq is obsolete "
- + ", config=" + config
- + ", mConfiguration=" + mConfiguration);
- }
return;
}
@@ -228,7 +213,7 @@ class ConfigurationController {
}
final ArrayList<ComponentCallbacks2> callbacks =
- mActivityThread.collectComponentCallbacks(false /* includeActivities */);
+ mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
freeTextLayoutCachesIfNeeded(configDiff);
@@ -238,13 +223,6 @@ class ConfigurationController {
ComponentCallbacks2 cb = callbacks.get(i);
if (!equivalent) {
performConfigurationChanged(cb, config);
- } else {
- // TODO (b/135719017): Temporary log for debugging IME service.
- if (Build.IS_DEBUGGABLE && cb instanceof InputMethodService) {
- Log.w(TAG, "performConfigurationChanged didn't callback to IME "
- + ", configDiff=" + configDiff
- + ", mConfiguration=" + mConfiguration);
- }
}
}
}
@@ -326,16 +304,4 @@ class ConfigurationController {
return newConfig;
}
- /** Ask test layout engine to free its caches if there is a locale change. */
- static void freeTextLayoutCachesIfNeeded(int configDiff) {
- if (configDiff != 0) {
- boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
- if (hasLocaleConfigChange) {
- Canvas.freeTextLayoutCaches();
- if (DEBUG_CONFIGURATION) {
- Slog.v(TAG, "Cleared TextLayout Caches");
- }
- }
- }
- }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4db1f71e8256..d0acacf20a89 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2260,7 +2260,7 @@ class ContextImpl extends Context {
int modeFlags) {
try {
return ActivityManager.getService().checkUriPermissions(uris, pid, uid, modeFlags,
- null);
+ getUserId(), null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2721,10 +2721,13 @@ class ContextImpl extends Context {
// need to override their display in ResourcesManager.
baseContext.mForceDisplayOverrideInResources = false;
baseContext.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
- baseContext.mDisplay = display;
final Resources windowContextResources = createWindowContextResources(baseContext);
baseContext.setResources(windowContextResources);
+ // Associate the display with window context resources so that configuration update from
+ // the server side will also apply to the display's metrics.
+ baseContext.mDisplay = ResourcesManager.getInstance()
+ .getAdjustedDisplay(display.getDisplayId(), windowContextResources);
return baseContext;
}
@@ -3188,12 +3191,6 @@ class ContextImpl extends Context {
@UnsupportedAppUsage
final void setOuterContext(@NonNull Context context) {
mOuterContext = context;
- // TODO(b/149463653): check if we still need this method after migrating IMS to
- // WindowContext.
- if (mOuterContext.isUiContext() && mContextType <= CONTEXT_TYPE_DISPLAY_CONTEXT) {
- mContextType = CONTEXT_TYPE_WINDOW_CONTEXT;
- mIsConfigurationBasedContext = true;
- }
}
@UnsupportedAppUsage
diff --git a/core/java/android/app/GameManager.java b/core/java/android/app/GameManager.java
index 5964f71d28db..b324fb68ff59 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -135,6 +135,7 @@ public final class GameManager {
throw e.rethrowFromSystemServer();
}
}
+
/**
* Returns a list of supported game modes for a given package.
* <p>
@@ -151,4 +152,19 @@ public final class GameManager {
}
}
+ /**
+ * Returns if ANGLE is enabled for a given package and user ID.
+ * <p>
+ * The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
+ try {
+ return mService.getAngleEnabled(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index c6649692d848..aba6eb9229f2 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -70,6 +70,7 @@ interface IActivityClientController {
boolean willActivityBeVisible(in IBinder token);
int getDisplayId(in IBinder activityToken);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
+ IBinder getActivityTokenBelow(IBinder token);
ComponentName getCallingActivity(in IBinder token);
String getCallingPackage(in IBinder token);
int getLaunchedFromUid(in IBinder token);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b90b9a11611e..6b738fff1eee 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -221,7 +221,7 @@ 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,
+ int[] checkUriPermissions(in List<Uri> uris, int pid, int uid, int mode, int userId,
in IBinder callerToken);
void grantUriPermission(in IApplicationThread caller, in String targetPkg, in Uri uri,
int mode, int userId);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 74d51a0bcf63..2be78033ddf7 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -330,4 +330,18 @@ interface IActivityTaskManager {
* When the Picture-in-picture state has changed.
*/
void onPictureInPictureStateChanged(in PictureInPictureUiState pipState);
+
+ /**
+ * Re-attach navbar to the display during a recents transition.
+ * TODO(188595497): Remove this once navbar attachment is in shell.
+ */
+ void detachNavigationBarFromApp(in IBinder transition);
+
+ /**
+ * Marks a process as a delegate for the currently playing remote transition animation. This
+ * must be called from a process that is already a remote transition player or delegate. Any
+ * marked delegates are cleaned-up automatically at the end of the transition.
+ * @param caller is the IApplicationThread representing the calling process.
+ */
+ void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index d6ff6d3dfc3a..2afd98e9d685 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -77,7 +77,7 @@ oneway interface IApplicationThread {
IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
int debugMode, boolean enableBinderTracking, boolean trackAllocation,
boolean restrictedBackupMode, boolean persistent, in Configuration config,
- in CompatibilityInfo compatInfo, in Map services,
+ in CompatibilityInfo compatInfo, in Map<String, IBinder> services,
in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,
in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges,
in SharedMemory serializedSystemFontMap);
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 4bf8a3f77bca..189f0a2fca23 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -23,4 +23,5 @@ interface IGameManagerService {
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
int[] getAvailableGameModes(String packageName);
+ boolean getAngleEnabled(String packageName, int userId);
} \ No newline at end of file
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b2184fe65887..370031a14f42 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -743,6 +743,18 @@ public class Instrumentation {
}
/**
+ * This overload is used for notifying the {@link android.window.TaskFragmentOrganizer}
+ * implementation internally about started activities.
+ *
+ * @see #onStartActivity(Intent)
+ * @hide
+ */
+ public ActivityResult onStartActivity(@NonNull Context who, @NonNull Intent intent,
+ @NonNull Bundle options) {
+ return onStartActivity(intent);
+ }
+
+ /**
* Used for intercepting any started activity.
*
* <p> A non-null return value here will be considered a hit for this monitor.
@@ -1722,7 +1734,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intent);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intent, options);
}
if (result != null) {
am.mHits++;
@@ -1790,7 +1805,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intents[0]);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intents[0], options);
}
if (result != null) {
am.mHits++;
@@ -1861,7 +1879,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intent);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intent, options);
}
if (result != null) {
am.mHits++;
@@ -1928,7 +1949,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intent);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intent, options);
}
if (result != null) {
am.mHits++;
@@ -1974,7 +1998,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intent);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intent, options);
}
if (result != null) {
am.mHits++;
@@ -2021,7 +2048,10 @@ public class Instrumentation {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
- result = am.onStartActivity(intent);
+ if (options == null) {
+ options = ActivityOptions.makeBasic().toBundle();
+ }
+ result = am.onStartActivity(who, intent, options);
}
if (result != null) {
am.mHits++;
@@ -2083,7 +2113,8 @@ public class Instrumentation {
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
- + "; have you declared this activity in your AndroidManifest.xml?");
+ + "; have you declared this activity in your AndroidManifest.xml"
+ + ", or does your intent not match its declared <intent-filter>?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4b054f49d910..ea7fa2ec5d6f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5387,8 +5387,8 @@ public class Notification implements Parcelable
contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
// Use different highlighted colors for conversations' unread count
if (p.mHighlightExpander) {
- pillColor = Colors.flattenAlpha(getPrimaryAccentColor(p), bgColor);
- textColor = Colors.flattenAlpha(bgColor, pillColor);
+ pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor);
+ textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor);
}
contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -5539,12 +5539,11 @@ public class Notification implements Parcelable
/**
* Determines if the notification should be colorized *for the purposes of applying colors*.
- * If this is the minimized view of a colorized notification, or if the app did not provide
- * a color to colorize with, this will return false so that internal coloring logic can
- * still render the notification normally.
+ * If this is the minimized view of a colorized notification, this will return false so that
+ * internal coloring logic can still render the notification normally.
*/
private boolean isBackgroundColorized(StandardTemplateParams p) {
- return p.allowColorization && mN.color != COLOR_DEFAULT && mN.isColorized();
+ return p.allowColorization && mN.isColorized();
}
private boolean isCallActionColorCustomizable() {
@@ -5552,8 +5551,7 @@ public class Notification implements Parcelable
// that is only used for disallowing colorization of headers for the minimized state,
// and neither of those conditions applies when showing actions.
// Not requiring StandardTemplateParams as an argument simplifies the creation process.
- return mN.color != COLOR_DEFAULT && mN.isColorized()
- && mContext.getResources().getBoolean(
+ return mN.isColorized() && mContext.getResources().getBoolean(
R.bool.config_callNotificationActionColorsRequireColorized);
}
@@ -6247,8 +6245,9 @@ public class Notification implements Parcelable
*
* @param color the color to check
* @return true if the color has higher contrast with white than black
+ * @hide
*/
- private static boolean isColorDark(int color) {
+ public static boolean isColorDark(int color) {
// as per ContrastColorUtil.shouldUseDark, this uses the color contrast midpoint.
return ContrastColorUtil.calculateLuminance(color) <= 0.17912878474;
}
@@ -12305,6 +12304,8 @@ public class Notification implements Parcelable
private int mSecondaryTextColor = COLOR_INVALID;
private int mPrimaryAccentColor = COLOR_INVALID;
private int mSecondaryAccentColor = COLOR_INVALID;
+ private int mTertiaryAccentColor = COLOR_INVALID;
+ private int mOnAccentTextColor = COLOR_INVALID;
private int mErrorColor = COLOR_INVALID;
private int mContrastColor = COLOR_INVALID;
private int mRippleAlpha = 0x33;
@@ -12362,7 +12363,7 @@ public class Notification implements Parcelable
if (isColorized) {
if (rawColor == COLOR_DEFAULT) {
- int[] attrs = {R.attr.colorAccentTertiary};
+ int[] attrs = {R.attr.colorAccentSecondary};
try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
mBackgroundColor = getColor(ta, 0, Color.WHITE);
}
@@ -12379,6 +12380,8 @@ public class Notification implements Parcelable
mContrastColor = mPrimaryTextColor;
mPrimaryAccentColor = mPrimaryTextColor;
mSecondaryAccentColor = mSecondaryTextColor;
+ mTertiaryAccentColor = flattenAlpha(mPrimaryTextColor, mBackgroundColor);
+ mOnAccentTextColor = mBackgroundColor;
mErrorColor = mPrimaryTextColor;
mRippleAlpha = 0x33;
} else {
@@ -12389,6 +12392,8 @@ public class Notification implements Parcelable
R.attr.textColorSecondary,
R.attr.colorAccent,
R.attr.colorAccentSecondary,
+ R.attr.colorAccentTertiary,
+ R.attr.textColorOnAccent,
R.attr.colorError,
R.attr.colorControlHighlight
};
@@ -12399,8 +12404,10 @@ public class Notification implements Parcelable
mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID);
mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID);
mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID);
- mErrorColor = getColor(ta, 6, COLOR_INVALID);
- mRippleAlpha = Color.alpha(getColor(ta, 7, 0x33ffffff));
+ mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID);
+ mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID);
+ mErrorColor = getColor(ta, 8, COLOR_INVALID);
+ mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff));
}
mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
mBackgroundColor, nightMode);
@@ -12420,6 +12427,14 @@ public class Notification implements Parcelable
if (mSecondaryAccentColor == COLOR_INVALID) {
mSecondaryAccentColor = mContrastColor;
}
+ if (mTertiaryAccentColor == COLOR_INVALID) {
+ mTertiaryAccentColor = mContrastColor;
+ }
+ if (mOnAccentTextColor == COLOR_INVALID) {
+ mOnAccentTextColor = ColorUtils.setAlphaComponent(
+ ContrastColorUtil.resolvePrimaryColor(
+ ctx, mTertiaryAccentColor, nightMode), 0xFF);
+ }
if (mErrorColor == COLOR_INVALID) {
mErrorColor = mPrimaryTextColor;
}
@@ -12485,6 +12500,16 @@ public class Notification implements Parcelable
return mSecondaryAccentColor;
}
+ /** @return the theme's tertiary accent color for colored UI elements. */
+ public @ColorInt int getTertiaryAccentColor() {
+ return mTertiaryAccentColor;
+ }
+
+ /** @return the theme's text color to be used on the tertiary accent color. */
+ public @ColorInt int getOnAccentTextColor() {
+ return mOnAccentTextColor;
+ }
+
/**
* @return the contrast-adjusted version of the color provided by the app, or the
* primary text color when colorized.
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 6ca7dfbe2284..ef4d7b1f42e2 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -22,6 +22,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -66,12 +67,12 @@ import java.util.concurrent.atomic.AtomicLong;
*
* <pre>
* public class UserBirthdayServiceImpl implements IUserBirthdayService {
- * private final HashMap<Integer, Birthday> mUidToBirthday;
- * @Override
+ * private final HashMap&lt;Integer, Birthday%&gt; mUidToBirthday;
+ * {@literal @}Override
* public synchronized Birthday getUserBirthday(int userId) {
* return mUidToBirthday.get(userId);
* }
- * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
* mUidToBirthday.clear();
* mUidToBirthday.putAll(uidToBirthday);
* }
@@ -105,9 +106,9 @@ import java.util.concurrent.atomic.AtomicLong;
* ...
* private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
* private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
- * private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
- * PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * @Override
+ * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ * {@literal @}Override
* protected Birthday recompute(Integer userId) {
* return GetService("birthdayd").getUserBirthday(userId);
* }
@@ -140,7 +141,7 @@ import java.util.concurrent.atomic.AtomicLong;
* ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
* }
*
- * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * private synchronized void updateBirthdays(Map&lt;Integer, Birthday%&gt; uidToBirthday) {
* mUidToBirthday.clear();
* mUidToBirthday.putAll(uidToBirthday);
* ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
@@ -169,6 +170,41 @@ import java.util.concurrent.atomic.AtomicLong;
* this local case, there's no IPC, so use of the cache is (depending on exact
* circumstance) unnecessary.
*
+ * There may be queries for which it is more efficient to bypass the cache than to cache
+ * the result. This would be true, for example, if some queries would require frequent
+ * cache invalidation while other queries require infrequent invalidation. To expand on
+ * the birthday example, suppose that there is a userId that signifies "the next
+ * birthday". When passed this userId, the server returns the next birthday among all
+ * users - this value changes as time advances. The userId value can be cached, but the
+ * cache must be invalidated whenever a birthday occurs, and this invalidates all
+ * birthdays. If there is a large number of users, invalidation will happen so often that
+ * the cache provides no value.
+ *
+ * The class provides a bypass mechanism to handle this situation.
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
+ * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ * private final PropertyInvalidatedCache&lt;Integer, Birthday%&gt; mBirthdayCache = new
+ * PropertyInvalidatedCache&lt;Integer, Birthday%&gt;(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ * {@literal @}Override
+ * protected Birthday recompute(Integer userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * {@literal @}Override
+ * protected boolean bypass(Integer userId) {
+ * return userId == NEXT_BIRTHDAY;
+ * }
+ * };
+ * ...
+ * }
+ * </pre>
+ *
+ * If the {@code bypass()} method returns true then the cache is not used for that
+ * particular query. The {@code bypass()} method is not abstract and the default
+ * implementation returns false.
+ *
* For security, there is a allowlist of processes that are allowed to invalidate a cache.
* The allowlist includes normal runtime processes but does not include test processes.
* Test processes must call {@code PropertyInvalidatedCache.disableForTestMode()} to disable
@@ -190,19 +226,23 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public abstract class PropertyInvalidatedCache<Query, Result> {
/**
- * Reserved nonce values. The code is written assuming that these
- * values are contiguous.
+ * Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note
+ * that all values cause the cache to be skipped.
*/
private static final int NONCE_UNSET = 0;
private static final int NONCE_DISABLED = 1;
private static final int NONCE_CORKED = 2;
- private static final int NONCE_RESERVED = NONCE_CORKED + 1;
+ private static final int NONCE_BYPASS = 3;
+
+ private static boolean isReservedNonce(long n) {
+ return n >= NONCE_UNSET && n <= NONCE_BYPASS;
+ }
/**
* The names of the nonces
*/
private static final String[] sNonceName =
- new String[]{ "unset", "disabled", "corked" };
+ new String[]{ "unset", "disabled", "corked", "bypass" };
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
@@ -220,7 +260,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private long mMisses = 0;
@GuardedBy("mLock")
- private long mSkips[] = new long[]{ 0, 0, 0 };
+ private long[] mSkips = new long[]{ 0, 0, 0, 0 };
@GuardedBy("mLock")
private long mMissOverflow = 0;
@@ -363,6 +403,91 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * SystemProperties are protected and cannot be written (or read, usually) by random
+ * processes. So, for testing purposes, the methods have a bypass mode that reads and
+ * writes to a HashMap and does not go out to the SystemProperties at all.
+ */
+
+ // If true, the cache might be under test. If false, there is no testing in progress.
+ private static volatile boolean sTesting = false;
+
+ // If sTesting is true then keys that are under test are in this map.
+ private static final HashMap<String, Long> sTestingPropertyMap = new HashMap<>();
+
+ /**
+ * Enable or disable testing. The testing property map is cleared every time this
+ * method is called.
+ */
+ @VisibleForTesting
+ public static void setTestMode(boolean mode) {
+ sTesting = mode;
+ synchronized (sTestingPropertyMap) {
+ sTestingPropertyMap.clear();
+ }
+ }
+
+ /**
+ * Enable testing the specific cache key. Only keys in the map are subject to testing.
+ * There is no method to stop testing a property name. Just disable the test mode.
+ */
+ @VisibleForTesting
+ public static void testPropertyName(String name) {
+ synchronized (sTestingPropertyMap) {
+ sTestingPropertyMap.put(name, (long) NONCE_UNSET);
+ }
+ }
+
+ // Read the system property associated with the current cache. This method uses the
+ // handle for faster reading.
+ private long getCurrentNonce() {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(mPropertyName);
+ if (n != null) {
+ return n;
+ }
+ }
+ }
+
+ SystemProperties.Handle handle = mPropertyHandle;
+ if (handle == null) {
+ handle = SystemProperties.find(mPropertyName);
+ if (handle == null) {
+ return NONCE_UNSET;
+ }
+ mPropertyHandle = handle;
+ }
+ return handle.getLong(NONCE_UNSET);
+ }
+
+ // Write the nonce in a static context. No handle is available.
+ private static void setNonce(String name, long val) {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(name);
+ if (n != null) {
+ sTestingPropertyMap.put(name, val);
+ return;
+ }
+ }
+ }
+ SystemProperties.set(name, Long.toString(val));
+ }
+
+ // Set the nonce in a static context. No handle is available.
+ private static long getNonce(String name) {
+ if (sTesting) {
+ synchronized (sTestingPropertyMap) {
+ Long n = sTestingPropertyMap.get(name);
+ if (n != null) {
+ return n;
+ }
+ }
+ }
+ return SystemProperties.getLong(name, NONCE_UNSET);
+ }
+
+ /**
* Forget all cached values.
*/
public final void clear() {
@@ -418,18 +543,6 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
return oldResult;
}
- private long getCurrentNonce() {
- SystemProperties.Handle handle = mPropertyHandle;
- if (handle == null) {
- handle = SystemProperties.find(mPropertyName);
- if (handle == null) {
- return NONCE_UNSET;
- }
- mPropertyHandle = handle;
- }
- return handle.getLong(NONCE_UNSET);
- }
-
/**
* Disable the use of this cache in this process.
*/
@@ -477,9 +590,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public Result query(Query query) {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
+ if (bypass(query)) {
+ currentNonce = NONCE_BYPASS;
+ }
for (;;) {
- if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET
- || currentNonce == NONCE_CORKED || bypass(query)) {
+ if (isReservedNonce(currentNonce)) {
if (!mDisabled) {
// Do not bother collecting statistics if the cache is
// locally disabled.
@@ -490,7 +605,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (DEBUG) {
if (!mDisabled) {
- Log.d(TAG, String.format(
+ Log.d(TAG, TextUtils.formatSimple(
"cache %s %s for %s",
cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
}
@@ -505,7 +620,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (cachedResult != null) mHits++;
} else {
if (DEBUG) {
- Log.d(TAG, String.format(
+ Log.d(TAG, TextUtils.formatSimple(
"clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
cacheName(), mCache.size(),
mLastSeenNonce, currentNonce));
@@ -531,9 +646,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (currentNonce != afterRefreshNonce) {
currentNonce = afterRefreshNonce;
if (DEBUG) {
- Log.d(TAG, String.format("restarting %s %s because nonce changed in refresh",
- cacheName(),
- queryToString(query)));
+ Log.d(TAG, TextUtils.formatSimple(
+ "restarting %s %s because nonce changed in refresh",
+ cacheName(),
+ queryToString(query)));
}
continue;
}
@@ -602,7 +718,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (!sEnabled) {
return;
}
- SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+ setNonce(name, NONCE_DISABLED);
}
/**
@@ -622,7 +738,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void invalidateCache(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"cache invalidate %s suppressed", name));
}
return;
@@ -651,7 +767,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private static void invalidateCacheLocked(@NonNull String name) {
// There's no race here: we don't require that values strictly increase, but instead
// only that each is unique in a single runtime-restart session.
- final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ final long nonce = getNonce(name);
if (nonce == NONCE_DISABLED) {
if (DEBUG) {
Log.d(TAG, "refusing to invalidate disabled cache: " + name);
@@ -662,18 +778,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
long newValue;
do {
newValue = NoPreloadHolder.next();
- } while (newValue >= 0 && newValue < NONCE_RESERVED);
- final String newValueString = Long.toString(newValue);
+ } while (isReservedNonce(newValue));
if (DEBUG) {
- Log.d(TAG,
- String.format("invalidating cache [%s]: [%s] -> [%s]",
- name,
- nonce,
- newValueString));
+ Log.d(TAG, TextUtils.formatSimple(
+ "invalidating cache [%s]: [%s] -> [%s]",
+ name, nonce, Long.toString(newValue)));
}
// TODO(dancol): add an atomic compare and exchange property set operation to avoid a
// small race with concurrent disable here.
- SystemProperties.set(name, newValueString);
+ setNonce(name, newValue);
long invalidateCount = sInvalidates.getOrDefault(name, (long) 0);
sInvalidates.put(name, ++invalidateCount);
}
@@ -693,7 +806,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void corkInvalidations(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"cache cork %s suppressed", name));
}
return;
@@ -702,7 +815,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
- Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
+ Log.d(TAG, TextUtils.formatSimple(
+ "corking %s: numberCorks=%s", name, numberCorks));
}
// If we're the first ones to cork this cache, set the cache to the corked state so
@@ -714,9 +828,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
// uncorking the cache, e.g., by holding a read lock across the cork-uncork pair.
// Implement this more dangerous mode of operation if necessary.
if (numberCorks == 0) {
- final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ final long nonce = getNonce(name);
if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
- SystemProperties.set(name, Long.toString(NONCE_CORKED));
+ setNonce(name, NONCE_CORKED);
}
} else {
final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
@@ -739,8 +853,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
public static void uncorkInvalidations(@NonNull String name) {
if (!sEnabled) {
if (DEBUG) {
- Log.w(TAG, String.format(
- "cache uncork %s suppressed", name));
+ Log.w(TAG, TextUtils.formatSimple(
+ "cache uncork %s suppressed", name));
}
return;
}
@@ -748,7 +862,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
- Log.d(TAG, String.format("uncorking %s: numberCorks=%s", name, numberCorks));
+ Log.d(TAG, TextUtils.formatSimple(
+ "uncorking %s: numberCorks=%s", name, numberCorks));
}
if (numberCorks < 1) {
@@ -816,7 +931,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
synchronized (mLock) {
boolean alreadyQueued = mUncorkDeadlineMs >= 0;
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"autoCork %s mUncorkDeadlineMs=%s", mPropertyName,
mUncorkDeadlineMs));
}
@@ -834,7 +949,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private void handleMessage(Message msg) {
synchronized (mLock) {
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"handleMsesage %s mUncorkDeadlineMs=%s",
mPropertyName, mUncorkDeadlineMs));
}
@@ -846,7 +961,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (mUncorkDeadlineMs > nowMs) {
mUncorkDeadlineMs = nowMs + mAutoCorkDelayMs;
if (DEBUG) {
- Log.w(TAG, String.format(
+ Log.w(TAG, TextUtils.formatSimple(
"scheduling uncork at %s",
mUncorkDeadlineMs));
}
@@ -880,10 +995,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
Result resultToCompare = recompute(query);
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
- Log.e(TAG, String.format(
- "cache %s inconsistent for %s is %s should be %s",
- cacheName(), queryToString(query),
- proposedResult, resultToCompare));
+ Log.e(TAG, TextUtils.formatSimple(
+ "cache %s inconsistent for %s is %s should be %s",
+ cacheName(), queryToString(query),
+ proposedResult, resultToCompare));
}
// Always return the "true" result in verification mode.
return resultToCompare;
@@ -953,21 +1068,24 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
}
synchronized (mLock) {
- pw.println(String.format(" Cache Name: %s", cacheName()));
- pw.println(String.format(" Property: %s", mPropertyName));
- final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED];
- pw.println(String.format(" Hits: %d, Misses: %d, Skips: %d, Clears: %d",
+ pw.println(TextUtils.formatSimple(" Cache Name: %s", cacheName()));
+ pw.println(TextUtils.formatSimple(" Property: %s", mPropertyName));
+ final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]
+ + mSkips[NONCE_BYPASS];
+ pw.println(TextUtils.formatSimple(
+ " Hits: %d, Misses: %d, Skips: %d, Clears: %d",
mHits, mMisses, skips, mClears));
- pw.println(String.format(" Skip-corked: %d, Skip-unset: %d, Skip-other: %d",
+ pw.println(TextUtils.formatSimple(
+ " Skip-corked: %d, Skip-unset: %d, Skip-bypass: %d, Skip-other: %d",
mSkips[NONCE_CORKED], mSkips[NONCE_UNSET],
- mSkips[NONCE_DISABLED]));
- pw.println(String.format(
+ mSkips[NONCE_BYPASS], mSkips[NONCE_DISABLED]));
+ pw.println(TextUtils.formatSimple(
" Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d",
mLastSeenNonce, invalidateCount, corkedInvalidates));
- pw.println(String.format(
+ pw.println(TextUtils.formatSimple(
" Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d",
mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow));
- pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true"));
+ pw.println(TextUtils.formatSimple(" Enabled: %s", mDisabled ? "false" : "true"));
pw.println("");
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
@@ -980,7 +1098,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
String key = Objects.toString(entry.getKey());
String value = Objects.toString(entry.getValue());
- pw.println(String.format(" Key: %s\n Value: %s\n", key, value));
+ pw.println(TextUtils.formatSimple(" Key: %s\n Value: %s\n", key, value));
}
}
}
@@ -1009,7 +1127,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
pw.println(" Corking Status:");
for (int i = 0; i < activeCorks.size(); i++) {
Map.Entry<String, Integer> entry = activeCorks.get(i);
- pw.println(String.format(" Property Name: %s Count: %d",
+ pw.println(TextUtils.formatSimple(" Property Name: %s Count: %d",
entry.getKey(), entry.getValue()));
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 198c33e83707..bf3778dfeecc 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -693,7 +693,7 @@ public class ResourcesManager {
* @return true if activity resources override config matches the provided one or they are both
* null, false otherwise.
*/
- boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
+ public boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
@Nullable Configuration overrideConfig) {
synchronized (mLock) {
final ActivityResources activityResources
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 336387204410..0145747e0c65 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -312,29 +312,25 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
private static final String TAG = "Service";
/**
- * Flag for {@link #stopForeground(int)}: if set, the notification previously provided
- * to {@link #startForeground} will be removed. Otherwise it will remain
- * until a later call (to {@link #startForeground(int, Notification)} or
- * {@link #stopForeground(int)} removes it, or the service is destroyed.
+ * Selector for {@link #stopForeground(int)}: if supplied, the notification previously
+ * supplied to {@link #startForeground} will be cancelled and removed from display.
*/
public static final int STOP_FOREGROUND_REMOVE = 1<<0;
/**
- * Flag for {@link #stopForeground(int)}: if set, the notification previously provided
- * to {@link #startForeground} will be detached from the service. Only makes sense
- * when {@link #STOP_FOREGROUND_REMOVE} is <b>not</b> set -- in this case, the notification
- * will remain shown, but be completely detached from the service and so no longer changed
- * except through direct calls to the notification manager.
+ * Selector for {@link #stopForeground(int)}: if set, the notification previously supplied
+ * to {@link #startForeground} will be detached from the service's lifecycle. The notification
+ * will remain shown even after the service is stopped and destroyed.
*/
public static final int STOP_FOREGROUND_DETACH = 1<<1;
/** @hide */
- @IntDef(flag = true, prefix = { "STOP_FOREGROUND_" }, value = {
+ @IntDef(flag = false, prefix = { "STOP_FOREGROUND_" }, value = {
STOP_FOREGROUND_REMOVE,
STOP_FOREGROUND_DETACH
})
@Retention(RetentionPolicy.SOURCE)
- public @interface StopForegroundFlags {}
+ public @interface StopForegroundSelector {}
public Service() {
super(null);
@@ -791,12 +787,16 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
- * Synonym for {@link #stopForeground(int)}.
- * @param removeNotification If true, the {@link #STOP_FOREGROUND_REMOVE} flag
- * will be supplied.
+ * Legacy version of {@link #stopForeground(int)}.
+ * @param removeNotification If true, the {@link #STOP_FOREGROUND_REMOVE}
+ * selector will be passed to {@link #stopForeground(int)}; otherwise
+ * {@code zero} will be passed.
* @see #stopForeground(int)
* @see #startForeground(int, Notification)
+ *
+ * @deprecated use {@link #stopForeground(int)} instead.
*/
+ @Deprecated
public final void stopForeground(boolean removeNotification) {
stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : 0);
}
@@ -807,14 +807,29 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* you use {@link #stopSelf()} or related methods), just takes it out of the
* foreground state.
*
- * @param flags additional behavior options.
+ * <p>If {@link #STOP_FOREGROUND_REMOVE} is supplied, the service's associated
+ * notification will be cancelled immediately.</p>
+ * <p>If {@link #STOP_FOREGROUND_DETACH} is supplied, the service's association
+ * with the notification will be severed. If the notification had not yet been
+ * shown, due to foreground-service notification deferral policy, it is
+ * immediately posted when {@code stopForeground(STOP_FOREGROUND_DETACH)}
+ * is called. In all cases, the notification remains shown
+ * even after this service is stopped fully and destroyed.</p>
+ * <p>If {@code zero} is passed as the argument, the result will be the legacy
+ * behavior as defined prior to Android L: the notification will remain posted until
+ * the service is fully stopped, at which time it will automatically be cancelled.</p>
+ *
+ * @param notificationBehavior the intended behavior for the service's associated
+ * notification
* @see #startForeground(int, Notification)
+ * @see #STOP_FOREGROUND_DETACH
+ * @see #STOP_FOREGROUND_REMOVE
*/
- public final void stopForeground(@StopForegroundFlags int flags) {
+ public final void stopForeground(@StopForegroundSelector int notificationBehavior) {
try {
mActivityManager.setServiceForeground(
new ComponentName(this, mClassName), mToken, 0, null,
- flags, 0);
+ notificationBehavior, 0);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 32ea41b2c75f..8ac8229042ad 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -205,7 +205,7 @@ import android.telephony.TelephonyRegistryManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.uwb.UwbManager;
+import android.uwb.UwbFrameworkInitializer;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.WindowManager;
@@ -728,14 +728,6 @@ public final class SystemServiceRegistry {
return new SerialManager(ctx, ISerialManager.Stub.asInterface(b));
}});
- registerService(Context.UWB_SERVICE, UwbManager.class,
- new CachedServiceFetcher<UwbManager>() {
- @Override
- public UwbManager createService(ContextImpl ctx) {
- return UwbManager.getInstance(ctx);
- }
- });
-
registerService(Context.VIBRATOR_MANAGER_SERVICE, VibratorManager.class,
new CachedServiceFetcher<VibratorManager>() {
@Override
@@ -1486,6 +1478,7 @@ public final class SystemServiceRegistry {
MediaFrameworkInitializer.registerServiceWrappers();
RoleFrameworkInitializer.registerServiceWrappers();
SchedulingFrameworkInitializer.registerServiceWrappers();
+ UwbFrameworkInitializer.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 85758a92fa98..cac763988d5e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -17,6 +17,7 @@
package android.app;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -119,6 +120,12 @@ public class TaskInfo {
public int displayId;
/**
+ * The feature id of {@link com.android.server.wm.TaskDisplayArea} this task is associated with.
+ * @hide
+ */
+ public int displayAreaFeatureId = FEATURE_UNDEFINED;
+
+ /**
* The recent activity values for the highest activity in the stack to have set the values.
* {@link Activity#setTaskDescription(android.app.ActivityManager.TaskDescription)}.
*/
@@ -243,6 +250,12 @@ public class TaskInfo {
*/
public boolean isVisible;
+ /**
+ * Whether this task is sleeping due to sleeping display.
+ * @hide
+ */
+ public boolean isSleeping;
+
TaskInfo() {
// Do nothing
}
@@ -329,11 +342,10 @@ public class TaskInfo {
}
/**
- * Returns {@code true} if parameters that are important for task organizers have changed
- * and {@link com.android.server.wm.TaskOrginizerController} needs to notify listeners
- * about that.
- * @hide
- */
+ * Returns {@code true} if the parameters that are important for task organizers are equal
+ * between this {@link TaskInfo} and {@param that}.
+ * @hide
+ */
public boolean equalsForTaskOrganizer(@Nullable TaskInfo that) {
if (that == null) {
return false;
@@ -341,13 +353,15 @@ public class TaskInfo {
return topActivityType == that.topActivityType
&& isResizeable == that.isResizeable
&& supportsMultiWindow == that.supportsMultiWindow
+ && displayAreaFeatureId == that.displayAreaFeatureId
&& Objects.equals(positionInParent, that.positionInParent)
&& Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
&& Objects.equals(displayCutoutInsets, that.displayCutoutInsets)
&& getWindowingMode() == that.getWindowingMode()
&& Objects.equals(taskDescription, that.taskDescription)
&& isFocused == that.isFocused
- && isVisible == that.isVisible;
+ && isVisible == that.isVisible
+ && isSleeping == that.isSleeping;
}
/**
@@ -402,8 +416,10 @@ public class TaskInfo {
parentTaskId = source.readInt();
isFocused = source.readBoolean();
isVisible = source.readBoolean();
+ isSleeping = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR);
+ displayAreaFeatureId = source.readInt();
}
/**
@@ -440,8 +456,10 @@ public class TaskInfo {
dest.writeInt(parentTaskId);
dest.writeBoolean(isFocused);
dest.writeBoolean(isVisible);
+ dest.writeBoolean(isSleeping);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeTypedObject(mTopActivityLocusId, flags);
+ dest.writeInt(displayAreaFeatureId);
}
@Override
@@ -468,8 +486,10 @@ public class TaskInfo {
+ " parentTaskId=" + parentTaskId
+ " isFocused=" + isFocused
+ " isVisible=" + isVisible
+ + " isSleeping=" + isSleeping
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
- + " locusId= " + mTopActivityLocusId
+ + " locusId=" + mTopActivityLocusId
+ + " displayAreaFeatureId=" + displayAreaFeatureId
+ "}";
}
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8d332ab1d58b..84752be7a42f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -66,6 +66,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemProperties;
+import android.service.wallpaper.WallpaperService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -221,6 +222,20 @@ public class WallpaperManager {
public static final String COMMAND_REAPPLY = "android.wallpaper.reapply";
/**
+ * Command for {@link #sendWallpaperCommand}: reported when the live wallpaper needs to be
+ * frozen.
+ * @hide
+ */
+ public static final String COMMAND_FREEZE = "android.wallpaper.freeze";
+
+ /**
+ * Command for {@link #sendWallpaperCommand}: reported when the live wallapper doesn't need
+ * to be frozen anymore.
+ * @hide
+ */
+ public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze";
+
+ /**
* Extra passed back from setWallpaper() giving the new wallpaper's assigned ID.
* @hide
*/
@@ -2316,22 +2331,24 @@ public class WallpaperManager {
* A {@link android.app.WallpaperColors} object containing a simplified
* color histogram will be given.
*
- * @param colors Wallpaper color info
+ * @param colors Wallpaper color info, {@code null} when not available.
* @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM}
+ * @see WallpaperService.Engine#onComputeColors()
*/
- void onColorsChanged(WallpaperColors colors, int which);
+ void onColorsChanged(@Nullable WallpaperColors colors, int which);
/**
* Called when colors change.
* A {@link android.app.WallpaperColors} object containing a simplified
* color histogram will be given.
*
- * @param colors Wallpaper color info
+ * @param colors Wallpaper color info, {@code null} when not available.
* @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM}
* @param userId Owner of the wallpaper
+ * @see WallpaperService.Engine#onComputeColors()
* @hide
*/
- default void onColorsChanged(WallpaperColors colors, int which, int userId) {
+ default void onColorsChanged(@Nullable WallpaperColors colors, int which, int userId) {
onColorsChanged(colors, which);
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0e04ad3768c7..710231477f42 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,6 +56,7 @@ import android.graphics.Bitmap;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -332,8 +333,12 @@ public class DevicePolicyManager {
* {@link android.Manifest.permission#DISPATCH_PROVISIONING_MESSAGE} to be launched.
* Only one {@link ComponentName} in the entire system should be enabled, and the rest of the
* components are not started by this intent.
+ *
+ * @deprecated Starting from Android T, the system no longer launches an intent with this action
+ * when user provisioning completes.
* @hide
*/
+ @Deprecated
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@SystemApi
public static final String ACTION_STATE_USER_SETUP_COMPLETE =
@@ -1216,7 +1221,8 @@ public class DevicePolicyManager {
PROVISIONING_TRIGGER_CLOUD_ENROLLMENT,
PROVISIONING_TRIGGER_QR_CODE,
PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER,
- PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+ PROVISIONING_TRIGGER_MANAGED_ACCOUNT,
+ PROVISIONING_TRIGGER_NFC
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProvisioningTrigger {}
@@ -1254,6 +1260,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+ * @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1265,6 +1272,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1276,6 +1284,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1295,6 +1304,7 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
@@ -1308,12 +1318,25 @@ public class DevicePolicyManager {
* @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
* @see #PROVISIONING_TRIGGER_QR_CODE
* @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @see #PROVISIONING_TRIGGER_NFC
* @hide
*/
@SystemApi
public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4;
/**
+ * A value for {@link #EXTRA_PROVISIONING_TRIGGER} indicating that the provisioning is
+ * triggered by tapping an NFC tag.
+ * @see #PROVISIONING_TRIGGER_CLOUD_ENROLLMENT
+ * @see #PROVISIONING_TRIGGER_QR_CODE
+ * @see #PROVISIONING_TRIGGER_UNSPECIFIED
+ * @see #PROVISIONING_TRIGGER_MANAGED_ACCOUNT
+ * @hide
+ */
+ @SystemApi
+ public static final int PROVISIONING_TRIGGER_NFC = 5;
+
+ /**
* Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is
* organization-owned.
*
@@ -3247,22 +3270,6 @@ public class DevicePolicyManager {
}
/**
- * Returns true if the Profile Challenge is available to use for the given profile user.
- *
- * @hide
- */
- public boolean isSeparateProfileChallengeAllowed(int userHandle) {
- if (mService != null) {
- try {
- return mService.isSeparateProfileChallengeAllowed(userHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- return false;
- }
-
- /**
* Constant for {@link #setPasswordQuality}: the policy has no requirements
* for the password. Note that quality constants are ordered so that higher
* values are more restrictive.
@@ -5690,8 +5697,10 @@ public class DevicePolicyManager {
/**
* Disable text entry into notifications on secure keyguard screens (e.g. PIN/Pattern/Password).
- * This flag has no effect starting from version {@link android.os.Build.VERSION_CODES#N}
+ * @deprecated This flag was added in version {@link android.os.Build.VERSION_CODES#N}, but it
+ * never had any effect.
*/
+ @Deprecated
public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 1 << 6;
/**
@@ -10571,11 +10580,16 @@ public class DevicePolicyManager {
* behavior of this API is changed such that passing {@code null} as the {@code admin} parameter
* will return if any admin has blocked the uninstallation. Before L MR1, passing {@code null}
* will cause a NullPointerException to be raised.
+ * <p>
+ * <strong>Note:</strong> If your app targets Android 11 (API level 30) or higher,
+ * this method returns a filtered result. Learn more about how to
+ * <a href="/training/basics/intents/package-visibility">manage package visibility</a>.
*
* @param admin The name of the admin component whose blocking policy will be checked, or
* {@code null} to check whether any admin has blocked the uninstallation.
* @param packageName package to check.
- * @return true if uninstallation is blocked.
+ * @return true if uninstallation is blocked and the given package is visible to you, false
+ * otherwise if uninstallation isn't blocked or the given package isn't visible to you.
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
public boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) {
@@ -14017,4 +14031,33 @@ public class DevicePolicyManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Creates a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent
+ * from the provided {@code nfcIntent}.
+ *
+ * <p>Prerequisites to create the provisioning intent:
+ *
+ * <ul>
+ * <li>{@code nfcIntent}'s action is {@link NfcAdapter#ACTION_NDEF_DISCOVERED}</li>
+ * <li>{@code nfcIntent}'s NFC properties contain either
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} or
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} </li>
+ * </ul>
+ *
+ * This method returns {@code null} if the prerequisites are not met or if an error occurs
+ * when reading the NFC properties.
+ *
+ * @param nfcIntent the nfc intent generated from scanning a NFC tag
+ * @return a {@link #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent with
+ * intent extras as read by {@code nfcIntent}'s NFC properties or {@code null} if the
+ * prerequisites are not met or if an error occurs when reading the NFC properties.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ public Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
+ return ProvisioningIntentHelper.createProvisioningIntentFromNfcIntent(nfcIntent);
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b6c48a1c057b..8cf1f809af92 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -373,8 +373,6 @@ interface IDevicePolicyManager {
CharSequence getShortSupportMessageForUser(in ComponentName admin, int userHandle);
CharSequence getLongSupportMessageForUser(in ComponentName admin, int userHandle);
- boolean isSeparateProfileChallengeAllowed(int userHandle);
-
void setOrganizationColor(in ComponentName admin, in int color);
void setOrganizationColorForUser(in int color, in int userId);
int getOrganizationColor(in ComponentName admin);
diff --git a/core/java/android/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java
new file mode 100644
index 000000000000..fbad90c30d7e
--- /dev/null
+++ b/core/java/android/app/admin/ProvisioningIntentHelper.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.admin;
+
+import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
+import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
+import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC;
+import static android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Utility class that provides functionality to create provisioning intents from nfc intents.
+ */
+final class ProvisioningIntentHelper {
+
+ private static final String TAG = "ProvisioningIntentHelper";
+
+ /**
+ * This class is never instantiated
+ */
+ private ProvisioningIntentHelper() { }
+
+ @Nullable
+ public static Intent createProvisioningIntentFromNfcIntent(@NonNull Intent nfcIntent) {
+ requireNonNull(nfcIntent);
+
+ if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(nfcIntent.getAction())) {
+ Log.e(TAG, "Wrong Nfc action: " + nfcIntent.getAction());
+ return null;
+ }
+
+ NdefRecord firstRecord = getFirstNdefRecord(nfcIntent);
+
+ if (firstRecord != null) {
+ return createProvisioningIntentFromNdefRecord(firstRecord);
+ }
+
+ return null;
+ }
+
+
+ private static Intent createProvisioningIntentFromNdefRecord(NdefRecord firstRecord) {
+ requireNonNull(firstRecord);
+
+ Properties properties = loadPropertiesFromPayload(firstRecord.getPayload());
+
+ if (properties == null) {
+ Log.e(TAG, "Failed to load NdefRecord properties.");
+ return null;
+ }
+
+ Bundle bundle = createBundleFromProperties(properties);
+
+ if (!containsRequiredProvisioningExtras(bundle)) {
+ Log.e(TAG, "Bundle does not contain the required provisioning extras.");
+ return null;
+ }
+
+ return createProvisioningIntentFromBundle(bundle);
+ }
+
+ private static Properties loadPropertiesFromPayload(byte[] payload) {
+ Properties properties = new Properties();
+
+ try {
+ properties.load(new StringReader(new String(payload, UTF_8)));
+ } catch (IOException e) {
+ Log.e(TAG, "NFC Intent properties loading failed.");
+ return null;
+ }
+
+ return properties;
+ }
+
+ private static Bundle createBundleFromProperties(Properties properties) {
+ Enumeration propertyNames = properties.propertyNames();
+ Bundle bundle = new Bundle();
+
+ while (propertyNames.hasMoreElements()) {
+ String propertyName = (String) propertyNames.nextElement();
+ addPropertyToBundle(propertyName, properties, bundle);
+ }
+ return bundle;
+ }
+
+ private static void addPropertyToBundle(
+ String propertyName, Properties properties, Bundle bundle) {
+ if(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME.equals(propertyName)) {
+ ComponentName componentName = ComponentName.unflattenFromString(
+ properties.getProperty(propertyName));
+ bundle.putParcelable(propertyName, componentName);
+ }
+ else {
+ bundle.putString(propertyName, properties.getProperty(propertyName));
+ }
+ }
+
+ private static Intent createProvisioningIntentFromBundle(Bundle bundle) {
+ requireNonNull(bundle);
+
+ Intent provisioningIntent = new Intent(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE);
+
+ provisioningIntent.putExtras(bundle);
+
+ provisioningIntent.putExtra(EXTRA_PROVISIONING_TRIGGER, PROVISIONING_TRIGGER_NFC);
+
+ return provisioningIntent;
+ }
+
+ private static boolean containsRequiredProvisioningExtras(Bundle bundle) {
+ return bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME) ||
+ bundle.containsKey(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
+ }
+
+ /**
+ * Returns the first {@link NdefRecord} found with a recognized MIME-type
+ */
+ private static NdefRecord getFirstNdefRecord(Intent nfcIntent) {
+ Parcelable[] ndefMessages = nfcIntent.getParcelableArrayExtra(EXTRA_NDEF_MESSAGES);
+ if (ndefMessages == null) {
+ Log.i(TAG, "No EXTRA_NDEF_MESSAGES from nfcIntent");
+ return null;
+ }
+
+ for (Parcelable rawMsg : ndefMessages) {
+ NdefMessage msg = (NdefMessage) rawMsg;
+ for (NdefRecord record : msg.getRecords()) {
+ String mimeType = new String(record.getType(), UTF_8);
+
+ // Only one first message with NFC_MIME_TYPE is used.
+ if (MIME_TYPE_PROVISIONING_NFC.equals(mimeType)) {
+ return record;
+ }
+
+ // Assume only first record of message is used.
+ break;
+ }
+ }
+
+ Log.i(TAG, "No compatible records found on nfcIntent");
+ return null;
+ }
+}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index fc4a2b49d0c2..e1f6af0cc128 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -658,6 +658,7 @@ public class AssistStructure implements Parcelable {
float mElevation;
float mAlpha = 1.0f;
+ // TODO: The FLAGS_* below have filled all bits, will need to be refactored.
static final int FLAGS_DISABLED = 0x00000001;
static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
static final int FLAGS_FOCUSABLE = 0x00000010;
@@ -673,6 +674,10 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_CONTEXT_CLICKABLE = 0x00004000;
static final int FLAGS_OPAQUE = 0x00008000;
+ // --IMPORTANT-- must update this flag if any below flags extend to further bits.
+ // This flag is used to clear all FLAGS_HAS_* values in mFlags prior to parceling.
+ static final int FLAGS_ALL_CONTROL = 0xffff0000;
+
static final int FLAGS_HAS_MIME_TYPES = 0x80000000;
static final int FLAGS_HAS_MATRIX = 0x40000000;
static final int FLAGS_HAS_ALPHA = 0x20000000;
@@ -689,7 +694,7 @@ public class AssistStructure implements Parcelable {
static final int FLAGS_HAS_INPUT_TYPE = 0x00040000;
static final int FLAGS_HAS_URL_SCHEME = 0x00020000;
static final int FLAGS_HAS_LOCALE_LIST = 0x00010000;
- static final int FLAGS_ALL_CONTROL = 0xfff00000;
+ // --IMPORTANT END--
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID = 0x0001;
static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID = 0x0002;
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index dae565e12fd7..67f631f98f0b 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -281,6 +281,32 @@ public class BackupManager {
}
/**
+ * Convenience method for callers who need to indicate that some other package or
+ * some other user needs a backup pass. This can be useful in the case of groups of
+ * packages that share a uid and/or have user-specific data.
+ * <p>
+ * This method requires that the application hold the "android.permission.BACKUP"
+ * permission if the package named in the package argument does not run under the
+ * same uid as the caller. This method also requires that the application hold the
+ * "android.permission.INTERACT_ACROSS_USERS_FULL" if the user argument is not the
+ * same as the user the caller is running under.
+ * @param userId The user to back up
+ * @param packageName The package name identifying the application to back up.
+ *
+ * @hide
+ */
+ public static void dataChangedForUser(int userId, String packageName) {
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ sService.dataChangedForUser(userId, packageName);
+ } catch (RemoteException e) {
+ Log.e(TAG, "dataChanged(userId,pkg) couldn't connect");
+ }
+ }
+ }
+
+ /**
* @deprecated Applications shouldn't request a restore operation using this method. In Android
* P and later, this method is a no-op.
*
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index fad6cd311021..ebc2945fb1a0 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -24,6 +24,7 @@ import android.os.Parcel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* An app compat override applied to a given package and change id pairing.
@@ -139,6 +140,22 @@ public final class PackageOverride {
/** @hide */
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PackageOverride that = (PackageOverride) o;
+ return mMinVersionCode == that.mMinVersionCode && mMaxVersionCode == that.mMaxVersionCode
+ && mEnabled == that.mEnabled;
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMinVersionCode, mMaxVersionCode, mEnabled);
+ }
+
+ /** @hide */
+ @Override
public String toString() {
if (mMinVersionCode == Long.MIN_VALUE && mMaxVersionCode == Long.MAX_VALUE) {
return Boolean.toString(mEnabled);
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 34e4fcdb9140..37cbccb89735 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -73,6 +73,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
private boolean mLaunchedFromBubble;
+ private IBinder mTaskFragmentToken;
/**
* It is only non-null if the process is the first time to launch activity. It is only an
* optimization for quick look up of the interface so the field is ignored for comparison.
@@ -86,7 +87,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
- mLaunchedFromBubble);
+ mLaunchedFromBubble, mTaskFragmentToken);
client.addLaunchingActivity(token, r);
client.updateProcessState(mProcState, false);
client.updatePendingConfiguration(mCurConfig);
@@ -124,7 +125,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
IActivityClientController activityClientController,
FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
- boolean launchedFromBubble) {
+ boolean launchedFromBubble, IBinder taskFragmentToken) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
instance = new LaunchActivityItem();
@@ -133,7 +134,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
voiceInteractor, procState, state, persistentState, pendingResults,
pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
activityClientController, fixedRotationAdjustments, shareableActivityToken,
- launchedFromBubble);
+ launchedFromBubble, taskFragmentToken);
return instance;
}
@@ -141,7 +142,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Override
public void recycle() {
setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
- null, false, null, null, null, null, null, false);
+ null, false, null, null, null, null, null, false, null);
ObjectPool.recycle(this);
}
@@ -172,6 +173,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
dest.writeTypedObject(mFixedRotationAdjustments, flags);
dest.writeStrongBinder(mShareableActivityToken);
dest.writeBoolean(mLaunchedFromBubble);
+ dest.writeStrongBinder(mTaskFragmentToken);
}
/** Read from Parcel. */
@@ -190,7 +192,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
- in.readBoolean());
+ in.readBoolean(),
+ in.readStrongBinder());
}
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
@@ -229,7 +232,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
&& Objects.equals(mProfilerInfo, other.mProfilerInfo)
&& Objects.equals(mAssistToken, other.mAssistToken)
&& Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
- && Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
+ && Objects.equals(mShareableActivityToken, other.mShareableActivityToken)
+ && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken);
}
@Override
@@ -252,6 +256,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
result = 31 * result + Objects.hashCode(mAssistToken);
result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
result = 31 * result + Objects.hashCode(mShareableActivityToken);
+ result = 31 * result + Objects.hashCode(mTaskFragmentToken);
return result;
}
@@ -301,7 +306,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
IBinder assistToken, IActivityClientController activityClientController,
FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
- boolean launchedFromBubble) {
+ boolean launchedFromBubble, IBinder taskFragmentToken) {
instance.mIntent = intent;
instance.mIdent = ident;
instance.mInfo = info;
@@ -323,5 +328,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
instance.mFixedRotationAdjustments = fixedRotationAdjustments;
instance.mShareableActivityToken = shareableActivityToken;
instance.mLaunchedFromBubble = launchedFromBubble;
+ instance.mTaskFragmentToken = taskFragmentToken;
}
}
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index f506f12a7ba0..17e9e939f03b 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -50,10 +50,10 @@ public final class LocationTimeZoneManager {
public static final String SHELL_COMMAND_STOP = "stop";
/**
- * A shell command that tells the service to record state information during tests. The next
- * argument value is "true" or "false".
+ * A shell command that clears recorded provider state information during tests.
*/
- public static final String SHELL_COMMAND_RECORD_PROVIDER_STATES = "record_provider_states";
+ public static final String SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES =
+ "clear_recorded_provider_states";
/**
* A shell command that tells the service to dump its current state.
@@ -65,44 +65,15 @@ public final class LocationTimeZoneManager {
*/
public static final String DUMP_STATE_OPTION_PROTO = "--proto";
- /**
- * A shell command that sends test commands to a provider
- */
- public static final String SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND =
- "send_provider_test_command";
-
- /**
- * Simulated provider test command that simulates the bind succeeding.
- */
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND = "on_bind";
-
- /**
- * Simulated provider test command that simulates the provider unbinding.
- */
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND = "on_unbind";
-
- /**
- * Simulated provider test command that simulates the provider entering the "permanent failure"
- * state.
- */
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE = "perm_fail";
-
- /**
- * Simulated provider test command that simulates the provider entering the "success" (time
- * zone(s) detected) state.
- */
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS = "success";
-
- /**
- * Argument for {@link #SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS} to specify TZDB time zone IDs.
- */
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ = "tz";
+ /** A shell command that starts the location_time_zone_manager with named test providers. */
+ public static final String SHELL_COMMAND_START_WITH_TEST_PROVIDERS =
+ "start_with_test_providers";
/**
- * Simulated provider test command that simulates the provider entering the "uncertain"
- * state.
+ * The token that can be passed to {@link #SHELL_COMMAND_START_WITH_TEST_PROVIDERS} to indicate
+ * there is no provider.
*/
- public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain";
+ public static final String NULL_PACKAGE_NAME_TOKEN = "@null";
private LocationTimeZoneManager() {
// No need to instantiate.
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index a33d0adaa9b6..9668f80e562e 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -3,3 +3,5 @@
yamasani@google.com
mwachens@google.com
varunshah@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 5094498dbee5..2b28c118e6a7 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3065,6 +3065,9 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.LE_AUDIO) {
BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.VOLUME_CONTROL) {
+ BluetoothVolumeControl vcs = new BluetoothVolumeControl(context, listener, this);
+ return true;
} else {
return false;
}
@@ -3157,6 +3160,11 @@ public final class BluetoothAdapter {
case BluetoothProfile.LE_AUDIO:
BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
leAudio.close();
+ break;
+ case BluetoothProfile.VOLUME_CONTROL:
+ BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
+ vcs.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index bbb550fd6343..12211485cc07 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1096,6 +1096,8 @@ public final class BluetoothDevice implements Parcelable, Attributable {
/** Address is either resolvable, non-resolvable or static. */
public static final int ADDRESS_TYPE_RANDOM = 1;
+ private static final String NULL_MAC_ADDRESS = "00:00:00:00:00:00";
+
/**
* Lazy initialization. Guaranteed final after first object constructed, or
* getService() called.
@@ -1493,6 +1495,10 @@ public final class BluetoothDevice implements Parcelable, Attributable {
Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
return false;
}
+ if (NULL_MAC_ADDRESS.equals(mAddress)) {
+ Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
+ return false;
+ }
try {
return service.createBond(
this, transport, remoteP192Data, remoteP256Data, mAttributionSource);
@@ -1785,13 +1791,41 @@ public final class BluetoothDevice implements Parcelable, Attributable {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean fetchUuidsWithSdp() {
+ return fetchUuidsWithSdp(TRANSPORT_AUTO);
+ }
+
+ /**
+ * Perform a service discovery on the remote device to get the UUIDs supported with the
+ * specific transport.
+ *
+ * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent,
+ * with the UUIDs supported by the remote end. If there is an error
+ * in getting the SDP or GATT records or if the process takes a long time, or the device
+ * is bonding and we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the
+ * UUIDs that is currently present in the cache. Clients should use the {@link #getUuids}
+ * to get UUIDs if service discovery is not to be performed. If there is an ongoing bonding
+ * process, service discovery or device inquiry, the request will be queued.
+ *
+ * @param transport - provide type of transport (e.g. LE or Classic).
+ * @return False if the check fails, True if the process of initiating an ACL connection
+ * to the remote device was started or cached UUIDs will be broadcast with the specific
+ * transport.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean fetchUuidsWithSdp(@Transport int transport) {
final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
return false;
}
try {
- return service.fetchRemoteUuidsWithAttribution(this, mAttributionSource);
+ return service.fetchRemoteUuidsWithAttribution(this, transport, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index aea82102ca36..65cdc83a9e8f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -82,6 +82,9 @@ public final class BluetoothGatt implements BluetoothProfile {
private static final int CONN_STATE_DISCONNECTING = 3;
private static final int CONN_STATE_CLOSED = 4;
+ private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
+ private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 1000; // milliseconds
+
private List<BluetoothGattService> mServices;
/** A GATT operation completed successfully */
@@ -105,6 +108,9 @@ public final class BluetoothGatt implements BluetoothProfile {
/** A read or write operation was requested with an invalid offset */
public static final int GATT_INVALID_OFFSET = 0x7;
+ /** Insufficient authorization for a given operation */
+ public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8;
+
/** A write operation exceeds the maximum length of the attribute */
public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd;
@@ -134,6 +140,27 @@ public final class BluetoothGatt implements BluetoothProfile {
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
/**
+ * A GATT writeCharacteristic request is started successfully.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
+
+ /**
+ * A GATT writeCharacteristic request failed to start.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_FAIL = 1;
+
+ /**
+ * A GATT writeCharacteristic request is issued to a busy remote device.
+ *
+ * @hide
+ */
+ public static final int GATT_WRITE_REQUEST_BUSY = 2;
+
+ /**
* No authentication required.
*
* @hide
@@ -440,9 +467,19 @@ public final class BluetoothGatt implements BluetoothProfile {
try {
final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
- mService.writeCharacteristic(mClientIf, address, handle,
- characteristic.getWriteType(), authReq,
- characteristic.getValue(), mAttributionSource);
+ int requestStatus = GATT_WRITE_REQUEST_FAIL;
+ for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+ requestStatus = mService.writeCharacteristic(mClientIf, address, handle,
+ characteristic.getWriteType(), authReq,
+ characteristic.getValue(), mAttributionSource);
+ if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ break;
+ }
+ try {
+ Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+ } catch (InterruptedException e) {
+ }
+ }
mAuthRetryState++;
return;
} catch (RemoteException e) {
@@ -1190,7 +1227,9 @@ public final class BluetoothGatt implements BluetoothProfile {
characteristic.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1226,7 +1265,9 @@ public final class BluetoothGatt implements BluetoothProfile {
mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1264,21 +1305,35 @@ public final class BluetoothGatt implements BluetoothProfile {
if (device == null) return false;
synchronized (mDeviceBusyLock) {
- if (mDeviceBusy) return false;
+ if (mDeviceBusy) {
+ return false;
+ }
mDeviceBusy = true;
}
+ int requestStatus = GATT_WRITE_REQUEST_FAIL;
try {
- mService.writeCharacteristic(mClientIf, device.getAddress(),
+ for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
+ requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
characteristic.getInstanceId(), characteristic.getWriteType(),
AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
+ if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+ break;
+ }
+ try {
+ Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT);
+ } catch (InterruptedException e) {
+ }
+ }
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
- return true;
+ return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
}
/**
@@ -1317,7 +1372,9 @@ public final class BluetoothGatt implements BluetoothProfile {
descriptor.getInstanceId(), AUTHENTICATION_NONE, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1359,7 +1416,9 @@ public final class BluetoothGatt implements BluetoothProfile {
AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
@@ -1428,7 +1487,9 @@ public final class BluetoothGatt implements BluetoothProfile {
mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- mDeviceBusy = false;
+ synchronized (mDeviceBusyLock) {
+ mDeviceBusy = false;
+ }
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 9de27ff97fe5..c438dd34f813 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -356,7 +356,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* earbud)
* @param device LE Audio capable device
* @return group id that this device currently belongs to
- * @hide
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 161c843f0398..5b67a75a2b43 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -208,17 +208,31 @@ public interface BluetoothProfile {
/**
* LE Audio Device
*
- * @hide
*/
int LE_AUDIO = 22;
/**
+ * Volume Control profile
+ *
+ * @hide
+ */
+ @SystemApi
+ int VOLUME_CONTROL = 23;
+
+ /**
+ * @hide
+ * Media Control Profile server
+ *
+ */
+ int MCP_SERVER = 24;
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 22;
+ int MAX_PROFILE_ID = 24;
/**
* Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index bc3754a2fc56..ff250e63cc85 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -174,6 +174,16 @@ public final class BluetoothUuid {
/** @hide */
@NonNull
@SystemApi
+ public static final ParcelUuid GENERIC_MEDIA_CONTROL =
+ ParcelUuid.fromString("00001849-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid MEDIA_CONTROL =
+ ParcelUuid.fromString("00001848-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/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
new file mode 100644
index 000000000000..678c11a59f31
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Attributable;
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Volume Control service.
+ *
+ * <p>BluetoothVolumeControl is a proxy object for controlling the Bluetooth VC
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothVolumeControl proxy object.
+ * @hide
+ */
+@SystemApi
+public final class BluetoothVolumeControl implements BluetoothProfile, AutoCloseable {
+ private static final String TAG = "BluetoothVolumeControl";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
+
+ private CloseGuard mCloseGuard;
+
+ /**
+ * Intent used to broadcast the change in connection state of the Volume Control
+ * profile.
+ *
+ * <p>This intent will have 3 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SuppressLint("ActionValue")
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED";
+
+ private BluetoothAdapter mAdapter;
+ private final AttributionSource mAttributionSource;
+ private final BluetoothProfileConnector<IBluetoothVolumeControl> mProfileConnector =
+ new BluetoothProfileConnector(this, BluetoothProfile.VOLUME_CONTROL, TAG,
+ IBluetoothVolumeControl.class.getName()) {
+ @Override
+ public IBluetoothVolumeControl getServiceInterface(IBinder service) {
+ return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ }
+ };
+
+ /**
+ * Create a BluetoothVolumeControl proxy object for interacting with the local
+ * Bluetooth Volume Control service.
+ */
+ /*package*/ BluetoothVolumeControl(Context context, ServiceListener listener,
+ BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ mAttributionSource = adapter.getAttributionSource();
+ mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
+ }
+
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void close() {
+ mProfileConnector.disconnect();
+ }
+
+ private IBluetoothVolumeControl getService() { return mProfileConnector.getService(); }
+
+ /**
+ * Get the list of connected devices. Currently at most one.
+ *
+ * @return list of connected devices
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @NonNull List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) log("getConnectedDevices()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get the list of devices matching specified states. Currently at most one.
+ *
+ * @return list of matching devices
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) log("getDevicesMatchingStates()");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled()) {
+ try {
+ return Attributable.setAttributionSource(
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<BluetoothDevice>();
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<BluetoothDevice>();
+ }
+
+ /**
+ * Get connection state of device
+ *
+ * @return device connection state
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) log("getConnectionState(" + device + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionState(device, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Tells remote device to set an absolute volume.
+ *
+ * @param volume Absolute volume to be set on remote device.
+ * Minimum value is 0 and maximum value is 255
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public void setVolume(@Nullable BluetoothDevice device,
+ @IntRange(from = 0, to = 255) int volume) {
+ if (DBG)
+ log("setVolume(" + volume + ")");
+ final IBluetoothVolumeControl service = getService();
+ try {
+ if (service != null && isEnabled()) {
+ service.setVolume(device, volume, mAttributionSource);
+ return;
+ }
+ if (service == null)
+ Log.w(TAG, "Proxy not attached to service");
+ } catch (RemoteException e) {
+ Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Set connection policy of the profile
+ *
+ * <p> The device should already be paired.
+ * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+ * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Paired bluetooth device
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+ return false;
+ }
+ try {
+ return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the connection policy of the profile.
+ *
+ * <p> The connection policy can be any of:
+ * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN},
+ * {@link #CONNECTION_POLICY_UNKNOWN}
+ *
+ * @param device Bluetooth device
+ * @return connection policy of the device
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
+ final IBluetoothVolumeControl service = getService();
+ if (service != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return service.getConnectionPolicy(device, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ }
+
+ private boolean isEnabled() {
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
+ }
+
+ private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index 4e5ede74ce46..bb0b95649b17 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -937,17 +937,18 @@ public final class OobData implements Parcelable {
}
@NonNull
- private String toHexString(@NonNull int b) {
+ private String toHexString(int b) {
return toHexString(new byte[] {(byte) b});
}
@NonNull
- private String toHexString(@NonNull byte b) {
+ private String toHexString(byte b) {
return toHexString(new byte[] {b});
}
@NonNull
- private String toHexString(@NonNull byte[] array) {
+ private String toHexString(byte[] array) {
+ if (array == null) return "null";
StringBuilder builder = new StringBuilder(array.length * 2);
for (byte b: array) {
builder.append(String.format("%02x", b));
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 34aac8bfdb25..ee173dbc4ad4 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -151,7 +151,7 @@ public final class BluetoothLeScanner {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
- startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
+ startScan(filters, settings, null, callback, /*callbackIntent=*/ null);
}
/**
@@ -185,7 +185,7 @@ public final class BluetoothLeScanner {
@NonNull PendingIntent callbackIntent) {
return startScan(filters,
settings != null ? settings : new ScanSettings.Builder().build(),
- null, null, callbackIntent, null);
+ null, null, callbackIntent);
}
/**
@@ -231,14 +231,13 @@ public final class BluetoothLeScanner {
@SuppressLint("AndroidFrameworkRequiresPermission")
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
- startScan(filters, settings, workSource, callback, null, null);
+ startScan(filters, settings, workSource, callback, null);
}
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
- final PendingIntent callbackIntent,
- List<List<ResultStorageDescriptor>> resultStorages) {
+ final PendingIntent callbackIntent) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null && callbackIntent == null) {
throw new IllegalArgumentException("callback is null");
@@ -274,7 +273,7 @@ public final class BluetoothLeScanner {
}
if (callback != null) {
BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
- settings, workSource, callback, resultStorages);
+ settings, workSource, callback);
wrapper.startRegistration();
} else {
try {
@@ -357,8 +356,11 @@ public final class BluetoothLeScanner {
/**
* Start truncated scan.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+ @Deprecated
@SystemApi
@RequiresBluetoothScanPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@@ -366,13 +368,10 @@ public final class BluetoothLeScanner {
final ScanCallback callback) {
int filterSize = truncatedFilters.size();
List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize);
- List<List<ResultStorageDescriptor>> scanStorages =
- new ArrayList<List<ResultStorageDescriptor>>(filterSize);
for (TruncatedFilter filter : truncatedFilters) {
scanFilters.add(filter.getFilter());
- scanStorages.add(filter.getStorageDescriptors());
}
- startScan(scanFilters, settings, null, callback, null, scanStorages);
+ startScan(scanFilters, settings, null, callback, null);
}
/**
@@ -397,7 +396,6 @@ public final class BluetoothLeScanner {
private final WorkSource mWorkSource;
private ScanSettings mSettings;
private IBluetoothGatt mBluetoothGatt;
- private List<List<ResultStorageDescriptor>> mResultStorages;
// mLeHandle 0: not registered
// -2: registration failed because app is scanning to frequently
@@ -407,15 +405,13 @@ public final class BluetoothLeScanner {
public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt,
List<ScanFilter> filters, ScanSettings settings,
- WorkSource workSource, ScanCallback scanCallback,
- List<List<ResultStorageDescriptor>> resultStorages) {
+ WorkSource workSource, ScanCallback scanCallback) {
mBluetoothGatt = bluetoothGatt;
mFilters = filters;
mSettings = settings;
mWorkSource = workSource;
mScanCallback = scanCallback;
mScannerId = 0;
- mResultStorages = resultStorages;
}
public void startRegistration() {
@@ -493,7 +489,7 @@ public final class BluetoothLeScanner {
} else {
mScannerId = scannerId;
mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
- mResultStorages, mAttributionSource);
+ mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
index 796c815d69bb..f65048975deb 100644
--- a/core/java/android/bluetooth/le/ResultStorageDescriptor.java
+++ b/core/java/android/bluetooth/le/ResultStorageDescriptor.java
@@ -23,8 +23,11 @@ import android.os.Parcelable;
/**
* Describes the way to store scan result.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+@Deprecated
@SystemApi
public final class ResultStorageDescriptor implements Parcelable {
private int mType;
diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java
index 93f526bb9f09..25925888a0d2 100644
--- a/core/java/android/bluetooth/le/TruncatedFilter.java
+++ b/core/java/android/bluetooth/le/TruncatedFilter.java
@@ -24,8 +24,11 @@ import java.util.List;
/**
* A special scan filter that lets the client decide how the scan record should be stored.
*
+ * @deprecated this is not used anywhere
+ *
* @hide
*/
+@Deprecated
@SystemApi
@SuppressLint("AndroidFrameworkBluetoothPermission")
public final class TruncatedFilter {
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 9007d9d8bbcc..b060ce2f4460 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -53,8 +53,6 @@ public final class Association implements Parcelable {
-
-
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
@@ -242,7 +240,7 @@ public final class Association implements Parcelable {
};
@DataClass.Generated(
- time = 1612832377589L,
+ time = 1611795283642L,
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 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)")
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index f49362e9300e..2ecd71bc1f06 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -124,6 +124,16 @@ public class ClipDescription implements Parcelable {
*/
public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
+ /**
+ * An instance id used for logging.
+ * <p>
+ * Type: {@link com.android.internal.logging.InstanceId}
+ * </p>
+ * @hide
+ */
+ public static final String EXTRA_LOGGING_INSTANCE_ID =
+ "android.intent.extra.LOGGING_INSTANCE_ID";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value =
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 265ff331ffa0..563b6cf0349c 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -3193,6 +3193,21 @@ public abstract class ContentResolver implements ContentInterface {
}
/**
+ * Returns the package name of the syncadapter that matches a given account type, authority
+ * and user.
+ * @hide
+ */
+ @Nullable
+ public static String getSyncAdapterPackageAsUser(@NonNull String accountType,
+ @NonNull String authority, @UserIdInt int userId) {
+ try {
+ return getContentService().getSyncAdapterPackageAsUser(accountType, authority, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Check if the provider should be synced when a network tickle is received
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
@@ -3289,7 +3304,7 @@ public abstract class ContentResolver implements ContentInterface {
* @param authority the provider to specify in the sync request
* @param extras extra parameters to go along with the sync request
* @param pollFrequency how frequently the sync should be performed, in seconds.
- * On Android API level 24 and above, a minmam interval of 15 minutes is enforced.
+ * On Android API level 24 and above, a minimum interval of 15 minutes is enforced.
* On previous versions, the minimum interval is 1 hour.
* @throws IllegalArgumentException if an illegal extra was set or if any of the parameters
* are null.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 27027721109d..ad1163ff01f7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3301,6 +3301,12 @@ public abstract class Context {
* to startService() are not counted -- this stops the service no matter
* how many times it was started.
*
+ * <p>If the service is running as a foreground service when it is
+ * stopped, its associated notification will be removed. To avoid this,
+ * apps can use {@link android.app.Service#stopForeground(int)
+ * stopForeground(STOP_FOREGROUND_DETACH)} to decouple the notification
+ * from the service's lifecycle before stopping it.</p>
+ *
* <p>Note that if a stopped service still has {@link ServiceConnection}
* objects bound to it with the {@link #BIND_AUTO_CREATE} set, it will
* not be destroyed until all of these bindings are removed. See
@@ -5229,6 +5235,15 @@ public abstract class Context {
public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.tare.EconomyManager} instance for understanding economic standing.
+ * @see #getSystemService(String)
+ * @hide
+ * @see android.app.tare.EconomyManager
+ */
+ public static final String RESOURCE_ECONOMY_SERVICE = "tare";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.service.persistentdata.PersistentDataBlockManager} instance
* for interacting with a storage device that lives across factory resets.
@@ -5597,6 +5612,7 @@ public abstract class Context {
* @see #getSystemService(String)
* @hide
*/
+ @SystemApi
public static final String UWB_SERVICE = "uwb";
/**
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 2044fc02a7e5..127466d58629 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -150,6 +150,7 @@ interface IContentService {
SyncAdapterType[] getSyncAdapterTypesAsUser(int userId);
String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId);
+ String getSyncAdapterPackageAsUser(String accountType, String authority, int userId);
/**
* Returns true if there is currently a operation for the given account/authority or service
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e35a32638a8..325324600cc2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1805,7 +1805,7 @@ public class Intent implements Parcelable, Cloneable {
* the package name of the current installed package to be uninstalled.
* You can optionally supply {@link #EXTRA_RETURN_RESULT}.
* <p>
- * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the install
+ * Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the uninstall
* succeeded.
* <p>
* Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
@@ -1866,7 +1866,7 @@ public class Intent implements Parcelable, Cloneable {
* that should be managed by the launched UI.
* </p>
* <p>
- * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
+ * <li> {@link #EXTRA_USER} specifies the {@link UserHandle} of the user that owns the app.
* </p>
* <p>
* Output: Nothing.
@@ -2850,8 +2850,8 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
/**
- * Broadcast Action: A user ID has been removed from the system. The user
- * ID number is stored in the extra data under {@link #EXTRA_UID}.
+ * Broadcast Action: A uid has been removed from the system. The uid
+ * number is stored in the extra data under {@link #EXTRA_UID}.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
@@ -3726,10 +3726,12 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USER_BACKGROUND";
/**
- * Broadcast sent to the system when a user is added. Carries an extra
- * EXTRA_USER_HANDLE that has the userHandle of the new user. It is sent to
- * all running users. You must hold
- * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
+ * Broadcast sent to the system when a user is added.
+ * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the new user
+ * (and for legacy reasons, also carries an int extra {@link #EXTRA_USER_HANDLE} specifying that
+ * user's user ID).
+ * It is sent to all running users.
+ * You must hold {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
@SystemApi
@@ -3738,7 +3740,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent by the system when a user is started. Carries an extra
- * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only sent to
+ * {@link #EXTRA_USER_HANDLE} that has the user ID of the user. This is only sent to
* registered receivers, not manifest receivers. It is sent to the user
* that has been started. This is sent as a foreground
* broadcast, since it is part of a visible user interaction; be as quick
@@ -3750,7 +3752,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent when a user is in the process of starting. Carries an extra
- * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only
+ * {@link #EXTRA_USER_HANDLE} that has the user ID of the user. This is only
* sent to registered receivers, not manifest receivers. It is sent to all
* users (including the one that is being started). You must hold
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive
@@ -3767,7 +3769,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent when a user is going to be stopped. Carries an extra
- * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is only
+ * {@link #EXTRA_USER_HANDLE} that has the user ID of the user. This is only
* sent to registered receivers, not manifest receivers. It is sent to all
* users (including the one that is being stopped). You must hold
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS} to receive
@@ -3785,7 +3787,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the system when a user is stopped. Carries an extra
- * {@link EXTRA_USER_HANDLE} that has the userHandle of the user. This is similar to
+ * {@link #EXTRA_USER_HANDLE} that has the user ID of the user. This is similar to
* {@link #ACTION_PACKAGE_RESTARTED}, but for an entire user instead of a
* specific package. This is only sent to registered receivers, not manifest
* receivers. It is sent to all running users <em>except</em> the one that
@@ -3797,8 +3799,12 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USER_STOPPED";
/**
- * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USER_HANDLE that has
- * the userHandle of the user. It is sent to all running users except the
+ * Broadcast sent to the system when a user is removed.
+ * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the user that
+ * was removed
+ * (and for legacy reasons, also carries an int extra {@link #EXTRA_USER_HANDLE} specifying that
+ * user's user ID).
+ * It is sent to all running users except the
* one that has been removed. The user will not be completely removed until all receivers have
* handled the broadcast. You must hold
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
@@ -3809,9 +3815,13 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.USER_REMOVED";
/**
- * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USER_HANDLE that has
- * the userHandle of the user to become the current one. This is only sent to
- * registered receivers, not manifest receivers. It is sent to all running users.
+ * Broadcast sent to the system when the user switches.
+ * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle}
+ * of the user to become the current one
+ * (and for legacy reasons, also carries an int extra {@link #EXTRA_USER_HANDLE} specifying that
+ * user's user ID).
+ * This is only sent to registered receivers, not manifest receivers.
+ * It is sent to all running users.
* You must hold
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
@@ -3840,17 +3850,18 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the primary user when an associated managed profile is added (the profile
* was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
- * the UserHandle of the profile that was added. Only applications (for example Launchers)
- * that need to display merged content across both primary and managed profiles need to
- * worry about this broadcast. This is only sent to registered receivers,
+ * the {@link UserHandle} of the profile that was added. Only applications (for example
+ * Launchers) that need to display merged content across both primary and managed profiles need
+ * to worry about this broadcast. This is only sent to registered receivers,
* not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_ADDED =
"android.intent.action.MANAGED_PROFILE_ADDED";
/**
- * Broadcast sent to the primary user when an associated managed profile is removed. Carries an
- * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed.
+ * Broadcast sent to the primary user when an associated managed profile is removed.
+ * Carries an extra {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile
+ * that was removed.
* Only applications (for example Launchers) that need to display merged content across both
* primary and managed profiles need to worry about this broadcast. This is only sent to
* registered receivers, not manifest receivers.
@@ -3861,9 +3872,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the primary user when the credential-encrypted private storage for
* an associated managed profile is unlocked. Carries an extra {@link #EXTRA_USER} that
- * specifies the UserHandle of the profile that was unlocked. Only applications (for example
- * Launchers) that need to display merged content across both primary and managed profiles
- * need to worry about this broadcast. This is only sent to registered receivers,
+ * specifies the {@link UserHandle} of the profile that was unlocked. Only applications (for
+ * example Launchers) that need to display merged content across both primary and managed
+ * profiles need to worry about this broadcast. This is only sent to registered receivers,
* not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_UNLOCKED =
@@ -3872,9 +3883,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the primary user when an associated managed profile has become available.
* Currently this includes when the user disables quiet mode for the profile. Carries an extra
- * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
- * this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
- * of quiet mode. This is only sent to registered receivers, not manifest receivers.
+ * {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. When quiet mode is
+ * changed, this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the
+ * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_AVAILABLE =
"android.intent.action.MANAGED_PROFILE_AVAILABLE";
@@ -3882,9 +3893,9 @@ public class Intent implements Parcelable, Cloneable {
/**
* Broadcast sent to the primary user when an associated managed profile has become unavailable.
* Currently this includes when the user enables quiet mode for the profile. Carries an extra
- * {@link #EXTRA_USER} that specifies the UserHandle of the profile. When quiet mode is changed,
- * this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the new state
- * of quiet mode. This is only sent to registered receivers, not manifest receivers.
+ * {@link #EXTRA_USER} that specifies the {@link UserHandle} of the profile. When quiet mode is
+ * changed, this broadcast will carry a boolean extra {@link #EXTRA_QUIET_MODE} indicating the
+ * new state of quiet mode. This is only sent to registered receivers, not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE =
"android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
@@ -5033,7 +5044,7 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK";
/**
- * An activity to run when device is inserted into a car dock.
+ * An activity to run when device is inserted into a desk dock.
* Used with {@link #ACTION_MAIN} to launch an activity. For more
* information, see {@link android.app.UiModeManager}.
*/
@@ -5253,6 +5264,30 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the weather application.
+ * The activity should be able to give the user information about the weather
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_WEATHER = "android.intent.category.APP_WEATHER";
+
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the fitness application.
+ * The activity should be able to give the user fitness information and manage workouts
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_FITNESS = "android.intent.category.APP_FITNESS";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard extra data keys.
@@ -5315,7 +5350,7 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_INTENT = "android.intent.extra.INTENT";
/**
- * An int representing the user id to be used.
+ * An int representing the user ID to be used.
*
* @hide
*/
@@ -5351,6 +5386,22 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
/**
+ * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD},
+ * that specifies whether the system displayed attribution information in the
+ * permission usage system UI for the chosen entry.
+ *
+ * <p> The extra can only be true if application has specified attributionsAreUserVisible
+ * in its manifest. </p>
+ *
+ * <p> Applications can use this extra to improve their permission usage explanation
+ * experience. </p>
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SHOWING_ATTRIBUTION =
+ "android.intent.extra.SHOWING_ATTRIBUTION";
+
+ /**
* An Intent[] describing additional, alternate choices you would like shown with
* {@link #ACTION_CHOOSER}.
*
@@ -5909,7 +5960,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.extra.ALLOW_MULTIPLE";
/**
- * The integer userHandle (i.e. userId) carried with broadcast intents related to addition,
+ * The user ID integer carried with broadcast intents related to addition,
* removal and switching of users and managed profiles - {@link #ACTION_USER_ADDED},
* {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
*
@@ -5919,7 +5970,7 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.extra.user_handle";
/**
- * The UserHandle carried with intents.
+ * The {@link UserHandle} carried with intents.
*/
public static final String EXTRA_USER =
"android.intent.extra.USER";
@@ -6958,6 +7009,7 @@ public class Intent implements Parcelable, Cloneable {
private int mContentUserHint = UserHandle.USER_CURRENT;
/** Token to track instant app launches. Local only; do not copy cross-process. */
private String mLaunchToken;
+ private Intent mOriginalIntent; // Used for the experimental "component alias" feature.
// ---------------------------------------------------------------------
@@ -6994,6 +7046,7 @@ public class Intent implements Parcelable, Cloneable {
this.mIdentifier = o.mIdentifier;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
+ this.mOriginalIntent = o.mOriginalIntent;
if (o.mCategories != null) {
this.mCategories = new ArraySet<>(o.mCategories);
@@ -8158,6 +8211,22 @@ public class Intent implements Parcelable, Cloneable {
return mType;
}
+
+ /**
+ * @hide For the experimental component alias feature. Do not use, unless you know what it is.
+ */
+ @Nullable
+ public Intent getOriginalIntent() {
+ return mOriginalIntent;
+ }
+
+ /**
+ * @hide For the experimental component alias feature. Do not use, unless you know what it is.
+ */
+ public void setOriginalIntent(@Nullable Intent originalIntent) {
+ mOriginalIntent = originalIntent;
+ }
+
/**
* Return the MIME data type of this intent. If the type field is
* explicitly set, that is simply returned. Otherwise, if the data is set,
@@ -9394,7 +9463,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* This is NOT a secure mechanism to identify the user who sent the intent.
- * When the intent is sent to a different user, it is used to fix uris by adding the userId
+ * When the intent is sent to a different user, it is used to fix uris by adding the user ID
* who sent the intent.
* @hide
*/
@@ -10803,6 +10872,11 @@ public class Intent implements Parcelable, Cloneable {
mSelector.toShortString(b, secure, comp, extras, clip);
b.append("}");
}
+ if (mOriginalIntent != null) {
+ b.append(" org={");
+ mOriginalIntent.toShortString(b, secure, comp, extras, clip);
+ b.append("}");
+ }
}
/** @hide */
@@ -11098,6 +11172,13 @@ public class Intent implements Parcelable, Cloneable {
}
out.writeInt(mContentUserHint);
out.writeBundle(mExtras);
+
+ if (mOriginalIntent != null) {
+ out.writeInt(1);
+ mOriginalIntent.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR
@@ -11151,6 +11232,9 @@ public class Intent implements Parcelable, Cloneable {
}
mContentUserHint = in.readInt();
mExtras = in.readBundle();
+ if (in.readInt() != 0) {
+ mOriginalIntent = new Intent(in);
+ }
}
/**
@@ -11347,8 +11431,17 @@ public class Intent implements Parcelable, Cloneable {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public void prepareToLeaveProcess(Context context) {
- final boolean leavingPackage = (mComponent == null)
- || !Objects.equals(mComponent.getPackageName(), context.getPackageName());
+ final boolean leavingPackage;
+ if (mComponent != null) {
+ leavingPackage = !Objects.equals(mComponent.getPackageName(), context.getPackageName());
+ } else if (mPackage != null) {
+ leavingPackage = !Objects.equals(mPackage, context.getPackageName());
+ } else {
+ // When no specific component or package has been defined, we have
+ // to assume that we might be routed through an intent
+ // disambiguation dialog which might leave our package
+ leavingPackage = true;
+ }
prepareToLeaveProcess(leavingPackage);
}
@@ -11366,6 +11459,9 @@ public class Intent implements Parcelable, Cloneable {
if (mClipData != null) {
mClipData.prepareToLeaveProcess(leavingPackage, getFlags());
}
+ if (mOriginalIntent != null) {
+ mOriginalIntent.prepareToLeaveProcess(leavingPackage);
+ }
if (mExtras != null && !mExtras.isParcelled()) {
final Object intent = mExtras.get(Intent.EXTRA_INTENT);
@@ -11461,6 +11557,9 @@ public class Intent implements Parcelable, Cloneable {
if (mClipData != null) {
mClipData.prepareToEnterProcess(source);
}
+ if (mOriginalIntent != null) {
+ mOriginalIntent.prepareToEnterProcess(false, source);
+ }
if (mContentUserHint != UserHandle.USER_CURRENT) {
if (UserHandle.getAppId(Process.myUid()) != Process.SYSTEM_UID) {
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index c5badb9148ea..32827ae11e0b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -17,6 +17,7 @@
package android.content;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -44,8 +45,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Predicate;
/**
* Structured description of Intent values to be matched. An IntentFilter can
@@ -147,6 +150,8 @@ import java.util.function.BiConsumer;
* will only match an Intent that does not have any categories.
*/
public class IntentFilter implements Parcelable {
+ private static final String TAG = "IntentFilter";
+
private static final String AGLOB_STR = "aglob";
private static final String SGLOB_STR = "sglob";
private static final String PREFIX_STR = "prefix";
@@ -1761,6 +1766,35 @@ public class IntentFilter implements Parcelable {
}
/**
+ * Return a {@link Predicate} which tests whether this filter matches the
+ * given <var>intent</var>.
+ * <p>
+ * The intent's type will always be tested using a simple
+ * {@link Intent#getType()} check. To instead perform a detailed type
+ * resolution before matching, use
+ * {@link #asPredicateWithTypeResolution(ContentResolver)}.
+ */
+ public @NonNull Predicate<Intent> asPredicate() {
+ return i -> match(null, i, false, TAG) >= 0;
+ }
+
+ /**
+ * Return a {@link Predicate} which tests whether this filter matches the
+ * given <var>intent</var>.
+ * <p>
+ * The intent's type will always be resolved by calling
+ * {@link Intent#resolveType(ContentResolver)} before matching.
+ *
+ * @param resolver to be used when calling
+ * {@link Intent#resolveType(ContentResolver)} before matching.
+ */
+ public @NonNull Predicate<Intent> asPredicateWithTypeResolution(
+ @NonNull ContentResolver resolver) {
+ Objects.requireNonNull(resolver);
+ return i -> match(resolver, i, true, TAG) >= 0;
+ }
+
+ /**
* Test whether this filter matches the given <var>intent</var>.
*
* @param intent The Intent to compare against.
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 8ad134971e0e..c7f92c9714f0 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,12 +1,14 @@
# Remain no owner because multiple modules may touch this file.
per-file Context.java = *
per-file ContextWrapper.java = *
-per-file Content* = varunshah@google.com, omakoto@google.com, jsharkey@google.com
+per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentFilter.java = toddke@google.com
per-file IntentFilter.java = patb@google.com
+per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
per-file Intent.java = toddke@google.com
per-file Intent.java = patb@google.com
per-file Intent.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file Intent.java = file:/services/core/java/com/android/server/am/OWNERS
per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index a167cb3d851f..8d3452ec2c60 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -597,7 +597,7 @@ public final class PermissionChecker {
* which will evaluate the permission access based on the current fg/bg state of the
* app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
@@ -634,7 +634,7 @@ public final class PermissionChecker {
* listener you should use this method which will evaluate the permission access based
* on the current fg/bg state of the app and leave a record that the data was accessed.
*
- * <p>This API assumes the the {@link Binder#getCallingUid()} is the same as
+ * <p>This API assumes the {@link Binder#getCallingUid()} is the same as
* {@link Process#myUid()}.
*
* @param context Context for accessing resources.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d3ed00608324..86030fdc1d72 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -40,6 +40,7 @@ import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
@@ -359,6 +360,11 @@ interface IPackageManager {
in int newState, in int flags, int userId);
/**
+ * As per {@link android.content.pm.PackageManager#setComponentEnabledSettings}.
+ */
+ void setComponentEnabledSettings(in List<ComponentEnabledSetting> settings, int userId);
+
+ /**
* As per {@link android.content.pm.PackageManager#getComponentEnabledSetting}.
*/
@UnsupportedAppUsage
@@ -752,6 +758,9 @@ interface IPackageManager {
void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
+ IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId);
+
//------------------------------------------------------------------------
//
// The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3f8aedb31ea9..c2a65d5bf85d 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1616,7 +1616,7 @@ public class PackageInstaller {
/** {@hide} */
public DataLoaderParams dataLoaderParams;
/** {@hide} */
- public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+ public int rollbackDataPolicy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
/** {@hide} */
public boolean forceQueryableOverride;
/** {@hide} */
@@ -1887,7 +1887,7 @@ public class PackageInstaller {
} else {
installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
- rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+ rollbackDataPolicy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
}
/**
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index dd2080b60b37..2bac066ed186 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -207,7 +207,9 @@ public class PackageItemInfo {
return loadSafeLabel(pm, DEFAULT_MAX_LABEL_SIZE_PX, SAFE_STRING_FLAG_TRIM
| SAFE_STRING_FLAG_FIRST_LINE);
} else {
- return loadUnsafeLabel(pm);
+ // Trims the label string to the MAX_SAFE_LABEL_LENGTH. This is to prevent that the
+ // system is overwhelmed by an enormous string returned by the application.
+ return TextUtils.trimToSize(loadUnsafeLabel(pm), MAX_SAFE_LABEL_LENGTH);
}
}
diff --git a/core/java/android/content/pm/PackageManager.aidl b/core/java/android/content/pm/PackageManager.aidl
index 31365a19f286..87bfa54571f7 100644
--- a/core/java/android/content/pm/PackageManager.aidl
+++ b/core/java/android/content/pm/PackageManager.aidl
@@ -18,3 +18,4 @@
package android.content.pm;
parcelable PackageManager.Property;
+parcelable PackageManager.ComponentEnabledSetting;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2ed00b5d2982..31b9c305f5a3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -83,6 +83,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DataClass;
import dalvik.system.VMRuntime;
@@ -380,6 +381,219 @@ public abstract class PackageManager {
}
/**
+ * The class containing the enabled setting of a package component.
+ * <p>
+ * This is used by the {@link #setComponentEnabledSettings(List)} to support the batch updates
+ * of the enabled settings of components.
+ *
+ * @see #setComponentEnabledSettings(List)
+ */
+ @DataClass(genConstructor = false)
+ public static final class ComponentEnabledSetting implements Parcelable {
+ /**
+ * The package name of the application to enable the setting.
+ */
+ private final @Nullable String mPackageName;
+
+ /**
+ * The component name of the application to enable the setting.
+ */
+ private final @Nullable ComponentName mComponentName;
+
+ /**
+ * The new enabled state
+ */
+ private final @EnabledState int mEnabledState;
+
+ /**
+ * The optional behavior flag
+ */
+ private final @EnabledFlags int mEnabledFlags;
+
+ /**
+ * Create an instance of the ComponentEnabledSetting for the component level's enabled
+ * setting update.
+ *
+ * @param componentName The component name to update the enabled setting.
+ * @param newState The new enabled state.
+ * @param flags The optional behavior flags.
+ */
+ public ComponentEnabledSetting(@NonNull ComponentName componentName,
+ @EnabledState int newState, @EnabledFlags int flags) {
+ Objects.nonNull(componentName);
+ mPackageName = null;
+ mComponentName = componentName;
+ mEnabledState = newState;
+ mEnabledFlags = flags;
+ }
+
+ /**
+ * Create an instance of the ComponentEnabledSetting for the application level's enabled
+ * setting update.
+ *
+ * @param packageName The package name to update the enabled setting.
+ * @param newState The new enabled state.
+ * @param flags The optional behavior flags.
+ * @hide
+ */
+ public ComponentEnabledSetting(@NonNull String packageName,
+ @EnabledState int newState, @EnabledFlags int flags) {
+ Objects.nonNull(packageName);
+ mPackageName = packageName;
+ mComponentName = null;
+ mEnabledState = newState;
+ mEnabledFlags = flags;
+ }
+
+ /**
+ * Returns the package name of the setting.
+ *
+ * @return the package name.
+ * @hide
+ */
+ public @NonNull String getPackageName() {
+ if (isComponent()) {
+ return mComponentName.getPackageName();
+ }
+ return mPackageName;
+ }
+
+ /**
+ * Returns the component class name of the setting.
+ *
+ * @return the class name.
+ * @hide
+ */
+ public @Nullable String getClassName() {
+ if (isComponent()) {
+ return mComponentName.getClassName();
+ }
+ return null;
+ }
+
+ /**
+ * Whether or not this is for the component level's enabled setting update.
+ *
+ * @return {@code true} if it's the component level enabled setting update.
+ * @hide
+ */
+ public boolean isComponent() {
+ return mComponentName != null;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/PackageManager.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * The component name of the application to enable the setting.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * The new enabled state
+ */
+ @DataClass.Generated.Member
+ public @EnabledState int getEnabledState() {
+ return mEnabledState;
+ }
+
+ /**
+ * The optional behavior flag
+ */
+ @DataClass.Generated.Member
+ public @EnabledFlags int getEnabledFlags() {
+ return mEnabledFlags;
+ }
+
+ @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 (mComponentName != null) flg |= 0x2;
+ dest.writeByte(flg);
+ if (mPackageName != null) dest.writeString(mPackageName);
+ if (mComponentName != null) dest.writeTypedObject(mComponentName, flags);
+ dest.writeInt(mEnabledState);
+ dest.writeInt(mEnabledFlags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ComponentEnabledSetting(@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();
+ ComponentName componentName = (flg & 0x2) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+ int enabledState = in.readInt();
+ int enabledFlags = in.readInt();
+
+ this.mPackageName = packageName;
+ this.mComponentName = componentName;
+ this.mEnabledState = enabledState;
+ com.android.internal.util.AnnotationValidations.validate(
+ EnabledState.class, null, mEnabledState);
+ this.mEnabledFlags = enabledFlags;
+ com.android.internal.util.AnnotationValidations.validate(
+ EnabledFlags.class, null, mEnabledFlags);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ComponentEnabledSetting> CREATOR
+ = new Parcelable.Creator<ComponentEnabledSetting>() {
+ @Override
+ public ComponentEnabledSetting[] newArray(int size) {
+ return new ComponentEnabledSetting[size];
+ }
+
+ @Override
+ public ComponentEnabledSetting createFromParcel(@NonNull Parcel in) {
+ return new ComponentEnabledSetting(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1628668290863L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/content/pm/PackageManager.java",
+ inputSignatures = "private final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable android.content.ComponentName mComponentName\nprivate final @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate final @android.content.pm.PackageManager.EnabledFlags int mEnabledFlags\npublic @android.annotation.NonNull java.lang.String getPackageName()\npublic @android.annotation.Nullable java.lang.String getClassName()\npublic boolean isComponent()\nclass ComponentEnabledSetting extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+ }
+
+ /**
* Listener for changes in permissions granted to a UID.
*
* @hide
@@ -1053,26 +1267,36 @@ public abstract class PackageManager {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- RollbackDataPolicy.RESTORE,
- RollbackDataPolicy.WIPE,
- RollbackDataPolicy.RETAIN
+ @IntDef(prefix = { "ROLLBACK_DATA_POLICY_" }, value = {
+ ROLLBACK_DATA_POLICY_RESTORE,
+ ROLLBACK_DATA_POLICY_WIPE,
+ ROLLBACK_DATA_POLICY_RETAIN
})
- public @interface RollbackDataPolicy {
- /**
- * User data will be backed up during install and restored during rollback.
- */
- int RESTORE = 0;
- /**
- * User data won't be backed up during install but will be wiped out during rollback.
- */
- int WIPE = 1;
- /**
- * User data won't be backed up during install and won't be restored during rollback.
- * TODO: Not implemented yet.
- */
- int RETAIN = 2;
- }
+ public @interface RollbackDataPolicy {}
+
+ /**
+ * User data will be backed up during install and restored during rollback.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROLLBACK_DATA_POLICY_RESTORE = 0;
+
+ /**
+ * User data won't be backed up during install but will be wiped out during rollback.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROLLBACK_DATA_POLICY_WIPE = 1;
+
+ /**
+ * User data won't be backed up during install and will remain unchanged during rollback.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROLLBACK_DATA_POLICY_RETAIN = 2;
/** @hide */
@IntDef(flag = true, prefix = { "INSTALL_" }, value = {
@@ -3699,6 +3923,17 @@ public abstract class PackageManager {
public static final String FEATURE_KEYSTORE_APP_ATTEST_KEY =
"android.hardware.keystore.app_attest_key";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * is opted-in to receive per-app compatibility overrides that are applied in
+ * {@link com.android.server.compat.overrides.AppCompatOverridesService}.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_APP_COMPAT_OVERRIDES =
+ "android.software.app_compat_overrides";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
@@ -4470,12 +4705,17 @@ public abstract class PackageManager {
* main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
* <code>null</code> if neither are found.
*
+ * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+ * the caller is not allowed to query for the <code>packageName</code>.
+ *
* @param packageName The name of the package to inspect.
*
* @return A fully-qualified {@link Intent} that can be used to launch the
* main activity in the package. Returns <code>null</code> if the package
* does not contain such an activity, or if <em>packageName</em> is not
* recognized.
+ *
+ * @see #getLaunchIntentSenderForPackage(String)
*/
public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
@@ -4510,6 +4750,28 @@ public abstract class PackageManager {
public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
/**
+ * Returns an {@link IntentSender} that can be used to launch a front-door activity in a
+ * package. This is used, for example, to implement an "open" button when browsing through
+ * packages. The current implementation is the same with
+ * {@link #getLaunchIntentForPackage(String)}. Instead of returning the {@link Intent}, it
+ * returns the {@link IntentSender} which is not restricted by the package visibility.
+ *
+ * <p>The caller can invoke
+ * {@link IntentSender#sendIntent(Context, int, Intent, IntentSender.OnFinished, Handler)}
+ * to launch the activity. An {@link IntentSender.SendIntentException} is thrown if the
+ * package does not contain such an activity, or if <em>packageName</em> is not recognized.
+ *
+ * @param packageName The name of the package to inspect.
+ * @return Returns a {@link IntentSender} to launch the activity.
+ *
+ * @see #getLaunchIntentForPackage(String)
+ */
+ public @NonNull IntentSender getLaunchIntentSenderForPackage(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getLaunchIntentSenderForPackage not implemented"
+ + "in subclass");
+ }
+
+ /**
* Return an array of all of the POSIX secondary group IDs that have been
* assigned to the given package.
* <p>
@@ -6798,7 +7060,7 @@ public abstract class PackageManager {
@NonNull
public Resources getResourcesForApplication(@NonNull ApplicationInfo app, @Nullable
Configuration configuration) throws NameNotFoundException {
- throw new UnsupportedOperationException();
+ return getResourcesForApplication(app);
}
/**
@@ -7626,6 +7888,9 @@ public abstract class PackageManager {
* This setting will override any enabled state which may have been set by the component in its
* manifest.
*
+ * <p>Consider using {@link #setComponentEnabledSettings(List)} if multiple components need to
+ * be updated atomically.
+ *
* @param componentName The component to enable
* @param newState The new enabled state for the component.
* @param flags Optional behavior flags.
@@ -7636,6 +7901,32 @@ public abstract class PackageManager {
@EnabledState int newState, @EnabledFlags int flags);
/**
+ * Set the enabled settings for package components such as activities, receivers, services and
+ * providers. This setting will override any enabled state which may have been set by the
+ * component in its manifest.
+ *
+ * <p>This api accepts a list of component changes, and applies them all atomically. The
+ * application can use this api if components have dependencies and need to be updated
+ * atomically.
+ *
+ * <p>The permission is not required if target components are running under the same uid with
+ * the caller.
+ *
+ * @param settings The list of component enabled settings to update. Note that an
+ * {@link IllegalArgumentException} is thrown if the duplicated component name
+ * is in the list or there's a conflict {@link #DONT_KILL_APP} flag between
+ * different components in the same package.
+ *
+ * @see #setComponentEnabledSetting(ComponentName, int, int)
+ */
+ @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE,
+ conditional = true)
+ public void setComponentEnabledSettings(@NonNull List<ComponentEnabledSetting> settings) {
+ throw new UnsupportedOperationException("setComponentEnabledSettings not implemented"
+ + "in subclass");
+ }
+
+ /**
* Return the enabled setting for a package component (activity,
* receiver, service, provider). This returns the last value set by
* {@link #setComponentEnabledSetting(ComponentName, int, int)}; in most
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ff26242dab2..29ce3971e83a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -55,7 +55,10 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.overlay.OverlayPaths;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.split.SplitAssetLoader;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
@@ -884,7 +887,11 @@ public class PackageParser {
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
if (p.mSigningDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
- pi.signingInfo = new SigningInfo(p.mSigningDetails);
+ pi.signingInfo = new SigningInfo(
+ new android.content.pm.SigningDetails(p.mSigningDetails.signatures,
+ p.mSigningDetails.signatureSchemeVersion,
+ p.mSigningDetails.publicKeys,
+ p.mSigningDetails.pastSigningCertificates));
} else {
pi.signingInfo = null;
}
@@ -1399,24 +1406,34 @@ public class PackageParser {
// must use v2 signing scheme
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
- SigningDetails verified;
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<android.content.pm.SigningDetails> result;
if (skipVerify) {
// systemDir APKs are already trusted, save time by not verifying; since the signature
// is not verified and some system apps can have their V2+ signatures stripped allow
// pulling the certs from the jar signature.
- verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- apkPath, SigningDetails.SignatureSchemeVersion.JAR);
+ result = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+ input, apkPath, SigningDetails.SignatureSchemeVersion.JAR);
} else {
- verified = ApkSignatureVerifier.verify(apkPath, minSignatureScheme);
+ result = ApkSignatureVerifier.verify(input, apkPath, minSignatureScheme);
+ }
+ if (result.isError()) {
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
+ result.getException());
}
// Verify that entries are signed consistently with the first pkg
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
+ final android.content.pm.SigningDetails verified = result.getResult();
if (pkg.mSigningDetails == SigningDetails.UNKNOWN) {
- pkg.mSigningDetails = verified;
+ pkg.mSigningDetails = new SigningDetails(verified.getSignatures(),
+ verified.getSignatureSchemeVersion(),
+ verified.getPublicKeys(),
+ verified.getPastSigningCertificates());
} else {
- if (!Signature.areExactMatch(pkg.mSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(pkg.mSigningDetails.signatures,
+ verified.getSignatures())) {
throw new PackageParserException(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
apkPath + " has mismatched certificates");
@@ -7410,7 +7427,7 @@ public class PackageParser {
mCompileSdkVersionCodename = dest.readString();
mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
- mKeySetMapping = readKeySetMapping(dest);
+ mKeySetMapping = ParsingPackageUtils.readKeySetMapping(dest);
cpuAbiOverride = dest.readString();
use32bitAbi = (dest.readInt() == 1);
@@ -7536,73 +7553,13 @@ public class PackageParser {
dest.writeInt(mCompileSdkVersion);
dest.writeString(mCompileSdkVersionCodename);
dest.writeArraySet(mUpgradeKeySets);
- writeKeySetMapping(dest, mKeySetMapping);
+ ParsingPackageUtils.writeKeySetMapping(dest, mKeySetMapping);
dest.writeString(cpuAbiOverride);
dest.writeInt(use32bitAbi ? 1 : 0);
dest.writeByteArray(restrictUpdateHash);
dest.writeInt(visibleToInstantApps ? 1 : 0);
}
- /**
- * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
- */
- private static void writeKeySetMapping(
- Parcel dest, ArrayMap<String, ArraySet<PublicKey>> keySetMapping) {
- if (keySetMapping == null) {
- dest.writeInt(-1);
- return;
- }
-
- final int N = keySetMapping.size();
- dest.writeInt(N);
-
- for (int i = 0; i < N; i++) {
- dest.writeString(keySetMapping.keyAt(i));
- ArraySet<PublicKey> keys = keySetMapping.valueAt(i);
- if (keys == null) {
- dest.writeInt(-1);
- continue;
- }
-
- final int M = keys.size();
- dest.writeInt(M);
- for (int j = 0; j < M; j++) {
- dest.writeSerializable(keys.valueAt(j));
- }
- }
- }
-
- /**
- * Reads a keyset mapping from the given parcel at the given data position. May return
- * {@code null} if the serialized mapping was {@code null}.
- */
- private static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(Parcel in) {
- final int N = in.readInt();
- if (N == -1) {
- return null;
- }
-
- ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
- for (int i = 0; i < N; ++i) {
- String key = in.readString();
- final int M = in.readInt();
- if (M == -1) {
- keySetMapping.put(key, null);
- continue;
- }
-
- ArraySet<PublicKey> keys = new ArraySet<>(M);
- for (int j = 0; j < M; ++j) {
- PublicKey pk = (PublicKey) in.readSerializable();
- keys.add(pk);
- }
-
- keySetMapping.put(key, keys);
- }
-
- return keySetMapping;
- }
-
public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Package>() {
public Package createFromParcel(Parcel in) {
return new Package(in);
@@ -8686,6 +8643,22 @@ public class PackageParser {
// clean and move all the legacy code to one place.
/**
+ * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+ * split APKs.
+ *
+ * @hide
+ * @deprecated Do not use. New changes should use
+ * {@link android.content.pm.split.SplitAssetLoader} instead.
+ */
+ @Deprecated
+ private interface SplitAssetLoader extends AutoCloseable {
+ AssetManager getBaseAssetManager() throws PackageParserException;
+ AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException;
+
+ ApkAssets getBaseApkAssets();
+ }
+
+ /**
* A helper class that implements the dependency tree traversal for splits. Callbacks
* are implemented by subclasses to notify whether a split has already been constructed
* and is cached, and to actually create the split requested.
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 691c69c2459a..a5d97f958ea5 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -211,6 +211,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
* Additional flag for {@link #protectionLevel}, corresponding to the
* {@code documenter} value of {@link android.R.attr#protectionLevel}.
*
+ * @deprecated this protectionLevel is obsolete. Permissions previously granted
+ * through this protectionLevel have been migrated to use <code>role</code> instead
* @hide
*/
@SystemApi
@@ -309,7 +311,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
PROTECTION_FLAG_OEM,
PROTECTION_FLAG_VENDOR_PRIVILEGED,
PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
- PROTECTION_FLAG_DOCUMENTER,
PROTECTION_FLAG_CONFIGURATOR,
PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
PROTECTION_FLAG_APP_PREDICTOR,
@@ -561,9 +562,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
protLevel.append("|textClassifier");
}
- if ((level & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0) {
- protLevel.append("|documenter");
- }
if ((level & PROTECTION_FLAG_CONFIGURATOR) != 0) {
protLevel.append("|configurator");
}
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index bce4b872b8a6..3f5c5d21428e 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -256,7 +256,7 @@ public class Signature implements Parcelable {
try {
if (obj != null) {
Signature other = (Signature)obj;
- // Note, some classes, such as PackageParser.SigningDetails, rely on equals
+ // Note, some classes, such as SigningDetails, rely on equals
// only comparing the mSignature arrays without the flags.
return this == other || Arrays.equals(mSignature, other.mSignature);
}
diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java
new file mode 100644
index 000000000000..584a058aaede
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.java
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+
+import libcore.util.HexEncoding;
+
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A container for signing-related data of an application package.
+ *
+ * @hide
+ */
+@DataClass(genConstructor = false, genConstDefs = false, genParcelable = true, genAidl = false)
+public final class SigningDetails implements Parcelable {
+
+ private static final String TAG = "SigningDetails";
+
+ @IntDef({SignatureSchemeVersion.UNKNOWN,
+ SignatureSchemeVersion.JAR,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2,
+ SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SignatureSchemeVersion.SIGNING_BLOCK_V4})
+ public @interface SignatureSchemeVersion {
+ int UNKNOWN = 0;
+ int JAR = 1;
+ int SIGNING_BLOCK_V2 = 2;
+ int SIGNING_BLOCK_V3 = 3;
+ int SIGNING_BLOCK_V4 = 4;
+ }
+
+ /** The signing certificates associated with this application package. */
+ private final @Nullable Signature[] mSignatures;
+
+ /** The signature scheme version for this application package. */
+ private final @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+ /** The public keys set for the certificates. */
+ private final @Nullable ArraySet<PublicKey> mPublicKeys;
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ private final @Nullable Signature[] mPastSigningCertificates;
+
+ /** special value used to see if cert is in package - not exposed to callers */
+ private static final int PAST_CERT_EXISTS = 0;
+
+ @IntDef(flag = true,
+ value = {CertCapabilities.INSTALLED_DATA,
+ CertCapabilities.SHARED_USER_ID,
+ CertCapabilities.PERMISSION,
+ CertCapabilities.ROLLBACK})
+ public @interface CertCapabilities {
+
+ /** accept data from already installed pkg with this cert */
+ int INSTALLED_DATA = 1;
+
+ /** accept sharedUserId with pkg with this cert */
+ int SHARED_USER_ID = 2;
+
+ /** grant SIGNATURE permissions to pkgs with this cert */
+ int PERMISSION = 4;
+
+ /** allow pkg to update to one signed by this certificate */
+ int ROLLBACK = 8;
+
+ /** allow pkg to continue to have auth access gated by this cert */
+ int AUTH = 16;
+ }
+
+ /** A representation of unknown signing details. Use instead of null. */
+ public static final SigningDetails UNKNOWN = new SigningDetails(/* signatures */ null,
+ SignatureSchemeVersion.UNKNOWN, /* keys */ null, /* pastSigningCertificates */ null);
+
+ @VisibleForTesting
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable ArraySet<PublicKey> keys, @Nullable Signature[] pastSigningCertificates) {
+ mSignatures = signatures;
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ mPublicKeys = keys;
+ mPastSigningCertificates = pastSigningCertificates;
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ @Nullable Signature[] pastSigningCertificates)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates);
+ }
+
+ public SigningDetails(@Nullable Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, /* pastSigningCertificates */ null);
+ }
+
+ public SigningDetails(@Nullable SigningDetails orig) {
+ if (orig != null) {
+ if (orig.mSignatures != null) {
+ mSignatures = orig.mSignatures.clone();
+ } else {
+ mSignatures = null;
+ }
+ mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+ mPublicKeys = new ArraySet<>(orig.mPublicKeys);
+ if (orig.mPastSigningCertificates != null) {
+ mPastSigningCertificates = orig.mPastSigningCertificates.clone();
+ } else {
+ mPastSigningCertificates = null;
+ }
+ } else {
+ mSignatures = null;
+ mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ mPublicKeys = null;
+ mPastSigningCertificates = null;
+ }
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage in the provided {@code
+ * otherSigningDetails} when one has the same or an ancestor signer of the other.
+ *
+ * <p>Merging two signing lineages will result in a new {@code SigningDetails} instance
+ * containing the longest common lineage with the most restrictive capabilities. If the two
+ * lineages contain the same signers with the same capabilities then the instance on which
+ * this was invoked is returned without any changes. Similarly if neither instance has a
+ * lineage, or if neither has the same or an ancestor signer then this instance is returned.
+ *
+ * Following are some example results of this method for lineages with signers A, B, C, D:
+ * - lineage B merged with lineage A -> B returns lineage A -> B.
+ * - lineage A -> B merged with lineage B -> C returns lineage A -> B -> C
+ * - lineage A -> B with the {@code PERMISSION} capability revoked for A merged with
+ * lineage A -> B with the {@code SHARED_USER_ID} capability revoked for A returns
+ * lineage A -> B with both capabilities revoked for A.
+ * - lineage A -> B -> C merged with lineage A -> B -> D would return the original lineage
+ * A -> B -> C since the current signer of both instances is not the same or in the
+ * lineage of the other.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance when one has the same or an ancestor signer
+ * of the other. If neither instance has a lineage, or if neither has the same or an
+ * ancestor signer then this instance is returned.
+ */
+ public @NonNull SigningDetails mergeLineageWith(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ return otherSigningDetails.hasPastSigningCertificates()
+ && otherSigningDetails.hasAncestorOrSelf(this) ? otherSigningDetails : this;
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return this;
+ }
+ // Use the utility method to determine which SigningDetails instance is the descendant
+ // and to confirm that the signing lineage does not diverge.
+ SigningDetails descendantSigningDetails = getDescendantOrSelf(otherSigningDetails);
+ if (descendantSigningDetails == null) {
+ return this;
+ }
+ return descendantSigningDetails == this ? mergeLineageWithAncestorOrSelf(
+ otherSigningDetails) : otherSigningDetails.mergeLineageWithAncestorOrSelf(this);
+ }
+
+ /**
+ * Merges the signing lineage of this instance with the lineage of the ancestor (or same)
+ * signer in the provided {@code otherSigningDetails}.
+ *
+ * @param otherSigningDetails The {@code SigningDetails} you would like to merge with.
+ * @return Merged {@code SigningDetails} instance.
+ */
+ private @NonNull SigningDetails mergeLineageWithAncestorOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ // This method should only be called with instances that contain lineages.
+ int index = mPastSigningCertificates.length - 1;
+ int otherIndex = otherSigningDetails.mPastSigningCertificates.length - 1;
+ if (index < 0 || otherIndex < 0) {
+ return this;
+ }
+
+ List<Signature> mergedSignatures = new ArrayList<>();
+ boolean capabilitiesModified = false;
+ // If this is a descendant lineage then add all of the descendant signer(s) to the
+ // merged lineage until the ancestor signer is reached.
+ while (index >= 0 && !mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex])) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+ // If the signing lineage was exhausted then the provided ancestor is not actually an
+ // ancestor of this lineage.
+ if (index < 0) {
+ return this;
+ }
+
+ do {
+ // Add the common signer to the merged lineage with the most restrictive
+ // capabilities of the two lineages.
+ Signature signature = mPastSigningCertificates[index--];
+ Signature ancestorSignature =
+ otherSigningDetails.mPastSigningCertificates[otherIndex--];
+ Signature mergedSignature = new Signature(signature);
+ int mergedCapabilities = signature.getFlags() & ancestorSignature.getFlags();
+ if (signature.getFlags() != mergedCapabilities) {
+ capabilitiesModified = true;
+ mergedSignature.setFlags(mergedCapabilities);
+ }
+ mergedSignatures.add(mergedSignature);
+ } while (index >= 0 && otherIndex >= 0 && mPastSigningCertificates[index].equals(
+ otherSigningDetails.mPastSigningCertificates[otherIndex]));
+
+ // If both lineages still have elements then their lineages have diverged; since this is
+ // not supported return the invoking instance.
+ if (index >= 0 && otherIndex >= 0) {
+ return this;
+ }
+
+ // Add any remaining elements from either lineage that is not yet exhausted to the
+ // the merged lineage.
+ while (otherIndex >= 0) {
+ mergedSignatures.add(new Signature(
+ otherSigningDetails.mPastSigningCertificates[otherIndex--]));
+ }
+ while (index >= 0) {
+ mergedSignatures.add(new Signature(mPastSigningCertificates[index--]));
+ }
+
+ // if this lineage already contains all the elements in the ancestor and none of the
+ // capabilities were changed then just return this instance.
+ if (mergedSignatures.size() == mPastSigningCertificates.length
+ && !capabilitiesModified) {
+ return this;
+ }
+ // Since the signatures were added to the merged lineage from newest to oldest reverse
+ // the list to ensure the oldest signer is at index 0.
+ Collections.reverse(mergedSignatures);
+ try {
+ return new SigningDetails(new Signature[]{new Signature(mSignatures[0])},
+ mSignatureSchemeVersion, mergedSignatures.toArray(new Signature[0]));
+ } catch (CertificateException e) {
+ Slog.e(TAG, "Caught an exception creating the merged lineage: ", e);
+ return this;
+ }
+ }
+
+ /**
+ * Returns whether this and the provided {@code otherSigningDetails} share a common
+ * ancestor.
+ *
+ * <p>The two SigningDetails have a common ancestor if any of the following conditions are
+ * met:
+ * - If neither has a lineage and their current signer(s) are equal.
+ * - If only one has a lineage and the signer of the other is the same or in the lineage.
+ * - If both have a lineage and their current signers are the same or one is in the lineage
+ * of the other, and their lineages do not diverge to different signers.
+ */
+ public boolean hasCommonAncestor(@NonNull SigningDetails otherSigningDetails) {
+ if (!hasPastSigningCertificates()) {
+ // If this instance does not have a lineage then it must either be in the ancestry
+ // of or the same signer of the otherSigningDetails.
+ return otherSigningDetails.hasAncestorOrSelf(this);
+ }
+ if (!otherSigningDetails.hasPastSigningCertificates()) {
+ return hasAncestorOrSelf(otherSigningDetails);
+ }
+ // If both have a lineage then use getDescendantOrSelf to obtain the descendant signing
+ // details; a null return from that method indicates there is no common lineage between
+ // the two or that they diverge at a point in the lineage.
+ return getDescendantOrSelf(otherSigningDetails) != null;
+ }
+
+ /**
+ * 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(@Nullable 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 (mSignatures.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() < mSignatures.length) {
+ return false;
+ }
+ for (Signature signature : mSignatures) {
+ String signatureDigest = PackageUtils.computeSha256Digest(
+ signature.toByteArray());
+ if (!certDigests.contains(signatureDigest)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String signatureDigest = PackageUtils.computeSha256Digest(mSignatures[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 < mPastSigningCertificates.length - 1; i++) {
+ signatureDigest = PackageUtils.computeSha256Digest(
+ mPastSigningCertificates[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.
+ *
+ * <p>If this instance and the provided {@code otherSigningDetails} do not share an
+ * ancestry, or if their lineages diverge then null is returned to indicate there is no
+ * valid descendant SigningDetails.
+ */
+ private @Nullable SigningDetails getDescendantOrSelf(
+ @NonNull SigningDetails otherSigningDetails) {
+ final SigningDetails descendantSigningDetails;
+ final SigningDetails ancestorSigningDetails;
+ if (hasAncestorOrSelf(otherSigningDetails)) {
+ // If the otherSigningDetails has the same signer or a signer in the lineage of this
+ // instance then treat this instance as the descendant.
+ descendantSigningDetails = this;
+ ancestorSigningDetails = otherSigningDetails;
+ } else if (otherSigningDetails.hasAncestor(this)) {
+ // The above check confirmed that the two instances do not have the same signer and
+ // the signer of otherSigningDetails is not in this instance's lineage; if this
+ // signer is in the otherSigningDetails lineage then treat this as the ancestor.
+ descendantSigningDetails = otherSigningDetails;
+ ancestorSigningDetails = this;
+ } else {
+ // The signers are not the same and neither has the current signer of the other in
+ // its lineage; return null to indicate there is no descendant signer.
+ return null;
+ }
+ // Once the descent (or same) signer is identified iterate through the ancestry until
+ // the current signer of the ancestor is found.
+ int descendantIndex = descendantSigningDetails.mPastSigningCertificates.length - 1;
+ int ancestorIndex = ancestorSigningDetails.mPastSigningCertificates.length - 1;
+ while (descendantIndex >= 0
+ && !descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex])) {
+ descendantIndex--;
+ }
+ // Since the ancestry was verified above the descendant lineage should never be
+ // exhausted, but if for some reason the ancestor signer is not found then return null.
+ if (descendantIndex < 0) {
+ return null;
+ }
+ // Once the common ancestor (or same) signer is found iterate over the lineage of both
+ // to ensure that they are either the same or one is a subset of the other.
+ do {
+ descendantIndex--;
+ ancestorIndex--;
+ } while (descendantIndex >= 0 && ancestorIndex >= 0
+ && descendantSigningDetails.mPastSigningCertificates[descendantIndex].equals(
+ ancestorSigningDetails.mPastSigningCertificates[ancestorIndex]));
+
+ // If both lineages still have elements then they diverge and cannot be considered a
+ // valid common lineage.
+ if (descendantIndex >= 0 && ancestorIndex >= 0) {
+ return null;
+ }
+ // Since one or both of the lineages was exhausted they are either the same or one is a
+ // subset of the other; return the valid descendant.
+ return descendantSigningDetails;
+ }
+
+ /** Returns true if the signing details have one or more signatures. */
+ public boolean hasSignatures() {
+ return mSignatures != null && mSignatures.length > 0;
+ }
+
+ /** Returns true if the signing details have past signing certificates. */
+ public boolean hasPastSigningCertificates() {
+ return mPastSigningCertificates != null && mPastSigningCertificates.length > 0;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+ * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+ * then that means it has authorized a signing certificate rotation, which eventually leads
+ * to our certificate, and thus can be trusted. If this method evaluates to true, this
+ * SigningDetails object should be trusted if the previous one is.
+ */
+ public boolean hasAncestorOrSelf(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we just compare current
+ // signers for an exact match
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates
+ return hasCertificate(oldDetails.mSignatures[0]);
+ }
+ }
+
+ /**
+ * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+ * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+ * determine if this object is newer than the provided one.
+ */
+ public boolean hasAncestor(@NonNull SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // the last entry in pastSigningCertificates is the current signer, ignore it
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(oldDetails.mSignatures[0])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether this {@code SigningDetails} has a signer in common with the provided
+ * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+ * signer.
+ *
+ * <p>Note this method allows for the signing lineage to diverge, so this should only be
+ * used for instances where the only requirement is a common signer in the lineage with
+ * the specified capabilities. If the current signer of this instance is an ancestor of
+ * {@code otherDetails} then {@code true} is immediately returned since the current signer
+ * has all capabilities granted.
+ */
+ public boolean hasCommonSignerWithCapability(@NonNull SigningDetails otherDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || otherDetails == UNKNOWN) {
+ return false;
+ }
+ // If either is signed with more than one signer then both must be signed by the same
+ // signers to consider the capabilities granted.
+ if (mSignatures.length > 1 || otherDetails.mSignatures.length > 1) {
+ return signaturesMatchExactly(otherDetails);
+ }
+ // The Signature class does not use the granted capabilities in the hashCode
+ // computation, so a Set can be used to check for a common signer.
+ Set<Signature> otherSignatures = new ArraySet<>();
+ if (otherDetails.hasPastSigningCertificates()) {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mPastSigningCertificates));
+ } else {
+ otherSignatures.addAll(Arrays.asList(otherDetails.mSignatures));
+ }
+ // If the current signer of this instance is an ancestor of the other than return true
+ // since all capabilities are granted to the current signer.
+ if (otherSignatures.contains(mSignatures[0])) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // Since the current signer was checked above and the last signature in the
+ // pastSigningCertificates is the current signer skip checking the last element.
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (otherSignatures.contains(mPastSigningCertificates[i])) {
+ // If the caller specified multiple capabilities ensure all are set.
+ if ((mPastSigningCertificates[i].getFlags() & flags) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+ * not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ */
+ public boolean checkCapability(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.mSignatures.length > 1) {
+ // multiple-signer packages cannot rotate signing certs, so we must have an exact
+ // match, which also means all capabilities are granted
+ return signaturesMatchExactly(oldDetails);
+ } else {
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates, and if we grant it the capability it's
+ // requesting
+ return hasCertificate(oldDetails.mSignatures[0], flags);
+ }
+ }
+
+ /**
+ * A special case of {@code checkCapability} which re-encodes both sets of signing
+ * certificates to counteract a previous re-encoding.
+ */
+ public boolean checkCapabilityRecover(@NonNull SigningDetails oldDetails,
+ @CertCapabilities int flags) throws CertificateException {
+ if (oldDetails == UNKNOWN || this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.mSignatures.length == 1) {
+ // signing certificates may have rotated, check entire history for effective match
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (Signature.areEffectiveMatch(
+ oldDetails.mSignatures[0],
+ mPastSigningCertificates[i])
+ && mPastSigningCertificates[i].getFlags() == flags) {
+ return true;
+ }
+ }
+ } else {
+ return Signature.areEffectiveMatch(oldDetails.mSignatures, mSignatures);
+ }
+ return false;
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer. Automatically returns false if this object has multiple
+ * signing certificates, since rotation is only supported for single-signers; this is
+ * enforced by {@code hasCertificateInternal}.
+ */
+ public boolean hasCertificate(@NonNull Signature signature) {
+ return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer, and whether or not it has the given permission.
+ * Certificates which match our current signer automatically get all capabilities.
+ * Automatically returns false if this object has multiple signing certificates, since
+ * rotation is only supported for single-signers.
+ */
+ public boolean hasCertificate(@NonNull Signature signature, @CertCapabilities int flags) {
+ return hasCertificateInternal(signature, flags);
+ }
+
+ /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+ public boolean hasCertificate(byte[] certificate) {
+ Signature signature = new Signature(certificate);
+ return hasCertificate(signature);
+ }
+
+ private boolean hasCertificateInternal(@NonNull Signature signature, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // only single-signed apps can have pastSigningCertificates
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the current one, which automatically gets all
+ // capabilities, since it is the same as the current signature
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ if (mPastSigningCertificates[i].equals(signature)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer and make sure
+ // we are singly-signed
+ return mSignatures.length == 1 && mSignatures[0].equals(signature);
+ }
+
+ /**
+ * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+ * or not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ *
+ * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+ * app with multiple signers, this represents the hex-encoded sha256
+ * digest of the combined hex-encoded sha256 digests of each individual
+ * signing certificate according to {@link
+ * PackageUtils#computeSignaturesSha256Digest(Signature[])}
+ */
+ public boolean checkCapability(@Nullable String sha256String, @CertCapabilities int flags) {
+ if (this == UNKNOWN || TextUtils.isEmpty(sha256String)) {
+ return false;
+ }
+
+ // first see if the hash represents a single-signer in our signing history
+ final byte[] sha256Bytes = HexEncoding.decode(sha256String, false /* allowSingleChar */);
+ if (hasSha256Certificate(sha256Bytes, flags)) {
+ return true;
+ }
+
+ // Not in signing history, either represents multiple signatures or not a match.
+ // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+ // We already check the single-signer case above as part of hasSha256Certificate, so no
+ // need to verify we have multiple signers, just run the old check
+ // just consider current signing certs
+ final String[] mSignaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(mSignatures);
+ final String mSignaturesSha256Digest =
+ PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+ return mSignaturesSha256Digest.equals(sha256String);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+ * history, including the current signer. Automatically returns false if this object has
+ * multiple signing certificates, since rotation is only supported for single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate) {
+ return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+ * certificate in this SigningDetails' signing certificate history, including the current
+ * signer, and whether or not it has the given permission. Certificates which match our
+ * current signer automatically get all capabilities. Automatically returns false if this
+ * object has multiple signing certificates, since rotation is only supported for
+ * single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+ return hasSha256CertificateInternal(sha256Certificate, flags);
+ }
+
+ private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates()) {
+ // check all past certs, except for the last one, which automatically gets all
+ // capabilities, since it is the same as the current signature, and is checked below
+ for (int i = 0; i < mPastSigningCertificates.length - 1; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ mPastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & mPastSigningCertificates[i].getFlags()) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer
+ if (mSignatures.length == 1) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(mSignatures[0].toByteArray());
+ return Arrays.equals(sha256Certificate, digest);
+ }
+ return false;
+ }
+
+ /** Returns true if the signatures in this and other match exactly. */
+ public boolean signaturesMatchExactly(@NonNull SigningDetails other) {
+ return Signature.areExactMatch(mSignatures, other.mSignatures);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ boolean isUnknown = UNKNOWN == this;
+ dest.writeBoolean(isUnknown);
+ if (isUnknown) {
+ return;
+ }
+ dest.writeTypedArray(mSignatures, flags);
+ dest.writeInt(mSignatureSchemeVersion);
+ dest.writeArraySet(mPublicKeys);
+ dest.writeTypedArray(mPastSigningCertificates, flags);
+ }
+
+ protected SigningDetails(@NonNull Parcel in) {
+ final ClassLoader boot = Object.class.getClassLoader();
+ mSignatures = in.createTypedArray(Signature.CREATOR);
+ mSignatureSchemeVersion = in.readInt();
+ mPublicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ mPastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ }
+
+ public static final @NonNull Parcelable.Creator<SigningDetails> CREATOR =
+ new Creator<SigningDetails>() {
+ @Override
+ public SigningDetails createFromParcel(@NonNull Parcel source) {
+ if (source.readBoolean()) {
+ return UNKNOWN;
+ }
+ return new SigningDetails(source);
+ }
+
+ @Override
+ public SigningDetails[] newArray(int size) {
+ return new SigningDetails[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SigningDetails)) return false;
+
+ final SigningDetails that = (SigningDetails) o;
+
+ if (mSignatureSchemeVersion != that.mSignatureSchemeVersion) return false;
+ if (!Signature.areExactMatch(mSignatures, that.mSignatures)) return false;
+ if (mPublicKeys != null) {
+ if (!mPublicKeys.equals((that.mPublicKeys))) {
+ return false;
+ }
+ } else if (that.mPublicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(mPastSigningCertificates, that.mPastSigningCertificates)) {
+ return false;
+ }
+ // The capabilities for the past signing certs must match as well.
+ for (int i = 0; i < mPastSigningCertificates.length; i++) {
+ if (mPastSigningCertificates[i].getFlags()
+ != that.mPastSigningCertificates[i].getFlags()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = +Arrays.hashCode(mSignatures);
+ result = 31 * result + mSignatureSchemeVersion;
+ result = 31 * result + (mPublicKeys != null ? mPublicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(mPastSigningCertificates);
+ return result;
+ }
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private @NonNull Signature[] mSignatures;
+ private @SignatureSchemeVersion int mSignatureSchemeVersion =
+ SignatureSchemeVersion.UNKNOWN;
+ private @Nullable Signature[] mPastSigningCertificates;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public SigningDetails.Builder setSignatures(@NonNull Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public SigningDetails.Builder setSignatureSchemeVersion(
+ @SignatureSchemeVersion int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public SigningDetails.Builder setPastSigningCertificates(
+ @Nullable Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates);
+ }
+ }
+
+ /** Parses the public keys from the set of signatures. */
+ public static ArraySet<PublicKey> toSigningKeys(@NonNull Signature[] signatures)
+ throws CertificateException {
+ final ArraySet<PublicKey> keys = new ArraySet<>(signatures.length);
+ for (int i = 0; i < signatures.length; i++) {
+ keys.add(signatures[i].getPublicKey());
+ }
+ return keys;
+ }
+
+
+
+ // 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/SigningDetails.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * The signing certificates associated with this application package.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getSignatures() {
+ return mSignatures;
+ }
+
+ /**
+ * The signature scheme version for this application package.
+ */
+ @DataClass.Generated.Member
+ public @SignatureSchemeVersion int getSignatureSchemeVersion() {
+ return mSignatureSchemeVersion;
+ }
+
+ /**
+ * The public keys set for the certificates.
+ */
+ @DataClass.Generated.Member
+ public @Nullable ArraySet<PublicKey> getPublicKeys() {
+ return mPublicKeys;
+ }
+
+ /**
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
+ */
+ @DataClass.Generated.Member
+ public @Nullable Signature[] getPastSigningCertificates() {
+ return mPastSigningCertificates;
+ }
+
+ @DataClass.Generated(
+ time = 1616984092921L,
+ codegenVersion = "1.0.22",
+ sourceFile = "frameworks/base/core/java/android/content/pm/SigningDetails.java",
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mSignatures\nprivate final @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate final @android.annotation.Nullable android.util.ArraySet<java.security.PublicKey> mPublicKeys\nprivate final @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\nprivate static final int PAST_CERT_EXISTS\npublic static final android.content.pm.SigningDetails UNKNOWN\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.SigningDetails> CREATOR\npublic @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWith(android.content.pm.SigningDetails)\nprivate @android.annotation.NonNull android.content.pm.SigningDetails mergeLineageWithAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasCommonAncestor(android.content.pm.SigningDetails)\npublic boolean hasAncestorOrSelfWithDigest(java.util.Set<java.lang.String>)\nprivate @android.annotation.Nullable android.content.pm.SigningDetails getDescendantOrSelf(android.content.pm.SigningDetails)\npublic boolean hasSignatures()\npublic boolean hasPastSigningCertificates()\npublic boolean hasAncestorOrSelf(android.content.pm.SigningDetails)\npublic boolean hasAncestor(android.content.pm.SigningDetails)\npublic boolean hasCommonSignerWithCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapability(android.content.pm.SigningDetails,int)\npublic boolean checkCapabilityRecover(android.content.pm.SigningDetails,int)\npublic boolean hasCertificate(android.content.pm.Signature)\npublic boolean hasCertificate(android.content.pm.Signature,int)\npublic boolean hasCertificate(byte[])\nprivate boolean hasCertificateInternal(android.content.pm.Signature,int)\npublic boolean checkCapability(java.lang.String,int)\npublic boolean hasSha256Certificate(byte[])\npublic boolean hasSha256Certificate(byte[],int)\nprivate boolean hasSha256CertificateInternal(byte[],int)\npublic boolean signaturesMatchExactly(android.content.pm.SigningDetails)\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\npublic @java.lang.Override boolean equals(java.lang.Object)\npublic @java.lang.Override int hashCode()\npublic static android.util.ArraySet<java.security.PublicKey> toSigningKeys(android.content.pm.Signature[])\nclass SigningDetails extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.content.pm.Signature[] mSignatures\nprivate @android.content.pm.SigningDetails.SignatureSchemeVersion int mSignatureSchemeVersion\nprivate @android.annotation.Nullable android.content.pm.Signature[] mPastSigningCertificates\npublic android.content.pm.SigningDetails.Builder setSignatures(android.content.pm.Signature[])\npublic android.content.pm.SigningDetails.Builder setSignatureSchemeVersion(int)\npublic android.content.pm.SigningDetails.Builder setPastSigningCertificates(android.content.pm.Signature[])\nprivate void checkInvariants()\npublic android.content.pm.SigningDetails build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false, genParcelable=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index d14be9c6b734..7459a9029212 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,7 +16,6 @@
package android.content.pm;
-
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,25 +26,25 @@ import android.os.Parcelable;
public final class SigningInfo implements Parcelable {
@NonNull
- private final PackageParser.SigningDetails mSigningDetails;
+ private final SigningDetails mSigningDetails;
public SigningInfo() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
/**
* @hide only packagemanager should be populating this
*/
- public SigningInfo(PackageParser.SigningDetails signingDetails) {
- mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+ public SigningInfo(SigningDetails signingDetails) {
+ mSigningDetails = new SigningDetails(signingDetails);
}
public SigningInfo(SigningInfo orig) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
}
private SigningInfo(Parcel source) {
- mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+ mSigningDetails = SigningDetails.CREATOR.createFromParcel(source);
}
/**
@@ -53,7 +52,8 @@ public final class SigningInfo implements Parcelable {
* their identity is viewed as being the set of all signers, not just any one.
*/
public boolean hasMultipleSigners() {
- return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+ return mSigningDetails.getSignatures() != null
+ && mSigningDetails.getSignatures().length > 1;
}
/**
@@ -65,8 +65,8 @@ public final class SigningInfo implements Parcelable {
* signing history, since it could change to a new signing certificate at any time.
*/
public boolean hasPastSigningCertificates() {
- return mSigningDetails.signatures != null
- && mSigningDetails.pastSigningCertificates != null;
+ return mSigningDetails.getPastSigningCertificates() != null
+ && mSigningDetails.getPastSigningCertificates().length > 0;
}
/**
@@ -93,11 +93,11 @@ public final class SigningInfo implements Parcelable {
} else if (!hasPastSigningCertificates()) {
// this package is only signed by one signer with no history, return it
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
} else {
// this package has provided proof of past signing certificates, include them
- return mSigningDetails.pastSigningCertificates;
+ return mSigningDetails.getPastSigningCertificates();
}
}
@@ -111,7 +111,7 @@ public final class SigningInfo implements Parcelable {
* </note>
*/
public Signature[] getApkContentsSigners() {
- return mSigningDetails.signatures;
+ return mSigningDetails.getSignatures();
}
@Override
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 8f9a0d79f33b..e75aa065d3d3 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -19,9 +19,10 @@ package android.content.pm.dex;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
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.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.JsonReader;
@@ -176,18 +177,16 @@ 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 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, String packageName, long versionCode)
- throws PackageParserException {
- validateDexMetadataFile(dmaPath, packageName, versionCode,
+ public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
+ String packageName, long versionCode) {
+ return validateDexMetadataFile(input, 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 {
+ public static ParseResult validateDexMetadataFile(ParseInput input, String dmaPath,
+ String packageName, long versionCode, boolean requireManifest) {
StrictJarFile jarFile = null;
if (DEBUG) {
@@ -197,11 +196,10 @@ public class DexMetadataHelper {
try {
jarFile = new StrictJarFile(dmaPath, false, false);
- validateDexMetadataManifest(dmaPath, jarFile, packageName, versionCode,
+ return validateDexMetadataManifest(input, dmaPath, jarFile, packageName, versionCode,
requireManifest);
} catch (IOException e) {
- throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
- "Error opening " + dmaPath, e);
+ return input.error(INSTALL_FAILED_BAD_DEX_METADATA, "Error opening " + dmaPath, e);
} finally {
if (jarFile != null) {
try {
@@ -213,21 +211,21 @@ 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 {
+ private static ParseResult validateDexMetadataManifest(ParseInput input, String dmaPath,
+ StrictJarFile jarFile, String packageName, long versionCode, boolean requireManifest)
+ throws IOException {
if (!requireManifest) {
if (DEBUG) {
Log.v(TAG, "validateDexMetadataManifest: " + dmaPath
+ " manifest.json check skipped");
}
- return;
+ return input.success(null);
}
ZipEntry zipEntry = jarFile.findEntry("manifest.json");
if (zipEntry == null) {
- throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
- "Missing manifest.json in " + dmaPath);
+ return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
+ "Missing manifest.json in " + dmaPath);
}
InputStream inputStream = jarFile.getInputStream(zipEntry);
@@ -235,7 +233,7 @@ public class DexMetadataHelper {
try {
reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8"));
} catch (UnsupportedEncodingException e) {
- throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
"Error opening manifest.json in " + dmaPath, e);
}
String jsonPackageName = null;
@@ -255,19 +253,19 @@ public class DexMetadataHelper {
reader.endObject();
if (jsonPackageName == null || jsonVersionCode == -1) {
- throw new PackageParserException(INSTALL_FAILED_BAD_DEX_METADATA,
+ return input.error(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,
+ return input.error(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,
+ return input.error(INSTALL_FAILED_BAD_DEX_METADATA,
"manifest.json in " + dmaPath + " has invalid versionCode: " + jsonVersionCode
+ ", expected: " + versionCode);
}
@@ -276,6 +274,7 @@ public class DexMetadataHelper {
Log.v(TAG, "validateDexMetadataManifest: " + dmaPath + ", " + packageName +
", " + versionCode + ": successful");
}
+ return input.success(null);
}
/**
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index d8ec512d9f1e..024c18c30d37 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -19,7 +19,7 @@ package android.content.pm.parsing;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import com.android.internal.util.DataClass;
@@ -398,10 +398,10 @@ public class ApkLite {
}
@DataClass.Generated(
- time = 1610596637723L,
+ time = 1616985847981L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.PackageParser.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final int mRollbackDataPolicy\npublic long getLongVersionCode()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 154d9234d00c..f727a4869208 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -18,6 +18,8 @@ package android.content.pm.parsing;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+import static android.content.pm.parsing.ParsingPackageUtils.checkRequiredSystemProperties;
+import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static android.content.pm.parsing.ParsingPackageUtils.validateName;
import static android.content.pm.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
import static android.content.pm.parsing.ParsingUtils.DEFAULT_MIN_SDK_VERSION;
@@ -27,7 +29,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
@@ -77,8 +79,6 @@ public class ApkLiteParseUtils {
* This performs validity checking on cluster style packages, such as
* requiring identical package name and version codes, a single base APK,
* and unique split names.
- *
- * @see PackageParser#parsePackage(File, int)
*/
public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
File packageFile, int flags) {
@@ -301,16 +301,15 @@ public class ApkLiteParseUtils {
parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- ParseResult<PackageParser.SigningDetails> result =
- ParsingPackageUtils.getSigningDetails(input,
- apkFile.getAbsolutePath(), skipVerify, false,
- PackageParser.SigningDetails.UNKNOWN,
- DEFAULT_TARGET_SDK_VERSION);
+ final ParseResult<SigningDetails> result =
+ ParsingPackageUtils.getSigningDetails(input, apkFile.getAbsolutePath(),
+ skipVerify, /* isStaticSharedLibrary */ false,
+ SigningDetails.UNKNOWN, DEFAULT_TARGET_SDK_VERSION);
if (result.isError()) {
return input.error(result);
}
@@ -319,7 +318,7 @@ public class ApkLiteParseUtils {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
- signingDetails = PackageParser.SigningDetails.UNKNOWN;
+ signingDetails = SigningDetails.UNKNOWN;
}
return parseApkLite(input, apkPath, parser, signingDetails);
@@ -340,7 +339,7 @@ public class ApkLiteParseUtils {
}
private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
- XmlResourceParser parser, PackageParser.SigningDetails signingDetails)
+ XmlResourceParser parser, SigningDetails signingDetails)
throws IOException, XmlPullParserException {
ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser);
if (result.isError()) {
@@ -506,7 +505,7 @@ public class ApkLiteParseUtils {
}
// Check to see if overlay should be excluded based on system property condition
- if (!PackageParser.checkRequiredSystemProperties(requiredSystemPropertyName,
+ if (!checkRequiredSystemProperties(requiredSystemPropertyName,
requiredSystemPropertyValue)) {
Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and "
+ codePath + ": overlay ignored due to required system property: "
@@ -577,7 +576,7 @@ public class ApkLiteParseUtils {
return null;
}
- final PublicKey publicKey = PackageParser.parsePublicKey(encodedPublicKey);
+ final PublicKey publicKey = parsePublicKey(encodedPublicKey);
if (publicKey == null) {
Slog.i(TAG, "Unable to parse verifier public key for " + packageName);
return null;
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index c9054fd8976d..f2a6a5c9382f 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -32,7 +32,6 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -40,6 +39,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ComponentParseUtils;
@@ -273,16 +273,16 @@ public class PackageInfoWithoutStateUtils {
pi.requestedPermissionsFlags = new int[size];
for (int i = 0; i < size; i++) {
final ParsedUsesPermission usesPermission = usesPermissions.get(i);
- pi.requestedPermissions[i] = usesPermission.name;
+ pi.requestedPermissions[i] = usesPermission.getName();
// The notion of required permissions is deprecated but for compatibility.
pi.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_REQUIRED;
if (grantedPermissions != null
- && grantedPermissions.contains(usesPermission.name)) {
+ && grantedPermissions.contains(usesPermission.getName())) {
pi.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
- if ((usesPermission.usesPermissionFlags
+ if ((usesPermission.getUsesPermissionFlags()
& ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) {
pi.requestedPermissionsFlags[i] |=
PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
@@ -330,26 +330,26 @@ public class PackageInfoWithoutStateUtils {
pi.isApex = true;
}
- PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+ final SigningDetails signingDetails = pkg.getSigningDetails();
// deprecated method of getting signing certificates
if ((flags & PackageManager.GET_SIGNATURES) != 0) {
if (signingDetails.hasPastSigningCertificates()) {
// Package has included signing certificate rotation information. Return the oldest
// cert so that programmatic checks keep working even if unaware of key rotation.
pi.signatures = new Signature[1];
- pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+ pi.signatures[0] = signingDetails.getPastSigningCertificates()[0];
} else if (signingDetails.hasSignatures()) {
// otherwise keep old behavior
- int numberOfSigs = signingDetails.signatures.length;
+ int numberOfSigs = signingDetails.getSignatures().length;
pi.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+ System.arraycopy(signingDetails.getSignatures(), 0, pi.signatures, 0,
numberOfSigs);
}
}
// replacement for GET_SIGNATURES
if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ if (signingDetails != SigningDetails.UNKNOWN) {
// only return a valid SigningInfo if there is signing information to report
pi.signingInfo = new SigningInfo(signingDetails);
} else {
@@ -398,6 +398,13 @@ public class PackageInfoWithoutStateUtils {
assignUserFields(pkg, ai, userId);
}
+ updateApplicationInfo(ai, flags, state);
+
+ return ai;
+ }
+
+ private static void updateApplicationInfo(ApplicationInfo ai, int flags,
+ PackageUserState state) {
if ((flags & PackageManager.GET_META_DATA) == 0) {
ai.metaData = null;
}
@@ -407,7 +414,7 @@ public class PackageInfoWithoutStateUtils {
}
// CompatibilityMode is global state.
- if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) {
+ if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
@@ -439,7 +446,37 @@ public class PackageInfoWithoutStateUtils {
ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]);
ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]);
}
+ }
+
+ @Nullable
+ public static ApplicationInfo generateDelegateApplicationInfo(@Nullable ApplicationInfo ai,
+ @PackageManager.ApplicationInfoFlags int flags, @NonNull PackageUserState state,
+ int userId) {
+ if (ai == null || !checkUseInstalledOrHidden(flags, state, ai)) {
+ return null;
+ }
+ // This is used to return the ResolverActivity or instantAppInstallerActivity;
+ // we will just always make a copy.
+ ai = new ApplicationInfo(ai);
+ ai.initForUser(userId);
+ ai.icon = (ParsingPackageUtils.sUseRoundIcon && ai.roundIconRes != 0) ? ai.roundIconRes
+ : ai.iconRes;
+ updateApplicationInfo(ai, flags, state);
+ return ai;
+ }
+ @Nullable
+ public static ActivityInfo generateDelegateActivityInfo(@Nullable ActivityInfo a,
+ @PackageManager.ComponentInfoFlags int flags, @NonNull PackageUserState state,
+ int userId) {
+ if (a == null || !checkUseInstalledOrHidden(flags, state, a.applicationInfo)) {
+ return null;
+ }
+ // This is used to return the ResolverActivity or instantAppInstallerActivity;
+ // we will just always make a copy.
+ final ActivityInfo ai = new ActivityInfo(a);
+ ai.applicationInfo =
+ generateDelegateApplicationInfo(ai.applicationInfo, flags, state, userId);
return ai;
}
@@ -498,7 +535,7 @@ public class PackageInfoWithoutStateUtils {
ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f);
Float minAspectRatio = a.getMinAspectRatio();
ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f);
- ai.supportsSizeChanges = a.getSupportsSizeChanges();
+ ai.supportsSizeChanges = a.isSupportsSizeChanges();
ai.requestedVrComponent = a.getRequestedVrComponent();
ai.rotationAnimation = a.getRotationAnimation();
ai.colorMode = a.getColorMode();
@@ -713,7 +750,24 @@ public class PackageInfoWithoutStateUtils {
@Nullable
public static Attribution generateAttribution(ParsedAttribution pa) {
if (pa == null) return null;
- return new Attribution(pa.tag, pa.label);
+ return new Attribution(pa.getTag(), pa.getLabel());
+ }
+
+ private static boolean checkUseInstalledOrHidden(int flags, @NonNull PackageUserState state,
+ @Nullable ApplicationInfo appInfo) {
+ // Returns false if the package is hidden system app until installed.
+ if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+ && !state.installed
+ && appInfo != null && appInfo.hiddenUntilInstalled) {
+ return false;
+ }
+
+ // If available for the target user, or trying to match uninstalled packages and it's
+ // a system app.
+ return state.isAvailable(flags)
+ || (appInfo != null && appInfo.isSystemApp()
+ && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+ || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
}
private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index ed68dbf0087c..d6e1ac903c42 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -25,7 +25,7 @@ import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -320,7 +320,7 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setSharedUserLabel(int sharedUserLabel);
- ParsingPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsingPackage setSigningDetails(SigningDetails signingDetails);
ParsingPackage setSplitClassLoaderName(int splitIndex, String classLoaderName);
@@ -360,7 +360,7 @@ public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage setCompileSdkVersion(int compileSdkVersion);
- ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename);
+ ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName);
ParsingPackage setAttributionsAreUserVisible(boolean attributionsAreUserVisible);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 5a7f21040d0a..f0d95d97263a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -32,7 +32,7 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedComponent;
@@ -292,7 +292,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@DataClass.ParcelWith(ForInternedString.class)
protected String volumeUuid;
@Nullable
- private PackageParser.SigningDetails signingDetails;
+ private SigningDetails signingDetails;
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
@@ -553,7 +553,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
setCompileSdkVersion(manifestArray.getInteger(
R.styleable.AndroidManifest_compileSdkVersion, 0));
- setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
+ setCompileSdkVersionCodeName(manifestArray.getNonConfigurationString(
R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
setIsolatedSplitLoading(manifestArray.getBoolean(
@@ -716,13 +716,15 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
// Continue populating legacy data structures to avoid performance
// issues until all that code can be migrated
- this.requestedPermissions = CollectionUtils.add(this.requestedPermissions, permission.name);
+ this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
+ permission.getName());
return this;
}
@Override
public ParsingPackageImpl addImplicitPermission(String permission) {
+ addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
TextUtils.safeIntern(permission));
return this;
@@ -1714,7 +1716,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@Nullable
@Override
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signingDetails;
}
@@ -2276,7 +2278,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
- public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public ParsingPackageImpl setSigningDetails(@Nullable SigningDetails value) {
signingDetails = value;
return this;
}
@@ -2684,8 +2686,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
}
@Override
- public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) {
- this.compileSdkVersionCodeName = compileSdkVersionCodename;
+ public ParsingPackage setCompileSdkVersionCodeName(String compileSdkVersionCodeName) {
+ this.compileSdkVersionCodeName = compileSdkVersionCodeName;
return this;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index a6e189d32b54..d5bd3a93bcbd 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -26,8 +26,8 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -768,7 +768,7 @@ public interface ParsingPackageRead extends Parcelable {
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* @see ApplicationInfo#splitClassLoaderNames
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index dce242c9d87c..80befcdf02de 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -18,8 +18,10 @@ package android.content.pm.parsing;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
@@ -45,10 +47,8 @@ import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ComponentParseUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedActivityUtils;
@@ -74,6 +74,7 @@ import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -89,6 +90,7 @@ import android.os.Bundle;
import android.os.FileUtils;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.ext.SdkExtensions;
@@ -97,6 +99,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.Slog;
@@ -120,7 +123,12 @@ import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.spec.EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -273,31 +281,25 @@ public class ParsingPackageUtils {
manifestArray);
}
});
- try {
- result = parser.parsePackage(input, file, parseFlags);
- if (result.isError()) {
- return result;
- }
- } catch (PackageParser.PackageParserException e) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Error parsing package", e);
+ result = parser.parsePackage(input, file, parseFlags);
+ if (result.isError()) {
+ return input.error(result);
}
- try {
- ParsingPackage pkg = result.getResult();
- if (collectCertificates) {
- pkg.setSigningDetails(
- ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */));
+ final ParsingPackage pkg = result.getResult();
+ if (collectCertificates) {
+ final ParseResult<SigningDetails> ret =
+ ParsingPackageUtils.getSigningDetails(input, pkg, false /*skipVerify*/);
+ if (ret.isError()) {
+ return input.error(ret);
}
+ pkg.setSigningDetails(ret.getResult());
+ }
- // Need to call this to finish the parsing stage
- pkg.hideAsParsed();
+ // Need to call this to finish the parsing stage
+ pkg.hideAsParsed();
- return input.success(pkg);
- } catch (PackageParser.PackageParserException e) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Error collecting package certificates", e);
- }
+ return input.success(pkg);
}
private boolean mOnlyCoreApps;
@@ -327,19 +329,15 @@ public class ParsingPackageUtils {
* requiring identical package name and version codes, a single base APK,
* and unique split names.
* <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+ * Note that this <em>does not</em> perform signature verification; that must
+ * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
*
* If {@code useCaches} is true, the package parser might return a cached
* result from a previous parse of the same {@code packageFile} with the same
* {@code flags}. Note that this method does not check whether {@code packageFile}
* has changed since the last parse, it's up to callers to do so.
- *
- * @see PackageParser#parsePackageLite(File, int)
*/
- public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
- int flags)
- throws PackageParserException {
+ public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
if (packageFile.isDirectory()) {
return parseClusterPackage(input, packageFile, flags);
} else {
@@ -353,8 +351,8 @@ public class ParsingPackageUtils {
* identical package name and version codes, a single base APK, and unique
* split names.
* <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+ * Note that this <em>does not</em> perform signature verification; that must
+ * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
*/
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
int flags) {
@@ -404,15 +402,19 @@ public class ParsingPackageUtils {
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
- parseSplitApk(input, pkg, i, splitAssets, flags);
+ final ParseResult<ParsingPackage> split =
+ parseSplitApk(input, pkg, i, splitAssets, flags);
+ if (split.isError()) {
+ return input.error(split);
+ }
}
}
pkg.setUse32BitAbi(lite.isUse32bitAbi());
return input.success(pkg);
- } catch (PackageParserException e) {
- return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to load assets: " + lite.getBaseApkPath(), e);
+ } catch (IllegalArgumentException e) {
+ return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+ : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
@@ -421,11 +423,11 @@ public class ParsingPackageUtils {
/**
* Parse the given APK file, treating it as as a single monolithic package.
* <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.
+ * Note that this <em>does not</em> perform signature verification; that must
+ * be done separately in {@link #getSigningDetails(ParseInput, ParsingPackageRead, boolean)}.
*/
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
- int flags) throws PackageParserException {
+ int flags) {
final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
if (liteResult.isError()) {
@@ -459,8 +461,7 @@ public class ParsingPackageUtils {
}
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
- String codePath, SplitAssetLoader assetLoader, int flags)
- throws PackageParserException {
+ String codePath, SplitAssetLoader assetLoader, int flags) {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
@@ -471,7 +472,13 @@ public class ParsingPackageUtils {
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
- final AssetManager assets = assetLoader.getBaseAssetManager();
+ final AssetManager assets;
+ try {
+ assets = assetLoader.getBaseAssetManager();
+ } catch (IllegalArgumentException e) {
+ return input.error(e.getCause() instanceof IOException ? INSTALL_FAILED_INVALID_APK
+ : INSTALL_PARSE_FAILED_NOT_APK, e.getMessage(), e);
+ }
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -528,7 +535,12 @@ public class ParsingPackageUtils {
pkg.setVolumeUuid(volumeUuid);
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
- pkg.setSigningDetails(getSigningDetails(pkg, false));
+ final ParseResult<SigningDetails> ret =
+ getSigningDetails(input, pkg, false /*skipVerify*/);
+ if (ret.isError()) {
+ return input.error(ret);
+ }
+ pkg.setSigningDetails(ret.getResult());
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
@@ -634,11 +646,13 @@ public class ParsingPackageUtils {
*/
private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser, int flags, int splitIndex)
- throws XmlPullParserException, IOException, PackageParserException {
- AttributeSet attrs = parser;
-
+ throws XmlPullParserException, IOException {
// We parsed manifest tag earlier; just skip past it
- PackageParser.parsePackageSplitNames(parser, attrs);
+ final ParseResult<Pair<String, String>> packageSplitResult =
+ ApkLiteParseUtils.parsePackageSplitNames(input, parser);
+ if (packageSplitResult.isError()) {
+ return input.error(packageSplitResult);
+ }
int type;
@@ -905,7 +919,7 @@ public class ParsingPackageUtils {
);
}
- convertNewPermissions(pkg);
+ convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
@@ -982,6 +996,11 @@ public class ParsingPackageUtils {
private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
ParsingPackage pkg, TypedArray sa) {
+ int maxSdkVersion = anInteger(0, R.styleable.AndroidManifest_sharedUserMaxSdkVersion, sa);
+ if ((maxSdkVersion != 0) && maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT) {
+ return input.success(pkg);
+ }
+
String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
if (TextUtils.isEmpty(str)) {
return input.success(pkg);
@@ -1059,7 +1078,7 @@ public class ParsingPackageUtils {
+ " must define a public-key value on first use at "
+ parser.getPositionDescription());
} else if (encodedKey != null) {
- PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
+ PublicKey currentKey = parsePublicKey(encodedKey);
if (currentKey == null) {
Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+ parser.getPositionDescription() + " key-set "
@@ -1297,8 +1316,8 @@ public class ParsingPackageUtils {
final int size = usesPermissions.size();
for (int i = 0; i < size; i++) {
final ParsedUsesPermission usesPermission = usesPermissions.get(i);
- if (Objects.equals(usesPermission.name, name)) {
- if (usesPermission.usesPermissionFlags != usesPermissionFlags) {
+ if (Objects.equals(usesPermission.getName(), name)) {
+ if (usesPermission.getUsesPermissionFlags() != usesPermissionFlags) {
return input.error("Conflicting uses-permissions flags: "
+ name + " in package: " + pkg.getPackageName() + " at: "
+ parser.getPositionDescription());
@@ -1603,8 +1622,39 @@ public class ParsingPackageUtils {
}
/**
- * {@link ParseResult} version of
- * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])}
+ * Computes the minSdkVersion to use at runtime. If the package is not
+ * compatible with this platform, populates {@code outError[0]} with an
+ * error message.
+ * <p>
+ * If {@code minCode} is not specified, e.g. the value is {@code null},
+ * then behavior varies based on the {@code platformSdkVersion}:
+ * <ul>
+ * <li>If the platform SDK version is greater than or equal to the
+ * {@code minVers}, returns the {@code mniVers} unmodified.
+ * <li>Otherwise, returns -1 to indicate that the package is not
+ * compatible with this platform.
+ * </ul>
+ * <p>
+ * Otherwise, the behavior varies based on whether the current platform
+ * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+ * has length > 0:
+ * <ul>
+ * <li>If this is a pre-release platform and the value specified by
+ * {@code targetCode} is contained within the array of allowed pre-release
+ * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+ * <li>If this is a released platform, this method will return -1 to
+ * indicate that the package is not compatible with this platform.
+ * </ul>
+ *
+ * @param minVers minSdkVersion number, if specified in the application
+ * manifest, or 1 otherwise
+ * @param minCode minSdkVersion code, if specified in the application
+ * manifest, or {@code null} otherwise
+ * @param platformSdkVersion platform SDK version number, typically
+ * Build.VERSION.SDK_INT
+ * @param platformSdkCodenames array of allowed prerelease SDK codenames
+ * for this platform
+ * @return the minSdkVersion to use at runtime if successful
*/
public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
@Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
@@ -1641,8 +1691,31 @@ public class ParsingPackageUtils {
}
/**
- * {@link ParseResult} version of
- * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])}
+ * Computes the targetSdkVersion to use at runtime. If the package is not
+ * compatible with this platform, populates {@code outError[0]} with an
+ * error message.
+ * <p>
+ * If {@code targetCode} is not specified, e.g. the value is {@code null},
+ * then the {@code targetVers} will be returned unmodified.
+ * <p>
+ * Otherwise, the behavior varies based on whether the current platform
+ * is a pre-release version, e.g. the {@code platformSdkCodenames} array
+ * has length > 0:
+ * <ul>
+ * <li>If this is a pre-release platform and the value specified by
+ * {@code targetCode} is contained within the array of allowed pre-release
+ * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}.
+ * <li>If this is a released platform, this method will return -1 to
+ * indicate that the package is not compatible with this platform.
+ * </ul>
+ *
+ * @param targetVers targetSdkVersion number, if specified in the
+ * application manifest, or 0 otherwise
+ * @param targetCode targetSdkVersion code, if specified in the application
+ * manifest, or {@code null} otherwise
+ * @param platformSdkCodenames array of allowed pre-release SDK codenames
+ * for this platform
+ * @return the targetSdkVersion to use at runtime if successful
*/
public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
@Nullable String targetCode, @NonNull String[] platformSdkCodenames,
@@ -2674,7 +2747,7 @@ public class ParsingPackageUtils {
R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
String propValue = sa.getString(
R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
- if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) {
+ if (!checkRequiredSystemProperties(propName, propValue)) {
String message = "Skipping target and overlay pair " + target + " and "
+ pkg.getBaseApkPath()
+ ": overlay ignored due to required system property: "
@@ -2791,31 +2864,16 @@ public class ParsingPackageUtils {
}
}
- private static void convertNewPermissions(ParsingPackage pkg) {
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- StringBuilder newPermsMsg = null;
- for (int ip = 0; ip < NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+ private static void convertCompatPermissions(ParsingPackage pkg) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
break;
}
- if (!pkg.getRequestedPermissions().contains(npi.name)) {
- if (newPermsMsg == null) {
- newPermsMsg = new StringBuilder(128);
- newPermsMsg.append(pkg.getPackageName());
- newPermsMsg.append(": compat added ");
- } else {
- newPermsMsg.append(' ');
- }
- newPermsMsg.append(npi.name);
- pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
- .addImplicitPermission(npi.name);
+ if (!pkg.getRequestedPermissions().contains(info.getName())) {
+ pkg.addImplicitPermission(info.getName());
}
}
- if (newPermsMsg != null) {
- Slog.i(TAG, newPermsMsg.toString());
- }
}
private void convertSplitPermissions(ParsingPackage pkg) {
@@ -2831,8 +2889,7 @@ public class ParsingPackageUtils {
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!requestedPermissions.contains(perm)) {
- pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
- .addImplicitPermission(perm);
+ pkg.addImplicitPermission(perm);
}
}
}
@@ -2968,6 +3025,114 @@ public class ParsingPackageUtils {
}
/**
+ * @return {@link PublicKey} of a given encoded public key.
+ */
+ public static final PublicKey parsePublicKey(final String encodedPublicKey) {
+ if (encodedPublicKey == null) {
+ Slog.w(TAG, "Could not parse null public key");
+ return null;
+ }
+
+ try {
+ return parsePublicKey(Base64.decode(encodedPublicKey, Base64.DEFAULT));
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
+ return null;
+ }
+ }
+
+ /**
+ * @return {@link PublicKey} of the given byte array of a public key.
+ */
+ public static final PublicKey parsePublicKey(final byte[] publicKey) {
+ if (publicKey == null) {
+ Slog.w(TAG, "Could not parse null public key");
+ return null;
+ }
+
+ final EncodedKeySpec keySpec;
+ try {
+ keySpec = new X509EncodedKeySpec(publicKey);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Could not parse verifier public key; invalid Base64");
+ return null;
+ }
+
+ /* First try the key as an RSA key. */
+ try {
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.wtf(TAG, "Could not parse public key: RSA KeyFactory not included in build");
+ } catch (InvalidKeySpecException e) {
+ // Not a RSA public key.
+ }
+
+ /* Now try it as a ECDSA key. */
+ try {
+ final KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return keyFactory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.wtf(TAG, "Could not parse public key: EC KeyFactory not included in build");
+ } catch (InvalidKeySpecException e) {
+ // Not a ECDSA public key.
+ }
+
+ /* Now try it as a DSA key. */
+ try {
+ final KeyFactory keyFactory = KeyFactory.getInstance("DSA");
+ return keyFactory.generatePublic(keySpec);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.wtf(TAG, "Could not parse public key: DSA KeyFactory not included in build");
+ } catch (InvalidKeySpecException e) {
+ // Not a DSA public key.
+ }
+
+ /* Not a supported key type */
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if both the property name and value are empty or if the given system
+ * property is set to the specified value. Properties can be one or more, and if properties are
+ * more than one, they must be separated by comma, and count of names and values must be equal,
+ * and also every given system property must be set to the corresponding value.
+ * In all other cases, returns {@code false}
+ */
+ public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames,
+ @Nullable String rawPropValues) {
+ if (TextUtils.isEmpty(rawPropNames) || TextUtils.isEmpty(rawPropValues)) {
+ if (!TextUtils.isEmpty(rawPropNames) || !TextUtils.isEmpty(rawPropValues)) {
+ // malformed condition - incomplete
+ Slog.w(TAG, "Disabling overlay - incomplete property :'" + rawPropNames
+ + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+ + " AND requiredSystemPropertyValue to be specified.");
+ return false;
+ }
+ // no valid condition set - so no exclusion criteria, overlay will be included.
+ return true;
+ }
+
+ final String[] propNames = rawPropNames.split(",");
+ final String[] propValues = rawPropValues.split(",");
+
+ if (propNames.length != propValues.length) {
+ Slog.w(TAG, "Disabling overlay - property :'" + rawPropNames
+ + "=" + rawPropValues + "' - require both requiredSystemPropertyName"
+ + " AND requiredSystemPropertyValue lists to have the same size.");
+ return false;
+ }
+ for (int i = 0; i < propNames.length; i++) {
+ // Check property value: make sure it is both set and equal to expected value
+ final String currValue = SystemProperties.get(propNames[i]);
+ if (!TextUtils.equals(currValue, propValues[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Collect certificates from all the APKs described in the given package. Also asserts that
* all APK contents are signed correctly and consistently.
*
@@ -2976,12 +3141,10 @@ public class ParsingPackageUtils {
* construct a dummy ParseInput.
*/
@CheckResult
- public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify)
- throws PackageParserException {
+ public static ParseResult<SigningDetails> getSigningDetails(ParseInput input,
+ ParsingPackageRead pkg, boolean skipVerify) {
SigningDetails signingDetails = SigningDetails.UNKNOWN;
- ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
-
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
ParseResult<SigningDetails> result = getSigningDetails(
@@ -2993,8 +3156,7 @@ public class ParsingPackageUtils {
pkg.getTargetSdkVersion()
);
if (result.isError()) {
- throw new PackageParser.PackageParserException(result.getErrorCode(),
- result.getErrorMessage(), result.getException());
+ return input.error(result);
}
signingDetails = result.getResult();
@@ -3011,15 +3173,11 @@ public class ParsingPackageUtils {
pkg.getTargetSdkVersion()
);
if (result.isError()) {
- throw new PackageParser.PackageParserException(result.getErrorCode(),
- result.getErrorMessage(), result.getException());
+ return input.error(result);
}
-
-
- signingDetails = result.getResult();
}
}
- return signingDetails;
+ return result;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3035,29 +3193,29 @@ public class ParsingPackageUtils {
// must use v2 signing scheme
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
- SigningDetails verified;
- try {
- if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying; since the
- // signature is not verified and some system apps can have their V2+ signatures
- // stripped allow pulling the certs from the jar signature.
- verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- baseCodePath, SigningDetails.SignatureSchemeVersion.JAR);
- } else {
- verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
- }
- } catch (PackageParserException e) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "Failed collecting certificates for " + baseCodePath, e);
+ final ParseResult<SigningDetails> verified;
+ if (skipVerify) {
+ // systemDir APKs are already trusted, save time by not verifying; since the
+ // signature is not verified and some system apps can have their V2+ signatures
+ // stripped allow pulling the certs from the jar signature.
+ verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(input, baseCodePath,
+ SigningDetails.SignatureSchemeVersion.JAR);
+ } else {
+ verified = ApkSignatureVerifier.verify(input, baseCodePath, minSignatureScheme);
+ }
+
+ if (verified.isError()) {
+ return input.error(verified);
}
// Verify that entries are signed consistently with the first pkg
// we encountered. Note that for splits, certificates may have
// already been populated during an earlier parse of a base APK.
if (existingSigningDetails == SigningDetails.UNKNOWN) {
- return input.success(verified);
+ return verified;
} else {
- if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+ if (!Signature.areExactMatch(existingSigningDetails.getSignatures(),
+ verified.getResult().getSignatures())) {
return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
baseCodePath + " has mismatched certificates");
}
@@ -3069,6 +3227,13 @@ public class ParsingPackageUtils {
/**
* @hide
*/
+ public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) {
+ sCompatibilityModeEnabled = compatibilityModeEnabled;
+ }
+
+ /**
+ * @hide
+ */
public static void readConfigUseRoundIcon(Resources r) {
if (r != null) {
sUseRoundIcon = r.getBoolean(com.android.internal.R.bool.config_useRoundIcon);
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
index 07ec6a80c283..289716a95f2c 100644
--- a/core/java/android/content/pm/parsing/ParsingUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -16,9 +16,10 @@
package android.content.pm.parsing;
+import static android.content.pm.parsing.ParsingPackageUtils.RIGID_PARSER;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.PackageParser;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.res.XmlResourceParser;
@@ -62,7 +63,7 @@ public class ParsingUtils {
@NonNull
public static ParseResult unknownTag(String parentTag, ParsingPackage pkg,
XmlResourceParser parser, ParseInput input) throws IOException, XmlPullParserException {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
return input.error("Bad element under " + parentTag + ": " + parser.getName());
}
Slog.w(TAG, "Unknown element under " + parentTag + ": "
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index 0403a25f5ea1..e5d030cc8d6e 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -152,7 +152,7 @@ public class ComponentParseUtils {
@Nullable
public static CharSequence getNonLocalizedLabel(
ParsedComponent component) {
- return component.nonLocalizedLabel;
+ return component.getNonLocalizedLabel();
}
/**
@@ -161,7 +161,7 @@ public class ComponentParseUtils {
* This is a method of the utility class to discourage use.
*/
public static int getIcon(ParsedComponent component) {
- return component.icon;
+ return component.getIcon();
}
public static boolean isMatch(PackageUserState state, boolean isSystem,
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 6f478accedd7..adb6b76473eb 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
@@ -38,8 +39,8 @@ import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
/** @hide **/
public class ParsedActivity extends ParsedMainComponent {
- int theme;
- int uiOptions;
+ private int theme;
+ private int uiOptions;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
@@ -49,22 +50,22 @@ public class ParsedActivity extends ParsedMainComponent {
@DataClass.ParcelWith(ForInternedString.class)
private String parentActivityName;
@Nullable
- String taskAffinity;
- int privateFlags;
+ private String taskAffinity;
+ private int privateFlags;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String permission;
- int launchMode;
- int documentLaunchMode;
- int maxRecents;
- int configChanges;
- int softInputMode;
- int persistableMode;
- int lockTaskLaunchMode;
+ private int launchMode;
+ private int documentLaunchMode;
+ private int maxRecents;
+ private int configChanges;
+ private int softInputMode;
+ private int persistableMode;
+ private int lockTaskLaunchMode;
- int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+ private int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ private int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
@Nullable
private Float maxAspectRatio;
@@ -75,12 +76,12 @@ public class ParsedActivity extends ParsedMainComponent {
private boolean supportsSizeChanges;
@Nullable
- String requestedVrComponent;
- int rotationAnimation = -1;
- int colorMode;
+ private String requestedVrComponent;
+ private int rotationAnimation = -1;
+ private int colorMode;
@Nullable
- ActivityInfo.WindowLayout windowLayout;
+ private ActivityInfo.WindowLayout windowLayout;
public ParsedActivity(ParsedActivity other) {
super(other);
@@ -188,6 +189,35 @@ public class ParsedActivity extends ParsedMainComponent {
// alias.metaData = target.metaData;
}
+ public boolean isSupportsSizeChanges() {
+ return supportsSizeChanges;
+ }
+
+ public ParsedActivity setColorMode(int colorMode) {
+ this.colorMode = colorMode;
+ return this;
+ }
+
+ public ParsedActivity setConfigChanges(int configChanges) {
+ this.configChanges = configChanges;
+ return this;
+ }
+
+ public ParsedActivity setDocumentLaunchMode(int documentLaunchMode) {
+ this.documentLaunchMode = documentLaunchMode;
+ return this;
+ }
+
+ public ParsedActivity setLaunchMode(int launchMode) {
+ this.launchMode = launchMode;
+ return this;
+ }
+
+ public ParsedActivity setLockTaskLaunchMode(int lockTaskLaunchMode) {
+ this.lockTaskLaunchMode = lockTaskLaunchMode;
+ return this;
+ }
+
public ParsedActivity setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
|| resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
@@ -204,6 +234,16 @@ public class ParsedActivity extends ParsedMainComponent {
return this;
}
+ public ParsedActivity setMaxAspectRatio(Float maxAspectRatio) {
+ this.maxAspectRatio = maxAspectRatio;
+ return this;
+ }
+
+ public ParsedActivity setMaxRecents(int maxRecents) {
+ this.maxRecents = maxRecents;
+ return this;
+ }
+
public ParsedActivity setMinAspectRatio(int resizeMode, float minAspectRatio) {
if (resizeMode == RESIZE_MODE_RESIZEABLE
|| resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
@@ -220,13 +260,48 @@ public class ParsedActivity extends ParsedMainComponent {
return this;
}
- public ParsedActivity setSupportsSizeChanges(boolean supportsSizeChanges) {
- this.supportsSizeChanges = supportsSizeChanges;
+ public ParsedActivity setMinAspectRatio(Float minAspectRatio) {
+ this.minAspectRatio = minAspectRatio;
+ return this;
+ }
+
+ public ParsedActivity setParentActivityName(String parentActivityName) {
+ this.parentActivityName = parentActivityName;
return this;
}
- public ParsedActivity setFlags(int flags) {
- this.flags = flags;
+ public ParsedActivity setPersistableMode(int persistableMode) {
+ this.persistableMode = persistableMode;
+ return this;
+ }
+
+ public ParsedActivity setPrivateFlags(int privateFlags) {
+ this.privateFlags = privateFlags;
+ return this;
+ }
+
+ public ParsedActivity setRequestedVrComponent(String requestedVrComponent) {
+ this.requestedVrComponent = requestedVrComponent;
+ return this;
+ }
+
+ public ParsedActivity setRotationAnimation(int rotationAnimation) {
+ this.rotationAnimation = rotationAnimation;
+ return this;
+ }
+
+ public ParsedActivity setScreenOrientation(int screenOrientation) {
+ this.screenOrientation = screenOrientation;
+ return this;
+ }
+
+ public ParsedActivity setSoftInputMode(int softInputMode) {
+ this.softInputMode = softInputMode;
+ return this;
+ }
+
+ public ParsedActivity setSupportsSizeChanges(boolean supportsSizeChanges) {
+ this.supportsSizeChanges = supportsSizeChanges;
return this;
}
@@ -240,17 +315,32 @@ public class ParsedActivity extends ParsedMainComponent {
return this;
}
- public ParsedActivity setParentActivity(String parentActivity) {
- this.parentActivityName = TextUtils.safeIntern(parentActivity);
- return this;
- }
-
public ParsedActivity setPermission(String permission) {
// Empty string must be converted to null
this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
return this;
}
+ public ParsedActivity setTaskAffinity(String taskAffinity) {
+ this.taskAffinity = taskAffinity;
+ return this;
+ }
+
+ public ParsedActivity setTheme(int theme) {
+ this.theme = theme;
+ return this;
+ }
+
+ public ParsedActivity setUiOptions(int uiOptions) {
+ this.uiOptions = uiOptions;
+ return this;
+ }
+
+ public ParsedActivity setWindowLayout(ActivityInfo.WindowLayout windowLayout) {
+ this.windowLayout = windowLayout;
+ return this;
+ }
+
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("Activity{");
@@ -334,6 +424,7 @@ public class ParsedActivity extends ParsedMainComponent {
}
}
+ @NonNull
public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@Override
public ParsedActivity createFromParcel(Parcel source) {
@@ -424,10 +515,6 @@ public class ParsedActivity extends ParsedMainComponent {
return minAspectRatio;
}
- public boolean getSupportsSizeChanges() {
- return supportsSizeChanges;
- }
-
@Nullable
public String getRequestedVrComponent() {
return requestedVrComponent;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 92a90e98cb43..ac6bcd0ca59b 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -120,58 +120,58 @@ public class ParsedActivityUtils {
// Multi-lining them to fit within the column restriction makes it hard to tell what
// field is assigned where.
// @formatter:off
- activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
- activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions());
-
- activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
- | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
- | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
- | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
- | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
- | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
- | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
- | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
- | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
- | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
- | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
- | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
- | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa);
+ activity.setTheme(sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0))
+ .setUiOptions(sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions()));
+
+ activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
+ | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
+ | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
+ | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
+ | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
+ | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
+ | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
+ | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
+ | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
+ | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
+ | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
+ | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
+ | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa)));
if (!receiver) {
- activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
- | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
- | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
- | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
- | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
- | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
- | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
- | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
- | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
- | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa);
-
- activity.privateFlags |= flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
- R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
- | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
- R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa);
-
- activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);
- activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE);
- activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
- activity.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
- activity.maxRecents = sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic());
- activity.persistableMode = sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY);
- activity.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
- activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
- activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
-
- activity.configChanges = getActivityConfigChanges(
- sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
- sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+ activity.setFlags(activity.getFlags() | (flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
+ | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
+ | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
+ | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
+ | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
+ | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
+ | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
+ | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
+ | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
+ | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
+
+ activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
+ R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
+ | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND,
+ R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa)));
+
+ activity.setColorMode(sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT))
+ .setDocumentLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE))
+ .setLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE))
+ .setLockTaskLaunchMode(sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0))
+ .setMaxRecents(sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic()))
+ .setPersistableMode(sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY))
+ .setRequestedVrComponent(sa.getString(R.styleable.AndroidManifestActivity_enableVrMode))
+ .setRotationAnimation(sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED))
+ .setSoftInputMode(sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0))
+ .setConfigChanges(getActivityConfigChanges(
+ sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+ sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0))
+ );
int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
- activity.screenOrientation = screenOrientation;
- activity.resizeMode = resizeMode;
+ activity.setScreenOrientation(screenOrientation)
+ .setResizeMode(resizeMode);
if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
&& sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
@@ -189,9 +189,9 @@ public class ParsedActivityUtils {
0 /*default*/));
}
} else {
- activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- activity.configChanges = 0;
- activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
+ activity.setLaunchMode(ActivityInfo.LAUNCH_MULTIPLE)
+ .setConfigChanges(0)
+ .setFlags(activity.getFlags()|flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa));
}
// @formatter:on
@@ -205,11 +205,11 @@ public class ParsedActivityUtils {
return input.error(affinityNameResult);
}
- activity.taskAffinity = affinityNameResult.getResult();
+ activity.setTaskAffinity(affinityNameResult.getResult());
boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
if (visibleToEphemeral) {
- activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
pkg.setVisibleToInstantApps(true);
}
@@ -320,7 +320,7 @@ public class ParsedActivityUtils {
Log.e(TAG, "Activity " + activity.getName()
+ " specified invalid parentActivityName " + parentActivityName);
} else {
- activity.setParentActivity(parentClassName);
+ activity.setParentActivityName(parentClassName);
}
}
@@ -336,7 +336,7 @@ public class ParsedActivityUtils {
final boolean setExported = array.hasValue(exportedAttr);
if (setExported) {
- activity.exported = array.getBoolean(exportedAttr, false);
+ activity.setExported(array.getBoolean(exportedAttr, false));
}
final int depth = parser.getDepth();
@@ -355,7 +355,7 @@ public class ParsedActivityUtils {
if (intentResult.isSuccess()) {
ParsedIntentInfo intent = intentResult.getResult();
if (intent != null) {
- activity.order = Math.max(intent.getOrder(), activity.order);
+ activity.setOrder(Math.max(intent.getOrder(), activity.getOrder()));
activity.addIntent(intent);
if (LOG_UNSAFE_BROADCASTS && isReceiver
&& pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
@@ -396,7 +396,7 @@ public class ParsedActivityUtils {
ParseResult<ActivityInfo.WindowLayout> layoutResult =
parseActivityWindowLayout(resources, parser, input);
if (layoutResult.isSuccess()) {
- activity.windowLayout = layoutResult.getResult();
+ activity.setWindowLayout(layoutResult.getResult());
}
result = layoutResult;
} else {
@@ -408,13 +408,13 @@ public class ParsedActivityUtils {
}
}
- if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK
- && activity.metaData != null && activity.metaData.containsKey(
+ if (!isAlias && activity.getLaunchMode() != LAUNCH_SINGLE_INSTANCE_PER_TASK
+ && activity.getMetaData() != null && activity.getMetaData().containsKey(
ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
- final String launchMode = activity.metaData.getString(
+ final String launchMode = activity.getMetaData().getString(
ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
- activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;
+ activity.setLaunchMode(LAUNCH_SINGLE_INSTANCE_PER_TASK);
}
}
@@ -423,7 +423,7 @@ public class ParsedActivityUtils {
if (layoutResult.isError()) {
return input.error(layoutResult);
}
- activity.windowLayout = layoutResult.getResult();
+ activity.setWindowLayout(layoutResult.getResult());
if (!setExported) {
boolean hasIntentFilters = activity.getIntents().size() > 0;
@@ -437,7 +437,7 @@ public class ParsedActivityUtils {
return input.error(exportedCheckResult);
}
}
- activity.exported = hasIntentFilters;
+ activity.setExported(hasIntentFilters);
}
return input.success(activity);
@@ -459,10 +459,11 @@ public class ParsedActivityUtils {
ParsedIntentInfo intent = result.getResult();
if (intent != null) {
if (intent.isVisibleToInstantApp()) {
- activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
}
if (intent.isImplicitlyVisibleToInstantApp()) {
- activity.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+ activity.setFlags(
+ activity.getFlags() | ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP);
}
}
@@ -553,19 +554,19 @@ public class ParsedActivityUtils {
private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout(
ParsedActivity activity, ParseInput input) {
// There isn't a metadata for us to fall back. Whatever is in layout is correct.
- if (activity.metaData == null || !activity.metaData.containsKey(
+ if (activity.getMetaData() == null || !activity.getMetaData().containsKey(
ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
- return input.success(activity.windowLayout);
+ return input.success(activity.getWindowLayout());
}
// Layout already specifies a value. We should just use that one.
- if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) {
- return input.success(activity.windowLayout);
+ if (activity.getWindowLayout() != null && activity.getWindowLayout().windowLayoutAffinity != null) {
+ return input.success(activity.getWindowLayout());
}
- String windowLayoutAffinity = activity.metaData.getString(
+ String windowLayoutAffinity = activity.getMetaData().getString(
ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
- ActivityInfo.WindowLayout layout = activity.windowLayout;
+ ActivityInfo.WindowLayout layout = activity.getWindowLayout();
if (layout == null) {
layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
-1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY,
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index 4ec2e73a0b83..db3a1c46524d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -34,7 +34,7 @@ import java.util.List;
*
* @hide
*/
-@DataClass(genAidl = false)
+@DataClass(genAidl = false, genSetters = true, genBuilder = false)
public class ParsedAttribution implements Parcelable {
/** Maximum length of attribution tag */
public static final int MAX_ATTRIBUTION_TAG_LEN = 50;
@@ -43,13 +43,15 @@ public class ParsedAttribution implements Parcelable {
private static final int MAX_NUM_ATTRIBUTIONS = 10000;
/** Tag of the attribution */
- public final @NonNull String tag;
+ private @NonNull String tag;
/** User visible label fo the attribution */
- public final @StringRes int label;
+ private @StringRes int label;
/** Ids of previously declared attributions this attribution inherits from */
- public final @NonNull List<String> inheritFrom;
+ private @NonNull List<String> inheritFrom;
+
+ public ParsedAttribution() {}
/**
* @return Is this set of attributions a valid combination for a single package?
@@ -160,6 +162,63 @@ public class ParsedAttribution implements Parcelable {
// onConstructed(); // You can define this method to get a callback
}
+ /**
+ * Tag of the attribution
+ */
+ @DataClass.Generated.Member
+ public @NonNull String getTag() {
+ return tag;
+ }
+
+ /**
+ * User visible label fo the attribution
+ */
+ @DataClass.Generated.Member
+ public @StringRes int getLabel() {
+ return label;
+ }
+
+ /**
+ * Ids of previously declared attributions this attribution inherits from
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getInheritFrom() {
+ return inheritFrom;
+ }
+
+ /**
+ * Tag of the attribution
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParsedAttribution setTag(@NonNull String value) {
+ tag = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, tag);
+ return this;
+ }
+
+ /**
+ * User visible label fo the attribution
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParsedAttribution setLabel(@StringRes int value) {
+ label = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ StringRes.class, null, label);
+ return this;
+ }
+
+ /**
+ * Ids of previously declared attributions this attribution inherits from
+ */
+ @DataClass.Generated.Member
+ public @NonNull ParsedAttribution setInheritFrom(@NonNull List<String> value) {
+ inheritFrom = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, inheritFrom);
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -215,10 +274,10 @@ public class ParsedAttribution implements Parcelable {
};
@DataClass.Generated(
- time = 1618351459610L,
+ time = 1624050667337L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
- inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
+ inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\nprivate @android.annotation.NonNull java.lang.String tag\nprivate @android.annotation.StringRes int label\nprivate @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false, genSetters=true, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 9d830ec7a227..838adfd3215e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -43,38 +43,38 @@ import java.util.Map;
/** @hide */
public abstract class ParsedComponent implements Parcelable {
- private static ParsedIntentInfo.ListParceler sForIntentInfos = Parcelling.Cache.getOrCreate(
- ParsedIntentInfo.ListParceler.class);
+ private static final ParsedIntentInfo.ListParceler sForIntentInfos =
+ Parcelling.Cache.getOrCreate(ParsedIntentInfo.ListParceler.class);
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
- private String name;
- int icon;
- int labelRes;
+ protected String name;
+ protected int icon;
+ protected int labelRes;
@Nullable
- CharSequence nonLocalizedLabel;
- int logo;
- int banner;
- int descriptionRes;
+ protected CharSequence nonLocalizedLabel;
+ protected int logo;
+ protected int banner;
+ protected int descriptionRes;
// TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
- int flags;
+ protected int flags;
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
- private String packageName;
+ protected String packageName;
@Nullable
@DataClass.PluralOf("intent")
@DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
- private List<ParsedIntentInfo> intents;
+ protected List<ParsedIntentInfo> intents;
- private ComponentName componentName;
+ protected ComponentName componentName;
@Nullable
protected Bundle metaData;
- private Map<String, Property> mProperties = emptyMap();
+ protected Map<String, Property> mProperties = emptyMap();
ParsedComponent() {
@@ -112,11 +112,51 @@ public abstract class ParsedComponent implements Parcelable {
return intents != null ? intents : Collections.emptyList();
}
+ public ParsedComponent setBanner(int banner) {
+ this.banner = banner;
+ return this;
+ }
+
+ public ParsedComponent setDescriptionRes(int descriptionRes) {
+ this.descriptionRes = descriptionRes;
+ return this;
+ }
+
+ public ParsedComponent setFlags(int flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ public ParsedComponent setIcon(int icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ public ParsedComponent setLabelRes(int labelRes) {
+ this.labelRes = labelRes;
+ return this;
+ }
+
+ public ParsedComponent setLogo(int logo) {
+ this.logo = logo;
+ return this;
+ }
+
+ public ParsedComponent setMetaData(Bundle metaData) {
+ this.metaData = metaData;
+ return this;
+ }
+
public ParsedComponent setName(String name) {
this.name = TextUtils.safeIntern(name);
return this;
}
+ public ParsedComponent setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
+ this.nonLocalizedLabel = nonLocalizedLabel;
+ return this;
+ }
+
@CallSuper
public void setPackageName(@NonNull String packageName) {
this.packageName = TextUtils.safeIntern(packageName);
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index 46b941955fc7..ab596d305a61 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -56,40 +56,40 @@ class ParsedComponentUtils {
}
//noinspection ConstantConditions; null check done above with isEmpty
- component.setName(className);
- component.setPackageName(packageName);
+ component.setName(className)
+ .setPackageName(packageName);
int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
if (roundIconVal != 0) {
- component.icon = roundIconVal;
- component.nonLocalizedLabel = null;
+ component.setIcon(roundIconVal)
+ .setNonLocalizedLabel(null);
} else {
int iconVal = array.getResourceId(iconAttr, 0);
if (iconVal != 0) {
- component.icon = iconVal;
- component.nonLocalizedLabel = null;
+ component.setIcon(iconVal);
+ component.setNonLocalizedLabel(null);
}
}
int logoVal = array.getResourceId(logoAttr, 0);
if (logoVal != 0) {
- component.logo = logoVal;
+ component.setLogo(logoVal);
}
int bannerVal = array.getResourceId(bannerAttr, 0);
if (bannerVal != 0) {
- component.banner = bannerVal;
+ component.setBanner(bannerVal);
}
if (descriptionAttr != null) {
- component.descriptionRes = array.getResourceId(descriptionAttr, 0);
+ component.setDescriptionRes(array.getResourceId(descriptionAttr, 0));
}
TypedValue v = array.peekValue(labelAttr);
if (v != null) {
- component.labelRes = v.resourceId;
+ component.setLabelRes(v.resourceId);
if (v.resourceId == 0) {
- component.nonLocalizedLabel = v.coerceToString();
+ component.setNonLocalizedLabel(v.coerceToString());
}
}
@@ -105,9 +105,9 @@ class ParsedComponentUtils {
}
final Property property = result.getResult();
if (property != null) {
- component.metaData = property.toBundle(component.metaData);
+ component.setMetaData(property.toBundle(component.getMetaData()));
}
- return input.success(component.metaData);
+ return input.success(component.getMetaData());
}
static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg,
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
index aa33e79c4aa9..41789204a76d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
@@ -18,6 +18,7 @@ package android.content.pm.parsing.component;
import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.Parcel;
@@ -36,18 +37,30 @@ public class ParsedInstrumentation extends ParsedComponent {
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String targetProcesses;
- boolean handleProfiling;
- boolean functionalTest;
+ private boolean handleProfiling;
+ private boolean functionalTest;
public ParsedInstrumentation() {
}
- public void setTargetPackage(@Nullable String targetPackage) {
+ public ParsedInstrumentation setFunctionalTest(boolean functionalTest) {
+ this.functionalTest = functionalTest;
+ return this;
+ }
+
+ public ParsedInstrumentation setHandleProfiling(boolean handleProfiling) {
+ this.handleProfiling = handleProfiling;
+ return this;
+ }
+
+ public ParsedInstrumentation setTargetPackage(@Nullable String targetPackage) {
this.targetPackage = TextUtils.safeIntern(targetPackage);
+ return this;
}
- public void setTargetProcesses(@Nullable String targetProcesses) {
+ public ParsedInstrumentation setTargetProcesses(@Nullable String targetProcesses) {
this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+ return this;
}
public String toString() {
@@ -82,6 +95,7 @@ public class ParsedInstrumentation extends ParsedComponent {
this.functionalTest = in.readByte() != 0;
}
+ @NonNull
public static final Parcelable.Creator<ParsedInstrumentation> CREATOR =
new Parcelable.Creator<ParsedInstrumentation>() {
@Override
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
index 89645fc3e9c9..5977c83bf691 100644
--- a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -59,10 +59,10 @@ public class ParsedInstrumentationUtils {
// @formatter:off
// Note: don't allow this value to be a reference to a resource
// that may change.
- instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage));
- instrumentation.setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses));
- instrumentation.handleProfiling = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
- instrumentation.functionalTest = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false);
+ instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage))
+ .setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses))
+ .setHandleProfiling(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false))
+ .setFunctionalTest(sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false));
// @formatter:on
} finally {
sa.recycle();
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 463a1810b511..59d4a95edb0a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -16,6 +16,7 @@
package android.content.pm.parsing.component;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.IntentFilter;
import android.os.Parcel;
@@ -31,6 +32,26 @@ public final class ParsedIntentInfo extends IntentFilter {
public static final Parceler PARCELER = new Parceler();
+ public ParsedIntentInfo setHasDefault(boolean hasDefault) {
+ this.hasDefault = hasDefault;
+ return this;
+ }
+
+ public ParsedIntentInfo setIcon(int icon) {
+ this.icon = icon;
+ return this;
+ }
+
+ public ParsedIntentInfo setLabelRes(int labelRes) {
+ this.labelRes = labelRes;
+ return this;
+ }
+
+ public ParsedIntentInfo setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
+ this.nonLocalizedLabel = nonLocalizedLabel;
+ return this;
+ }
+
public static class Parceler implements Parcelling<ParsedIntentInfo> {
@Override
@@ -38,6 +59,7 @@ public final class ParsedIntentInfo extends IntentFilter {
item.writeIntentInfoToParcel(dest, parcelFlags);
}
+ @NonNull
@Override
public ParsedIntentInfo unparcel(Parcel source) {
return new ParsedIntentInfo(source);
@@ -135,11 +157,11 @@ public final class ParsedIntentInfo extends IntentFilter {
}
}
- boolean hasDefault;
- int labelRes;
+ private boolean hasDefault;
+ private int labelRes;
@Nullable
- CharSequence nonLocalizedLabel;
- int icon;
+ private CharSequence nonLocalizedLabel;
+ private int icon;
public ParsedIntentInfo() {
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 939e77f6067c..668b9ccdfdcf 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -16,10 +16,11 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingUtils.ANDROID_RES_NAMESPACE;
+
import android.annotation.NonNull;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
@@ -60,19 +61,20 @@ public class ParsedIntentInfoUtils {
TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
if (v != null) {
- intentInfo.labelRes = v.resourceId;
+ intentInfo.setLabelRes(v.resourceId);
if (v.resourceId == 0) {
- intentInfo.nonLocalizedLabel = v.coerceToString();
+ intentInfo.setNonLocalizedLabel(v.coerceToString());
}
}
if (ParsingPackageUtils.sUseRoundIcon) {
- intentInfo.icon = sa.getResourceId(
- R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
+ intentInfo.setIcon(sa.getResourceId(
+ R.styleable.AndroidManifestIntentFilter_roundIcon, 0));
}
- if (intentInfo.icon == 0) {
- intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
+ if (intentInfo.getIcon() == 0) {
+ intentInfo.setIcon(
+ sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0));
}
if (allowAutoVerify) {
@@ -96,8 +98,7 @@ public class ParsedIntentInfoUtils {
String nodeName = parser.getName();
switch (nodeName) {
case "action": {
- String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
- "name");
+ String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
if (value == null) {
result = input.error("No value supplied for <android:name>");
} else if (value.isEmpty()) {
@@ -112,8 +113,7 @@ public class ParsedIntentInfoUtils {
break;
}
case "category": {
- String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
- "name");
+ String value = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "name");
if (value == null) {
result = input.error("No value supplied for <android:name>");
} else if (value.isEmpty()) {
@@ -140,7 +140,7 @@ public class ParsedIntentInfoUtils {
}
}
- intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+ intentInfo.setHasDefault(intentInfo.hasCategory(Intent.CATEGORY_DEFAULT));
if (DEBUG) {
final StringBuilder cats = new StringBuilder("Intent d=");
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
index 033e30fe7582..433bfd30c50f 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -31,16 +31,16 @@ public class ParsedMainComponent extends ParsedComponent {
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
- private String processName;
- boolean directBootAware;
- boolean enabled = true;
- boolean exported;
- int order;
+ protected String processName;
+ protected boolean directBootAware;
+ protected boolean enabled = true;
+ protected boolean exported;
+ protected int order;
@Nullable
- String splitName;
+ protected String splitName;
@Nullable
- String[] attributionTags;
+ protected String[] attributionTags;
public ParsedMainComponent() {
}
@@ -56,6 +56,11 @@ public class ParsedMainComponent extends ParsedComponent {
this.attributionTags = other.attributionTags;
}
+ public ParsedMainComponent setOrder(int order) {
+ this.order = order;
+ return this;
+ }
+
public ParsedMainComponent setProcessName(String processName) {
this.processName = TextUtils.safeIntern(processName);
return this;
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
index 54bcbdddfb34..7ccca93709b1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -58,14 +58,14 @@ class ParsedMainComponentUtils {
}
if (directBootAwareAttr != null) {
- component.directBootAware = array.getBoolean(directBootAwareAttr, false);
+ component.setDirectBootAware(array.getBoolean(directBootAwareAttr, false));
if (component.isDirectBootAware()) {
pkg.setPartiallyDirectBootAware(true);
}
}
if (enabledAttr != null) {
- component.enabled = array.getBoolean(enabledAttr, true);
+ component.setEnabled(array.getBoolean(enabledAttr, true));
}
if (processAttr != null) {
@@ -92,13 +92,13 @@ class ParsedMainComponentUtils {
}
if (splitNameAttr != null) {
- component.splitName = array.getNonConfigurationString(splitNameAttr, 0);
+ component.setSplitName(array.getNonConfigurationString(splitNameAttr, 0));
}
if (attributionTagsAttr != null) {
final String attributionTags = array.getNonConfigurationString(attributionTagsAttr, 0);
if (attributionTags != null) {
- component.attributionTags = attributionTags.split("\\|");
+ component.setAttributionTags(attributionTags.split("\\|"));
}
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
index 37e0e87c548c..0f82941f92b1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -16,6 +16,7 @@
package android.content.pm.parsing.component;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PermissionInfo;
import android.os.Parcel;
@@ -38,17 +39,17 @@ public class ParsedPermission extends ParsedComponent {
private static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class);
@Nullable
- String backgroundPermission;
+ private String backgroundPermission;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String group;
- int requestRes;
- int protectionLevel;
- boolean tree;
+ private int requestRes;
+ private int protectionLevel;
+ private boolean tree;
@Nullable
private ParsedPermissionGroup parsedPermissionGroup;
@Nullable
- Set<String> knownCerts;
+ private Set<String> knownCerts;
@VisibleForTesting
public ParsedPermission() {
@@ -64,13 +65,13 @@ public class ParsedPermission extends ParsedComponent {
this.parsedPermissionGroup = other.parsedPermissionGroup;
}
- public ParsedPermission setGroup(String group) {
- this.group = TextUtils.safeIntern(group);
+ public ParsedPermission setBackgroundPermission(String backgroundPermission) {
+ this.backgroundPermission = backgroundPermission;
return this;
}
- public ParsedPermission setFlags(int flags) {
- this.flags = flags;
+ public ParsedPermission setGroup(String group) {
+ this.group = TextUtils.safeIntern(group);
return this;
}
@@ -116,6 +117,21 @@ public class ParsedPermission extends ParsedComponent {
return size;
}
+ public ParsedPermission setKnownCerts(Set<String> knownCerts) {
+ this.knownCerts = knownCerts;
+ return this;
+ }
+
+ public ParsedPermission setRequestRes(int requestRes) {
+ this.requestRes = requestRes;
+ return this;
+ }
+
+ public ParsedPermission setTree(boolean tree) {
+ this.tree = tree;
+ return this;
+ }
+
public String toString() {
return "Permission{"
+ Integer.toHexString(System.identityHashCode(this))
@@ -152,6 +168,7 @@ public class ParsedPermission extends ParsedComponent {
this.knownCerts = sForStringSet.unparcel(in);
}
+ @NonNull
public static final Parcelable.Creator<ParsedPermission> CREATOR =
new Parcelable.Creator<ParsedPermission>() {
@Override
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
index 741c00cbb723..9fb95c46eac5 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
@@ -22,17 +22,15 @@ import android.os.Parcelable;
import com.android.internal.util.DataClass;
/** @hide */
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+ genAidl = false)
public class ParsedPermissionGroup extends ParsedComponent {
- int requestDetailResourceId;
- int backgroundRequestResourceId;
- int backgroundRequestDetailResourceId;
- int requestRes;
- int priority;
-
- public void setPriority(int priority) {
- this.priority = priority;
- }
+ private int requestDetailResourceId;
+ private int backgroundRequestResourceId;
+ private int backgroundRequestDetailResourceId;
+ private int requestRes;
+ private int priority;
public String toString() {
return "PermissionGroup{"
@@ -40,63 +38,162 @@ public class ParsedPermissionGroup extends ParsedComponent {
+ " " + getName() + "}";
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(this.requestDetailResourceId);
- dest.writeInt(this.backgroundRequestResourceId);
- dest.writeInt(this.backgroundRequestDetailResourceId);
- dest.writeInt(this.requestRes);
- dest.writeInt(this.priority);
- }
-
public ParsedPermissionGroup() {
}
- protected ParsedPermissionGroup(Parcel in) {
- super(in);
- this.requestDetailResourceId = in.readInt();
- this.backgroundRequestResourceId = in.readInt();
- this.backgroundRequestDetailResourceId = in.readInt();
- this.requestRes = in.readInt();
- this.priority = in.readInt();
- }
- public static final Parcelable.Creator<ParsedPermissionGroup> CREATOR =
- new Parcelable.Creator<ParsedPermissionGroup>() {
- @Override
- public ParsedPermissionGroup createFromParcel(Parcel source) {
- return new ParsedPermissionGroup(source);
- }
- @Override
- public ParsedPermissionGroup[] newArray(int size) {
- return new ParsedPermissionGroup[size];
- }
- };
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public ParsedPermissionGroup(
+ int requestDetailResourceId,
+ int backgroundRequestResourceId,
+ int backgroundRequestDetailResourceId,
+ int requestRes,
+ int priority) {
+ this.requestDetailResourceId = requestDetailResourceId;
+ this.backgroundRequestResourceId = backgroundRequestResourceId;
+ this.backgroundRequestDetailResourceId = backgroundRequestDetailResourceId;
+ this.requestRes = requestRes;
+ this.priority = priority;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+ @DataClass.Generated.Member
public int getRequestDetailResourceId() {
return requestDetailResourceId;
}
+ @DataClass.Generated.Member
public int getBackgroundRequestResourceId() {
return backgroundRequestResourceId;
}
+ @DataClass.Generated.Member
public int getBackgroundRequestDetailResourceId() {
return backgroundRequestDetailResourceId;
}
+ @DataClass.Generated.Member
public int getRequestRes() {
return requestRes;
}
+ @DataClass.Generated.Member
public int getPriority() {
return priority;
}
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ParsedPermissionGroup setRequestDetailResourceId( int value) {
+ requestDetailResourceId = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestResourceId( int value) {
+ backgroundRequestResourceId = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ParsedPermissionGroup setBackgroundRequestDetailResourceId( int value) {
+ backgroundRequestDetailResourceId = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ParsedPermissionGroup setRequestRes( int value) {
+ requestRes = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull ParsedPermissionGroup setPriority( int value) {
+ priority = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ super.writeToParcel(dest, flags);
+
+ dest.writeInt(requestDetailResourceId);
+ dest.writeInt(backgroundRequestResourceId);
+ dest.writeInt(backgroundRequestDetailResourceId);
+ dest.writeInt(requestRes);
+ dest.writeInt(priority);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParsedPermissionGroup(@android.annotation.NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ super(in);
+
+ int _requestDetailResourceId = in.readInt();
+ int _backgroundRequestResourceId = in.readInt();
+ int _backgroundRequestDetailResourceId = in.readInt();
+ int _requestRes = in.readInt();
+ int _priority = in.readInt();
+
+ this.requestDetailResourceId = _requestDetailResourceId;
+ this.backgroundRequestResourceId = _backgroundRequestResourceId;
+ this.backgroundRequestDetailResourceId = _backgroundRequestDetailResourceId;
+ this.requestRes = _requestRes;
+ this.priority = _priority;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<ParsedPermissionGroup> CREATOR
+ = new Parcelable.Creator<ParsedPermissionGroup>() {
+ @Override
+ public ParsedPermissionGroup[] newArray(int size) {
+ return new ParsedPermissionGroup[size];
+ }
+
+ @Override
+ public ParsedPermissionGroup createFromParcel(@android.annotation.NonNull Parcel in) {
+ return new ParsedPermissionGroup(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1624052057830L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java",
+ inputSignatures = "private int requestDetailResourceId\nprivate int backgroundRequestResourceId\nprivate int backgroundRequestDetailResourceId\nprivate int requestRes\nprivate int priority\npublic java.lang.String toString()\nclass ParsedPermissionGroup extends android.content.pm.parsing.component.ParsedComponent implements []\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index 8afa70ec6364..eec333c094b7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -66,9 +66,8 @@ public class ParsedPermissionUtils {
if (sa.hasValue(
R.styleable.AndroidManifestPermission_backgroundPermission)) {
if ("android".equals(packageName)) {
- permission.backgroundPermission = sa.getNonResourceString(
- R.styleable
- .AndroidManifestPermission_backgroundPermission);
+ permission.setBackgroundPermission(sa.getNonResourceString(
+ R.styleable.AndroidManifestPermission_backgroundPermission));
} else {
Slog.w(TAG, packageName + " defines a background permission. Only the "
+ "'android' package can do that.");
@@ -78,17 +77,14 @@ public class ParsedPermissionUtils {
// Note: don't allow this value to be a reference to a resource
// that may change.
permission.setGroup(sa.getNonResourceString(
- R.styleable.AndroidManifestPermission_permissionGroup));
-
- permission.requestRes = sa.getResourceId(
- R.styleable.AndroidManifestPermission_request, 0);
-
- permission.protectionLevel = sa.getInt(
- R.styleable.AndroidManifestPermission_protectionLevel,
- PermissionInfo.PROTECTION_NORMAL);
-
- permission.flags = sa.getInt(
- R.styleable.AndroidManifestPermission_permissionFlags, 0);
+ R.styleable.AndroidManifestPermission_permissionGroup))
+ .setRequestRes(sa.getResourceId(
+ R.styleable.AndroidManifestPermission_request, 0))
+ .setProtectionLevel(sa.getInt(
+ R.styleable.AndroidManifestPermission_protectionLevel,
+ PermissionInfo.PROTECTION_NORMAL))
+ .setFlags(sa.getInt(
+ R.styleable.AndroidManifestPermission_permissionFlags, 0));
final int knownCertsResource = sa.getResourceId(
R.styleable.AndroidManifestPermission_knownCerts, 0);
@@ -108,7 +104,7 @@ public class ParsedPermissionUtils {
permission.setKnownCert(knownCert);
}
}
- if (permission.knownCerts == null) {
+ if (permission.getKnownCerts() == null) {
Slog.w(TAG, packageName + " defines a knownSigner permission but"
+ " the provided knownCerts resource is null");
}
@@ -124,12 +120,12 @@ public class ParsedPermissionUtils {
// For now only platform runtime permissions can be restricted
if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
- permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
- permission.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+ permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_HARD_RESTRICTED);
+ permission.setFlags(permission.getFlags() & ~PermissionInfo.FLAG_SOFT_RESTRICTED);
} else {
// The platform does not get to specify conflicting permissions
- if ((permission.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
- && (permission.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+ if ((permission.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+ && (permission.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
throw new IllegalStateException("Permission cannot be both soft and hard"
+ " restricted: " + permission.getName());
}
@@ -138,7 +134,8 @@ public class ParsedPermissionUtils {
sa.recycle();
}
- permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
+ permission.setProtectionLevel(
+ PermissionInfo.fixProtectionLevel(permission.getProtectionLevel()));
final int otherProtectionFlags = permission.getProtectionFlags()
& ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
@@ -188,8 +185,8 @@ public class ParsedPermissionUtils {
+ permission.getName());
}
- permission.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
- permission.tree = true;
+ permission.setProtectionLevel(PermissionInfo.PROTECTION_NORMAL)
+ .setTree(true);
return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission,
input);
@@ -219,12 +216,12 @@ public class ParsedPermissionUtils {
}
// @formatter:off
- permissionGroup.requestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
- permissionGroup.backgroundRequestResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0);
- permissionGroup.backgroundRequestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
- permissionGroup.requestRes = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0);
- permissionGroup.flags = sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0);
- permissionGroup.priority = sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0);
+ permissionGroup.setRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0))
+ .setBackgroundRequestResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0))
+ .setBackgroundRequestDetailResourceId(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0))
+ .setRequestRes(sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0))
+ .setPriority(sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0))
+ .setFlags(sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0));
// @formatter:on
} finally {
sa.recycle();
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
index 54a60d349331..fe102252931e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcess.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -19,7 +19,6 @@ package android.content.pm.parsing.component;
import static java.util.Collections.emptySet;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,22 +31,22 @@ import com.android.internal.util.Parcelling;
import java.util.Set;
/** @hide */
-@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+@DataClass(genGetters = true, genSetters = true, genParcelable = true, genAidl = false,
genBuilder = false)
public class ParsedProcess implements Parcelable {
@NonNull
- protected String name;
+ private String name;
@NonNull
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
- protected Set<String> deniedPermissions = emptySet();
+ private Set<String> deniedPermissions = emptySet();
@ApplicationInfo.GwpAsanMode
- protected int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
+ private int gwpAsanMode = ApplicationInfo.GWP_ASAN_DEFAULT;
@ApplicationInfo.MemtagMode
- protected int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
+ private int memtagMode = ApplicationInfo.MEMTAG_DEFAULT;
@ApplicationInfo.NativeHeapZeroInitialized
- protected int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
+ private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
public ParsedProcess() {
}
@@ -63,7 +62,7 @@ public class ParsedProcess implements Parcelable {
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -128,6 +127,46 @@ public class ParsedProcess implements Parcelable {
}
@DataClass.Generated.Member
+ public @NonNull ParsedProcess setName(@NonNull String value) {
+ name = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcess setDeniedPermissions(@NonNull Set<String> value) {
+ deniedPermissions = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, deniedPermissions);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcess setGwpAsanMode(@ApplicationInfo.GwpAsanMode int value) {
+ gwpAsanMode = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.GwpAsanMode.class, null, gwpAsanMode);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcess setMemtagMode(@ApplicationInfo.MemtagMode int value) {
+ memtagMode = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.MemtagMode.class, null, memtagMode);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedProcess setNativeHeapZeroInitialized(@ApplicationInfo.NativeHeapZeroInitialized int value) {
+ nativeHeapZeroInitialized = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+ return this;
+ }
+
+ @DataClass.Generated.Member
static Parcelling<Set<String>> sParcellingForDeniedPermissions =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -202,10 +241,10 @@ public class ParsedProcess implements Parcelable {
};
@DataClass.Generated(
- time = 1615850515058L,
- codegenVersion = "1.0.22",
+ time = 1623692988845L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
- inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprotected @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprotected @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprotected @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
index e417e7407fbb..d4e19afeeb96 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -85,31 +85,26 @@ public class ParsedProcessUtils {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
try {
if (perms != null) {
- proc.deniedPermissions = new ArraySet<>(perms);
+ proc.setDeniedPermissions(new ArraySet<>(perms));
}
- proc.name = sa.getNonConfigurationString(
+ String processName = sa.getNonConfigurationString(
R.styleable.AndroidManifestProcess_process, 0);
ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
- pkg.getPackageName(), pkg.getPackageName(), proc.name, flags, separateProcesses,
+ pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
input);
if (processNameResult.isError()) {
return input.error(processNameResult);
}
- proc.name = processNameResult.getResult();
-
- if (proc.name == null || proc.name.length() <= 0) {
- return input.error("<process> does not specify android:process");
- }
-
- proc.gwpAsanMode = sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1);
- proc.memtagMode = sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1);
+ proc.setName(processNameResult.getResult());
+ proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
+ proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
Boolean v = sa.getBoolean(
R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
- proc.nativeHeapZeroInitialized =
- v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED;
+ proc.setNativeHeapZeroInitialized(
+ v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
}
} finally {
sa.recycle();
@@ -129,18 +124,18 @@ public class ParsedProcessUtils {
switch (tagName) {
case "deny-permission":
ParseResult<Set<String>> denyResult = parseDenyPermission(
- proc.deniedPermissions, res, parser, input);
+ proc.getDeniedPermissions(), res, parser, input);
result = denyResult;
if (denyResult.isSuccess()) {
- proc.deniedPermissions = denyResult.getResult();
+ proc.setDeniedPermissions(denyResult.getResult());
}
break;
case "allow-permission":
ParseResult<Set<String>> allowResult = parseAllowPermission(
- proc.deniedPermissions, res, parser, input);
+ proc.getDeniedPermissions(), res, parser, input);
result = allowResult;
if (allowResult.isSuccess()) {
- proc.deniedPermissions = allowResult.getResult();
+ proc.setDeniedPermissions(allowResult.getResult());
}
break;
default:
@@ -198,9 +193,9 @@ public class ParsedProcessUtils {
result = processResult;
if (processResult.isSuccess()) {
ParsedProcess process = processResult.getResult();
- if (processes.put(process.name, process) != null) {
+ if (processes.put(process.getName(), process) != null) {
result = input.error(
- "<process> specified existing name '" + process.name + "'");
+ "<process> specified existing name '" + process.getName() + "'");
}
}
break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java
index fcf6e8767760..9a12b48cffb0 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProvider.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProvider.java
@@ -36,21 +36,21 @@ public class ParsedProvider extends ParsedMainComponent {
@NonNull
@DataClass.ParcelWith(ForInternedString.class)
private String authority;
- boolean syncable;
+ private boolean syncable;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String readPermission;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String writePermission;
- boolean grantUriPermissions;
- boolean forceUriPermissions;
- boolean multiProcess;
- int initOrder;
+ private boolean grantUriPermissions;
+ private boolean forceUriPermissions;
+ private boolean multiProcess;
+ private int initOrder;
@Nullable
- PatternMatcher[] uriPermissionPatterns;
+ private PatternMatcher[] uriPermissionPatterns;
@Nullable
- PathPermission[] pathPermissions;
+ private PathPermission[] pathPermissions;
public ParsedProvider(ParsedProvider other) {
super(other);
@@ -67,24 +67,58 @@ public class ParsedProvider extends ParsedMainComponent {
this.pathPermissions = other.pathPermissions;
}
- public void setAuthority(String authority) {
+ public ParsedProvider setAuthority(String authority) {
this.authority = TextUtils.safeIntern(authority);
+ return this;
}
- public void setSyncable(boolean syncable) {
+ public ParsedProvider setForceUriPermissions(boolean forceUriPermissions) {
+ this.forceUriPermissions = forceUriPermissions;
+ return this;
+ }
+
+ public ParsedProvider setGrantUriPermissions(boolean grantUriPermissions) {
+ this.grantUriPermissions = grantUriPermissions;
+ return this;
+ }
+
+ public ParsedProvider setInitOrder(int initOrder) {
+ this.initOrder = initOrder;
+ return this;
+ }
+
+ public ParsedProvider setMultiProcess(boolean multiProcess) {
+ this.multiProcess = multiProcess;
+ return this;
+ }
+
+ public ParsedProvider setPathPermissions(PathPermission[] pathPermissions) {
+ this.pathPermissions = pathPermissions;
+ return this;
+ }
+
+ public ParsedProvider setSyncable(boolean syncable) {
this.syncable = syncable;
+ return this;
}
- public void setReadPermission(String readPermission) {
+ public ParsedProvider setReadPermission(String readPermission) {
// Empty string must be converted to null
this.readPermission = TextUtils.isEmpty(readPermission)
? null : readPermission.intern();
+ return this;
}
- public void setWritePermission(String writePermission) {
+ public ParsedProvider setUriPermissionPatterns(PatternMatcher[] uriPermissionPatterns) {
+ this.uriPermissionPatterns = uriPermissionPatterns;
+ return this;
+ }
+
+ public ParsedProvider setWritePermission(String writePermission) {
// Empty string must be converted to null
this.writePermission = TextUtils.isEmpty(writePermission)
? null : writePermission.intern();
+ return this;
}
public String toString() {
@@ -135,6 +169,7 @@ public class ParsedProvider extends ParsedMainComponent {
this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
}
+ @NonNull
public static final Parcelable.Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
@Override
public ParsedProvider createFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 28fd919e2b7e..c4b8b98b2411 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -16,10 +16,10 @@
package android.content.pm.parsing.component;
+import static android.content.pm.parsing.ParsingPackageUtils.RIGID_PARSER;
import static android.content.pm.parsing.component.ComponentParseUtils.flag;
import android.annotation.NonNull;
-import android.content.pm.PackageParser;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.parsing.ParsingPackage;
@@ -85,10 +85,10 @@ public class ParsedProviderUtils {
// For compatibility, applications targeting API level 16 or lower
// should have their content providers exported by default, unless they
// specify otherwise.
- provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
- targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1);
-
- provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false);
+ provider.setSyncable(sa.getBoolean(
+ R.styleable.AndroidManifestProvider_syncable, false))
+ .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
+ targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
String permission = sa.getNonConfigurationString(
R.styleable.AndroidManifestProvider_permission, 0);
@@ -113,16 +113,21 @@ public class ParsedProviderUtils {
provider.setWritePermission(writePermission);
}
- provider.grantUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false);
- provider.forceUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions, false);
- provider.multiProcess = sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false);
- provider.initOrder = sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0);
-
- provider.flags |= flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa);
-
- visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+ provider.setGrantUriPermissions(
+ sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false))
+ .setForceUriPermissions(
+ sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions,
+ false))
+ .setMultiProcess(
+ sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false))
+ .setInitOrder(sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0))
+ .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
+ R.styleable.AndroidManifestProvider_singleUser, sa));
+
+ visibleToEphemeral = sa.getBoolean(
+ R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
if (visibleToEphemeral) {
- provider.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ provider.setFlags(provider.getFlags() | ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP);
pkg.setVisibleToInstantApps(true);
}
} finally {
@@ -174,7 +179,7 @@ public class ParsedProviderUtils {
result = intentResult;
if (intentResult.isSuccess()) {
ParsedIntentInfo intent = intentResult.getResult();
- provider.order = Math.max(intent.getOrder(), provider.order);
+ provider.setOrder(Math.max(intent.getOrder(), provider.getOrder()));
provider.addIntent(intent);
}
break;
@@ -245,19 +250,19 @@ public class ParsedProviderUtils {
}
if (pa != null) {
- if (provider.uriPermissionPatterns == null) {
- provider.uriPermissionPatterns = new PatternMatcher[1];
- provider.uriPermissionPatterns[0] = pa;
+ if (provider.getUriPermissionPatterns() == null) {
+ provider.setUriPermissionPatterns(new PatternMatcher[1]);
+ provider.getUriPermissionPatterns()[0] = pa;
} else {
- final int N = provider.uriPermissionPatterns.length;
+ final int N = provider.getUriPermissionPatterns().length;
PatternMatcher[] newp = new PatternMatcher[N + 1];
- System.arraycopy(provider.uriPermissionPatterns, 0, newp, 0, N);
+ System.arraycopy(provider.getUriPermissionPatterns(), 0, newp, 0, N);
newp[N] = pa;
- provider.uriPermissionPatterns = newp;
+ provider.setUriPermissionPatterns(newp);
}
- provider.grantUriPermissions = true;
+ provider.setGrantUriPermissions(true);
} else {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
}
@@ -303,7 +308,7 @@ public class ParsedProviderUtils {
}
if (!havePerm) {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
return input.error(
"No readPermission or writePermission for <path-permission>");
}
@@ -349,18 +354,18 @@ public class ParsedProviderUtils {
}
if (pa != null) {
- if (provider.pathPermissions == null) {
- provider.pathPermissions = new PathPermission[1];
- provider.pathPermissions[0] = pa;
+ if (provider.getPathPermissions() == null) {
+ provider.setPathPermissions(new PathPermission[1]);
+ provider.getPathPermissions()[0] = pa;
} else {
- final int N = provider.pathPermissions.length;
+ final int N = provider.getPathPermissions().length;
PathPermission[] newp = new PathPermission[N + 1];
- System.arraycopy(provider.pathPermissions, 0, newp, 0, N);
+ System.arraycopy(provider.getPathPermissions(), 0, newp, 0, N);
newp[N] = pa;
- provider.pathPermissions = newp;
+ provider.setPathPermissions(newp);
}
} else {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
return input.error(
"No path, pathPrefix, or pathPattern for <path-permission>");
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java
index 7adb2624056e..5499a130a4ad 100644
--- a/core/java/android/content/pm/parsing/component/ParsedService.java
+++ b/core/java/android/content/pm/parsing/component/ParsedService.java
@@ -18,6 +18,7 @@ package android.content.pm.parsing.component;
import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.Parcel;
@@ -30,7 +31,7 @@ import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
/** @hide **/
public class ParsedService extends ParsedMainComponent {
- int foregroundServiceType;
+ private int foregroundServiceType;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String permission;
@@ -41,6 +42,11 @@ public class ParsedService extends ParsedMainComponent {
this.permission = other.permission;
}
+ public ParsedService setForegroundServiceType(int foregroundServiceType) {
+ this.foregroundServiceType = foregroundServiceType;
+ return this;
+ }
+
public ParsedMainComponent setPermission(String permission) {
// Empty string must be converted to null
this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
@@ -78,6 +84,7 @@ public class ParsedService extends ParsedMainComponent {
this.permission = sForInternedString.unparcel(in);
}
+ @NonNull
public static final Parcelable.Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
@Override
public ParsedService createFromParcel(Parcel source) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index ae107ce65bf8..59267f9d2b6e 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -78,33 +78,32 @@ public class ParsedServiceUtils {
setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
if (setExported) {
- service.exported = sa.getBoolean(R.styleable.AndroidManifestService_exported,
- false);
+ service.setExported(sa.getBoolean(R.styleable.AndroidManifestService_exported,
+ false));
}
String permission = sa.getNonConfigurationString(
R.styleable.AndroidManifestService_permission, 0);
service.setPermission(permission != null ? permission : pkg.getPermission());
- service.foregroundServiceType = sa.getInt(
+ service.setForegroundServiceType(sa.getInt(
R.styleable.AndroidManifestService_foregroundServiceType,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
-
- service.flags |= flag(ServiceInfo.FLAG_STOP_WITH_TASK,
- R.styleable.AndroidManifestService_stopWithTask, sa)
- | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
- R.styleable.AndroidManifestService_isolatedProcess, sa)
- | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
- R.styleable.AndroidManifestService_externalService, sa)
- | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
- R.styleable.AndroidManifestService_useAppZygote, sa)
- | flag(ServiceInfo.FLAG_SINGLE_USER,
- R.styleable.AndroidManifestService_singleUser, sa);
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE))
+ .setFlags(service.getFlags() | (flag(ServiceInfo.FLAG_STOP_WITH_TASK,
+ R.styleable.AndroidManifestService_stopWithTask, sa)
+ | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
+ R.styleable.AndroidManifestService_isolatedProcess, sa)
+ | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
+ R.styleable.AndroidManifestService_externalService, sa)
+ | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
+ R.styleable.AndroidManifestService_useAppZygote, sa)
+ | flag(ServiceInfo.FLAG_SINGLE_USER,
+ R.styleable.AndroidManifestService_singleUser, sa)));
visibleToEphemeral = sa.getBoolean(
R.styleable.AndroidManifestService_visibleToInstantApps, false);
if (visibleToEphemeral) {
- service.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ service.setFlags(service.getFlags() | ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP);
pkg.setVisibleToInstantApps(true);
}
} finally {
@@ -139,7 +138,7 @@ public class ParsedServiceUtils {
parseResult = intentResult;
if (intentResult.isSuccess()) {
ParsedIntentInfo intent = intentResult.getResult();
- service.order = Math.max(intent.getOrder(), service.order);
+ service.setOrder(Math.max(intent.getOrder(), service.getOrder()));
service.addIntent(intent);
}
break;
@@ -172,7 +171,7 @@ public class ParsedServiceUtils {
return input.error(exportedCheckResult);
}
}
- service.exported = hasIntentFilters;
+ service.setExported(hasIntentFilters);
}
return input.success(service);
diff --git a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
index adf8da0fbe25..020784d361d8 100644
--- a/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+++ b/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
@@ -24,6 +24,9 @@ import android.content.pm.PackageInfo;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -33,50 +36,135 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genParcelable = true,
+ genAidl = false)
public class ParsedUsesPermission implements Parcelable {
- /** Name of the permission requested */
- public @NonNull String name;
- /** Set of flags that should apply to this permission request. */
- public @UsesPermissionFlags int usesPermissionFlags;
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedString.class)
+ @NonNull
+ private String name;
+
+ @UsesPermissionFlags
+ private int usesPermissionFlags;
/**
- * Strong assertion by a developer that they will never use this permission
- * to derive the physical location of the device, regardless of
- * ACCESS_FINE_LOCATION and/or ACCESS_COARSE_LOCATION being granted.
+ * Strong assertion by a developer that they will never use this permission to derive the
+ * physical location of the device, regardless of ACCESS_FINE_LOCATION and/or
+ * ACCESS_COARSE_LOCATION being granted.
*/
public static final int FLAG_NEVER_FOR_LOCATION =
PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION;
- /** @hide */
+ /**
+ * @hide
+ */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
FLAG_NEVER_FOR_LOCATION
})
public @interface UsesPermissionFlags {}
- public ParsedUsesPermission(@NonNull String name,
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public ParsedUsesPermission(
+ @NonNull String name,
@UsesPermissionFlags int usesPermissionFlags) {
- this.name = name.intern();
+ this.name = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
this.usesPermissionFlags = usesPermissionFlags;
+ com.android.internal.util.AnnotationValidations.validate(
+ UsesPermissionFlags.class, null, usesPermissionFlags);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return name;
+ }
+
+ @DataClass.Generated.Member
+ public @UsesPermissionFlags int getUsesPermissionFlags() {
+ return usesPermissionFlags;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedUsesPermission setName(@NonNull String value) {
+ name = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ParsedUsesPermission setUsesPermissionFlags(@UsesPermissionFlags int value) {
+ usesPermissionFlags = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ UsesPermissionFlags.class, null, usesPermissionFlags);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<String> sParcellingForName =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedString.class);
+ static {
+ if (sParcellingForName == null) {
+ sParcellingForName = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedString());
+ }
}
@Override
+ @DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
- sForInternedString.parcel(this.name, dest, flags);
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ sParcellingForName.parcel(name, dest, flags);
dest.writeInt(usesPermissionFlags);
}
@Override
- public int describeContents() {
- return 0;
- }
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
protected ParsedUsesPermission(@NonNull Parcel in) {
- this.name = sForInternedString.unparcel(in);
- this.usesPermissionFlags = in.readInt();
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String _name = sParcellingForName.unparcel(in);
+ int _usesPermissionFlags = in.readInt();
+
+ this.name = _name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.usesPermissionFlags = _usesPermissionFlags;
+ com.android.internal.util.AnnotationValidations.validate(
+ UsesPermissionFlags.class, null, usesPermissionFlags);
+
+ // onConstructed(); // You can define this method to get a callback
}
+ @DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<ParsedUsesPermission> CREATOR
= new Parcelable.Creator<ParsedUsesPermission>() {
@Override
@@ -89,4 +177,17 @@ public class ParsedUsesPermission implements Parcelable {
return new ParsedUsesPermission(in);
}
};
+
+ @DataClass.Generated(
+ time = 1626207990753L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedUsesPermission.java",
+ inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @android.content.pm.parsing.component.ParsedUsesPermission.UsesPermissionFlags int usesPermissionFlags\npublic static final int FLAG_NEVER_FOR_LOCATION\nclass ParsedUsesPermission extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 7ac78b7c6a24..324612da2846 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -119,7 +119,6 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
// how many APKs they're going through.
mDeferredErrors.erase();
}
- mPackageName = null;
mTargetSdkVersion = null;
return this;
}
diff --git a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
new file mode 100644
index 000000000000..9198b95a6f71
--- /dev/null
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.permission;
+
+import android.Manifest;
+import android.content.pm.parsing.component.ParsedUsesPermission;
+
+/**
+ * Implements compatibility support for permissions, and old applications
+ * will be automatically granted it.
+ *
+ * Compatibility permissions are permissions that are automatically granted to
+ * packages that target an SDK prior to when the permission was introduced.
+ * Sometimes the platform makes breaking behaviour changes and hides the legacy
+ * behaviour behind a permission. In these instances, we ensure applications
+ * targeting older platform versions are implicitly granted the correct set of
+ * permissions.
+ *
+ * @hide
+ */
+public class CompatibilityPermissionInfo extends ParsedUsesPermission {
+
+ public final int sdkVersion;
+
+ /**
+ * List of new permissions that have been added since 1.0.
+ *
+ * @hide
+ */
+ public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
+ new CompatibilityPermissionInfo[]{
+ new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
+ new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
+ android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
+ };
+
+ private CompatibilityPermissionInfo(String name, int sdkVersion,
+ @UsesPermissionFlags int usesPermissionFlags) {
+ super(name, usesPermissionFlags);
+ this.sdkVersion = sdkVersion;
+ }
+}
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index c1a83967dea1..47cf28b1d9d2 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,6 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -52,23 +48,21 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader {
}
private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
- throws PackageParserException {
+ throws IllegalArgumentException {
if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
&& !ApkLiteParseUtils.isApkPath(path)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + path);
+ throw new IllegalArgumentException("Invalid package file: " + path);
}
try {
return ApkAssets.loadFromPath(path);
} catch (IOException e) {
- throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
- "Failed to load APK at path " + path, e);
+ throw new IllegalArgumentException("Failed to load APK at path " + path, e);
}
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParserException {
+ public AssetManager getBaseAssetManager() throws IllegalArgumentException {
if (mCachedAssetManager != null) {
return mCachedAssetManager;
}
@@ -97,7 +91,7 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader {
}
@Override
- public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+ public AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException {
return getBaseAssetManager();
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index e5c2158fcd38..a0c3f752243c 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,11 +15,7 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-
import android.annotation.NonNull;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
@@ -40,7 +36,7 @@ import java.util.Collections;
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArgumentException>
implements SplitAssetLoader {
private final String[] mSplitPaths;
private final @ParseFlags int mFlags;
@@ -67,18 +63,16 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar
}
private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
- throws PackageParserException {
+ throws IllegalArgumentException {
if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
&& !ApkLiteParseUtils.isApkPath(path)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + path);
+ throw new IllegalArgumentException("Invalid package file: " + path);
}
try {
return ApkAssets.loadFromPath(path);
} catch (IOException e) {
- throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
- "Failed to load APK at path " + path, e);
+ throw new IllegalArgumentException("Failed to load APK at path " + path, e);
}
}
@@ -92,7 +86,7 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
- int parentSplitIdx) throws PackageParserException {
+ int parentSplitIdx) throws IllegalArgumentException {
final ArrayList<ApkAssets> assets = new ArrayList<>();
// Include parent ApkAssets.
@@ -114,15 +108,15 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackagePar
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParserException {
+ public AssetManager getBaseAssetManager() throws IllegalArgumentException {
loadDependenciesForSplit(0);
return mCachedAssetManagers[0];
}
@Override
- public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
- // Since we insert the base at position 0, and PackageParser keeps splits separate from
- // the base, we need to adjust the index.
+ public AssetManager getSplitAssetManager(int idx) throws IllegalArgumentException {
+ // Since we insert the base at position 0, and ParsingPackageUtils keeps splits separate
+ // from the base, we need to adjust the index.
loadDependenciesForSplit(idx + 1);
return mCachedAssetManagers[idx + 1];
}
diff --git a/core/java/android/content/pm/split/SplitAssetLoader.java b/core/java/android/content/pm/split/SplitAssetLoader.java
index 7584e15fb6d8..d314e06cb4a8 100644
--- a/core/java/android/content/pm/split/SplitAssetLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetLoader.java
@@ -15,19 +15,18 @@
*/
package android.content.pm.split;
-import android.content.pm.PackageParser;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
/**
- * Simple interface for loading base Assets and Splits. Used by PackageParser when parsing
+ * Simple interface for loading base Assets and Splits. Used by ParsingPackageUtils when parsing
* split APKs.
*
* @hide
*/
public interface SplitAssetLoader extends AutoCloseable {
- AssetManager getBaseAssetManager() throws PackageParser.PackageParserException;
- AssetManager getSplitAssetManager(int splitIdx) throws PackageParser.PackageParserException;
+ AssetManager getBaseAssetManager() throws IllegalArgumentException;
+ AssetManager getSplitAssetManager(int splitIdx) throws IllegalArgumentException;
ApkAssets getBaseApkAssets();
}
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 0140280cd3d5..8df7c37f313b 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -180,7 +180,7 @@ public final class PackageRollbackInfo implements Parcelable {
@NonNull List<Integer> pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
boolean isApex, boolean isApkInApex, @NonNull List<Integer> snapshottedUsers) {
this(packageRolledBackFrom, packageRolledBackTo, pendingBackups, pendingRestores, isApex,
- isApkInApex, snapshottedUsers, PackageManager.RollbackDataPolicy.RESTORE);
+ isApkInApex, snapshottedUsers, PackageManager.ROLLBACK_DATA_POLICY_RESTORE);
}
/** @hide */
@@ -207,7 +207,7 @@ public final class PackageRollbackInfo implements Parcelable {
this.mPendingRestores = null;
this.mPendingBackups = null;
this.mSnapshottedUsers = null;
- this.mRollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
+ this.mRollbackDataPolicy = PackageManager.ROLLBACK_DATA_POLICY_RESTORE;
}
@Override
diff --git a/core/java/android/debug/FingerprintAndPairDevice.aidl b/core/java/android/debug/FingerprintAndPairDevice.aidl
new file mode 100644
index 000000000000..b439e14282e7
--- /dev/null
+++ b/core/java/android/debug/FingerprintAndPairDevice.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.debug;
+
+import android.debug.PairDevice;
+
+/**
+ * @see {@link android.debug.IAdbManager#getPairedDevices()}
+ * @hide
+ */
+parcelable FingerprintAndPairDevice {
+ String keyFingerprint;
+ PairDevice device;
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/core/java/android/debug/IAdbCallback.aidl
index 0037059e2c51..9c8f1cfac527 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
+++ b/core/java/android/debug/IAdbCallback.aidl
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.pip
+package android.debug;
-import android.content.ComponentName
-import com.android.server.wm.traces.common.windowmanager.WindowManagerState
-import com.android.server.wm.traces.parser.toWindowName
+import android.debug.AdbTransportType;
/**
- * Checks that an activity [activity] is in PIP mode
+ * Callback interface of {@link android.debug.IAdbCallbackManager}.
+ *
+ * @hide
*/
-fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
- val windowName = activity.toWindowName()
- return isInPipMode(windowName)
+oneway interface IAdbCallback {
+ /**
+ * On debugging enabled, service providing IAdbManager calls this function.
+ */
+ void onDebuggingChanged(boolean enabled, AdbTransportType type);
}
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index aea7633d91dc..314c4058dd06 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -16,6 +16,9 @@
package android.debug;
+import android.debug.FingerprintAndPairDevice;
+import android.debug.IAdbCallback;
+
/**
* Interface to communicate remotely with the {@code AdbService} in the system server.
*
@@ -58,9 +61,10 @@ interface IAdbManager {
void denyWirelessDebugging();
/**
- * Returns a Map<String, PairDevice> with the key fingerprint mapped to the device information.
+ * Returns an array of NamedPairDevice with the key fingerprint mapped to the device
+ * information.
*/
- Map getPairedDevices();
+ FingerprintAndPairDevice[] getPairedDevices();
/**
* Unpair the device identified by the key fingerprint it uses.
@@ -108,4 +112,14 @@ interface IAdbManager {
* QR code.
*/
boolean isAdbWifiQrSupported();
+
+ /**
+ * Register callback for ADB debugging changed notification.
+ */
+ void registerCallback(IAdbCallback callback);
+
+ /**
+ * Unregister callback for ADB debugging changed notification.
+ */
+ void unregisterCallback(IAdbCallback callback);
}
diff --git a/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl b/core/java/android/debug/PairDevice.aidl
index d3b34c632bcc..c72a5ede3bd4 100644
--- a/core/java/android/uwb/IUwbAdapterStateCallbacks.aidl
+++ b/core/java/android/debug/PairDevice.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,20 +14,26 @@
* limitations under the License.
*/
-package android.uwb;
-
-import android.uwb.StateChangeReason;
-import android.uwb.AdapterState;
+package android.debug;
/**
+ * Contains information about the client in an ADB connection.
+ * @see {@link android.debug.IAdbManager#getPairedDevices()}
* @hide
*/
-interface IUwbAdapterStateCallbacks {
- /**
- * Called whenever the adapter state changes
- *
- * @param state UWB state; enabled_active, enabled_inactive, or disabled.
- * @param reason the reason that the state has changed
+parcelable PairDevice {
+ /**
+ * The human-readable name of the device.
+ */
+ String name;
+
+ /**
+ * The device's guid.
+ */
+ String guid;
+
+ /**
+ * Indicates whether the device is currently connected to adbd.
*/
- void onAdapterStateChanged(AdapterState state, StateChangeReason reason);
+ boolean connected;
} \ No newline at end of file
diff --git a/core/java/android/debug/PairDevice.java b/core/java/android/debug/PairDevice.java
deleted file mode 100644
index 2d5b446b4f8f..000000000000
--- a/core/java/android/debug/PairDevice.java
+++ /dev/null
@@ -1,112 +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.debug;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.Immutable;
-import com.android.internal.util.Preconditions;
-
-/**
- * Contains information about the client in an ADB connection.
- * @hide
- */
-@Immutable
-public class PairDevice implements Parcelable {
- /**
- * The human-readable name of the device.
- */
- @NonNull private final String mName;
-
- /**
- * The device's guid.
- */
- @NonNull private final String mGuid;
-
- /**
- * Indicates whether the device is currently connected to adbd.
- */
- private final boolean mConnected;
-
- public PairDevice(@NonNull String name, @NonNull String guid, boolean connected) {
- Preconditions.checkStringNotEmpty(name);
- Preconditions.checkStringNotEmpty(guid);
- mName = name;
- mGuid = guid;
- mConnected = connected;
- }
-
- /**
- * @return the device name.
- */
- @NonNull
- public String getDeviceName() {
- return mName;
- }
-
- /**
- * @return the device GUID.
- */
- @NonNull
- public String getGuid() {
- return mGuid;
- }
-
- /**
- * @return the adb connection state of the device.
- */
- public boolean isConnected() {
- return mConnected;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mName);
- dest.writeString(mGuid);
- dest.writeBoolean(mConnected);
- }
-
- /**
- * @return Human-readable info about the object.
- */
- @Override
- public String toString() {
- return "\n" + mName + "\n" + mGuid + "\n" + mConnected;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public static final Parcelable.Creator<PairDevice> CREATOR =
- new Creator<PairDevice>() {
- @Override
- public PairDevice createFromParcel(Parcel source) {
- return new PairDevice(source.readString(), source.readString(),
- source.readBoolean());
- }
-
- @Override
- public PairDevice[] newArray(int size) {
- return new PairDevice[size];
- }
- };
-}
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 572a8a81883a..ad4e64b9d7a7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -506,6 +506,7 @@ public abstract class SensorManager {
if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION
|| type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE
|| type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE
+ || type == Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT
|| type == Sensor.TYPE_WRIST_TILT_GESTURE
|| type == Sensor.TYPE_DYNAMIC_SENSOR_META || type == Sensor.TYPE_HINGE_ANGLE) {
wakeUpSensor = true;
diff --git a/core/java/android/hardware/biometrics/OWNERS b/core/java/android/hardware/biometrics/OWNERS
index 2065ffacca7c..0b4d9d9d6f54 100644
--- a/core/java/android/hardware/biometrics/OWNERS
+++ b/core/java/android/hardware/biometrics/OWNERS
@@ -1,8 +1,3 @@
# Bug component: 879035
-curtislb@google.com
-ilyamaty@google.com
-jaggies@google.com
-joshmccloskey@google.com
-kchyn@google.com
-
+include /services/core/java/com/android/server/biometrics/OWNERS
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e13a7b6eac65..5034ef1dfec5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -940,6 +940,34 @@ public final class DisplayManager {
}
/**
+ * Sets the brightness configuration for the specified display.
+ * If the specified display doesn't exist, then this will return and do nothing.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ public void setBrightnessConfigurationForDisplay(@NonNull BrightnessConfiguration c,
+ @NonNull String uniqueId) {
+ mGlobal.setBrightnessConfigurationForDisplay(c, uniqueId, mContext.getUserId(),
+ mContext.getPackageName());
+ }
+
+ /**
+ * Gets the brightness configuration for the specified display and default user.
+ * Returns the default configuration if unset or display is invalid.
+ *
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ public BrightnessConfiguration getBrightnessConfigurationForDisplay(
+ @NonNull String uniqueId) {
+ return mGlobal.getBrightnessConfigurationForDisplay(uniqueId, mContext.getUserId());
+ }
+
+ /**
* Sets the global display brightness configuration for a specific user.
*
* Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a9b95fce8777..3e15c0ee6ebd 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -705,6 +705,34 @@ public final class DisplayManagerGlobal {
}
/**
+ * Sets the brightness configuration for a given display.
+ *
+ * @hide
+ */
+ public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
+ String uniqueDisplayId, int userId, String packageName) {
+ try {
+ mDm.setBrightnessConfigurationForDisplay(c, uniqueDisplayId, userId, packageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the brightness configuration for a given display or null if one hasn't been set.
+ *
+ * @hide
+ */
+ public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId,
+ int userId) {
+ try {
+ return mDm.getBrightnessConfigurationForDisplay(uniqueDisplayId, userId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the global brightness configuration for a given user or null if one hasn't been set.
*
* @hide
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index abcc33c43c74..4f205530ef0d 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.PowerManager;
import android.util.IntArray;
import android.util.Slog;
@@ -340,6 +341,28 @@ public abstract class DisplayManagerInternal {
public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
/**
+ * Returns the window token of the level of the WindowManager hierarchy to mirror. Returns null
+ * if layer mirroring by SurfaceFlinger should not be performed for the given displayId.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ public abstract IBinder getWindowTokenClientToMirror(int displayId);
+
+ /**
+ * For the given displayId, updates the window token of the level of the WindowManager hierarchy
+ * to mirror. If windowToken is null, then SurfaceFlinger performs no layer mirroring to the
+ * given display.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ public abstract void setWindowTokenClientToMirror(int displayId, IBinder windowToken);
+
+ /**
+ * Returns the default size of the surface associated with the display, or null if the surface
+ * is not provided for layer mirroring by SurfaceFlinger.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ public abstract Point getDisplaySurfaceDefaultSize(int displayId);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 2303353ad101..116214625e26 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -118,6 +118,16 @@ interface IDisplayManager {
void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId,
String packageName);
+ // Sets the global brightness configuration for a given display. Requires
+ // CONFIGURE_DISPLAY_BRIGHTNESS.
+ void setBrightnessConfigurationForDisplay(in BrightnessConfiguration c, String uniqueDisplayId,
+ int userId, String packageName);
+
+ // Gets the brightness configuration for a given display. Requires
+ // CONFIGURE_DISPLAY_BRIGHTNESS.
+ BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId,
+ int userId);
+
// Gets the global brightness configuration for a given user. Requires
// CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not
// the same as the calling user.
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 71688c7cc7e8..0e86f43207aa 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.projection.MediaProjection;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
@@ -91,9 +92,16 @@ public final class VirtualDisplayConfig implements Parcelable {
*/
private int mDisplayIdToMirror = DEFAULT_DISPLAY;
+ /**
+ * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+ * should not be performed.
+ */
+ @Nullable
+ private IBinder mWindowTokenClientToMirror = null;
- // Code below generated by codegen v1.0.20.
+
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -115,7 +123,8 @@ public final class VirtualDisplayConfig implements Parcelable {
int flags,
@Nullable Surface surface,
@Nullable String uniqueId,
- int displayIdToMirror) {
+ int displayIdToMirror,
+ @Nullable IBinder windowTokenClientToMirror) {
this.mName = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mName);
@@ -135,6 +144,7 @@ public final class VirtualDisplayConfig implements Parcelable {
this.mSurface = surface;
this.mUniqueId = uniqueId;
this.mDisplayIdToMirror = displayIdToMirror;
+ this.mWindowTokenClientToMirror = windowTokenClientToMirror;
// onConstructed(); // You can define this method to get a callback
}
@@ -212,6 +222,15 @@ public final class VirtualDisplayConfig implements Parcelable {
return mDisplayIdToMirror;
}
+ /**
+ * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+ * should not be performed.
+ */
+ @DataClass.Generated.Member
+ public @Nullable IBinder getWindowTokenClientToMirror() {
+ return mWindowTokenClientToMirror;
+ }
+
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -221,6 +240,7 @@ public final class VirtualDisplayConfig implements Parcelable {
int flg = 0;
if (mSurface != null) flg |= 0x20;
if (mUniqueId != null) flg |= 0x40;
+ if (mWindowTokenClientToMirror != null) flg |= 0x100;
dest.writeInt(flg);
dest.writeString(mName);
dest.writeInt(mWidth);
@@ -230,6 +250,7 @@ public final class VirtualDisplayConfig implements Parcelable {
if (mSurface != null) dest.writeTypedObject(mSurface, flags);
if (mUniqueId != null) dest.writeString(mUniqueId);
dest.writeInt(mDisplayIdToMirror);
+ if (mWindowTokenClientToMirror != null) dest.writeStrongBinder(mWindowTokenClientToMirror);
}
@Override
@@ -252,6 +273,7 @@ public final class VirtualDisplayConfig implements Parcelable {
Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
int displayIdToMirror = in.readInt();
+ IBinder windowTokenClientToMirror = (flg & 0x100) == 0 ? null : (IBinder) in.readStrongBinder();
this.mName = name;
com.android.internal.util.AnnotationValidations.validate(
@@ -272,6 +294,7 @@ public final class VirtualDisplayConfig implements Parcelable {
this.mSurface = surface;
this.mUniqueId = uniqueId;
this.mDisplayIdToMirror = displayIdToMirror;
+ this.mWindowTokenClientToMirror = windowTokenClientToMirror;
// onConstructed(); // You can define this method to get a callback
}
@@ -305,6 +328,7 @@ public final class VirtualDisplayConfig implements Parcelable {
private @Nullable Surface mSurface;
private @Nullable String mUniqueId;
private int mDisplayIdToMirror;
+ private @Nullable IBinder mWindowTokenClientToMirror;
private long mBuilderFieldsSet = 0L;
@@ -439,10 +463,22 @@ public final class VirtualDisplayConfig implements Parcelable {
return this;
}
+ /**
+ * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+ * should not be performed.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setWindowTokenClientToMirror(@NonNull IBinder value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mWindowTokenClientToMirror = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull VirtualDisplayConfig build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x100; // Mark builder used
+ mBuilderFieldsSet |= 0x200; // Mark builder used
if ((mBuilderFieldsSet & 0x10) == 0) {
mFlags = 0;
@@ -456,6 +492,9 @@ public final class VirtualDisplayConfig implements Parcelable {
if ((mBuilderFieldsSet & 0x80) == 0) {
mDisplayIdToMirror = DEFAULT_DISPLAY;
}
+ if ((mBuilderFieldsSet & 0x100) == 0) {
+ mWindowTokenClientToMirror = null;
+ }
VirtualDisplayConfig o = new VirtualDisplayConfig(
mName,
mWidth,
@@ -464,12 +503,13 @@ public final class VirtualDisplayConfig implements Parcelable {
mFlags,
mSurface,
mUniqueId,
- mDisplayIdToMirror);
+ mDisplayIdToMirror,
+ mWindowTokenClientToMirror);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x100) != 0) {
+ if ((mBuilderFieldsSet & 0x200) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -477,10 +517,10 @@ public final class VirtualDisplayConfig implements Parcelable {
}
@DataClass.Generated(
- time = 1604456298440L,
- codegenVersion = "1.0.20",
+ time = 1620657851981L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ecfc0d582b1c..dac1b494e751 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -36,12 +36,10 @@ import android.sysprop.HdmiProperties;
import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ConcurrentUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -71,32 +69,6 @@ public final class HdmiControlManager {
private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
/**
- * A cache of the current device's physical address. When device's HDMI out port
- * is not connected to any device, it is set to {@link #INVALID_PHYSICAL_ADDRESS}.
- *
- * <p>Otherwise it is updated by the {@link ClientHotplugEventListener} registered
- * with {@link com.android.server.hdmi.HdmiControlService} by the
- * {@link #addHotplugEventListener(HotplugEventListener)} and the address is from
- * {@link com.android.server.hdmi.HdmiControlService#getPortInfo()}
- */
- @GuardedBy("mLock")
- private int mLocalPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
-
- private void setLocalPhysicalAddress(int physicalAddress) {
- synchronized (mLock) {
- mLocalPhysicalAddress = physicalAddress;
- }
- }
-
- private int getLocalPhysicalAddress() {
- synchronized (mLock) {
- return mLocalPhysicalAddress;
- }
- }
-
- private final Object mLock = new Object();
-
- /**
* Broadcast Action: Display OSD message.
* <p>Send when the service has a message to display on screen for events
* that need user's attention such as ARC status change.
@@ -346,7 +318,7 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecControl {}
- // -- Supported HDM-CEC versions.
+ // -- Supported HDMI-CEC versions.
/**
* Version constant for HDMI-CEC v1.4b.
*
@@ -371,23 +343,67 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface HdmiCecVersion {}
+ // -- Whether the Routing Control feature is enabled or disabled.
+ /**
+ * Routing Control feature enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_ENABLED = 1;
+ /**
+ * Routing Control feature disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int ROUTING_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "ROUTING_CONTROL_" }, value = {
+ ROUTING_CONTROL_ENABLED,
+ ROUTING_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoutingControl {}
+
// -- Scope of CEC power control messages sent by a playback device.
/**
- * Send CEC power control messages to TV only.
+ * Send CEC power control messages to TV only:
+ * Upon going to sleep, send {@code <Standby>} to TV only.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} but do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_TV = "to_tv";
/**
- * Broadcast CEC power control messages to all devices in the network.
+ * Send CEC power control messages to TV and Audio System:
+ * Upon going to sleep, send {@code <Standby>} to TV and Audio system.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM = "to_tv_and_audio_system";
+ /**
+ * Broadcast CEC power control messages to all devices in the network:
+ * Upon going to sleep, send {@code <Standby>} to all devices in the network.
+ * Upon waking up, attempt to turn on the TV via {@code <One Touch Play>} and attempt to turn on
+ * the Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@SystemApi
public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
/**
- * Don't send any CEC power control messages.
+ * Don't send any CEC power control messages:
+ * Upon going to sleep, do not send any {@code <Standby>} message.
+ * Upon waking up, do not turn on the TV via {@code <One Touch Play>} and do not turn on the
+ * Audio system via {@code <System Audio Mode Request>}.
*
* @hide
*/
@@ -398,6 +414,7 @@ public final class HdmiControlManager {
*/
@StringDef(prefix = { "POWER_CONTROL_MODE_" }, value = {
POWER_CONTROL_MODE_TV,
+ POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
POWER_CONTROL_MODE_BROADCAST,
POWER_CONTROL_MODE_NONE
})
@@ -429,6 +446,31 @@ public final class HdmiControlManager {
@Retention(RetentionPolicy.SOURCE)
public @interface ActiveSourceLostBehavior {}
+ // -- Whether System Audio Control is enabled or disabled.
+ /**
+ * System Audio Control enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_ENABLED = 1;
+ /**
+ * System Audio Control disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int SYSTEM_AUDIO_CONTROL_DISABLED = 0;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "SYSTEM_AUDIO_CONTROL_" }, value = {
+ SYSTEM_AUDIO_CONTROL_ENABLED,
+ SYSTEM_AUDIO_CONTROL_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SystemAudioControl {}
+
// -- Whether System Audio Mode muting is enabled or disabled.
/**
* System Audio Mode muting enabled.
@@ -710,6 +752,13 @@ public final class HdmiControlManager {
@SystemApi
public static final String CEC_SETTING_NAME_HDMI_CEC_VERSION = "hdmi_cec_version";
/**
+ * Name of a setting deciding whether the Routing Control feature is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_ROUTING_CONTROL = "routing_control";
+ /**
* Name of a setting deciding on the power control mode.
*
* @hide
@@ -725,6 +774,14 @@ public final class HdmiControlManager {
public static final String CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST =
"power_state_change_on_active_source_lost";
/**
+ * Name of a setting deciding whether System Audio Control is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL =
+ "system_audio_control";
+ /**
* Name of a setting deciding whether System Audio Muting is allowed.
*
* @hide
@@ -778,7 +835,7 @@ public final class HdmiControlManager {
public static final String CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY =
"tv_wake_on_one_touch_play";
/**
- * Name of a setting deciding whether the device will also turn off other CEC devices
+ * Name of a setting deciding whether the TV will also turn off other CEC devices
* when it goes to standby mode.
*
* @hide
@@ -842,6 +899,7 @@ public final class HdmiControlManager {
CEC_SETTING_NAME_HDMI_CEC_VERSION,
CEC_SETTING_NAME_POWER_CONTROL_MODE,
CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -886,37 +944,6 @@ public final class HdmiControlManager {
mHasAudioSystemDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
mHasSwitchDevice = hasDeviceType(types, HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH);
mIsSwitchDevice = HdmiProperties.is_switch().orElse(false);
- addHotplugEventListener(new ClientHotplugEventListener());
- }
-
- private final class ClientHotplugEventListener implements HotplugEventListener {
-
- @Override
- public void onReceived(HdmiHotplugEvent event) {
- List<HdmiPortInfo> ports = new ArrayList<>();
- try {
- ports = mService.getPortInfo();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- if (ports.isEmpty()) {
- Log.e(TAG, "Can't find port info, not updating connected status. "
- + "Hotplug event:" + event);
- return;
- }
- // If the HDMI OUT port is plugged or unplugged, update the mLocalPhysicalAddress
- for (HdmiPortInfo port : ports) {
- if (port.getId() == event.getPort()) {
- if (port.getType() == HdmiPortInfo.PORT_OUTPUT) {
- setLocalPhysicalAddress(
- event.isConnected()
- ? port.getAddress()
- : INVALID_PHYSICAL_ADDRESS);
- }
- break;
- }
- }
- }
}
private static boolean hasDeviceType(int[] types, int type) {
@@ -1318,7 +1345,11 @@ public final class HdmiControlManager {
*/
@SystemApi
public int getPhysicalAddress() {
- return getLocalPhysicalAddress();
+ try {
+ return mService.getPhysicalAddress();
+ } catch (RemoteException e) {
+ return INVALID_PHYSICAL_ADDRESS;
+ }
}
/**
@@ -1335,7 +1366,7 @@ public final class HdmiControlManager {
@SystemApi
public boolean isDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
Objects.requireNonNull(targetDevice);
- int physicalAddress = getLocalPhysicalAddress();
+ int physicalAddress = getPhysicalAddress();
if (physicalAddress == INVALID_PHYSICAL_ADDRESS) {
return false;
}
@@ -1356,7 +1387,7 @@ public final class HdmiControlManager {
@SystemApi
public boolean isRemoteDeviceConnected(@NonNull HdmiDeviceInfo targetDevice) {
Objects.requireNonNull(targetDevice);
- int physicalAddress = getLocalPhysicalAddress();
+ int physicalAddress = getPhysicalAddress();
if (physicalAddress == INVALID_PHYSICAL_ADDRESS) {
return false;
}
@@ -2021,6 +2052,56 @@ public final class HdmiControlManager {
}
/**
+ * Set the status of Routing Control feature.
+ *
+ * <p>This allows to enable/disable Routing Control on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setRoutingControl(@NonNull @RoutingControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of Routing Control feature.
+ *
+ * <p>Reflects whether Routing Control is currently enabled on the device.
+ * If enabled, the switch device will route to the correct input source on
+ * receiving Routing Control related messages. If disabled, you can only
+ * switch the input via controls on this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @RoutingControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getRoutingControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_ROUTING_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the status of Power Control.
*
* <p>Specifies to which devices Power Control messages should be sent:
@@ -2114,6 +2195,58 @@ public final class HdmiControlManager {
}
/**
+ * Set the current status of System Audio Control.
+ *
+ * <p>Sets whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void setSystemAudioControl(@NonNull @SystemAudioControl int value) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ mService.setCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL, value);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the current status of System Audio Control.
+ *
+ * <p>Reflects whether HDMI System Audio Control feature is enabled. If enabled,
+ * TV or Audio System will try to turn on the System Audio Mode if there's a
+ * connected CEC-enabled AV Receiver. Then an audio stream will be played on
+ * the AVR instead of TV speaker or Audio System speakers. If disabled, the
+ * System Audio Mode will never be activated.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @SystemAudioControl
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public int getSystemAudioControl() {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ throw new RuntimeException("HdmiControlService is not available");
+ }
+ try {
+ return mService.getCecSettingIntValue(CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Set the current status of System Audio Mode muting.
*
* <p>Sets whether the device should be muted when System Audio Mode is turned off.
diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
index cbfbe3940e47..79846cd70784 100644
--- a/core/java/android/hardware/hdmi/HdmiSwitchClient.java
+++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java
@@ -165,7 +165,9 @@ public class HdmiSwitchClient extends HdmiClient {
* there is none.
*
* @hide
+ * @deprecated Please use {@link HdmiControlManager#getConnectedDevices()} instead.
*/
+ @Deprecated
public List<HdmiDeviceInfo> getDeviceList() {
try {
return mService.getDeviceList();
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index f33a1370b3c1..e1ed3eaff1e1 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -158,7 +158,9 @@ public final class HdmiTvClient extends HdmiClient {
*
* @return list of {@link HdmiDeviceInfo} for connected CEC devices.
* Empty list is returned if there is none.
+ * @deprecated Please use {@link HdmiControlManager#getConnectedDevices()} instead.
*/
+ @Deprecated
public List<HdmiDeviceInfo> getDeviceList() {
try {
return mService.getDeviceList();
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 60d43fd077d0..861e4409b014 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -3,5 +3,4 @@
include /services/core/java/com/android/server/display/OWNERS
marvinramin@google.com
-nchalko@google.com
lcnathalie@google.com
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 17116e248646..e5d86204077b 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -435,7 +435,7 @@ public final class InputManager {
/**
* Enables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -454,7 +454,7 @@ public final class InputManager {
/**
* Disables an InputDevice.
* <p>
- * Requires {@link android.Manifest.permissions.DISABLE_INPUT_DEVICE}.
+ * Requires {@link android.Manifest.permission.DISABLE_INPUT_DEVICE}.
* </p>
*
* @param id The input device Id.
@@ -831,7 +831,7 @@ public final class InputManager {
* Sets the TouchCalibration to apply to the specified input device's coordinates.
* <p>
* This method may have the side-effect of causing the input device in question
- * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+ * to be reconfigured. Requires {@link android.Manifest.permission.SET_INPUT_CALIBRATION}.
* </p>
*
* @param inputDeviceDescriptor The input device descriptor.
@@ -874,7 +874,7 @@ public final class InputManager {
/**
* Sets the mouse pointer speed.
* <p>
- * Requires {@link android.Manifest.permissions.WRITE_SETTINGS}.
+ * Requires {@link android.Manifest.permission.WRITE_SETTINGS}.
* </p>
*
* @param context The application context.
@@ -1285,7 +1285,7 @@ public final class InputManager {
* @param inputPort The port of the input device.
* @param displayPort The physical port of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1302,7 +1302,7 @@ public final class InputManager {
* static association for the cleared input port will be restored.
* @param inputPort The port of the input device to be cleared.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1320,7 +1320,7 @@ public final class InputManager {
* @param inputDeviceName The name of the input device.
* @param displayUniqueId The unique id of the associated display.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
@@ -1337,7 +1337,7 @@ public final class InputManager {
* Removes a runtime association between the input device and display.
* @param inputDeviceName The name of the input device.
* <p>
- * Requires {@link android.Manifest.permissions.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
+ * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
* </p>
* @hide
*/
diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java
index 21ac71cb9163..bcdd519b1006 100644
--- a/core/java/android/hardware/location/ContextHubClient.java
+++ b/core/java/android/hardware/location/ContextHubClient.java
@@ -152,10 +152,7 @@ public class ContextHubClient implements Closeable {
* @see NanoAppMessage
* @see ContextHubTransaction.Result
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@ContextHubTransaction.Result
public int sendMessageToNanoApp(@NonNull NanoAppMessage message) {
Objects.requireNonNull(message, "NanoAppMessage cannot be null");
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 9af0e09ee97a..96923b0182e4 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -47,9 +47,7 @@ import java.util.concurrent.Executor;
* A class that exposes the Context hubs on a device to applications.
*
* Please note that this class is not expected to be used by unbundled applications. Also, calling
- * applications are expected to have LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permissions to use this
- * class. Use of LOCATION_HARDWARE to enable access to these APIs is deprecated and may be removed
- * in the future - all applications are recommended to move to the ACCESS_CONTEXT_HUB permission.
+ * applications are expected to have the ACCESS_CONTEXT_HUB permission to use this class.
*
* @hide
*/
@@ -253,10 +251,7 @@ public final class ContextHubManager {
* new APIs.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int[] getContextHubHandles() {
try {
return mService.getContextHubHandles();
@@ -277,10 +272,7 @@ public final class ContextHubManager {
* new APIs.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public ContextHubInfo getContextHubInfo(int hubHandle) {
try {
return mService.getContextHubInfo(hubHandle);
@@ -311,10 +303,7 @@ public final class ContextHubManager {
* @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
try {
return mService.loadNanoApp(hubHandle, app);
@@ -341,10 +330,7 @@ public final class ContextHubManager {
* @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int unloadNanoApp(int nanoAppHandle) {
try {
return mService.unloadNanoApp(nanoAppHandle);
@@ -384,10 +370,7 @@ public final class ContextHubManager {
* for loaded nanoapps.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
try {
return mService.getNanoAppInstanceInfo(nanoAppHandle);
@@ -410,10 +393,7 @@ public final class ContextHubManager {
* for loaded nanoapps.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
try {
return mService.findNanoAppOnHub(hubHandle, filter);
@@ -448,10 +428,7 @@ public final class ContextHubManager {
* or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
try {
return mService.sendMessage(hubHandle, nanoAppHandle, message);
@@ -467,10 +444,7 @@ public final class ContextHubManager {
*
* @see ContextHubInfo
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public List<ContextHubInfo> getContextHubs() {
try {
return mService.getContextHubs();
@@ -547,10 +521,7 @@ public final class ContextHubManager {
*
* @see NanoAppBinary
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubTransaction<Void> loadNanoApp(
@NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -579,10 +550,7 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubTransaction<Void> unloadNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -610,10 +578,7 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubTransaction<Void> enableNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -641,10 +606,7 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubTransaction<Void> disableNanoApp(
@NonNull ContextHubInfo hubInfo, long nanoAppId) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -671,10 +633,7 @@ public final class ContextHubManager {
*
* @throws NullPointerException if hubInfo is null
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
@NonNull ContextHubInfo hubInfo) {
Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
@@ -829,10 +788,7 @@ public final class ContextHubManager {
*
* @see ContextHubClientCallback
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubClient createClient(
@Nullable Context context, @NonNull ContextHubInfo hubInfo,
@NonNull @CallbackExecutor Executor executor,
@@ -876,10 +832,7 @@ public final class ContextHubManager {
* {@link #createClient(ContextHubInfo, Executor, String, ContextHubClientCallback)}
* with the {@link Context} being set to null.
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
@NonNull @CallbackExecutor Executor executor) {
@@ -890,10 +843,7 @@ public final class ContextHubManager {
* Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
* with the executor using the main thread's Looper.
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
@@ -931,6 +881,9 @@ public final class ContextHubManager {
* on the provided PendingIntent, then the client will be automatically unregistered by the
* service.
*
+ * Note that the {@link PendingIntent} supplied to this API must be mutable for Intent
+ * notifications to work.
+ *
* @param context the context of the application. If a PendingIntent client is recreated,
* the latest state in the context will be used and old state will be discarded
* @param hubInfo the hub to attach this client to
@@ -938,19 +891,20 @@ public final class ContextHubManager {
* @param nanoAppId the ID of the nanoapp that Intent events will be generated for
* @return the registered client object
*
- * @throws IllegalArgumentException if hubInfo does not represent a valid hub
+ * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or an immutable
+ * PendingIntent was supplied
* @throws IllegalStateException if there were too many registered clients at the service
* @throws NullPointerException if pendingIntent or hubInfo is null
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubClient createClient(
@Nullable Context context, @NonNull ContextHubInfo hubInfo,
@NonNull PendingIntent pendingIntent, long nanoAppId) {
Objects.requireNonNull(pendingIntent);
Objects.requireNonNull(hubInfo);
+ if (pendingIntent.isImmutable()) {
+ throw new IllegalArgumentException("PendingIntent must be mutable");
+ }
ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
@@ -975,10 +929,7 @@ public final class ContextHubManager {
* Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
* with {@link Context} being set to null.
*/
- @RequiresPermission(anyOf = {
- android.Manifest.permission.LOCATION_HARDWARE,
- android.Manifest.permission.ACCESS_CONTEXT_HUB
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@NonNull public ContextHubClient createClient(
@NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 429f1f351a93..7bf234b9d5a4 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -80,14 +80,14 @@ interface ITuner {
void setConfigFlag(int flag, boolean value);
/**
- * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
- * @return Vendor-specific key-value pairs, must be Map<String, String>
+ * @param parameters Vendor-specific key-value pairs
+ * @return Vendor-specific key-value pairs
*/
- Map setParameters(in Map parameters);
+ Map<String, String> setParameters(in Map<String, String> parameters);
/**
* @param keys Parameter keys to fetch
- * @return Vendor-specific key-value pairs, must be Map<String, String>
+ * @return Vendor-specific key-value pairs
*/
- Map getParameters(in List<String> keys);
+ Map<String, String> getParameters(in List<String> keys);
}
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index b32daa5a7609..f98947b50e34 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -36,7 +36,7 @@ oneway interface ITunerCallback {
void onProgramListUpdated(in ProgramList.Chunk chunk);
/**
- * @param parameters Vendor-specific key-value pairs, must be Map<String, String>
+ * @param parameters Vendor-specific key-value pairs
*/
- void onParametersUpdated(in Map parameters);
+ void onParametersUpdated(in Map<String, String> parameters);
}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index beff0f7607f8..e3f7001d8740 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -220,7 +220,7 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
}
@Override
- public void onParametersUpdated(Map parameters) {
+ public void onParametersUpdated(Map<String, String> parameters) {
mHandler.post(() -> mCallback.onParametersUpdated(parameters));
}
}
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a5c9a7fafbd8..ad0dc0965b0a 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -19,19 +19,19 @@ package android.hardware.soundtrigger;
import android.annotation.Nullable;
import android.media.AudioFormat;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
@@ -43,7 +43,7 @@ import java.util.UUID;
class ConversionUtil {
public static SoundTrigger.ModuleProperties aidl2apiModuleDescriptor(
SoundTriggerModuleDescriptor aidlDesc) {
- SoundTriggerModuleProperties properties = aidlDesc.properties;
+ Properties properties = aidlDesc.properties;
return new SoundTrigger.ModuleProperties(
aidlDesc.handle,
properties.implementor,
@@ -194,19 +194,19 @@ class ConversionUtil {
}
public static SoundTrigger.RecognitionEvent aidl2apiRecognitionEvent(
- int modelHandle, RecognitionEvent aidlEvent) {
+ int modelHandle, int captureSession, RecognitionEvent aidlEvent) {
// The API recognition event doesn't allow for a null audio format, even though it doesn't
// always make sense. We thus replace it with a default.
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.audioConfig);
return new SoundTrigger.GenericRecognitionEvent(
aidlEvent.status,
- modelHandle, aidlEvent.captureAvailable, aidlEvent.captureSession,
+ modelHandle, aidlEvent.captureAvailable, captureSession,
aidlEvent.captureDelayMs, aidlEvent.capturePreambleMs, aidlEvent.triggerInData,
audioFormat, aidlEvent.data);
}
public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent(
- int modelHandle,
+ int modelHandle, int captureSession,
PhraseRecognitionEvent aidlEvent) {
SoundTrigger.KeyphraseRecognitionExtra[] apiExtras =
new SoundTrigger.KeyphraseRecognitionExtra[aidlEvent.phraseExtras.length];
@@ -218,7 +218,7 @@ class ConversionUtil {
AudioFormat audioFormat = aidl2apiAudioFormatWithDefault(aidlEvent.common.audioConfig);
return new SoundTrigger.KeyphraseRecognitionEvent(aidlEvent.common.status, modelHandle,
aidlEvent.common.captureAvailable,
- aidlEvent.common.captureSession, aidlEvent.common.captureDelayMs,
+ captureSession, aidlEvent.common.captureDelayMs,
aidlEvent.common.capturePreambleMs, aidlEvent.common.triggerInData,
audioFormat, aidlEvent.common.data,
apiExtras);
@@ -328,9 +328,9 @@ class ConversionUtil {
public static int api2aidlModelParameter(int apiParam) {
switch (apiParam) {
case ModelParams.THRESHOLD_FACTOR:
- return android.media.soundtrigger_middleware.ModelParameter.THRESHOLD_FACTOR;
+ return android.media.soundtrigger.ModelParameter.THRESHOLD_FACTOR;
default:
- return android.media.soundtrigger_middleware.ModelParameter.INVALID;
+ return android.media.soundtrigger.ModelParameter.INVALID;
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 11f3e4582ac2..163e6f0a0114 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -19,6 +19,7 @@ package android.hardware.soundtrigger;
import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
+import static android.system.OsConstants.EBUSY;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOSYS;
@@ -41,9 +42,9 @@ import android.media.AudioFormat;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -91,6 +92,8 @@ public class SoundTrigger {
public static final int STATUS_DEAD_OBJECT = -EPIPE;
/** @hide */
public static final int STATUS_INVALID_OPERATION = -ENOSYS;
+ /** @hide */
+ public static final int STATUS_BUSY = -EBUSY;
/*****************************************************************************
* A ModuleProperties describes a given sound trigger hardware module
@@ -1835,120 +1838,6 @@ public class SoundTrigger {
}
}
- /**
- * Status codes for {@link SoundModelEvent}
- */
- /**
- * Sound Model was updated
- *
- * @hide
- */
- public static final int SOUNDMODEL_STATUS_UPDATED = 0;
-
- /**
- * A SoundModelEvent is provided by the
- * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
- * callback when a sound model has been updated by the implementation
- *
- * @hide
- */
- public static class SoundModelEvent implements Parcelable {
- /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
- public final int status;
- /** The updated sound model handle */
- public final int soundModelHandle;
- /** New sound model data */
- @NonNull
- public final byte[] data;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
- this.status = status;
- this.soundModelHandle = soundModelHandle;
- this.data = data != null ? data : new byte[0];
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
- = new Parcelable.Creator<SoundModelEvent>() {
- public SoundModelEvent createFromParcel(Parcel in) {
- return SoundModelEvent.fromParcel(in);
- }
-
- public SoundModelEvent[] newArray(int size) {
- return new SoundModelEvent[size];
- }
- };
-
- private static SoundModelEvent fromParcel(Parcel in) {
- int status = in.readInt();
- int soundModelHandle = in.readInt();
- byte[] data = in.readBlob();
- return new SoundModelEvent(status, soundModelHandle, data);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(status);
- dest.writeInt(soundModelHandle);
- dest.writeBlob(data);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + Arrays.hashCode(data);
- result = prime * result + soundModelHandle;
- result = prime * result + status;
- return result;
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- SoundModelEvent other = (SoundModelEvent) obj;
- if (!Arrays.equals(data, other.data))
- return false;
- if (soundModelHandle != other.soundModelHandle)
- return false;
- if (status != other.status)
- return false;
- return true;
- }
-
- @Override
- public String toString() {
- return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
- + ", data=" + (data == null ? 0 : data.length) + "]";
- }
- }
-
- /**
- * Native service state. {@link StatusListener#onServiceStateChange(int)}
- */
- // Keep in sync with system/core/include/system/sound_trigger.h
- /**
- * Sound trigger service is enabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_ENABLED = 0;
- /**
- * Sound trigger service is disabled
- *
- * @hide
- */
- public static final int SERVICE_STATE_DISABLED = 1;
private static Object mServiceLock = new Object();
private static ISoundTriggerMiddlewareService mService;
@@ -1975,6 +1864,8 @@ public class SoundTrigger {
return STATUS_DEAD_OBJECT;
case Status.INTERNAL_ERROR:
return STATUS_ERROR;
+ case Status.RESOURCE_CONTENTION:
+ return STATUS_BUSY;
}
return STATUS_ERROR;
}
@@ -2224,27 +2115,28 @@ public class SoundTrigger {
*
* @hide
*/
- public static interface StatusListener {
+ public interface StatusListener {
/**
* Called when recognition succeeds of fails
*/
- public abstract void onRecognition(RecognitionEvent event);
+ void onRecognition(RecognitionEvent event);
/**
- * Called when a sound model has been updated
+ * Called when a sound model has been preemptively unloaded by the underlying
+ * implementation.
*/
- public abstract void onSoundModelUpdate(SoundModelEvent event);
+ void onModelUnloaded(int modelHandle);
/**
- * Called when the sound trigger native service state changes.
- * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
- * {@link SoundTrigger#SERVICE_STATE_DISABLED}
+ * Called whenever underlying conditions change, such that load/start operations that have
+ * previously failed or got preempted may now succeed. This is not a guarantee, merely a
+ * hint that the client may want to retry operations.
*/
- public abstract void onServiceStateChange(int state);
+ void onResourcesAvailable();
/**
* Called when the sound trigger native service dies
*/
- public abstract void onServiceDied();
+ void onServiceDied();
}
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 431c99dbdc1f..bf4b51431c6f 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -22,13 +22,13 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -48,7 +48,8 @@ public class SoundTriggerModule {
private static final int EVENT_RECOGNITION = 1;
private static final int EVENT_SERVICE_DIED = 2;
- private static final int EVENT_SERVICE_STATE_CHANGE = 3;
+ private static final int EVENT_RESOURCES_AVAILABLE = 3;
+ private static final int EVENT_MODEL_UNLOADED = 4;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mId;
private EventHandlerDelegate mEventHandlerDelegate;
@@ -120,6 +121,7 @@ public class SoundTriggerModule {
* @param soundModelHandle an array of int where the sound model handle will be returned.
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -181,6 +183,7 @@ public class SoundTriggerModule {
* recognition mode, keyphrases, users, minimum confidence levels...
* @return - {@link SoundTrigger#STATUS_OK} in case of success
* - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_BUSY} in case of transient resource constraints
* - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
* system permission
* - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
@@ -333,8 +336,11 @@ public class SoundTriggerModule {
listener.onRecognition(
(SoundTrigger.RecognitionEvent) msg.obj);
break;
- case EVENT_SERVICE_STATE_CHANGE:
- listener.onServiceStateChange((int) msg.obj);
+ case EVENT_RESOURCES_AVAILABLE:
+ listener.onResourcesAvailable();
+ break;
+ case EVENT_MODEL_UNLOADED:
+ listener.onModelUnloaded((Integer) msg.obj);
break;
case EVENT_SERVICE_DIED:
listener.onServiceDied();
@@ -348,27 +354,32 @@ public class SoundTriggerModule {
}
@Override
- public synchronized void onRecognition(int handle, RecognitionEvent event)
+ public synchronized void onRecognition(int handle, RecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event)
+ public synchronized void onPhraseRecognition(int handle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
Message m = mHandler.obtainMessage(EVENT_RECOGNITION,
- ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, event));
+ ConversionUtil.aidl2apiPhraseRecognitionEvent(handle, captureSession, event));
mHandler.sendMessage(m);
}
@Override
- public synchronized void onRecognitionAvailabilityChange(boolean available)
- throws RemoteException {
- Message m = mHandler.obtainMessage(EVENT_SERVICE_STATE_CHANGE,
- available ? SoundTrigger.SERVICE_STATE_ENABLED
- : SoundTrigger.SERVICE_STATE_DISABLED);
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_MODEL_UNLOADED, modelHandle);
+ mHandler.sendMessage(m);
+ }
+
+ @Override
+ public synchronized void onResourcesAvailable() throws RemoteException {
+ Message m = mHandler.obtainMessage(EVENT_RESOURCES_AVAILABLE);
mHandler.sendMessage(m);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 964d7c1658dc..c29a948c2a26 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -384,8 +384,9 @@ public class UsbManager {
"android.hardware.usb.extra.ACCESSORY_START";
/**
- * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE}
- * This is obtained with SystemClock.elapsedRealtime()
+
+ * A long extra indicating the timestamp just before
+ * sending {@link #ACTION_USB_ACCESSORY_HANDSHAKE}.
* Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts.
*
* {@hide}
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3cd13a212a4b..a6e77023790c 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -18,16 +18,20 @@ package android.inputmethodservice;
import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.app.Service;
+import android.annotation.Nullable;
+import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
-import android.util.proto.ProtoOutputStream;
+import android.os.RemoteException;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
+import android.window.WindowProviderService;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,9 +48,22 @@ import java.io.PrintWriter;
* implement. This base class takes care of reporting your InputMethod from
* the service when clients bind to it, but provides no standard implementation
* of the InputMethod interface itself. Derived classes must implement that
- * interface.
+ * interface.</p>
+ *
+ * <p>After {@link android.os.Build.VERSION_CODES#S}, the maximum possible area to show the soft
+ * input may not be the entire screen. For example, some devices may support to show the soft input
+ * on only half of screen.</p>
+ *
+ * <p>In that case, moving the soft input from one half screen to another will trigger a
+ * {@link android.content.res.Resources} update to match the new {@link Configuration} and
+ * this {@link AbstractInputMethodService} may also receive a
+ * {@link #onConfigurationChanged(Configuration)} callback if there's notable configuration changes
+ * </p>
+ *
+ * @see android.content.ComponentCallbacks#onConfigurationChanged(Configuration)
+ * @see Context#isUiContext Context#isUiContext to see the concept of UI Context.
*/
-public abstract class AbstractInputMethodService extends Service
+public abstract class AbstractInputMethodService extends WindowProviderService
implements KeyEvent.Callback {
private InputMethod mInputMethod;
@@ -196,16 +213,6 @@ public abstract class AbstractInputMethodService extends Service
public abstract AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
/**
- * Dumps the internal state of IME to a protocol buffer output stream.
- *
- * @param proto ProtoOutputStream to dump data to.
- * @param icProto {@link InputConnection} call data in proto format.
- * @hide
- */
- @SuppressWarnings("HiddenAbstractMethod")
- public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto);
-
- /**
* Implement this to handle {@link android.os.Binder#dump Binder.dump()}
* calls on your input method.
*/
@@ -218,9 +225,36 @@ public abstract class AbstractInputMethodService extends Service
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
- return new IInputMethodWrapper(this, mInputMethod);
+ return new IInputMethodWrapper(createInputMethodServiceInternal(), mInputMethod);
}
-
+
+ /**
+ * Used to inject custom {@link InputMethodServiceInternal}.
+ *
+ * @return the {@link InputMethodServiceInternal} to be used.
+ */
+ @NonNull
+ InputMethodServiceInternal createInputMethodServiceInternal() {
+ return new InputMethodServiceInternal() {
+ /**
+ * {@inheritDoc}
+ */
+ @NonNull
+ @Override
+ public Context getContext() {
+ return AbstractInputMethodService.this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ AbstractInputMethodService.this.dump(fd, fout, args);
+ }
+ };
+ }
+
/**
* Implement this to handle trackball events on your input method.
*
@@ -243,38 +277,33 @@ public abstract class AbstractInputMethodService extends Service
return false;
}
- /**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
- *
- * <p>Default implementation does nothing.</p>
- *
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param inputConnection {@link InputConnection} with which
- * {@link InputConnection#commitContent(InputContentInfo, int, android.os.Bundle)} will be
- * called.
- * @return {@code false} if we cannot allow a temporary access permission.
- * @hide
- */
- public void exposeContent(@NonNull InputContentInfo inputContentInfo,
- @NonNull InputConnection inputConnection) {
- return;
+ // TODO(b/149463653): remove it in T. We missed the API deadline in S.
+ /** @hide */
+ @Override
+ public final boolean isUiContext() {
+ return true;
}
- /**
- * Called when the user took some actions that should be taken into consideration to update the
- * MRU list for input method rotation.
- *
- * @hide
- */
- public void notifyUserActionIfNecessary() {
+ /** @hide */
+ @Override
+ public final int getWindowType() {
+ return WindowManager.LayoutParams.TYPE_INPUT_METHOD;
}
/** @hide */
@Override
- public final boolean isUiContext() {
- return true;
+ @Nullable
+ public final Bundle getWindowContextOptions() {
+ return super.getWindowContextOptions();
+ }
+
+ /** @hide */
+ @Override
+ public final int getInitialDisplayId() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getImeDisplayId();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 9198eb74d1f8..90990b45e552 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -47,7 +47,6 @@ import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputConnectionWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -77,7 +76,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_CHANGE_INPUTMETHOD_SUBTYPE = 80;
private static final int DO_CREATE_INLINE_SUGGESTIONS_REQUEST = 90;
- final WeakReference<AbstractInputMethodService> mTarget;
+ final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@UnsupportedAppUsage
final HandlerCaller mCaller;
@@ -86,7 +85,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
/**
* This is not {@null} only between {@link #bindInput(InputBinding)} and {@link #unbindInput()}
- * so that {@link InputConnectionWrapper} can query if {@link #unbindInput()} has already been
+ * so that {@link RemoteInputConnection} can query if {@link #unbindInput()} has already been
* called or not, mainly to avoid unnecessary blocking operations.
*
* <p>This field must be set and cleared only from the binder thread(s), where the system
@@ -130,12 +129,12 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
}
- public IInputMethodWrapper(AbstractInputMethodService context, InputMethod inputMethod) {
- mTarget = new WeakReference<>(context);
- mContext = context.getApplicationContext();
+ IInputMethodWrapper(InputMethodServiceInternal imsInternal, InputMethod inputMethod) {
+ mTarget = new WeakReference<>(imsInternal);
+ mContext = imsInternal.getContext().getApplicationContext();
mCaller = new HandlerCaller(mContext, null, this, true /*asyncHandler*/);
mInputMethod = new WeakReference<>(inputMethod);
- mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+ mTargetSdkVersion = imsInternal.getContext().getApplicationInfo().targetSdkVersion;
}
@MainThread
@@ -150,7 +149,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
switch (msg.what) {
case DO_DUMP: {
- AbstractInputMethodService target = mTarget.get();
+ InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
@@ -170,8 +169,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_INITIALIZE_INTERNAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- inputMethod.initializeInternal((IBinder) args.arg1, msg.arg1,
- (IInputMethodPrivilegedOperations) args.arg2, (int) args.arg3);
+ inputMethod.initializeInternal((IBinder) args.arg1,
+ (IInputMethodPrivilegedOperations) args.arg2, msg.arg1);
} finally {
args.recycle();
}
@@ -192,7 +191,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
SomeArgs moreArgs = (SomeArgs) args.arg5;
final InputConnection ic = inputContext != null
- ? new InputConnectionWrapper(
+ ? new RemoteInputConnection(
mTarget, inputContext, moreArgs.argi3, cancellationGroup)
: null;
info.makeCompatible(mTargetSdkVersion);
@@ -252,11 +251,11 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
- AbstractInputMethodService target = mTarget.get();
+ InputMethodServiceInternal target = mTarget.get();
if (target == null) {
return;
}
- if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (target.getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid="
@@ -279,11 +278,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
@BinderThread
@Override
- public void initializeInternal(IBinder token, int displayId,
- IInputMethodPrivilegedOperations privOps, int configChanges) {
+ public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
+ int configChanges) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageIOOO(DO_INITIALIZE_INTERNAL, displayId, token, privOps,
- configChanges));
+ mCaller.obtainMessageIOO(DO_INITIALIZE_INTERNAL, configChanges, token, privOps));
}
@BinderThread
@@ -303,7 +301,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCancellationGroup = new CancellationGroup();
// This IInputContext is guaranteed to implement all the methods.
final int missingMethodFlags = 0;
- InputConnection ic = new InputConnectionWrapper(mTarget,
+ InputConnection ic = new RemoteInputConnection(mTarget,
IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
mCancellationGroup);
InputBinding nu = new InputBinding(ic, binding);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 881e0cfb58d7..dadea6792ac1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -55,7 +55,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
@@ -94,7 +93,6 @@ import android.text.method.MovementMethod;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.Gravity;
import android.view.KeyCharacterMap;
@@ -133,6 +131,7 @@ import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -589,7 +588,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
@MainThread
@Override
- public final void initializeInternal(@NonNull IBinder token, int displayId,
+ public final void initializeInternal(@NonNull IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
if (InputMethodPrivilegedOperationsRegistry.isRegistered(token)) {
Log.w(TAG, "The token has already registered, ignore this initialization.");
@@ -599,7 +598,6 @@ public class InputMethodService extends AbstractInputMethodService {
mConfigTracker.onInitialize(configChanges);
mPrivOps.set(privilegedOperations);
InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps);
- updateInputMethodDisplay(displayId);
attachToken(token);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -629,29 +627,13 @@ public class InputMethodService extends AbstractInputMethodService {
throw new IllegalStateException(
"attachToken() must be called at most once. token=" + token);
}
+ attachToWindowToken(token);
mToken = token;
mWindow.setToken(token);
}
/**
* {@inheritDoc}
- * @hide
- */
- @MainThread
- @Override
- public void updateInputMethodDisplay(int displayId) {
- if (getDisplayId() == displayId) {
- return;
- }
- // Update display for adding IME window to the right display.
- // TODO(b/111364446) Need to address context lifecycle issue if need to re-create
- // for update resources & configuration correctly when show soft input
- // in non-default display.
- updateDisplay(displayId);
- }
-
- /**
- * {@inheritDoc}
*
* <p>Calls {@link InputMethodService#onBindInput()} when done.</p>
*/
@@ -757,7 +739,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this,
+ "InputMethodService.InputMethodImpl#hideSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
@@ -814,7 +796,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this,
+ "InputMethodService.InputMethodImpl#showSoftInput", mDumper,
null /* icProto */);
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
@@ -2239,7 +2221,7 @@ public class InputMethodService extends AbstractInputMethodService {
return;
}
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", mDumper,
null /* icProto */);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
@@ -2318,7 +2300,7 @@ public class InputMethodService extends AbstractInputMethodService {
*/
private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) {
ImeTracing.getInstance().triggerServiceDump(
- "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this,
+ "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
if (setVisible) {
cancelImeSurfaceRemoval();
@@ -2347,7 +2329,7 @@ public class InputMethodService extends AbstractInputMethodService {
public void hideWindow() {
if (DEBUG) Log.v(TAG, "CALL: hideWindow");
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
mWindowVisible = false;
finishViews(false /* finishingInput */);
@@ -2420,7 +2402,7 @@ public class InputMethodService extends AbstractInputMethodService {
void doFinishInput() {
if (DEBUG) Log.v(TAG, "CALL: doFinishInput");
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", mDumper,
null /* icProto */);
finishViews(true /* finishingInput */);
if (mInputStarted) {
@@ -2437,7 +2419,7 @@ public class InputMethodService extends AbstractInputMethodService {
if (!restarting && mInputStarted) {
doFinishInput();
}
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", mDumper,
null /* icProto */);
mInputStarted = true;
mStartedInputConnection = ic;
@@ -2597,7 +2579,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
null /* icProto */);
mPrivOps.hideMySoftInput(flags);
}
@@ -2611,7 +2593,7 @@ public class InputMethodService extends AbstractInputMethodService {
* @param flags Provides additional operating flags.
*/
public final void requestShowSelf(int flags) {
- ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this,
+ ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
null /* icProto */);
mPrivOps.showMySoftInput(flags);
}
@@ -3298,67 +3280,91 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
+ * Used to inject custom {@link InputMethodServiceInternal}.
*
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param inputConnection {@link InputConnection} with which
- * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)} will be called.
- * @hide
+ * @return the {@link InputMethodServiceInternal} to be used.
*/
+ @NonNull
@Override
- public final void exposeContent(@NonNull InputContentInfo inputContentInfo,
- @NonNull InputConnection inputConnection) {
- if (inputConnection == null) {
- return;
- }
- if (getCurrentInputConnection() != inputConnection) {
- return;
- }
- exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
- }
+ final InputMethodServiceInternal createInputMethodServiceInternal() {
+ return new InputMethodServiceInternal() {
+ /**
+ * {@inheritDoc}
+ */
+ @NonNull
+ @Override
+ public Context getContext() {
+ return InputMethodService.this;
+ }
- /**
- * {@inheritDoc}
- * @hide
- */
- @AnyThread
- @Override
- public final void notifyUserActionIfNecessary() {
- synchronized (mLock) {
- if (mNotifyUserActionSent) {
- return;
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ if (inputConnection == null) {
+ return;
+ }
+ if (getCurrentInputConnection() != inputConnection) {
+ return;
+ }
+ exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
}
- mPrivOps.notifyUserActionAsync();
- mNotifyUserActionSent = true;
- }
- }
- /**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
- *
- * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
- * InputConnection)} for details.</p>
- *
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param editorInfo The editor that receives {@link InputContentInfo}.
- */
- private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
- @NonNull EditorInfo editorInfo) {
- final Uri contentUri = inputContentInfo.getContentUri();
- final IInputContentUriToken uriToken =
- mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
- if (uriToken == null) {
- Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
- + " packageName=" + editorInfo.packageName);
- return;
- }
- inputContentInfo.setUriToken(uriToken);
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void notifyUserActionIfNecessary() {
+ synchronized (mLock) {
+ if (mNotifyUserActionSent) {
+ return;
+ }
+ mPrivOps.notifyUserActionAsync();
+ mNotifyUserActionSent = true;
+ }
+ }
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>See {@link #exposeContent(InputContentInfo, InputConnection)} for details.</p>
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to
+ * the application. This cannot be {@code null}.
+ * @param editorInfo The editor that receives {@link InputContentInfo}.
+ */
+ private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
+ @NonNull EditorInfo editorInfo) {
+ final Uri contentUri = inputContentInfo.getContentUri();
+ final IInputContentUriToken uriToken =
+ mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
+ if (uriToken == null) {
+ Log.e(TAG, "createInputContentAccessToken failed. contentUri="
+ + contentUri.toString() + " packageName=" + editorInfo.packageName);
+ return;
+ }
+ inputContentInfo.setUriToken(uriToken);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter fout, String[]args) {
+ InputMethodService.this.dump(fd, fout, args);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void triggerServiceDump(String where, @Nullable byte[] icProto) {
+ ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
+ }
+ };
}
private int mapToImeWindowStatus() {
@@ -3428,42 +3434,44 @@ public class InputMethodService extends AbstractInputMethodService {
p.println(" mSettingsObserver=" + mSettingsObserver);
}
- /**
- * @hide
- */
- @Override
- public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) {
- final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
- mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
- proto.write(VIEWS_CREATED, mViewsCreated);
- proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
- proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
- proto.write(WINDOW_VISIBLE, mWindowVisible);
- proto.write(IN_SHOW_WINDOW, mInShowWindow);
- proto.write(CONFIGURATION, getResources().getConfiguration().toString());
- proto.write(TOKEN, Objects.toString(mToken));
- proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
- proto.write(INPUT_STARTED, mInputStarted);
- proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
- proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
- if (mInputEditorInfo != null) {
- mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
- }
- proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
- proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
- proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
- proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
- proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
- proto.write(IS_FULLSCREEN, mIsFullscreen);
- proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
- proto.write(EXTRACTED_TOKEN, mExtractedToken);
- proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
- proto.write(STATUS_ICON, mStatusIcon);
- mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
- proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
- if (icProto != null) {
- proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
- }
- proto.end(token);
- }
+ private final ImeTracing.ServiceDumper mDumper = new ImeTracing.ServiceDumper() {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto) {
+ final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE);
+ mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW);
+ proto.write(VIEWS_CREATED, mViewsCreated);
+ proto.write(DECOR_VIEW_VISIBLE, mDecorViewVisible);
+ proto.write(DECOR_VIEW_WAS_VISIBLE, mDecorViewWasVisible);
+ proto.write(WINDOW_VISIBLE, mWindowVisible);
+ proto.write(IN_SHOW_WINDOW, mInShowWindow);
+ proto.write(CONFIGURATION, getResources().getConfiguration().toString());
+ proto.write(TOKEN, Objects.toString(mToken));
+ proto.write(INPUT_BINDING, Objects.toString(mInputBinding));
+ proto.write(INPUT_STARTED, mInputStarted);
+ proto.write(INPUT_VIEW_STARTED, mInputViewStarted);
+ proto.write(CANDIDATES_VIEW_STARTED, mCandidatesViewStarted);
+ if (mInputEditorInfo != null) {
+ mInputEditorInfo.dumpDebug(proto, INPUT_EDITOR_INFO);
+ }
+ proto.write(SHOW_INPUT_REQUESTED, mShowInputRequested);
+ proto.write(LAST_SHOW_INPUT_REQUESTED, mLastShowInputRequested);
+ proto.write(SHOW_INPUT_FLAGS, mShowInputFlags);
+ proto.write(CANDIDATES_VISIBILITY, mCandidatesVisibility);
+ proto.write(FULLSCREEN_APPLIED, mFullscreenApplied);
+ proto.write(IS_FULLSCREEN, mIsFullscreen);
+ proto.write(EXTRACT_VIEW_HIDDEN, mExtractViewHidden);
+ proto.write(EXTRACTED_TOKEN, mExtractedToken);
+ proto.write(IS_INPUT_VIEW_SHOWN, mIsInputViewShown);
+ proto.write(STATUS_ICON, mStatusIcon);
+ mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS);
+ proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver));
+ if (icProto != null) {
+ proto.write(INPUT_CONNECTION_CALL, icProto);
+ }
+ proto.end(token);
+ }
+ };
}
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
new file mode 100644
index 000000000000..7cd4ff61b2c8
--- /dev/null
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A set of internal methods exposed by {@link InputMethodService} to be called only from other
+ * framework classes for internal use.
+ *
+ * <p>CAVEATS: {@link AbstractInputMethodService} does not support all the methods here.</p>
+ */
+interface InputMethodServiceInternal {
+ /**
+ * @return {@link Context} associated with the service.
+ */
+ @NonNull
+ Context getContext();
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application. This cannot be {@code null}.
+ * @param inputConnection {@link InputConnection} with which
+ * {@link InputConnection#commitContent(InputContentInfo, int, Bundle)}
+ * will be called.
+ */
+ default void exposeContent(@NonNull InputContentInfo inputContentInfo,
+ @NonNull InputConnection inputConnection) {
+ }
+
+ /**
+ * Called when the user took some actions that should be taken into consideration to update the
+ * MRU list for input method rotation.
+ */
+ default void notifyUserActionIfNecessary() {
+ }
+
+ /**
+ * Called when the system is asking the IME to dump its information for debugging.
+ *
+ * <p>The caller is responsible for checking {@link android.Manifest.permission.DUMP}.</p>
+ *
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param fout The file to which you should dump your state. This will be
+ * closed for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ default void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ }
+
+ /**
+ * Called with {@link com.android.internal.inputmethod.ImeTracing#triggerServiceDump(String,
+ * com.android.internal.inputmethod.ImeTracing.ServiceDumper, byte[])} needs to be triggered
+ * with the given parameters.
+ *
+ * @param where {@code where} parameter to be passed.
+ * @param icProto {@code icProto} parameter to be passed.
+ */
+ default void triggerServiceDump(String where, @Nullable byte[] icProto) {
+ }
+}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
deleted file mode 100644
index f352f05d0488..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.CancellationGroup;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputConnectionWrapper;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread.
- *
- * <p>There are three types of per-client callbacks.</p>
- *
- * <ul>
- * <li>{@link IInputMethodSession} - from the IME client</li>
- * <li>{@link IMultiClientInputMethodSession} - from MultiClientInputMethodManagerService</li>
- * <li>{@link InputChannel} - from the IME client</li>
- * </ul>
- *
- * <p>This class serializes all the incoming events among those channels onto
- * {@link MultiClientInputMethodServiceDelegate.ClientCallback} on the specified {@link Looper}
- * thread.</p>
- */
-final class MultiClientInputMethodClientCallbackAdaptor {
- static final boolean DEBUG = false;
- static final String TAG = MultiClientInputMethodClientCallbackAdaptor.class.getSimpleName();
-
- private final Object mSessionLock = new Object();
- @GuardedBy("mSessionLock")
- CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- InputChannel mReadChannel;
- @GuardedBy("mSessionLock")
- KeyEvent.DispatcherState mDispatcherState;
- @GuardedBy("mSessionLock")
- Handler mHandler;
- @GuardedBy("mSessionLock")
- @Nullable
- InputEventReceiver mInputEventReceiver;
-
- private final CancellationGroup mCancellationGroup = new CancellationGroup();
-
- IInputMethodSession.Stub createIInputMethodSession() {
- synchronized (mSessionLock) {
- return new InputMethodSessionImpl(
- mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
- }
- }
-
- IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() {
- synchronized (mSessionLock) {
- return new MultiClientInputMethodSessionImpl(
- mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
- }
- }
-
- MultiClientInputMethodClientCallbackAdaptor(
- MultiClientInputMethodServiceDelegate.ClientCallback clientCallback, Looper looper,
- KeyEvent.DispatcherState dispatcherState, InputChannel readChannel) {
- synchronized (mSessionLock) {
- mCallbackImpl = new CallbackImpl(this, clientCallback);
- mDispatcherState = dispatcherState;
- mHandler = new Handler(looper, null, true);
- mReadChannel = readChannel;
- mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(),
- mCancellationGroup, mDispatcherState, mCallbackImpl.mOriginalCallback);
- }
- }
-
- private static final class KeyEventCallbackAdaptor implements KeyEvent.Callback {
- private final MultiClientInputMethodServiceDelegate.ClientCallback mLocalCallback;
-
- KeyEventCallbackAdaptor(
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- mLocalCallback = callback;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- return mLocalCallback.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return mLocalCallback.onKeyMultiple(keyCode, event);
- }
- }
-
- private static final class ImeInputEventReceiver extends InputEventReceiver {
- private final CancellationGroup mCancellationGroupOnFinishSession;
- private final KeyEvent.DispatcherState mDispatcherState;
- private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback;
- private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor;
-
- ImeInputEventReceiver(InputChannel readChannel, Looper looper,
- CancellationGroup cancellationGroupOnFinishSession,
- KeyEvent.DispatcherState dispatcherState,
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- super(readChannel, looper);
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- mDispatcherState = dispatcherState;
- mClientCallback = callback;
- mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- if (mCancellationGroupOnFinishSession.isCanceled()) {
- // The session has been finished.
- finishInputEvent(event, false);
- return;
- }
- boolean handled = false;
- try {
- if (event instanceof KeyEvent) {
- final KeyEvent keyEvent = (KeyEvent) event;
- handled = keyEvent.dispatch(mKeyEventCallbackAdaptor, mDispatcherState,
- mKeyEventCallbackAdaptor);
- } else {
- final MotionEvent motionEvent = (MotionEvent) event;
- if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
- handled = mClientCallback.onTrackballEvent(motionEvent);
- } else {
- handled = mClientCallback.onGenericMotionEvent(motionEvent);
- }
- }
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
- private static final class InputMethodSessionImpl extends IInputMethodSession.Stub {
- private final Object mSessionLock;
- @GuardedBy("mSessionLock")
- private CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- private Handler mHandler;
- private final CancellationGroup mCancellationGroupOnFinishSession;
-
- InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler,
- CancellationGroup cancellationGroupOnFinishSession) {
- mSessionLock = lock;
- mCallbackImpl = callback;
- mHandler = handler;
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- }
-
- @Override
- public void updateExtractedText(int token, ExtractedText text) {
- reportNotSupported();
- }
-
- @Override
- public void updateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd,
- int candidatesStart, int candidatesEnd) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- final SomeArgs args = SomeArgs.obtain();
- args.argi1 = oldSelStart;
- args.argi2 = oldSelEnd;
- args.argi3 = newSelStart;
- args.argi4 = newSelEnd;
- args.argi5 = candidatesStart;
- args.argi6 = candidatesEnd;
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::updateSelection, mCallbackImpl, args));
- }
- }
-
- @Override
- public void viewClicked(boolean focusChanged) {
- reportNotSupported();
- }
-
- @Override
- public void updateCursor(Rect newCursor) {
- reportNotSupported();
- }
-
- @Override
- public void displayCompletions(CompletionInfo[] completions) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::displayCompletions, mCallbackImpl, completions));
- }
- }
-
- @Override
- public void appPrivateCommand(String action, Bundle data) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::appPrivateCommand, mCallbackImpl, action, data));
- }
- }
-
- @Override
- public void finishSession() {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mCancellationGroupOnFinishSession.cancelAll();
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::finishSession, mCallbackImpl));
- mCallbackImpl = null;
- mHandler = null;
- }
- }
-
- @Override
- public void updateCursorAnchorInfo(CursorAnchorInfo info) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info));
- }
- }
-
- @Override
- public final void notifyImeHidden() {
- // no-op for multi-session since IME is responsible controlling navigation bar buttons.
- reportNotSupported();
- }
-
- @Override
- public void removeImeSurface() {
- // no-op for multi-session
- reportNotSupported();
- }
-
- @Override
- public void finishInput() throws RemoteException {
- // no-op for multi-session
- reportNotSupported();
- }
- }
-
- private static final class MultiClientInputMethodSessionImpl
- extends IMultiClientInputMethodSession.Stub {
- private final Object mSessionLock;
- @GuardedBy("mSessionLock")
- private CallbackImpl mCallbackImpl;
- @GuardedBy("mSessionLock")
- private Handler mHandler;
- private final CancellationGroup mCancellationGroupOnFinishSession;
-
- MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback,
- Handler handler, CancellationGroup cancellationGroupOnFinishSession) {
- mSessionLock = lock;
- mCallbackImpl = callback;
- mHandler = handler;
- mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
- }
-
- @Override
- public void startInputOrWindowGainedFocus(@Nullable IInputContext inputContext,
- int missingMethods, @Nullable EditorInfo editorInfo, int controlFlags,
- @SoftInputModeFlags int softInputMode, int windowHandle) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- final SomeArgs args = SomeArgs.obtain();
- // TODO(Bug 119211536): Remove dependency on AbstractInputMethodService from ICW
- final WeakReference<AbstractInputMethodService> fakeIMS =
- new WeakReference<>(null);
- args.arg1 = (inputContext == null) ? null
- : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods,
- mCancellationGroupOnFinishSession);
- args.arg2 = editorInfo;
- args.argi1 = controlFlags;
- args.argi2 = softInputMode;
- args.argi3 = windowHandle;
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::startInputOrWindowGainedFocus, mCallbackImpl, args));
- }
- }
-
- @Override
- public void showSoftInput(int flags, ResultReceiver resultReceiver) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::showSoftInput, mCallbackImpl, flags,
- resultReceiver));
- }
- }
-
- @Override
- public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- synchronized (mSessionLock) {
- if (mCallbackImpl == null || mHandler == null) {
- return;
- }
- mHandler.sendMessage(PooledLambda.obtainMessage(
- CallbackImpl::hideSoftInput, mCallbackImpl, flags,
- resultReceiver));
- }
- }
- }
-
- /**
- * The maim part of adaptor to {@link MultiClientInputMethodServiceDelegate.ClientCallback}.
- */
- @WorkerThread
- private static final class CallbackImpl {
- private final MultiClientInputMethodClientCallbackAdaptor mCallbackAdaptor;
- private final MultiClientInputMethodServiceDelegate.ClientCallback mOriginalCallback;
- private boolean mFinished = false;
-
- CallbackImpl(MultiClientInputMethodClientCallbackAdaptor callbackAdaptor,
- MultiClientInputMethodServiceDelegate.ClientCallback callback) {
- mCallbackAdaptor = callbackAdaptor;
- mOriginalCallback = callback;
- }
-
- void updateSelection(SomeArgs args) {
- try {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onUpdateSelection(args.argi1, args.argi2, args.argi3,
- args.argi4, args.argi5, args.argi6);
- } finally {
- args.recycle();
- }
- }
-
- void displayCompletions(CompletionInfo[] completions) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onDisplayCompletions(completions);
- }
-
- void appPrivateCommand(String action, Bundle data) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onAppPrivateCommand(action, data);
- }
-
- void finishSession() {
- if (mFinished) {
- return;
- }
- mFinished = true;
- mOriginalCallback.onFinishSession();
- synchronized (mCallbackAdaptor.mSessionLock) {
- mCallbackAdaptor.mDispatcherState = null;
- if (mCallbackAdaptor.mReadChannel != null) {
- mCallbackAdaptor.mReadChannel.dispose();
- mCallbackAdaptor.mReadChannel = null;
- }
- mCallbackAdaptor.mInputEventReceiver = null;
- }
- }
-
- void updateCursorAnchorInfo(CursorAnchorInfo info) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onUpdateCursorAnchorInfo(info);
- }
-
- void startInputOrWindowGainedFocus(SomeArgs args) {
- try {
- if (mFinished) {
- return;
- }
- final InputConnectionWrapper inputConnection = (InputConnectionWrapper) args.arg1;
- final EditorInfo editorInfo = (EditorInfo) args.arg2;
- final int startInputFlags = args.argi1;
- final int softInputMode = args.argi2;
- final int windowHandle = args.argi3;
- mOriginalCallback.onStartInputOrWindowGainedFocus(inputConnection, editorInfo,
- startInputFlags, softInputMode, windowHandle);
- } finally {
- args.recycle();
- }
- }
-
- void showSoftInput(int flags, ResultReceiver resultReceiver) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onShowSoftInput(flags, resultReceiver);
- }
-
- void hideSoftInput(int flags, ResultReceiver resultReceiver) {
- if (mFinished) {
- return;
- }
- mOriginalCallback.onHideSoftInput(flags, resultReceiver);
- }
- }
-
- private static void reportNotSupported() {
- if (DEBUG) {
- Log.d(TAG, Debug.getCaller() + " is not supported");
- }
- }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
deleted file mode 100644
index 0a2316508f09..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-import com.android.internal.inputmethod.StartInputFlags;
-
-/**
- * Defines all the public APIs and interfaces that are necessary to implement multi-client IMEs.
- *
- * <p>Actual implementation is further delegated to
- * {@link MultiClientInputMethodServiceDelegateImpl}.</p>
- *
- * @hide
- */
-public final class MultiClientInputMethodServiceDelegate {
- // @SdkConstant(SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.inputmethodservice.MultiClientInputMethodService";
-
- /**
- * Special value that is guaranteed to be not used for IME client ID.
- */
- public static final int INVALID_CLIENT_ID = -1;
-
- /**
- * Special value that is guaranteed to be not used for window handle.
- */
- public static final int INVALID_WINDOW_HANDLE = -1;
-
- private final MultiClientInputMethodServiceDelegateImpl mImpl;
-
- /**
- * Top-level callbacks for this {@link MultiClientInputMethodServiceDelegate}.
- */
- public interface ServiceCallback {
- /**
- * Called when this {@link MultiClientInputMethodServiceDelegate} is recognized by the
- * system and privileged operations like {@link #createInputMethodWindowToken(int)} are
- * ready to be called.
- */
- void initialized();
-
- /**
- * Called when a new IME client is recognized by the system.
- *
- * <p>Once the IME receives this callback, the IME can start interacting with the IME client
- * by calling {@link #acceptClient(int, ClientCallback, KeyEvent.DispatcherState, Looper)}.
- * </p>
- *
- * @param clientId ID of the client.
- * @param uid UID of the IME client.
- * @param pid PID of the IME client.
- * @param selfReportedDisplayId display ID reported from the IME client. Since the system
- * does not validate this display ID, and at any time the IME client can lose the
- * access to this display ID, the IME needs to call
- * {@link #isUidAllowedOnDisplay(int, int)} to check whether the IME client still
- * has access to this display or not.
- */
- void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
-
- /**
- * Called when an IME client is being destroyed.
- *
- * @param clientId ID of the client.
- */
- void removeClient(int clientId);
- }
-
- /**
- * Per-client callbacks.
- */
- public interface ClientCallback {
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#sendAppPrivateCommand(View, String, Bundle)}.
- *
- * @param action Name of the command to be performed.
- * @param data Any data to include with the command.
- * @see android.inputmethodservice.InputMethodService#onAppPrivateCommand(String, Bundle)
- */
- void onAppPrivateCommand(String action, Bundle data);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#displayCompletions(View, CompletionInfo[])}.
- *
- * @param completions Completion information provided from the IME client.
- * @see android.inputmethodservice.InputMethodService#onDisplayCompletions(CompletionInfo[])
- */
- void onDisplayCompletions(CompletionInfo[] completions);
-
- /**
- * Called when this callback session is closed. No further callback should not happen on
- * this callback object.
- */
- void onFinishSession();
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int)} or
- * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int,
- * ResultReceiver)}.
- *
- * @param flags The flag passed by the client.
- * @param resultReceiver The {@link ResultReceiver} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onWindowHidden()
- */
- void onHideSoftInput(int flags, ResultReceiver resultReceiver);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or {@link
- * android.view.inputmethod.InputMethodManager#showSoftInput(View, int, ResultReceiver)}.
- *
- * @param flags The flag passed by the client.
- * @param resultReceiver The {@link ResultReceiver} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onWindowShown()
- */
- void onShowSoftInput(int flags, ResultReceiver resultReceiver);
-
- /**
- * A generic callback when {@link InputConnection} is being established.
- *
- * @param inputConnection The {@link InputConnection} to be established.
- * @param editorInfo The {@link EditorInfo} reported from the IME client.
- * @param startInputFlags Any combinations of {@link StartInputFlags}.
- * @param softInputMode SoftWindowMode specified to this window.
- * @param targetWindowHandle A unique Window token.
- * @see android.inputmethodservice.InputMethodService#onStartInput(EditorInfo, boolean)
- */
- void onStartInputOrWindowGainedFocus(
- @Nullable InputConnection inputConnection,
- @Nullable EditorInfo editorInfo,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int targetWindowHandle);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#updateCursorAnchorInfo(View,
- * CursorAnchorInfo)}.
- *
- * @param info The {@link CursorAnchorInfo} passed by the client.
- * @see android.inputmethodservice.InputMethodService#onUpdateCursorAnchorInfo(
- * CursorAnchorInfo)
- */
- void onUpdateCursorAnchorInfo(CursorAnchorInfo info);
-
- /**
- * Called when the associated IME client called {@link
- * android.view.inputmethod.InputMethodManager#updateSelection(View, int, int, int, int)}.
- *
- * @param oldSelStart The previous selection start index.
- * @param oldSelEnd The previous selection end index.
- * @param newSelStart The new selection start index.
- * @param newSelEnd The new selection end index.
- * @param candidatesStart The new candidate start index.
- * @param candidatesEnd The new candidate end index.
- * @see android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
- * int, int)
- */
- void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
- int candidatesStart, int candidatesEnd);
-
- /**
- * Called to give a chance for the IME to intercept generic motion events before they are
- * processed by the application.
- *
- * @param event {@link MotionEvent} that is about to be handled by the IME client.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onGenericMotionEvent(MotionEvent)
- */
- boolean onGenericMotionEvent(MotionEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key down events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key down event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent)
- */
- boolean onKeyDown(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key long press events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key long press event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyLongPress(int, KeyEvent)
- */
- boolean onKeyLongPress(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key multiple events before they are
- * processed by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key multiple event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyMultiple(int, int, KeyEvent)
- */
- boolean onKeyMultiple(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept key up events before they are processed
- * by the application.
- *
- * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
- * @param event {@link KeyEvent} for this key up event.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent)
- */
- boolean onKeyUp(int keyCode, KeyEvent event);
-
- /**
- * Called to give a chance for the IME to intercept generic motion events before they are
- * processed by the application.
- *
- * @param event {@link MotionEvent} that is about to be handled by the IME client.
- * @return {@code true} to tell the IME client that the IME handled this event.
- * @see android.inputmethodservice.InputMethodService#onTrackballEvent(MotionEvent)
- */
- boolean onTrackballEvent(MotionEvent event);
- }
-
- private MultiClientInputMethodServiceDelegate(Context context,
- ServiceCallback serviceCallback) {
- mImpl = new MultiClientInputMethodServiceDelegateImpl(context, serviceCallback);
- }
-
- /**
- * Must be called by the multi-client IME implementer to create
- * {@link MultiClientInputMethodServiceDelegate}.
- *
- * @param context {@link Context} with which the delegate should interact with the system.
- * @param serviceCallback {@link ServiceCallback} to receive service-level callbacks.
- * @return A new instance of {@link MultiClientInputMethodServiceDelegate}.
- */
- public static MultiClientInputMethodServiceDelegate create(Context context,
- ServiceCallback serviceCallback) {
- return new MultiClientInputMethodServiceDelegate(context, serviceCallback);
- }
-
- /**
- * Must be called by the multi-client IME service when {@link android.app.Service#onDestroy()}
- * is called.
- */
- public void onDestroy() {
- mImpl.onDestroy();
- }
-
- /**
- * Must be called by the multi-client IME service when
- * {@link android.app.Service#onBind(Intent)} is called.
- *
- * @param intent {@link Intent} passed to {@link android.app.Service#onBind(Intent)}.
- * @return An {@link IBinder} object that needs to be returned from
- * {@link android.app.Service#onBind(Intent)}.
- */
- public IBinder onBind(Intent intent) {
- return mImpl.onBind(intent);
- }
-
- /**
- * Must be called by the multi-client IME service when
- * {@link android.app.Service#onUnbind(Intent)} is called.
- *
- * @param intent {@link Intent} passed to {@link android.app.Service#onUnbind(Intent)}.
- * @return A boolean value that needs to be returned from
- * {@link android.app.Service#onUnbind(Intent)}.
- */
- public boolean onUnbind(Intent intent) {
- return mImpl.onUnbind(intent);
- }
-
- /**
- * Must be called by the multi-client IME service to create a special window token for IME
- * window.
- *
- * <p>This method is available only after {@link ServiceCallback#initialized()}.</p>
- *
- * @param displayId display ID on which the IME window will be shown.
- * @return Window token to be specified to the IME window/
- */
- public IBinder createInputMethodWindowToken(int displayId) {
- return mImpl.createInputMethodWindowToken(displayId);
- }
-
- /**
- * Must be called by the multi-client IME service to notify the system when the IME is ready to
- * accept callback events from the specified IME client.
- *
- * @param clientId The IME client ID specified in
- * {@link ServiceCallback#addClient(int, int, int, int)}.
- * @param clientCallback The {@link ClientCallback} to receive callback events from this IME
- * client.
- * @param dispatcherState {@link KeyEvent.DispatcherState} to be used when receiving key-related
- * callbacks in {@link ClientCallback}.
- * @param looper {@link Looper} on which {@link ClientCallback} will be called back.
- */
- public void acceptClient(int clientId, ClientCallback clientCallback,
- KeyEvent.DispatcherState dispatcherState, Looper looper) {
- mImpl.acceptClient(clientId, clientCallback, dispatcherState, looper);
- }
-
- /**
- * Must be called by the multi-client IME service to notify the system when the IME is ready to
- * interact with the window in the IME client.
- *
- * @param clientId The IME client ID specified in
- * {@link ServiceCallback#addClient(int, int, int, int)}.
- * @param targetWindowHandle The window handle specified in
- * {@link ClientCallback#onStartInputOrWindowGainedFocus}.
- * @param imeWindowToken The IME window token returned from
- * {@link #createInputMethodWindowToken(int)}.
- */
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- mImpl.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- }
-
- /**
- * Can be called by the multi-client IME service to check if the given {@code uid} is allowed
- * to access to {@code displayId}.
- *
- * @param displayId Display ID to be queried.
- * @param uid UID to be queried.
- * @return {@code true} if {@code uid} is allowed to access to {@code displayId}.
- */
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mImpl.isUidAllowedOnDisplay(displayId, uid);
- }
-
- /**
- * Can be called by MSIME to activate/deactivate a client when it is gaining/losing focus
- * respectively.
- *
- * @param clientId client ID to activate/deactivate.
- * @param active {@code true} to activate a client.
- */
- public void setActive(int clientId, boolean active) {
- mImpl.setActive(clientId, active);
- }
-}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
deleted file mode 100644
index 04db8d625806..000000000000
--- a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.inputmethodservice;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.KeyEvent;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.MultiClientInputMethodPrivilegedOperations;
-
-import java.lang.annotation.Retention;
-import java.lang.ref.WeakReference;
-
-final class MultiClientInputMethodServiceDelegateImpl {
- private static final String TAG = "MultiClientInputMethodServiceDelegateImpl";
-
- private final Object mLock = new Object();
-
- @Retention(SOURCE)
- @IntDef({InitializationPhase.INSTANTIATED,
- InitializationPhase.ON_BIND_CALLED,
- InitializationPhase.INITIALIZE_CALLED,
- InitializationPhase.ON_UNBIND_CALLED,
- InitializationPhase.ON_DESTROY_CALLED})
- private @interface InitializationPhase {
- int INSTANTIATED = 1;
- int ON_BIND_CALLED = 2;
- int INITIALIZE_CALLED = 3;
- int ON_UNBIND_CALLED = 4;
- int ON_DESTROY_CALLED = 5;
- }
-
- @GuardedBy("mLock")
- @InitializationPhase
- private int mInitializationPhase;
-
- private final MultiClientInputMethodPrivilegedOperations mPrivOps =
- new MultiClientInputMethodPrivilegedOperations();
-
- private final MultiClientInputMethodServiceDelegate.ServiceCallback mServiceCallback;
-
- private final Context mContext;
-
- MultiClientInputMethodServiceDelegateImpl(Context context,
- MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
- mInitializationPhase = InitializationPhase.INSTANTIATED;
- mContext = context;
- mServiceCallback = serviceCallback;
- }
-
- void onDestroy() {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.INSTANTIATED:
- case InitializationPhase.ON_UNBIND_CALLED:
- mInitializationPhase = InitializationPhase.ON_DESTROY_CALLED;
- break;
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- }
-
- private static final class ServiceImpl extends IMultiClientInputMethod.Stub {
- private final WeakReference<MultiClientInputMethodServiceDelegateImpl> mImpl;
-
- ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
- mImpl = new WeakReference<>(service);
- }
-
- @Override
- public void initialize(IMultiClientInputMethodPrivilegedOperations privOps) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- synchronized (service.mLock) {
- switch (service.mInitializationPhase) {
- case InitializationPhase.ON_BIND_CALLED:
- service.mPrivOps.set(privOps);
- service.mInitializationPhase = InitializationPhase.INITIALIZE_CALLED;
- service.mServiceCallback.initialized();
- break;
- default:
- Log.e(TAG, "unexpected state=" + service.mInitializationPhase);
- break;
- }
- }
- }
-
- @Override
- public void addClient(int clientId, int uid, int pid, int selfReportedDisplayId) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- service.mServiceCallback.addClient(clientId, uid, pid, selfReportedDisplayId);
- }
-
- @Override
- public void removeClient(int clientId) {
- final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
- if (service == null) {
- return;
- }
- service.mServiceCallback.removeClient(clientId);
- }
- }
-
- IBinder onBind(Intent intent) {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.INSTANTIATED:
- mInitializationPhase = InitializationPhase.ON_BIND_CALLED;
- return new ServiceImpl(this);
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- return null;
- }
-
- boolean onUnbind(Intent intent) {
- synchronized (mLock) {
- switch (mInitializationPhase) {
- case InitializationPhase.ON_BIND_CALLED:
- case InitializationPhase.INITIALIZE_CALLED:
- mInitializationPhase = InitializationPhase.ON_UNBIND_CALLED;
- mPrivOps.dispose();
- break;
- default:
- Log.e(TAG, "unexpected state=" + mInitializationPhase);
- break;
- }
- }
- return false;
- }
-
- IBinder createInputMethodWindowToken(int displayId) {
- return mPrivOps.createInputMethodWindowToken(displayId);
- }
-
- void acceptClient(int clientId,
- MultiClientInputMethodServiceDelegate.ClientCallback clientCallback,
- KeyEvent.DispatcherState dispatcherState, Looper looper) {
- final InputChannel[] channels = InputChannel.openInputChannelPair("MSIMS-session");
- final InputChannel writeChannel = channels[0];
- final InputChannel readChannel = channels[1];
- try {
- final MultiClientInputMethodClientCallbackAdaptor callbackAdaptor =
- new MultiClientInputMethodClientCallbackAdaptor(clientCallback, looper,
- dispatcherState, readChannel);
- mPrivOps.acceptClient(clientId, callbackAdaptor.createIInputMethodSession(),
- callbackAdaptor.createIMultiClientInputMethodSession(), writeChannel);
- } finally {
- writeChannel.dispose();
- }
- }
-
- void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
- mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- }
-
- boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
- }
-
- void setActive(int clientId, boolean active) {
- mPrivOps.setActive(clientId, active);
- }
-}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 0e9d13595124..ec5bcf1f1199 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -14,19 +14,14 @@
* limitations under the License.
*/
-package com.android.internal.view;
+package android.inputmethodservice;
import android.annotation.AnyThread;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.Bundle;
import android.os.Handler;
-import android.os.RemoteException;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
-import android.util.proto.ProtoOutputStream;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
@@ -40,17 +35,34 @@ import android.view.inputmethod.SurroundingText;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.Completable;
-import com.android.internal.inputmethod.ResultCallbacks;
+import com.android.internal.inputmethod.IInputContextInvoker;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputConnectionProtoDumper;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethod;
import java.lang.ref.WeakReference;
-public class InputConnectionWrapper implements InputConnection {
- private static final String TAG = "InputConnectionWrapper";
+/**
+ * Takes care of remote method invocations of {@link InputConnection} in the IME side.
+ *
+ * <p>This class works as a proxy to forward API calls on {@link InputConnection} to
+ * {@link com.android.internal.inputmethod.RemoteInputConnectionImpl} running on the IME client
+ * (editor app) process then waits replies as needed.</p>
+ *
+ * <p>See also {@link IInputContext} for the actual {@link android.os.Binder} IPC protocols under
+ * the hood.</p>
+ */
+final class RemoteInputConnection implements InputConnection {
+ private static final String TAG = "RemoteInputConnection";
private static final int MAX_WAIT_TIME_MILLIS = 2000;
- private final IInputContext mIInputContext;
+
@NonNull
- private final WeakReference<AbstractInputMethodService> mInputMethodService;
+ private final IInputContextInvoker mInvoker;
+
+ @NonNull
+ private final WeakReference<InputMethodServiceInternal> mInputMethodService;
@MissingMethodFlags
private final int mMissingMethods;
@@ -64,12 +76,12 @@ public class InputConnectionWrapper implements InputConnection {
@NonNull
private final CancellationGroup mCancellationGroup;
- public InputConnectionWrapper(
- @NonNull WeakReference<AbstractInputMethodService> inputMethodService,
+ RemoteInputConnection(
+ @NonNull WeakReference<InputMethodServiceInternal> inputMethodService,
IInputContext inputContext, @MissingMethodFlags int missingMethods,
@NonNull CancellationGroup cancellationGroup) {
mInputMethodService = inputMethodService;
- mIInputContext = inputContext;
+ mInvoker = IInputContextInvoker.create(inputContext);
mMissingMethods = missingMethods;
mCancellationGroup = cancellationGroup;
}
@@ -84,21 +96,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getTextAfterCursor(length, flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(length,
flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getTextAfterCursor", icProto);
}
return result;
@@ -114,21 +120,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getTextBeforeCursor(length, flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(length,
flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getTextBeforeCursor", icProto);
}
return result;
@@ -144,21 +144,15 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return null;
}
- final Completable.CharSequence value = Completable.createCharSequence();
- try {
- mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- CharSequence result = Completable.getResultOrNull(
+ final Completable.CharSequence value = mInvoker.getSelectedText(flags);
+ final CharSequence result = Completable.getResultOrNull(
value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags,
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(flags,
result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getSelectedText", icProto);
}
return result;
@@ -187,22 +181,16 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return null;
}
- final Completable.SurroundingText value = Completable.createSurroundingText();
- try {
- mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
- ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- SurroundingText result = Completable.getResultOrNull(
+ final Completable.SurroundingText value = mInvoker.getSurroundingText(beforeLength,
+ afterLength, flags);
+ final SurroundingText result = Completable.getResultOrNull(
value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
beforeLength, afterLength, flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getSurroundingText", icProto);
}
return result;
@@ -214,21 +202,15 @@ public class InputConnectionWrapper implements InputConnection {
return 0;
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return 0;
- }
- int result = Completable.getResultOrZero(
+ final Completable.Int value = mInvoker.getCursorCapsMode(reqModes);
+ final int result = Completable.getResultOrZero(
value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
reqModes, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getCursorCapsMode", icProto);
}
return result;
@@ -240,21 +222,15 @@ public class InputConnectionWrapper implements InputConnection {
return null;
}
- final Completable.ExtractedText value = Completable.createExtractedText();
- try {
- mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return null;
- }
- ExtractedText result = Completable.getResultOrNull(
+ final Completable.ExtractedText value = mInvoker.getExtractedText(request, flags);
+ final ExtractedText result = Completable.getResultOrNull(
value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) {
- ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto(
+ final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
request, flags, result);
- ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText",
- inputMethodService, icProto);
+ inputMethodService.triggerServiceDump(TAG + "#getExtractedText", icProto);
}
return result;
@@ -262,18 +238,16 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public boolean commitText(CharSequence text, int newCursorPosition) {
- try {
- mIInputContext.commitText(text, newCursorPosition);
+ final boolean handled = mInvoker.commitText(text, newCursorPosition);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
private void notifyUserActionIfNecessary() {
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the the caller of this method.
return;
@@ -283,56 +257,31 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
public boolean commitCompletion(CompletionInfo text) {
- if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
- // This method is not implemented.
- return false;
- }
- try {
- mIInputContext.commitCompletion(text);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.commitCompletion(text);
}
@AnyThread
public boolean commitCorrection(CorrectionInfo correctionInfo) {
- try {
- mIInputContext.commitCorrection(correctionInfo);
- return true;
- } catch (RemoteException e) {
+ if (isMethodMissing(MissingMethodFlags.COMMIT_CORRECTION)) {
+ // This method is not implemented.
return false;
}
+ return mInvoker.commitCorrection(correctionInfo);
}
@AnyThread
public boolean setSelection(int start, int end) {
- try {
- mIInputContext.setSelection(start, end);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setSelection(start, end);
}
@AnyThread
public boolean performEditorAction(int actionCode) {
- try {
- mIInputContext.performEditorAction(actionCode);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performEditorAction(actionCode);
}
@AnyThread
public boolean performContextMenuAction(int id) {
- try {
- mIInputContext.performContextMenuAction(id);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performContextMenuAction(id);
}
@AnyThread
@@ -341,84 +290,50 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- try {
- mIInputContext.setComposingRegion(start, end);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setComposingRegion(start, end);
}
@AnyThread
public boolean setComposingText(CharSequence text, int newCursorPosition) {
- try {
- mIInputContext.setComposingText(text, newCursorPosition);
+ final boolean handled = mInvoker.setComposingText(text, newCursorPosition);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
public boolean finishComposingText() {
- try {
- mIInputContext.finishComposingText();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.finishComposingText();
}
@AnyThread
public boolean beginBatchEdit() {
- try {
- mIInputContext.beginBatchEdit();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.beginBatchEdit();
}
@AnyThread
public boolean endBatchEdit() {
- try {
- mIInputContext.endBatchEdit();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.endBatchEdit();
}
@AnyThread
public boolean sendKeyEvent(KeyEvent event) {
- try {
- mIInputContext.sendKeyEvent(event);
+ final boolean handled = mInvoker.sendKeyEvent(event);
+ if (handled) {
notifyUserActionIfNecessary();
- return true;
- } catch (RemoteException e) {
- return false;
}
+ return handled;
}
@AnyThread
public boolean clearMetaKeyStates(int states) {
- try {
- mIInputContext.clearMetaKeyStates(states);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.clearMetaKeyStates(states);
}
@AnyThread
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- try {
- mIInputContext.deleteSurroundingText(beforeLength, afterLength);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.deleteSurroundingText(beforeLength, afterLength);
}
@AnyThread
@@ -427,12 +342,7 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- try {
- mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
}
@AnyThread
@@ -442,24 +352,13 @@ public class InputConnectionWrapper implements InputConnection {
}
@AnyThread
- @Override
public boolean performSpellCheck() {
- try {
- mIInputContext.performSpellCheck();
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performSpellCheck();
}
@AnyThread
public boolean performPrivateCommand(String action, Bundle data) {
- try {
- mIInputContext.performPrivateCommand(action, data);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.performPrivateCommand(action, data);
}
@AnyThread
@@ -472,15 +371,9 @@ public class InputConnectionWrapper implements InputConnection {
// This method is not implemented.
return false;
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode,
- ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return false;
- }
- return Completable.getResultOrZero(value, TAG, "requestUpdateCursorAnchorInfo()",
- mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
+ final Completable.Boolean value = mInvoker.requestCursorUpdates(cursorUpdateMode);
+ return Completable.getResultOrFalse(value, TAG, "requestCursorUpdates()",
+ mCancellationGroup, MAX_WAIT_TIME_MILLIS);
}
@AnyThread
@@ -506,7 +399,7 @@ public class InputConnectionWrapper implements InputConnection {
}
if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+ final InputMethodServiceInternal inputMethodService = mInputMethodService.get();
if (inputMethodService == null) {
// This basically should not happen, because it's the caller of this method.
return false;
@@ -514,14 +407,9 @@ public class InputConnectionWrapper implements InputConnection {
inputMethodService.exposeContent(inputContentInfo, this);
}
- final Completable.Int value = Completable.createInt();
- try {
- mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
- } catch (RemoteException e) {
- return false;
- }
- return Completable.getResultOrZero(
- value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
+ final Completable.Boolean value = mInvoker.commitContent(inputContentInfo, flags, opts);
+ return Completable.getResultOrFalse(
+ value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS);
}
/**
@@ -529,12 +417,7 @@ public class InputConnectionWrapper implements InputConnection {
*/
@AnyThread
public boolean setImeConsumesInput(boolean imeConsumesInput) {
- try {
- mIInputContext.setImeConsumesInput(imeConsumesInput);
- return true;
- } catch (RemoteException e) {
- return false;
- }
+ return mInvoker.setImeConsumesInput(imeConsumesInput);
}
@AnyThread
@@ -545,7 +428,7 @@ public class InputConnectionWrapper implements InputConnection {
@AnyThread
@Override
public String toString() {
- return "InputConnectionWrapper{idHash=#"
+ return "RemoteInputConnection{idHash=#"
+ Integer.toHexString(System.identityHashCode(this))
+ " mMissingMethods="
+ InputConnectionInspector.getMissingMethodFlagsAsString(mMissingMethods) + "}";
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 3bde6fa6913d..1d07a0330bc5 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -26,8 +26,10 @@ import android.service.NetworkIdentityProto;
import android.telephony.Annotation.NetworkType;
import android.util.proto.ProtoOutputStream;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.NetworkIdentityUtils;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -121,11 +123,37 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
builder.append(", metered=").append(mMetered);
builder.append(", defaultNetwork=").append(mDefaultNetwork);
- // TODO(180557699): Print a human readable string for OEM managed state.
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
return builder.append("}").toString();
}
+ /**
+ * Get the human readable representation of a bitfield representing the OEM managed state of a
+ * network.
+ */
+ static String getOemManagedNames(int oemManaged) {
+ if (oemManaged == OEM_NONE) {
+ return "OEM_NONE";
+ }
+ final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+ final ArrayList<String> oemManagedNames = new ArrayList<String>();
+ for (int position : bitPositions) {
+ oemManagedNames.add(nameOfOemManaged(1 << position));
+ }
+ return String.join(",", oemManagedNames);
+ }
+
+ private static String nameOfOemManaged(int oemManagedBit) {
+ switch (oemManagedBit) {
+ case OEM_PAID:
+ return "OEM_PAID";
+ case OEM_PRIVATE:
+ return "OEM_PRIVATE";
+ default:
+ return "Invalid(" + oemManagedBit + ")";
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 288b06ee52c8..46c83df96b04 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,6 +16,8 @@
package android.net;
+import static com.android.internal.net.NetworkUtilsInternal.multiplySafeByRational;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1581,32 +1583,37 @@ public final class NetworkStats implements Parcelable {
// processes this every time device has transmitted/received amount equivalent to
// global threshold alert (~ 2MB) across all interfaces.
final long rxBytesAcrossUnderlyingIfaces =
- underlyingIfacesTotal.rxBytes * rxBytes[i] / tunIfaceTotal.rxBytes;
+ multiplySafeByRational(underlyingIfacesTotal.rxBytes,
+ rxBytes[i], tunIfaceTotal.rxBytes);
// app must not be blamed for more than it consumed on tunIface
totalRxBytes = Math.min(rxBytes[i], rxBytesAcrossUnderlyingIfaces);
}
long totalRxPackets = 0;
if (tunIfaceTotal.rxPackets > 0) {
final long rxPacketsAcrossUnderlyingIfaces =
- underlyingIfacesTotal.rxPackets * rxPackets[i] / tunIfaceTotal.rxPackets;
+ multiplySafeByRational(underlyingIfacesTotal.rxPackets,
+ rxPackets[i], tunIfaceTotal.rxPackets);
totalRxPackets = Math.min(rxPackets[i], rxPacketsAcrossUnderlyingIfaces);
}
long totalTxBytes = 0;
if (tunIfaceTotal.txBytes > 0) {
final long txBytesAcrossUnderlyingIfaces =
- underlyingIfacesTotal.txBytes * txBytes[i] / tunIfaceTotal.txBytes;
+ multiplySafeByRational(underlyingIfacesTotal.txBytes,
+ txBytes[i], tunIfaceTotal.txBytes);
totalTxBytes = Math.min(txBytes[i], txBytesAcrossUnderlyingIfaces);
}
long totalTxPackets = 0;
if (tunIfaceTotal.txPackets > 0) {
final long txPacketsAcrossUnderlyingIfaces =
- underlyingIfacesTotal.txPackets * txPackets[i] / tunIfaceTotal.txPackets;
+ multiplySafeByRational(underlyingIfacesTotal.txPackets,
+ txPackets[i], tunIfaceTotal.txPackets);
totalTxPackets = Math.min(txPackets[i], txPacketsAcrossUnderlyingIfaces);
}
long totalOperations = 0;
if (tunIfaceTotal.operations > 0) {
final long operationsAcrossUnderlyingIfaces =
- underlyingIfacesTotal.operations * operations[i] / tunIfaceTotal.operations;
+ multiplySafeByRational(underlyingIfacesTotal.operations,
+ operations[i], tunIfaceTotal.operations);
totalOperations = Math.min(operations[i], operationsAcrossUnderlyingIfaces);
}
// In a second pass, distribute these values across interfaces in the proportion that
@@ -1618,37 +1625,37 @@ public final class NetworkStats implements Parcelable {
tmpEntry.set = set[i];
if (underlyingIfacesTotal.rxBytes > 0) {
tmpEntry.rxBytes =
- totalRxBytes
- * perInterfaceTotal[j].rxBytes
- / underlyingIfacesTotal.rxBytes;
+ multiplySafeByRational(totalRxBytes,
+ perInterfaceTotal[j].rxBytes,
+ underlyingIfacesTotal.rxBytes);
}
tmpEntry.rxPackets = 0;
if (underlyingIfacesTotal.rxPackets > 0) {
tmpEntry.rxPackets =
- totalRxPackets
- * perInterfaceTotal[j].rxPackets
- / underlyingIfacesTotal.rxPackets;
+ multiplySafeByRational(totalRxPackets,
+ perInterfaceTotal[j].rxPackets,
+ underlyingIfacesTotal.rxPackets);
}
tmpEntry.txBytes = 0;
if (underlyingIfacesTotal.txBytes > 0) {
tmpEntry.txBytes =
- totalTxBytes
- * perInterfaceTotal[j].txBytes
- / underlyingIfacesTotal.txBytes;
+ multiplySafeByRational(totalTxBytes,
+ perInterfaceTotal[j].txBytes,
+ underlyingIfacesTotal.txBytes);
}
tmpEntry.txPackets = 0;
if (underlyingIfacesTotal.txPackets > 0) {
tmpEntry.txPackets =
- totalTxPackets
- * perInterfaceTotal[j].txPackets
- / underlyingIfacesTotal.txPackets;
+ multiplySafeByRational(totalTxPackets,
+ perInterfaceTotal[j].txPackets,
+ underlyingIfacesTotal.txPackets);
}
tmpEntry.operations = 0;
if (underlyingIfacesTotal.operations > 0) {
tmpEntry.operations =
- totalOperations
- * perInterfaceTotal[j].operations
- / underlyingIfacesTotal.operations;
+ multiplySafeByRational(totalOperations,
+ perInterfaceTotal[j].operations,
+ underlyingIfacesTotal.operations);
}
// tmpEntry now contains the migrated data of the i-th entry for the j-th underlying
// interface. Add that data usage to this object.
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 249154aa9129..68917a82884b 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -427,7 +427,7 @@ public class NetworkTemplate implements Parcelable {
builder.append(", subType=").append(mSubType);
}
if (mOemManaged != OEM_MANAGED_ALL) {
- builder.append(", oemManaged=").append(mOemManaged);
+ builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
}
builder.append(", subscriberIdMatchRule=")
.append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
@@ -781,6 +781,19 @@ public class NetworkTemplate implements Parcelable {
}
}
+ private static String getOemManagedNames(int oemManaged) {
+ switch (oemManaged) {
+ case OEM_MANAGED_ALL:
+ return "OEM_MANAGED_ALL";
+ case OEM_MANAGED_NO:
+ return "OEM_MANAGED_NO";
+ case OEM_MANAGED_YES:
+ return "OEM_MANAGED_YES";
+ default:
+ return NetworkIdentity.getOemManagedNames(oemManaged);
+ }
+ }
+
/**
* Examine the given template and normalize if it refers to a "merged"
* mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index fa3ff8a26862..2ced05693755 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -529,7 +529,7 @@ public class VpnService extends Service {
/**
* Sets an HTTP proxy for the VPN network. This proxy is only a recommendation
- * and it is possible that some apps will ignore it.
+ * and it is possible that some apps will ignore it. PAC proxies are not supported.
*/
@NonNull
public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) {
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 2c088e233f65..7f526c13a75a 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -262,7 +262,7 @@ public final class BatteryStatsManager {
/**
* Indicates that a new wifi scan has started.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiScanStartedFromSource(@NonNull WorkSource ws) {
@@ -276,7 +276,7 @@ public final class BatteryStatsManager {
/**
* Indicates that an ongoing wifi scan has stopped.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -290,7 +290,7 @@ public final class BatteryStatsManager {
/**
* Indicates that a new wifi batched scan has started.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
* @param csph Channels scanned per hour.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
@@ -306,7 +306,7 @@ public final class BatteryStatsManager {
/**
* Indicates that an ongoing wifi batched scan has stopped.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportWifiBatchedScanStoppedFromSource(@NonNull WorkSource ws) {
@@ -322,7 +322,9 @@ public final class BatteryStatsManager {
*
* @return Instance of {@link CellularBatteryStats}.
*/
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.BATTERY_STATS,
+ android.Manifest.permission.UPDATE_DEVICE_STATS})
public @NonNull CellularBatteryStats getCellularBatteryStats() {
try {
return mBatteryStats.getCellularBatteryStats();
@@ -337,7 +339,9 @@ public final class BatteryStatsManager {
*
* @return Instance of {@link WifiBatteryStats}.
*/
- @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.BATTERY_STATS,
+ android.Manifest.permission.UPDATE_DEVICE_STATS})
public @NonNull WifiBatteryStats getWifiBatteryStats() {
try {
return mBatteryStats.getWifiBatteryStats();
@@ -350,7 +354,7 @@ public final class BatteryStatsManager {
/**
* Indicates an app acquiring full wifi lock.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportFullWifiLockAcquiredFromSource(@NonNull WorkSource ws) {
@@ -364,7 +368,7 @@ public final class BatteryStatsManager {
/**
* Indicates an app releasing full wifi lock.
*
- * @param ws Worksource (to be used for battery blaming).
+ * @param ws worksource (to be used for battery blaming).
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
public void reportFullWifiLockReleasedFromSource(@NonNull WorkSource ws) {
@@ -479,6 +483,63 @@ public final class BatteryStatsManager {
}
}
+ /**
+ * Indicates that a new Bluetooth LE scan has started.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param isUnoptimized whether or not the scan has a filter.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanStarted(@NonNull WorkSource ws, boolean isUnoptimized) {
+ try {
+ mBatteryStats.noteBleScanStarted(ws, isUnoptimized);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that an ongoing Bluetooth LE scan has stopped.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param isUnoptimized whether or not the scan has a filter.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanStopped(@NonNull WorkSource ws, boolean isUnoptimized) {
+ try {
+ mBatteryStats.noteBleScanStopped(ws, isUnoptimized);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that Bluetooth LE has been reset.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanReset() {
+ try {
+ mBatteryStats.noteBleScanReset();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Indicates that Bluetooth LE scan has received new results.
+ *
+ * @param ws worksource (to be used for battery blaming).
+ * @param numNewResults number of results received since last update.
+ */
+ @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+ public void reportBleScanResults(@NonNull WorkSource ws, int numNewResults) {
+ try {
+ mBatteryStats.noteBleScanResults(ws, numNewResults);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private static int getDataConnectionPowerState(boolean isActive) {
// TODO: DataConnectionRealTimeInfo is under telephony package but the constants are used
// for both Wifi and mobile. It would make more sense to separate the constants to a
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index f48375246616..a7b67ef4b208 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -82,8 +82,6 @@ public final class BatteryUsageStats implements Parcelable {
public static final int AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT = 2;
- private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
-
// XML tags and attributes for BatteryUsageStats persistence
static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
static final String XML_TAG_AGGREGATE = "aggregate";
@@ -112,6 +110,8 @@ public final class BatteryUsageStats implements Parcelable {
static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+ private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
@@ -271,6 +271,16 @@ public final class BatteryUsageStats implements Parcelable {
}
/**
+ * Returns the names of custom power components in order, so the first name in the array
+ * corresponds to the custom componentId
+ * {@link BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID}.
+ */
+ @NonNull
+ public String[] getCustomPowerComponentNames() {
+ return mCustomPowerComponentNames;
+ }
+
+ /**
* Returns an iterator for {@link android.os.BatteryStats.HistoryItem}'s.
*/
@NonNull
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 3d466a0bf007..3d18125f1812 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -53,6 +53,12 @@ public final class BinderProxy implements IBinder {
private static volatile Binder.ProxyTransactListener sTransactListener = null;
+ private static class BinderProxyMapSizeException extends AssertionError {
+ BinderProxyMapSizeException(String s) {
+ super(s);
+ }
+ };
+
/**
* @see {@link Binder#setProxyTransactListener(listener)}.
*/
@@ -73,7 +79,10 @@ public final class BinderProxy implements IBinder {
private static final int LOG_MAIN_INDEX_SIZE = 8;
private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE;
private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1;
- // Debuggable builds will throw an AssertionError if the number of map entries exceeds:
+ /**
+ * Debuggable builds will throw an BinderProxyMapSizeException if the number of
+ * map entries exceeds:
+ */
private static final int CRASH_AT_SIZE = 20_000;
/**
@@ -228,7 +237,8 @@ public final class BinderProxy implements IBinder {
dumpProxyInterfaceCounts();
dumpPerUidProxyCounts();
Runtime.getRuntime().gc();
- throw new AssertionError("Binder ProxyMap has too many entries: "
+ throw new BinderProxyMapSizeException(
+ "Binder ProxyMap has too many entries: "
+ totalSize + " (total), " + totalUnclearedSize + " (uncleared), "
+ unclearedSize() + " (uncleared after GC). BinderProxy leak?");
} else if (totalSize > 3 * totalUnclearedSize / 2) {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bf394dc347b..394d270e1e51 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1130,6 +1130,11 @@ public class Build {
* S.
*/
public static final int S = 31;
+
+ /**
+ * Tiramisu.
+ */
+ public static final int TIRAMISU = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
@@ -1394,7 +1399,11 @@ public class Build {
public static final boolean IS_USER = "user".equals(TYPE);
/**
- * Whether this build is running inside a container.
+ * Whether this build is running on ARC, the Android Runtime for Chrome
+ * (https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md).
+ * Prior to R this was implemented as a container but from R this will be
+ * a VM. The name of the property remains ro.boot.conntainer as it is
+ * referenced in other projects.
*
* We should try to avoid checking this flag if possible to minimize
* unnecessarily diverging from non-container Android behavior.
@@ -1405,7 +1414,7 @@ public class Build {
* For higher-level behavior differences, other checks should be preferred.
* @hide
*/
- public static final boolean IS_CONTAINER =
+ public static final boolean IS_ARC =
SystemProperties.getBoolean("ro.boot.container", false);
/**
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index f38271aad867..403f55ceb94a 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -34,6 +34,7 @@ import android.util.Log;
import com.android.internal.os.IDropBoxManagerService;
+import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.File;
@@ -257,7 +258,8 @@ public class DropBoxManager {
} else {
return null;
}
- return (mFlags & IS_GZIPPED) != 0 ? new GZIPInputStream(is) : is;
+ return (mFlags & IS_GZIPPED) != 0
+ ? new GZIPInputStream(new BufferedInputStream(is)) : is;
}
public static final @android.annotation.NonNull Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator() {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2ed0bad69460..0257408b3e42 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -189,13 +189,11 @@ public class Environment {
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStorageDirectory() {
return getExternalDirs()[0];
}
@UnsupportedAppUsage
- @Deprecated
public File getExternalStoragePublicDirectory(String type) {
return buildExternalStoragePublicDirs(type)[0];
}
@@ -695,14 +693,13 @@ public class Environment {
* <p>
* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* monitor_storage}
+ * <p>
+ * Note that alternatives such as {@link Context#getExternalFilesDir(String)} or
+ * {@link MediaStore} offer better performance.
*
* @see #getExternalStorageState()
* @see #isExternalStorageRemovable()
- * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
- * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
- * performance.
*/
- @Deprecated
public static File getExternalStorageDirectory() {
throwIfUserRequired();
return sCurrentUser.getExternalDirs()[0];
@@ -999,6 +996,9 @@ public class Environment {
* </p>
* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java
* public_picture}
+ * <p>
+ * Note that alternatives such as {@link Context#getExternalFilesDir(String)} or
+ * {@link MediaStore} offer better performance.
*
* @param type The type of storage directory to return. Should be one of
* {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS},
@@ -1009,11 +1009,7 @@ public class Environment {
* @return Returns the File path for the directory. Note that this directory
* may not yet exist, so you must make sure it exists before using
* it such as with {@link File#mkdirs File.mkdirs()}.
- * @deprecated Alternatives such as {@link Context#getExternalFilesDir(String)},
- * {@link MediaStore}, or {@link Intent#ACTION_OPEN_DOCUMENT} offer better
- * performance.
*/
- @Deprecated
public static File getExternalStoragePublicDirectory(String type) {
throwIfUserRequired();
return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index be21fea1d0df..b3416e98147e 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -17,6 +17,7 @@
package android.os;
import android.app.Activity;
+import android.app.GameManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -26,8 +27,6 @@ import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -37,9 +36,6 @@ import dalvik.system.VMRuntime;
import java.io.BufferedReader;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
@@ -88,9 +84,6 @@ public class GraphicsEnvironment {
private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
- // ANGLE related properties.
- private static final String ANGLE_RULES_FILE = "a4a_rules.json";
- private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
"android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
@@ -121,6 +114,7 @@ public class GraphicsEnvironment {
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
+ private GameManager mGameManager;
private int mAngleOptInIndex = -1;
@@ -133,6 +127,8 @@ public class GraphicsEnvironment {
final ApplicationInfo appInfoWithMetaData =
getAppInfoWithMetadata(context, pm, packageName);
+ mGameManager = context.getSystemService(GameManager.class);
+
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupGpuLayers");
setupGpuLayers(context, coreSettings, pm, packageName, appInfoWithMetaData);
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
@@ -151,6 +147,23 @@ public class GraphicsEnvironment {
}
/**
+ * Query to determine if the Game Mode has enabled ANGLE.
+ */
+ private boolean isAngleEnabledByGameMode(Context context, String packageName) {
+ try {
+ final boolean gameModeEnabledAngle =
+ (mGameManager != null) && mGameManager.getAngleEnabled(packageName);
+ Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
+ return gameModeEnabledAngle;
+ } catch (SecurityException e) {
+ Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
+ + "for package: " + packageName);
+ }
+
+ return false;
+ }
+
+ /**
* Query to determine if ANGLE should be used
*/
private boolean shouldUseAngle(Context context, Bundle coreSettings,
@@ -164,21 +177,16 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + devOptIn + "'");
- // We only want to use ANGLE if the app is in the allowlist, or the developer has
- // explicitly chosen something other than default driver.
- // The allowlist will be generated by the ANGLE APK at both boot time and
- // ANGLE update time. It will only include apps mentioned in the rules file.
- final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+ // We only want to use ANGLE if the developer has explicitly chosen something other than
+ // default driver.
final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
-
- if (allowed) {
- Log.v(TAG, "ANGLE allowlist includes " + packageName);
- }
if (requested) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
- return allowed || requested;
+ final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+
+ return requested || gameModeEnabledAngle;
}
private int getVulkanVersion(PackageManager pm) {
@@ -475,117 +483,6 @@ public class GraphicsEnvironment {
}
/**
- * Attempt to setup ANGLE with a temporary rules file.
- * True: Temporary rules file was loaded.
- * False: Temporary rules file was *not* loaded.
- */
- private boolean setupAngleWithTempRulesFile(Context context,
- String packageName,
- String paths,
- String devOptIn) {
- /**
- * We only want to load a temp rules file for:
- * - apps that are marked 'debuggable' in their manifest
- * - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
- * debugging (PR_SET_DUMPABLE).
- */
- if (!isDebuggable()) {
- Log.v(TAG, "Skipping loading temporary rules file");
- return false;
- }
-
- final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-
- if (TextUtils.isEmpty(angleTempRules)) {
- Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
- return false;
- }
-
- Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-
- final File tempRulesFile = new File(angleTempRules);
- if (tempRulesFile.exists()) {
- Log.i(TAG, angleTempRules + " exists, loading file.");
- try {
- final FileInputStream stream = new FileInputStream(angleTempRules);
-
- try {
- final FileDescriptor rulesFd = stream.getFD();
- final long rulesOffset = 0;
- final long rulesLength = stream.getChannel().size();
- Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-
- setAngleInfo(paths, packageName, devOptIn, null,
- rulesFd, rulesOffset, rulesLength);
-
- stream.close();
-
- // We successfully setup ANGLE, so return with good status
- return true;
- } catch (IOException e) {
- Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Temp ANGLE rules file not found: " + e);
- } catch (SecurityException e) {
- Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
- }
- }
-
- return false;
- }
-
- /**
- * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
- * True: APK rules file was loaded.
- * False: APK rules file was *not* loaded.
- */
- private boolean setupAngleRulesApk(String anglePkgName,
- ApplicationInfo angleInfo,
- PackageManager pm,
- String packageName,
- String paths,
- String devOptIn,
- String[] features) {
- // Pass the rules file to loader for ANGLE decisions
- try {
- final AssetManager angleAssets = pm.getResourcesForApplication(angleInfo).getAssets();
-
- try {
- final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-
- setAngleInfo(paths, packageName, devOptIn, features, assetsFd.getFileDescriptor(),
- assetsFd.getStartOffset(), assetsFd.getLength());
-
- assetsFd.close();
-
- return true;
- } catch (IOException e) {
- Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
- + " from '" + anglePkgName + "': " + e);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
- }
-
- return false;
- }
-
- /**
- * Pull ANGLE allowlist from GlobalSettings and compare against current package
- */
- private boolean checkAngleAllowlist(Context context, Bundle bundle, String packageName) {
- final ContentResolver contentResolver = context.getContentResolver();
- final List<String> angleAllowlist =
- getGlobalSettingsString(contentResolver, bundle,
- Settings.Global.ANGLE_ALLOWLIST);
-
- if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
-
- return angleAllowlist.contains(packageName);
- }
-
- /**
* Pass ANGLE details down to trigger enable logic
*
* @param context
@@ -647,28 +544,21 @@ public class GraphicsEnvironment {
if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
- // If the user has set the developer option to something other than default,
- // we need to call setupAngleRulesApk() with the package name and the developer
- // option value (native/angle/other). Then later when we are actually trying to
- // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
- // and can confidently answer yes/no based on the previously set developer
- // option value.
- final String devOptIn = getDriverForPackage(context, bundle, packageName);
-
- if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
- // We setup ANGLE with a temp rules file, so we're done here.
- return true;
- }
-
- String[] features = getAngleEglFeatures(context, bundle);
-
- if (setupAngleRulesApk(
- anglePkgName, angleInfo, pm, packageName, paths, devOptIn, features)) {
- // ANGLE with rules is set up from the APK, hence return.
- return true;
+ // We need to call setAngleInfo() with the package name and the developer option value
+ //(native/angle/other). Then later when we are actually trying to load a driver,
+ //GraphicsEnv::getShouldUseAngle() has seen the package name before and can confidently
+ //answer yes/no based on the previously set developer option value.
+ final String devOptIn;
+ final String[] features = getAngleEglFeatures(context, bundle);
+ final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
+ if (gameModeEnabledAngle) {
+ devOptIn = ANGLE_GL_DRIVER_CHOICE_ANGLE;
+ } else {
+ devOptIn = getDriverForPackage(context, bundle, packageName);
}
+ setAngleInfo(paths, packageName, devOptIn, features);
- return false;
+ return true;
}
/**
@@ -956,7 +846,7 @@ public class GraphicsEnvironment {
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
private static native void setAngleInfo(String path, String appPackage, String devOptIn,
- String[] features, FileDescriptor rulesFd, long rulesOffset, long rulesLength);
+ String[] features);
private static native boolean getShouldUseAngle(String packageName);
private static native boolean setInjectLayersPrSetDumpable();
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index dfd935d0dfb3..018bc6b47dd2 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -70,3 +70,6 @@ per-file *Recovery* = file:/services/core/java/com/android/server/recoverysystem
# Bugreporting
per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
+
+# UpdateEngine
+per-file *UpdateEngine* = file:/platform/system/update_engine:/OWNERS
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index e5e9b5f6f53c..cad6e66888d9 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -316,7 +316,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
new MyReadMapCallback()));
}
}
- return EMPTY;
+ return new PersistableBundle(); // An empty mutable PersistableBundle
}
@Override
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3aa0bcb6abee..2f66a39c1b52 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2092,9 +2092,7 @@ public final class PowerManager {
* when light idle mode restrictions are being actively applied; it will return false if the
* device is in a long-term idle mode but currently running a maintenance window where
* restrictions have been lifted.
- * @hide
*/
- @UnsupportedAppUsage
public boolean isLightDeviceIdleMode() {
try {
return mService.isLightDeviceIdleMode();
@@ -2555,9 +2553,7 @@ public final class PowerManager {
/**
* Intent that is broadcast when the state of {@link #isLightDeviceIdleMode()} changes.
* This broadcast is only sent to registered receivers.
- * @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED
= "android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED";
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index f853e67f87d0..f7d7b21cb598 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -257,7 +257,12 @@ public final class ServiceManager {
*
* @return {@code null} only if there are permission problems or fatal errors.
*/
- public static native IBinder waitForService(@NonNull String name);
+ public static IBinder waitForService(@NonNull String name) {
+ return Binder.allowBlocking(waitForServiceNative(name));
+ }
+
+ private static native IBinder waitForServiceNative(@NonNull String name);
+
/**
* Returns the specified service from the service manager, if declared.
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 136e3de731a9..46eb2ece435c 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -93,6 +93,26 @@ public final class SharedMemory implements Parcelable, Closeable {
}
}
+ /**
+ * Creates an instance from existing shared memory passed as {@link ParcelFileDescriptor}.
+ *
+ * <p> The {@code fd} should be a shared memory created from
+ {@code SharedMemory or ASharedMemory}. This can be useful when shared memory is passed as
+ file descriptor through JNI or binder service implemented in cpp.
+ * <p> Note that newly created {@code SharedMemory} takes ownership of passed {@code fd} and
+ * the original {@code fd} becomes detached (Check {@link ParcelFileDescriptor#detachFd()}).
+ * If the caller wants to use the file descriptor after the call, the caller should duplicate
+ * the file descriptor (Check {@link ParcelFileDescriptor#dup()}) and pass the duped version
+ * instead.
+ *
+ * @param fd File descriptor of shared memory passed as {@link ParcelFileDescriptor}.
+ */
+ public static @NonNull SharedMemory fromFileDescriptor(@NonNull ParcelFileDescriptor fd) {
+ FileDescriptor f = new FileDescriptor();
+ f.setInt$(fd.detachFd());
+ return new SharedMemory(f);
+ }
+
private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
| OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 44c3d61b8760..7455f1fd7996 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -115,20 +115,18 @@ import java.util.function.Consumer;
*
* <pre>
* public void onCreate() {
- * if (DEVELOPER_MODE) {
- * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
- * .detectDiskReads()
- * .detectDiskWrites()
- * .detectNetwork() // or .detectAll() for all detectable problems
- * .penaltyLog()
- * .build());
- * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
- * .detectLeakedSqlLiteObjects()
- * .detectLeakedClosableObjects()
- * .penaltyLog()
- * .penaltyDeath()
- * .build());
- * }
+ * StrictMode.setThreadPolicy(new {@link ThreadPolicy.Builder StrictMode.ThreadPolicy.Builder}()
+ * .detectDiskReads()
+ * .detectDiskWrites()
+ * .detectNetwork() // or .detectAll() for all detectable problems
+ * .penaltyLog()
+ * .build());
+ * StrictMode.setVmPolicy(new {@link VmPolicy.Builder StrictMode.VmPolicy.Builder}()
+ * .detectLeakedSqlLiteObjects()
+ * .detectLeakedClosableObjects()
+ * .penaltyLog()
+ * .penaltyDeath()
+ * .build());
* super.onCreate();
* }
* </pre>
@@ -147,9 +145,7 @@ import java.util.function.Consumer;
* <p class="note">StrictMode is not a security mechanism and is not guaranteed to find all disk or
* network accesses. While it does propagate its state across process boundaries when doing {@link
* android.os.Binder} calls, it's still ultimately a best effort mechanism. Notably, disk or network
- * access from JNI calls won't necessarily trigger it. Future versions of Android may catch more (or
- * fewer) operations, so you should never leave StrictMode enabled in applications distributed on
- * Google Play.
+ * access from JNI calls won't necessarily trigger it.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 5a48242f26ea..3e01c53f0469 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -409,10 +409,11 @@ public class UpdateEngine {
/**
* Resets the bootable flag on the non-current partition and all internal
- * update_engine state. This can be used after an unwanted payload has been
- * successfully applied and the device has not yet been rebooted to signal
- * that we no longer want to boot into that updated system. After this call
- * completes, update_engine will no longer report
+ * update_engine state. Note this call will clear the entire update
+ * progress. So a subsequent {@link #applyPayload} will apply the update
+ * from scratch.
+ *
+ * <p>After this call completes, update_engine will no longer report
* {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
* notification that rebooting into the new system is possible.
*/
@@ -425,6 +426,39 @@ public class UpdateEngine {
}
/**
+ * Sets the A/B slot switch for the next boot after applying an ota update. If
+ * {@link #applyPayload} hasn't switched the slot, the updater APP can call
+ * this API to switch the slot and apply the update on next boot.
+ *
+ * @param payloadMetadataFilename the location of the metadata without the
+ * {@code file://} prefix.
+ */
+ public void setShouldSwitchSlotOnReboot(@NonNull String payloadMetadataFilename) {
+ try {
+ mUpdateEngine.setShouldSwitchSlotOnReboot(payloadMetadataFilename);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resets the boot slot to the source/current slot, without cancelling the
+ * update progress. This can be called after the update is installed, and to
+ * prevent the device from accidentally taking the update when it reboots.
+ *
+ * This is useful when users don't want to take the update immediately; or
+ * the updater determines some condition hasn't met, e.g. insufficient space
+ * for boot.
+ */
+ public void resetShouldSwitchSlotOnReboot() {
+ try {
+ mUpdateEngine.resetShouldSwitchSlotOnReboot();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Unbinds the last bound callback function.
*/
public boolean unbind() {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8709f071f222..09b7aaa9545c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2491,6 +2491,10 @@ public class UserManager {
throw re.rethrowFromSystemServer();
}
}
+ @Override
+ protected boolean bypass(Integer query) {
+ return query < 0;
+ }
};
// Uses IS_USER_UNLOCKED_PROPERTY for invalidation as the APIs have the same dependencies.
@@ -2505,6 +2509,10 @@ public class UserManager {
throw re.rethrowFromSystemServer();
}
}
+ @Override
+ protected boolean bypass(Integer query) {
+ return query < 0;
+ }
};
/** {@hide} */
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d7893e4bbefd..feffcbd96097 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -352,6 +352,8 @@ public abstract class Vibrator {
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @deprecated Use {@link #vibrate(VibrationEffect)} instead.
*/
@@ -364,6 +366,9 @@ public abstract class Vibrator {
/**
* Vibrate constantly for the specified period of time.
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param milliseconds The number of milliseconds to vibrate.
* @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
* specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
@@ -398,6 +403,8 @@ public abstract class Vibrator {
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -423,6 +430,9 @@ public abstract class Vibrator {
* to start the repeat, or -1 to disable repeating.
* </p>
*
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
* @param pattern an array of longs of times for which to turn the vibrator on or off.
* @param repeat the index into pattern at which to repeat, or -1 if
* you don't want to repeat.
@@ -450,11 +460,30 @@ public abstract class Vibrator {
}
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe) {
vibrate(vibe, null);
}
+ /**
+ * Vibrate with a given effect.
+ *
+ * <p>The app should be in foreground for the vibration to happen. Background apps should
+ * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+ *
+ * @param vibe {@link VibrationEffect} describing the vibration to be performed.
+ * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,
+ * specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or
+ * {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for
+ * vibrations associated with incoming calls.
+ */
@RequiresPermission(android.Manifest.permission.VIBRATE)
public void vibrate(VibrationEffect vibe, AudioAttributes attributes) {
vibrate(Process.myUid(), mPackageName, vibe, null, attributes);
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 9385402c3d72..fbac954b0a35 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -198,9 +198,9 @@ interface IStorageManager {
void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
void fixupAppDir(in String path) = 89;
void disableAppDataIsolation(in String pkgName, int pid, int userId) = 90;
- void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 91;
- void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 92;
- PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 93;
- boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 94;
- int getExternalStorageMountMode(int uid, in String packageName) = 95;
-}
+ PendingIntent getManageSpaceActivityIntent(in String packageName, int requestCode) = 91;
+ void notifyAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 92;
+ void notifyAppIoResumed(in String volumeUuid, int uid, int tid, int reason) = 93;
+ int getExternalStorageMountMode(int uid, in String packageName) = 94;
+ boolean isAppIoBlocked(in String volumeUuid, int uid, int tid, int reason) = 95;
+} \ No newline at end of file
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 63bcc9c89ca9..4cd9fb933c2d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1315,6 +1315,7 @@ public final class PermissionManager {
private static final class PackageNamePermissionQuery {
final String permName;
final String pkgName;
+ @UserIdInt
final int userId;
PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName,
@@ -1374,6 +1375,10 @@ public final class PermissionManager {
return checkPackageNamePermissionUncached(
query.permName, query.pkgName, query.userId);
}
+ @Override
+ protected boolean bypass(PackageNamePermissionQuery query) {
+ return query.userId < 0;
+ }
};
/**
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 400b312bb913..69a09fb9b7f1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -214,7 +214,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
return AudioManager.getAudioProductStrategies().stream()
.map(strategy -> strategy.getVolumeGroupIdForAudioAttributes(
- AudioProductStrategy.sDefaultAttributes))
+ AudioProductStrategy.getDefaultAttributes()))
.filter(volumeGroupId -> volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP)
.findFirst()
.orElse(AudioVolumeGroup.DEFAULT_VOLUME_GROUP);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 376d9421e8d3..570d73d4293b 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8588,12 +8588,27 @@ public final class ContactsContract {
* Type: INTEGER
*/
public static final String UNGROUPED_WITH_PHONES = "summ_phones";
+
+ /**
+ * Flag indicating if the account is the default account for new contacts. At most one
+ * account has this flag set at a time. It can only be set to 1 on a row with null data set.
+ * <p>
+ * Type: INTEGER (boolean)
+ * @hide
+ */
+ String IS_DEFAULT = "x_is_default";
}
/**
* <p>
* Contacts-specific settings for various {@link Account}'s.
* </p>
+ * <p>
+ * A settings entry for an account is created automatically when a raw contact or group
+ * is inserted that references it. Settings entries cannot be deleted as long as raw
+ * contacts or groups continue to reference it; in order to delete a settings entry all
+ * raw contacts and groups referencing the account must be deleted first.
+ * </p>
* <h2>Columns</h2>
* <table class="jd-sumtable">
* <tr>
@@ -8675,6 +8690,13 @@ public final class ContactsContract {
* The MIME-type of {@link #CONTENT_URI} providing a single setting.
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/setting";
+
+ /**
+ * Action used to launch the UI to set the default account for new contacts.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SET_DEFAULT_ACCOUNT =
+ "android.provider.action.SET_DEFAULT_ACCOUNT";
}
/**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 431bf4c54b4b..ef486a9e27e2 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -258,6 +258,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
/**
+ * Namespace for all lmkd related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_LMKD_NATIVE = "lmkd_native";
+
+ /**
* Namespace for all location related features.
*
* @hide
@@ -432,6 +440,25 @@ public final class DeviceConfig {
public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
/**
+ * Namespace for all SurfaceFlinger features that are used at the native level.
+ * These features are applied on boot or after reboot.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT =
+ "surface_flinger_native_boot";
+
+ /**
+ * Namespace for swcodec native related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SWCODEC_NATIVE = "swcodec_native";
+
+
+ /**
* Namespace for System UI related features.
*
* @hide
@@ -597,6 +624,14 @@ public final class DeviceConfig {
@TestApi
public static final String NAMESPACE_CONSTRAIN_DISPLAY_APIS = "constrain_display_apis";
+ /**
+ * Namespace for App Compat Overrides related features.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String NAMESPACE_APP_COMPAT_OVERRIDES = "app_compat_overrides";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
@@ -767,7 +802,7 @@ public final class DeviceConfig {
}
/**
- * Create a new property with the the provided name and value in the provided namespace, or
+ * Create a new property with the provided name and value in the provided namespace, or
* update the value of such a property if it already exists. The same name can exist in multiple
* namespaces and might have different values in any or all namespaces.
* <p>
@@ -817,6 +852,22 @@ public final class DeviceConfig {
}
/**
+ * Delete a property with the provided name and value in the provided namespace
+ *
+ * @param namespace The namespace containing the property to delete.
+ * @param name The name of the property to delete.
+ * @return True if the property was deleted or it did not exist in the first place.
+ * False if the storage implementation throws errors.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.deleteString(contentResolver, namespace, name);
+ }
+
+ /**
* Reset properties to their default values by removing the underlying values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
@@ -851,32 +902,31 @@ public final class DeviceConfig {
/**
* Disables or re-enables bulk modifications ({@link #setProperties(Properties)}) to device
* config values. This is intended for use during tests to prevent a sync operation clearing
- * config values, which could influence the outcome of the tests, i.e. by changing behavior.
+ * config values which could influence the outcome of the tests, i.e. by changing behavior.
*
* @param syncDisabledMode the mode to use, see {@link Settings.Config#SYNC_DISABLED_MODE_NONE},
* {@link Settings.Config#SYNC_DISABLED_MODE_PERSISTENT} and {@link
* Settings.Config#SYNC_DISABLED_MODE_UNTIL_REBOOT}
*
- * @see #isSyncDisabled()
+ * @see #getSyncDisabledMode()
* @hide
*/
@RequiresPermission(WRITE_DEVICE_CONFIG)
- public static void setSyncDisabled(@SyncDisabledMode int syncDisabledMode) {
+ public static void setSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- Settings.Config.setSyncDisabled(contentResolver, syncDisabledMode);
+ Settings.Config.setSyncDisabledMode(contentResolver, syncDisabledMode);
}
/**
- * Returns the current state of sync disabling, {@code true} when disabled, {@code false}
- * otherwise.
+ * Returns the current mode of sync disabling.
*
- * @see #setSyncDisabled(int)
+ * @see #setSyncDisabledMode(int)
* @hide
*/
@RequiresPermission(WRITE_DEVICE_CONFIG)
- public static boolean isSyncDisabled() {
+ public static @SyncDisabledMode int getSyncDisabledMode() {
ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
- return Settings.Config.isSyncDisabled(contentResolver);
+ return Settings.Config.getSyncDisabledMode(contentResolver);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ac520e8b3dec..7a2898a7cf57 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -299,8 +299,8 @@ public final class Settings {
public static final String KEY_CONFIG_SET_ALL_RETURN = "config_set_all_return";
/** @hide */
- public static final String KEY_CONFIG_IS_SYNC_DISABLED_RETURN =
- "config_is_sync_disabled_return";
+ public static final String KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN =
+ "config_get_sync_disabled_mode_return";
/**
* An int extra specifying a subscription ID.
@@ -2439,13 +2439,15 @@ public final class Settings {
public static final String CALL_METHOD_LIST_CONFIG = "LIST_config";
/** @hide - Private call() method to disable / re-enable syncs to the 'configuration' table */
- public static final String CALL_METHOD_SET_SYNC_DISABLED_CONFIG = "SET_SYNC_DISABLED_config";
+ public static final String CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG =
+ "SET_SYNC_DISABLED_MODE_config";
/**
- * @hide - Private call() method to return whether syncs are disabled for the 'configuration'
- * table
+ * @hide - Private call() method to return the current mode of sync disabling for the
+ * 'configuration' table
*/
- public static final String CALL_METHOD_IS_SYNC_DISABLED_CONFIG = "IS_SYNC_DISABLED_config";
+ public static final String CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG =
+ "GET_SYNC_DISABLED_MODE_config";
/** @hide - Private call() method to register monitor callback for 'configuration' table */
public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
@@ -2768,6 +2770,7 @@ public final class Settings {
// for the fast path of retrieving settings.
private final String mCallGetCommand;
private final String mCallSetCommand;
+ private final String mCallDeleteCommand;
private final String mCallListCommand;
private final String mCallSetAllCommand;
@@ -2779,17 +2782,19 @@ public final class Settings {
private GenerationTracker mGenerationTracker;
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
- this(uri, getCommand, setCommand, null, null, providerHolder,
+ String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+ Class<T> callerClass) {
+ this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
callerClass);
}
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, String listCommand, String setAllCommand,
+ String setCommand, String deleteCommand, String listCommand, String setAllCommand,
ContentProviderHolder providerHolder, Class<T> callerClass) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
+ mCallDeleteCommand = deleteCommand;
mCallListCommand = listCommand;
mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
@@ -2847,6 +2852,20 @@ public final class Settings {
}
}
+ public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+ cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+ return false;
+ }
+ return true;
+ }
+
@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
@@ -3309,6 +3328,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM,
+ CALL_METHOD_DELETE_SYSTEM,
sProviderHolder,
System.class);
@@ -3980,6 +4000,16 @@ public final class Settings {
public static final int ADVANCED_SETTINGS_DEFAULT = 0;
/**
+ * If the triple press gesture for toggling accessibility is enabled.
+ * Set to 1 for true and 0 for false.
+ *
+ * This setting is used only internally.
+ * @hide
+ */
+ public static final String WEAR_ACCESSIBILITY_GESTURE_ENABLED
+ = "wear_accessibility_gesture_enabled";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#AIRPLANE_MODE_ON} instead
*/
@Deprecated
@@ -4698,7 +4728,7 @@ public final class Settings {
*
* @hide
*/
- @Readable
+ @Readable(maxTargetSdk = Build.VERSION_CODES.R)
public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
/**
@@ -5241,6 +5271,7 @@ public final class Settings {
PRIVATE_SETTINGS.add(WIFI_USE_STATIC_IP);
PRIVATE_SETTINGS.add(END_BUTTON_BEHAVIOR);
PRIVATE_SETTINGS.add(ADVANCED_SETTINGS);
+ PRIVATE_SETTINGS.add(WEAR_ACCESSIBILITY_GESTURE_ENABLED);
PRIVATE_SETTINGS.add(SCREEN_AUTO_BRIGHTNESS_ADJ);
PRIVATE_SETTINGS.add(VIBRATE_INPUT_DEVICES);
PRIVATE_SETTINGS.add(VOLUME_MASTER);
@@ -5629,6 +5660,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
+ CALL_METHOD_DELETE_SECURE,
sProviderHolder,
Secure.class);
@@ -6378,8 +6410,15 @@ public final class Settings {
public static final String DATA_ROAMING = Global.DATA_ROAMING;
/**
- * Setting to record the input method used by default, holding the ID
- * of the desired method.
+ * Stores {@link android.view.inputmethod.InputMethodInfo#getId()} of the input method
+ * service that is currently selected.
+ *
+ * <p>Although the name {@link #DEFAULT_INPUT_METHOD} implies that there is a concept of
+ * <i>default</i> input method, in reality this setting is no more or less than the
+ * <strong>currently selected</strong> input method. This setting can be updated at any
+ * time as a result of user-initiated and system-initiated input method switching.</p>
+ *
+ * <p>Use {@link ComponentName#unflattenFromString(String)} to parse the stored value.</p>
*/
@Readable
public static final String DEFAULT_INPUT_METHOD = "default_input_method";
@@ -7198,6 +7237,13 @@ public final class Settings {
"touch_exploration_granted_accessibility_services";
/**
+ * Is talkback service enabled or not. 0 == no, 1 == yes
+ *
+ * @hide
+ */
+ public static final String WEAR_TALKBACK_ENABLED = "wear_talkback_enabled";
+
+ /**
* Whether the Global Actions Panel is enabled.
* @hide
*/
@@ -8823,7 +8869,7 @@ public final class Settings {
/**
* Control if rotation suggestions are sent to System UI when in rotation locked mode.
- * Done to enable screen rotation while the the screen rotation is locked. Enabling will
+ * Done to enable screen rotation while the screen rotation is locked. Enabling will
* poll the accelerometer in rotation locked mode.
*
* If 0, then rotation suggestions are not sent to System UI. If 1, suggestions are sent.
@@ -9964,15 +10010,18 @@ public final class Settings {
/**
* Controls the accessibility button mode. System will force-set the value to {@link
- * #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU} if {@link #NAVIGATION_MODE} is fully
- * gestural.
+ * #ACCESSIBILITY_BUTTON_MODE_GESTURE} if {@link #NAVIGATION_MODE} is button; force-set the
+ * value to {@link ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR} if {@link #NAVIGATION_MODE} is
+ * gestural; otherwise, remain the option.
* <ul>
* <li> 0 = button in navigation bar </li>
* <li> 1 = button floating on the display </li>
+ * <li> 2 = button using gesture to trigger </li>
* </ul>
*
* @see #ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR
* @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
+ * @see #ACCESSIBILITY_BUTTON_MODE_GESTURE
* @hide
*/
public static final String ACCESSIBILITY_BUTTON_MODE =
@@ -9995,6 +10044,14 @@ public final class Settings {
public static final int ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU = 0x1;
/**
+ * Accessibility button mode value that specifying the accessibility service or feature to
+ * be toggled via the gesture.
+ *
+ * @hide
+ */
+ public static final int ACCESSIBILITY_BUTTON_MODE_GESTURE = 0x2;
+
+ /**
* The size of the accessibility floating menu.
* <ul>
* <li> 0 = small size
@@ -10108,6 +10165,68 @@ public final class Settings {
@Readable
public static final String GAME_DASHBOARD_ALWAYS_ON = "game_dashboard_always_on";
+
+ /**
+ * For this device state, no specific auto-rotation lock setting should be applied.
+ * If the user toggles the auto-rotate lock in this state, the setting will apply to the
+ * previously valid device state.
+ * @hide
+ */
+ public static final int DEVICE_STATE_ROTATION_LOCK_IGNORED = 0;
+ /**
+ * For this device state, the setting for auto-rotation is locked.
+ * @hide
+ */
+ public static final int DEVICE_STATE_ROTATION_LOCK_LOCKED = 1;
+ /**
+ * For this device state, the setting for auto-rotation is unlocked.
+ * @hide
+ */
+ public static final int DEVICE_STATE_ROTATION_LOCK_UNLOCKED = 2;
+
+ /**
+ * The different settings that can be used as values with
+ * {@link #DEVICE_STATE_ROTATION_LOCK}.
+ * @hide
+ */
+ @IntDef(prefix = {"DEVICE_STATE_ROTATION_LOCK_"}, value = {
+ DEVICE_STATE_ROTATION_LOCK_IGNORED,
+ DEVICE_STATE_ROTATION_LOCK_LOCKED,
+ DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DeviceStateRotationLockSetting {
+ }
+
+ /**
+ * Rotation lock setting keyed on device state.
+ *
+ * This holds a serialized map using int keys that represent Device States and value of
+ * {@link DeviceStateRotationLockSetting} representing the rotation lock setting for that
+ * device state.
+ *
+ * Serialized as key0:value0:key1:value1:...:keyN:valueN.
+ *
+ * Example: "0:1:1:2:2:1"
+ * This example represents a map of:
+ * <ul>
+ * <li>0 -> DEVICE_STATE_ROTATION_LOCK_LOCKED</li>
+ * <li>1 -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED</li>
+ * <li>2 -> DEVICE_STATE_ROTATION_LOCK_IGNORED</li>
+ * </ul>
+ *
+ * @hide
+ */
+ public static final String DEVICE_STATE_ROTATION_LOCK =
+ "device_state_rotation_lock";
+
+ /**
+ * Control whether communal mode is allowed on this device.
+ *
+ * @hide
+ */
+ public static final String COMMUNAL_MODE_ENABLED = "communal_mode_enabled";
+
/**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
@@ -10255,6 +10374,14 @@ public final class Settings {
"enable_accessibility_global_gesture_enabled";
/**
+ * Whether select sound track with audio description by default.
+ * @hide
+ */
+ @Readable
+ public static final String ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT =
+ "enable_accessibility_audio_description_by_default";
+
+ /**
* Whether Airplane Mode is on.
*/
@Readable
@@ -10978,6 +11105,9 @@ public final class Settings {
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV} Upon going to sleep, device
* sends {@code <Standby>} to TV only. Upon waking up, device does not turn on the Audio
* system via {@code <System Audio Mode Request>}.</li>
+ * <li>{@link HdmiControlManager#POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM} Upon going to
+ * sleep, sends {@code <Standby>} to TV and Audio system. Upon waking up, device attempts
+ * to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
* <li>{@link HdmiControlManager#POWER_CONTROL_MODE_BROADCAST} Upon going to sleep,
* device sends {@code <Standby>} to all devices in the network. Upon waking up, device
* attempts to turn on the Audio system via {@code <System Audio Mode Request>}.</li>
@@ -13381,6 +13511,69 @@ public final class Settings {
= "forced_app_standby_for_small_battery_enabled";
/**
+ * Whether to enable the TARE subsystem as a whole or not.
+ * 1 means enable, 0 means disable.
+ *
+ * @hide
+ */
+ public static final String ENABLE_TARE = "enable_tare";
+
+ /**
+ * Default value for {@link #ENABLE_TARE}.
+ *
+ * @hide
+ */
+ public static final int DEFAULT_ENABLE_TARE = 0;
+
+ /**
+ * Whether to enable the TARE AlarmManager economic policy or not.
+ * 1 means enable, 0 means disable.
+ *
+ * @hide
+ */
+ public static final String ENABLE_TARE_ALARM_MANAGER = "enable_tare_alarm_manager";
+
+ /**
+ * Default value for {@link #ENABLE_TARE_ALARM_MANAGER}.
+ *
+ * @hide
+ */
+ public static final int DEFAULT_ENABLE_TARE_ALARM_MANAGER = 0;
+
+ /**
+ * Settings for AlarmManager's TARE EconomicPolicy (list of its economic factors).
+ *
+ * Keys are listed in {@link android.app.tare.EconomyManager}.
+ *
+ * @hide
+ */
+ public static final String TARE_ALARM_MANAGER_CONSTANTS = "tare_alarm_manager_constants";
+
+ /**
+ * Whether to enable the TARE JobScheduler economic policy or not.
+ * 1 means enable, 0 means disable.
+ *
+ * @hide
+ */
+ public static final String ENABLE_TARE_JOB_SCHEDULER = "enable_tare_job_scheduler";
+
+ /**
+ * Default value for {@link #ENABLE_TARE_JOB_SCHEDULER}.
+ *
+ * @hide
+ */
+ public static final int DEFAULT_ENABLE_TARE_JOB_SCHEDULER = 0;
+
+ /**
+ * Settings for JobScheduler's TARE EconomicPolicy (list of its economic factors).
+ *
+ * Keys are listed in {@link android.app.tare.EconomyManager}.
+ *
+ * @hide
+ */
+ public static final String TARE_JOB_SCHEDULER_CONSTANTS = "tare_job_scheduler_constants";
+
+ /**
* Whether or not to enable the User Absent, Radios Off feature on small battery devices.
* Type: int (0 for false, 1 for true)
* Default: 0
@@ -13670,13 +13863,6 @@ public final class Settings {
"angle_gl_driver_selection_values";
/**
- * List of package names that should check ANGLE rules
- * @hide
- */
- @Readable
- public static final String ANGLE_ALLOWLIST = "angle_allowlist";
-
- /**
* Lists of ANGLE EGL features for debugging.
* Each list of features is separated by a comma, each feature in each list is separated by
* a colon.
@@ -14324,8 +14510,11 @@ public final class Settings {
"are_user_disabled_hdr_formats_allowed";
/**
- * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are disabled
- * currently. The value is boolean (1 or 0). The value '1' means that {@link
+ * Whether or not syncs (bulk set operations) for {@link DeviceConfig} are currently
+ * persistently disabled. This is only used for the {@link
+ * Config#SYNC_DISABLED_MODE_PERSISTENT persistent} mode, {@link
+ * Config#SYNC_DISABLED_MODE_UNTIL_REBOOT until_reboot} mode is not stored in settings.
+ * The value is boolean (1 or 0). The value '1' means that {@link
* DeviceConfig#setProperties(DeviceConfig.Properties)} will return {@code false}.
*
* @hide
@@ -14898,6 +15087,33 @@ public final class Settings {
"max_sound_trigger_detection_service_ops_per_day";
/**
+ * Setting indicating the name of the Wear OS app package containing the device's sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_PACKAGE_NAME =
+ "clockwork_sysui_package_name";
+
+ /**
+ * Setting indicating the name of the main activity of the Wear OS sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME =
+ "clockwork_sysui_main_activity_name";
+
+ /**
+ * Setting to determine if the Clockwork Home application is ready.
+ *
+ * <p>
+ * Set to 1 when the Clockwork Home application has finished starting up.
+ * </p>
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_HOME_READY = "clockwork_home_ready";
+
+ /**
* Indicates whether aware is available in the current location.
* @hide
*/
@@ -14915,6 +15131,16 @@ public final class Settings {
"power_button_long_press";
/**
+ * Override internal R.integer.config_longPressOnPowerDurationMs. It determines the length
+ * of power button press to be considered a long press in milliseconds.
+ * Used by PhoneWindowManager.
+ * @hide
+ */
+ @Readable
+ public static final String POWER_BUTTON_LONG_PRESS_DURATION_MS =
+ "power_button_long_press_duration_ms";
+
+ /**
* Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
* Allowable values detailed in frameworks/base/core/res/res/values/config.xml.
* Used by PhoneWindowManager.
@@ -14956,6 +15182,15 @@ public final class Settings {
public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
/**
+ * Global settings that shouldn't be persisted.
+ *
+ * @hide
+ */
+ public static final String[] TRANSIENT_SETTINGS = {
+ CLOCKWORK_HOME_READY,
+ };
+
+ /**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
*
@@ -14977,6 +15212,7 @@ public final class Settings {
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL,
+ CALL_METHOD_DELETE_GLOBAL,
sProviderHolder,
Global.class);
@@ -16158,6 +16394,556 @@ public final class Settings {
* @hide
*/
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
+
+ /**
+ * Settings migrated from Wear OS settings provider.
+ * @hide
+ */
+ public static class Wearable {
+ /**
+ * Whether the user has any pay tokens on their watch.
+ * @hide
+ */
+ public static final String HAS_PAY_TOKENS = "has_pay_tokens";
+
+ /**
+ * Gcm checkin timeout in minutes.
+ * @hide
+ */
+ public static final String GMS_CHECKIN_TIMEOUT_MIN = "gms_checkin_timeout_min";
+
+ /**
+ * If hotword detection should be enabled.
+ * @hide
+ */
+ public static final String HOTWORD_DETECTION_ENABLED = "hotword_detection_enabled";
+
+ /**
+ * Whether Smart Replies are enabled within Wear.
+ * @hide
+ */
+ public static final String SMART_REPLIES_ENABLED = "smart_replies_enabled";
+
+ /**
+ * The default vibration pattern.
+ * @hide
+ */
+ public static final String DEFAULT_VIBRATION = "default_vibration";
+
+ /**
+ * If FLP should obtain location data from the paired device.
+ * @hide
+ */
+ public static final String OBTAIN_PAIRED_DEVICE_LOCATION =
+ "obtain_paired_device_location";
+
+ /**
+ * Whether the device is in retail mode.
+ * @hide
+ */
+ public static final String RETAIL_MODE = "retail_mode";
+
+ // Possible retail mode states
+ /** @hide */
+ public static final int RETAIL_MODE_CONSUMER = 0;
+ /** @hide */
+ public static final int RETAIL_MODE_RETAIL = 1;
+
+ /**
+ * The play store availability on companion phone.
+ * @hide
+ */
+ public static final String PHONE_PLAY_STORE_AVAILABILITY =
+ "phone_play_store_availability";
+
+ // Possible phone play store availability states
+ /** @hide */
+ public static final int PHONE_PLAY_STORE_AVAILABILITY_UNKNOWN = 0;
+ /** @hide */
+ public static final int PHONE_PLAY_STORE_AVAILABLE = 1;
+ /** @hide */
+ public static final int PHONE_PLAY_STORE_UNAVAILABLE = 2;
+
+ /**
+ * Whether the bug report is enabled.
+ * @hide
+ */
+ public static final String BUG_REPORT = "bug_report";
+
+ // Possible bug report states
+ /** @hide */
+ public static final int BUG_REPORT_DISABLED = 0;
+ /** @hide */
+ public static final int BUG_REPORT_ENABLED = 1;
+
+ /**
+ * The enabled/disabled state of the SmartIlluminate.
+ * @hide
+ */
+ public static final String SMART_ILLUMINATE_ENABLED = "smart_illuminate_enabled";
+
+ /**
+ * Whether automatic time is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_AUTO_TIME = "clockwork_auto_time";
+
+ // Possible clockwork auto time states
+ /** @hide */
+ public static final int SYNC_TIME_FROM_PHONE = 0;
+ /** @hide */
+ public static final int SYNC_TIME_FROM_NETWORK = 1;
+ /** @hide */
+ public static final int AUTO_TIME_OFF = 2;
+ /** @hide */
+ public static final int INVALID_AUTO_TIME_STATE = 3;
+
+
+ /**
+ * Whether automatic time zone is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_AUTO_TIME_ZONE = "clockwork_auto_time_zone";
+
+ // Possible clockwork auto time zone states
+ /** @hide */
+ public static final int SYNC_TIME_ZONE_FROM_PHONE = 0;
+ /** @hide */
+ public static final int SYNC_TIME_ZONE_FROM_NETWORK = 1;
+ /** @hide */
+ public static final int AUTO_TIME_ZONE_OFF = 2;
+ /** @hide */
+ public static final int INVALID_AUTO_TIME_ZONE_STATE = 3;
+
+ /**
+ * Whether 24 hour time format is enabled on the watch.
+ * @hide
+ */
+ public static final String CLOCKWORK_24HR_TIME = "clockwork_24hr_time";
+
+ /**
+ * Whether the auto wifi toggle setting is enabled.
+ * @hide
+ */
+ public static final String AUTO_WIFI = "auto_wifi";
+
+ // Possible force wifi on states
+ /** @hide */
+ public static final int AUTO_WIFI_DISABLED = 0;
+ /** @hide */
+ public static final int AUTO_WIFI_ENABLED = 1;
+
+ /**
+ * The number of minutes after the WiFi enters power save mode.
+ * @hide
+ */
+ public static final String WIFI_POWER_SAVE = "wifi_power_save";
+
+ /**
+ * The time at which we should no longer skip the wifi requirement check (we skip the
+ * wifi requirement until this time). The time is in millis since epoch.
+ * @hide
+ */
+ public static final String ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS =
+ "alt_bypass_wifi_requirement_time_millis";
+
+ /**
+ * Whether or not Up/Down Gestures are enabled.
+ * @hide
+ */
+ public static final String UPDOWN_GESTURES_ENABLED = "updown_gestures_enabled";
+
+ /**
+ * Whether the setup was skipped.
+ * @hide
+ */
+ public static final String SETUP_SKIPPED = "setup_skipped";
+
+ // Possible setup_skipped states
+ /** @hide */
+ public static final int SETUP_SKIPPED_UNKNOWN = 0;
+ /** @hide */
+ public static final int SETUP_SKIPPED_YES = 1;
+ /** @hide */
+ public static final int SETUP_SKIPPED_NO = 2;
+
+ /**
+ * The last requested call forwarding action.
+ * @hide
+ */
+ public static final String LAST_CALL_FORWARD_ACTION = "last_call_forward_action";
+
+ // Possible call forwarding actions
+ /** @hide */
+ public static final int CALL_FORWARD_ACTION_ON = 1;
+ /** @hide */
+ public static final int CALL_FORWARD_ACTION_OFF = 2;
+ /** @hide */
+ public static final int CALL_FORWARD_NO_LAST_ACTION = -1;
+
+ // Stem button settings.
+ /** @hide */
+ public static final String STEM_1_TYPE = "STEM_1_TYPE";
+ /** @hide */
+ public static final String STEM_1_DATA = "STEM_1_DATA";
+ /** @hide */
+ public static final String STEM_1_DEFAULT_DATA = "STEM_1_DEFAULT_DATA";
+ /** @hide */
+ public static final String STEM_2_TYPE = "STEM_2_TYPE";
+ /** @hide */
+ public static final String STEM_2_DATA = "STEM_2_DATA";
+ /** @hide */
+ public static final String STEM_2_DEFAULT_DATA = "STEM_2_DEFAULT_DATA";
+ /** @hide */
+ public static final String STEM_3_TYPE = "STEM_3_TYPE";
+ /** @hide */
+ public static final String STEM_3_DATA = "STEM_3_DATA";
+ /** @hide */
+ public static final String STEM_3_DEFAULT_DATA = "STEM_3_DEFAULT_DATA";
+
+ // Stem types
+ /** @hide */
+ public static final int STEM_TYPE_UNKNOWN = -1;
+ /** @hide */
+ public static final int STEM_TYPE_APP_LAUNCH = 0;
+ /** @hide */
+ public static final int STEM_TYPE_CONTACT_LAUNCH = 1;
+
+ /**
+ * If the device should be muted when off body.
+ * @hide
+ */
+ public static final String MUTE_WHEN_OFF_BODY_ENABLED = "obtain_mute_when_off_body";
+
+ /**
+ * Wear OS version string.
+ * @hide
+ */
+ public static final String WEAR_OS_VERSION_STRING = "wear_os_version_string";
+
+ /**
+ * If an alternate launcher is enabled.
+ * @hide
+ */
+ public static final String ALTERNATE_LAUNCHER_ENABLED = "alternate_launcher_enabled";
+
+ /**
+ * How round the corners of square screens are.
+ * @hide
+ */
+ public static final String CORNER_ROUNDNESS = "corner_roundness";
+
+ /**
+ * Whether the physical button has been set.
+ * @hide
+ */
+ public static final String BUTTON_SET = "button_set";
+
+ /**
+ * Whether there is a side button.
+ * @hide
+ */
+ public static final String SIDE_BUTTON = "side_button";
+
+ /**
+ * The android wear system version.
+ * @hide
+ */
+ public static final String ANDROID_WEAR_VERSION = "android_wear_version";
+
+ /**
+ * The wear system capabiltiies.
+ * @hide
+ */
+ public static final String SYSTEM_CAPABILITIES = "system_capabilities";
+
+ /**
+ * The android wear system edition.
+ * @hide
+ */
+ public static final String SYSTEM_EDITION = "android_wear_system_edition";
+
+ /**
+ * The Wear platform MR number.
+ * @hide
+ */
+ public static final String WEAR_PLATFORM_MR_NUMBER = "wear_platform_mr_number";
+
+ /**
+ * The bluetooth settings storing duplicate address of companion device.
+ * @hide
+ */
+ public static final String COMPANION_BT_ADDRESS_DUAL = "companion_bt_address_dual";
+
+ /**
+ * The offset of the visible screen from the display bottom (overscan bottom).
+ * @hide
+ */
+ public static final String BOTTOM_OFFSET = "bottom_offset";
+
+ /**
+ * The shape of the display.
+ * @hide
+ */
+ public static final String DISPLAY_SHAPE = "display_shape";
+
+ // Possible display shapes
+ /** @hide */
+ public static final int DISPLAY_SHAPE_SQUARE = 0;
+ /** @hide */
+ public static final int DISPLAY_SHAPE_ROUND = 1;
+
+ /**
+ * The different levels of screen brightness the user can select.
+ * @hide
+ */
+ public static final String SCREEN_BRIGHTNESS_LEVEL = "screen_brightness_level";
+
+ /**
+ * The mobile signal detector setting.
+ * @hide
+ */
+ public static final String MOBILE_SIGNAL_DETECTOR = "mobile_signal_detector";
+
+
+ /**
+ * Whether ambient is currently enabled.
+ * @hide
+ */
+ public static final String AMBIENT_ENABLED = "ambient_enabled";
+
+ /**
+ * Whether ambient tilt to wake is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TILT_TO_WAKE = "ambient_tilt_to_wake";
+
+ /**
+ * Whether ambient low bit mode is enabled by developer options.
+ * @hide
+ */
+ public static final String AMBIENT_LOW_BIT_ENABLED_DEV = "ambient_low_bit_enabled_dev";
+
+ /**
+ * Whether ambient touch to wake is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TOUCH_TO_WAKE = "ambient_touch_to_wake";
+
+ /**
+ * Whether ambient tilt to bright is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_TILT_TO_BRIGHT = "ambient_tilt_to_bright";
+
+ /**
+ * Whether the current watchface is decomposable.
+ * @hide
+ */
+ public static final String DECOMPOSABLE_WATCHFACE = "current_watchface_decomposable";
+
+ /**
+ * Whether to force ambient when docked.
+ * @hide
+ */
+ public static final String AMBIENT_FORCE_WHEN_DOCKED = "ambient_force_when_docked";
+
+ /**
+ * The id of the gesture sensor.
+ * @hide
+ */
+ public static final String AMBIENT_GESTURE_SENSOR_ID = "ambient_gesture_sensor_id";
+
+ /**
+ * Whether the ambient low bit mode is enabled.
+ * @hide
+ */
+ public static final String AMBIENT_LOW_BIT_ENABLED = "ambient_low_bit_enabled";
+
+ /**
+ * The timeout duration in minutes of ambient mode when plugged in.
+ * @hide
+ */
+ public static final String AMBIENT_PLUGGED_TIMEOUT_MIN = "ambient_plugged_timeout_min";
+
+ /**
+ * The companion device's bluetooth address.
+ * @hide
+ */
+ public static final String COMPANION_ADDRESS = "companion_address";
+
+ /**
+ * What OS does paired device has.
+ * @hide
+ */
+ public static final String PAIRED_DEVICE_OS_TYPE = "paired_device_os_type";
+
+ // Possible values of PAIRED_DEVICE_OS_TYPE
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_UNKNOWN = 0;
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1;
+ /** @hide */
+ public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2;
+
+ /**
+ * The bluetooth settings selected BLE role for the companion.
+ * @hide
+ */
+ public static final String COMPANION_BLE_ROLE = "companion_ble_role";
+
+ // Possible values of COMPANION_BLE_ROLE
+ /** @hide */
+ public static final int BLUETOOTH_ROLE_CENTRAL = 1;
+ /** @hide */
+ public static final int BLUETOOTH_ROLE_PERIPHERAL = 2;
+
+ /**
+ * The bluetooth settings stored companion device name.
+ * @hide
+ */
+ public static final String COMPANION_NAME = "companion_bt_name";
+
+ /**
+ * The user's last setting for hfp client.
+ * @hide
+ */
+ public static final String USER_HFP_CLIENT_SETTING = "user_hfp_client_setting";
+
+ // Possible hfp client user setting values
+ /** @hide */
+ public static final int HFP_CLIENT_UNSET = 0;
+ /** @hide */
+ public static final int HFP_CLIENT_ENABLED = 1;
+ /** @hide */
+ public static final int HFP_CLIENT_DISABLED = 2;
+
+ /**
+ * The current HFP client profile setting.
+ * @hide
+ */
+ public static final String HFP_CLIENT_PROFILE_ENABLED = "hfp_client_profile_enabled";
+
+ /**
+ * The companion phone's android version.
+ * @hide
+ */
+ public static final String COMPANION_OS_VERSION = "wear_companion_os_version";
+
+ // Companion os version constants
+ /** @hide */
+ public static final int COMPANION_OS_VERSION_UNDEFINED = -1;
+
+ /**
+ * A boolean value to indicate if we want to support all languages in LE edition on
+ * wear. 1 for supporting, 0 for not supporting.
+ * @hide
+ */
+ public static final String ENABLE_ALL_LANGUAGES = "enable_all_languages";
+
+ /**
+ * The Locale (as language tag) the user chose at startup.
+ * @hide
+ */
+ public static final String SETUP_LOCALE = "setup_locale";
+
+ /**
+ * The version of oem setup present.
+ * @hide
+ */
+ public static final String OEM_SETUP_VERSION = "oem_setup_version";
+
+ /**
+ * Controls the gestures feature.
+ * @hide
+ */
+ public static final String MASTER_GESTURES_ENABLED = "master_gestures_enabled";
+
+ /**
+ * Whether or not ungaze is enabled.
+ * @hide
+ */
+ public static final String UNGAZE_ENABLED = "ungaze_enabled";
+
+ /**
+ * The device's battery saver mode, which can be one of the following:
+ * -{@link BATTERY_SAVER_MODE_NONE}
+ * -{@link BATTERY_SAVER_MODE_LIGHT}
+ * -{@link BATTERY_SAVER_MODE_TRADITIONAL_WATCH}
+ * -{@link BATTERY_SAVER_MODE_TIME_ONLY}
+ * -{@link BATTERY_SAVER_MODE_CUSTOM}
+ * @hide
+ */
+ public static final String BATTERY_SAVER_MODE = "battery_saver_mode";
+
+ /**
+ * Not in Battery Saver Mode
+ * @hide
+ */
+ public static final int BATTERY_SAVER_MODE_NONE = 0;
+ /**
+ * In Lightweight Battery Saver Mode
+ * @hide
+ */
+ public static final int BATTERY_SAVER_MODE_LIGHT = 1;
+ /**
+ * In Traditional Watch Mode Battery Saver Mode
+ * @hide
+ */
+ public static final int BATTERY_SAVER_MODE_TRADITIONAL_WATCH = 2;
+ /**
+ * In Time-only Mode Battery Saver Mode
+ * @hide
+ */
+ public static final int BATTERY_SAVER_MODE_TIME_ONLY = 3;
+ /**
+ * Partner's Battery Saver implementation is being used
+ * @hide
+ */
+ public static final int BATTERY_SAVER_MODE_CUSTOM = 4;
+
+ /**
+ * The maximum ambient mode duration when an activity is allowed to auto resume.
+ * @hide
+ */
+ public static final String WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS =
+ "wear_activity_auto_resume_timeout_ms";
+
+ /**
+ * If burn in protection is enabled.
+ * @hide
+ */
+ public static final String BURN_IN_PROTECTION_ENABLED = "burn_in_protection";
+
+ /**
+ * Whether the device has combined location setting enabled.
+ * @hide
+ */
+ public static final String COMBINED_LOCATION_ENABLED = "combined_location_enable";
+
+ /**
+ * The wrist orientation mode of the device
+ * Valid values - LEFT_WRIST_ROTATION_0 = "0" (default), LEFT_WRIST_ROTATION_180 = "1",
+ * RIGHT_WRIST_ROTATION_0 = "2", RIGHT_WRIST_ROTATION_180 = "3"
+ * @hide
+ */
+ public static final String WRIST_ORIENTATION_MODE = "wear_wrist_orientation_mode";
+
+ /**
+ * Setting indicating the name of the Wear OS app package containing the device's sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_PACKAGE = "clockwork_sysui_package";
+
+ /**
+ * Setting indicating the name of the main activity of the Wear OS sysui.
+ *
+ * @hide
+ */
+ public static final String CLOCKWORK_SYSUI_MAIN_ACTIVITY =
+ "clockwork_sysui_main_activity";
+ }
}
/**
@@ -16209,6 +16995,7 @@ public final class Settings {
DeviceConfig.CONTENT_URI,
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
+ CALL_METHOD_DELETE_CONFIG,
CALL_METHOD_LIST_CONFIG,
CALL_METHOD_SET_ALL_CONFIG,
sProviderHolder,
@@ -16318,6 +17105,26 @@ public final class Settings {
}
/**
+ * Delete a name/value pair from the database for the specified namespace.
+ *
+ * @param resolver to access the database with.
+ * @param namespace to delete the name/value pair from.
+ * @param name to delete.
+ * @return true if the value was deleted, false on database errors. If the name/value pair
+ * did not exist, return True.
+ *
+ * @see #resetToDefaults(ContentResolver, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name) {
+ return sNameValueCache.deleteStringForUser(resolver,
+ createCompositeName(namespace, name), resolver.getUserId());
+ }
+
+ /**
* Reset the values to their defaults.
* <p>
* The method accepts an optional prefix parameter. If provided, only pairs with a name that
@@ -16350,47 +17157,47 @@ public final class Settings {
}
/**
- * Bridge method between {@link DeviceConfig#setSyncDisabled(int)} and the
+ * Bridge method between {@link DeviceConfig#setSyncDisabledMode(int)} and the
* {@link com.android.providers.settings.SettingsProvider} implementation.
*
* @hide
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static void setSyncDisabled(
+ static void setSyncDisabledMode(
@NonNull ContentResolver resolver, @SyncDisabledMode int disableSyncMode) {
try {
Bundle args = new Bundle();
args.putInt(CALL_METHOD_SYNC_DISABLED_MODE_KEY, disableSyncMode);
IContentProvider cp = sProviderHolder.getProvider(resolver);
- cp.call(resolver.getAttributionSource(),
- sProviderHolder.mUri.getAuthority(), CALL_METHOD_SET_SYNC_DISABLED_CONFIG,
- null, args);
+ cp.call(resolver.getAttributionSource(), sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG, null, args);
} catch (RemoteException e) {
- Log.w(TAG, "Can't set sync disabled " + DeviceConfig.CONTENT_URI, e);
+ Log.w(TAG, "Can't set sync disabled mode " + DeviceConfig.CONTENT_URI, e);
}
}
/**
- * Bridge method between {@link DeviceConfig#isSyncDisabled()} and the
+ * Bridge method between {@link DeviceConfig#getSyncDisabledMode()} and the
* {@link com.android.providers.settings.SettingsProvider} implementation.
*
* @hide
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
@RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
- static boolean isSyncDisabled(@NonNull ContentResolver resolver) {
+ static int getSyncDisabledMode(@NonNull ContentResolver resolver) {
try {
Bundle args = Bundle.EMPTY;
IContentProvider cp = sProviderHolder.getProvider(resolver);
Bundle bundle = cp.call(resolver.getAttributionSource(),
- sProviderHolder.mUri.getAuthority(), CALL_METHOD_IS_SYNC_DISABLED_CONFIG,
+ sProviderHolder.mUri.getAuthority(),
+ CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG,
null, args);
- return bundle.getBoolean(KEY_CONFIG_IS_SYNC_DISABLED_RETURN);
+ return bundle.getInt(KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN);
} catch (RemoteException e) {
- Log.w(TAG, "Can't query sync disabled " + DeviceConfig.CONTENT_URI, e);
+ Log.w(TAG, "Can't query sync disabled mode " + DeviceConfig.CONTENT_URI, e);
}
- return false;
+ return -1;
}
/**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index f3a8b5d79ea0..387fc39a65e4 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5343,19 +5343,19 @@ public final class Telephony {
public static final String COLUMN_RCS_CONFIG = "rcs_config";
/**
- * TelephonyProvider column name for VoIMS provisioning. Default is 0.
- * <P>Type: INTEGER </P>
+ * TelephonyProvider column name for device to device sharing status.
*
* @hide
*/
- public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+ public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
/**
- * TelephonyProvider column name for device to device sharing status.
+ * TelephonyProvider column name for VoIMS provisioning. Default is 0.
+ * <P>Type: INTEGER </P>
*
* @hide
*/
- public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
+ public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
/**
* TelephonyProvider column name for information selected contacts that allow device to
@@ -5365,5 +5365,6 @@ public final class Telephony {
*/
public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
"d2d_sharing_contacts";
+
}
}
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index d15514dfb899..8eeecc293104 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -28,6 +28,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines actions to be applied to a {@link RemoteViews template presentation}.
@@ -94,7 +95,7 @@ public final class BatchUpdates implements Parcelable {
*/
public Builder updateTemplate(@NonNull RemoteViews updates) {
throwIfDestroyed();
- mUpdates = Preconditions.checkNotNull(updates);
+ mUpdates = Objects.requireNonNull(updates);
return this;
}
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index e3e884406188..5697e24a2925 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -32,6 +32,7 @@ import com.android.internal.util.Preconditions;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -147,9 +148,9 @@ public final class CharSequenceTransformation extends InternalTransformation imp
public Builder addField(@NonNull AutofillId id, @NonNull Pattern regex,
@NonNull String subst) {
throwIfDestroyed();
- Preconditions.checkNotNull(id);
- Preconditions.checkNotNull(regex);
- Preconditions.checkNotNull(subst);
+ Objects.requireNonNull(id);
+ Objects.requireNonNull(regex);
+ Objects.requireNonNull(subst);
mFields.put(id, new Pair<>(regex, subst));
return this;
diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java
index c7dc15a8c774..92952cb7dc24 100644
--- a/core/java/android/service/autofill/CompositeUserData.java
+++ b/core/java/android/service/autofill/CompositeUserData.java
@@ -25,10 +25,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Objects;
/**
* Holds both a generic and package-specific userData used for
@@ -103,7 +102,7 @@ public final class CompositeUserData implements FieldClassificationUserData, Par
@Nullable
@Override
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
return null;
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 6df01545c09f..f3f912bb3a5b 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,6 +32,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines a custom description for the autofill save UI.
@@ -157,7 +158,7 @@ public final class CustomDescription implements Parcelable {
* {@link android.os.Build.VERSION_CODES#P} or higher).
*/
public Builder(@NonNull RemoteViews parentPresentation) {
- mPresentation = Preconditions.checkNotNull(parentPresentation);
+ mPresentation = Objects.requireNonNull(parentPresentation);
}
/**
@@ -276,7 +277,7 @@ public final class CustomDescription implements Parcelable {
throwIfDestroyed();
Preconditions.checkArgument((condition instanceof InternalValidator),
"not provided by Android System: %s", condition);
- Preconditions.checkNotNull(updates);
+ Objects.requireNonNull(updates);
if (mUpdates == null) {
mUpdates = new ArrayList<>();
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 87c0d9d3e08c..8539bf58da27 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -34,6 +34,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -280,7 +281,7 @@ public final class Dataset implements Parcelable {
* @param presentation The presentation used to visualize this dataset.
*/
public Builder(@NonNull RemoteViews presentation) {
- Preconditions.checkNotNull(presentation, "presentation must be non-null");
+ Objects.requireNonNull(presentation, "presentation must be non-null");
mPresentation = presentation;
}
@@ -296,7 +297,7 @@ public final class Dataset implements Parcelable {
*/
@SystemApi
public Builder(@NonNull InlinePresentation inlinePresentation) {
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
}
@@ -321,7 +322,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setInlinePresentation(
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
mInlinePresentation = inlinePresentation;
return this;
}
@@ -343,8 +344,8 @@ public final class Dataset implements Parcelable {
@NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation must be non-null");
mInlinePresentation = inlinePresentation;
mInlineTooltipPresentation = inlineTooltipPresentation;
@@ -540,7 +541,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, null, null);
return this;
}
@@ -613,7 +614,7 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@Nullable Pattern filter, @NonNull RemoteViews presentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, null,
new DatasetFieldFilter(filter));
return this;
@@ -644,8 +645,8 @@ public final class Dataset implements Parcelable {
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
return this;
}
@@ -676,9 +677,9 @@ public final class Dataset implements Parcelable {
@NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
inlineTooltipPresentation, null);
@@ -722,8 +723,8 @@ public final class Dataset implements Parcelable {
@Nullable Pattern filter, @NonNull RemoteViews presentation,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
new DatasetFieldFilter(filter));
return this;
@@ -761,9 +762,9 @@ public final class Dataset implements Parcelable {
@NonNull InlinePresentation inlinePresentation,
@NonNull InlinePresentation inlineTooltipPresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(presentation, "presentation cannot be null");
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
- Preconditions.checkNotNull(inlineTooltipPresentation,
+ Objects.requireNonNull(presentation, "presentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlineTooltipPresentation,
"inlineTooltipPresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
inlineTooltipPresentation, new DatasetFieldFilter(filter));
@@ -800,7 +801,7 @@ public final class Dataset implements Parcelable {
@Nullable AutofillValue value, @Nullable Pattern filter,
@NonNull InlinePresentation inlinePresentation) {
throwIfDestroyed();
- Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+ Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null");
setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
new DatasetFieldFilter(filter));
return this;
@@ -819,7 +820,7 @@ public final class Dataset implements Parcelable {
@Nullable InlinePresentation inlinePresentation,
@Nullable InlinePresentation tooltip,
@Nullable DatasetFieldFilter filter) {
- Preconditions.checkNotNull(id, "id cannot be null");
+ Objects.requireNonNull(id, "id cannot be null");
if (mFieldIds != null) {
final int existingIdx = mFieldIds.indexOf(id);
if (existingIdx >= 0) {
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 338ba749eb79..734085737159 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -29,9 +29,8 @@ import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
import android.widget.TextView;
-import com.android.internal.util.Preconditions;
-
import java.util.Date;
+import java.util.Objects;
/**
* Replaces a {@link TextView} child of a {@link CustomDescription} with the contents of a field
@@ -57,8 +56,8 @@ public final class DateTransformation extends InternalTransformation implements
* @param dateFormat object used to transform the date value of the field to a String.
*/
public DateTransformation(@NonNull AutofillId id, @NonNull DateFormat dateFormat) {
- mFieldId = Preconditions.checkNotNull(id);
- mDateFormat = Preconditions.checkNotNull(dateFormat);
+ mFieldId = Objects.requireNonNull(id);
+ mDateFormat = Objects.requireNonNull(dateFormat);
}
/** @hide */
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 707bab1ec177..6f7808ee181a 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -27,9 +27,8 @@ import android.os.Parcelable;
import android.util.Log;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
import java.util.Date;
+import java.util.Objects;
/**
* Sanitizes a date {@link AutofillValue} using a {@link DateFormat}.
@@ -52,7 +51,7 @@ public final class DateValueSanitizer extends InternalSanitizer implements Sanit
* @param dateFormat date format applied to the actual date value of an input field.
*/
public DateValueSanitizer(@NonNull DateFormat dateFormat) {
- mDateFormat = Preconditions.checkNotNull(dateFormat);
+ mDateFormat = Objects.requireNonNull(dateFormat);
}
/** @hide */
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 5bf56cb9c1b5..5302b9b18441 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -22,12 +22,11 @@ import android.annotation.NonNull;
import android.os.Parcel;
import android.view.autofill.Helper;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
/**
* Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
@@ -39,7 +38,7 @@ public final class FieldClassification {
/** @hide */
public FieldClassification(@NonNull ArrayList<Match> matches) {
- mMatches = Preconditions.checkNotNull(matches);
+ mMatches = Objects.requireNonNull(matches);
Collections.sort(mMatches, new Comparator<Match>() {
@Override
public int compare(Match o1, Match o2) {
@@ -113,7 +112,7 @@ public final class FieldClassification {
/** @hide */
public Match(String categoryId, float score) {
- mCategoryId = Preconditions.checkNotNull(categoryId);
+ mCategoryId = Objects.requireNonNull(categoryId);
mScore = score;
}
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index 62becc507404..af846b62ae2c 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -96,6 +96,8 @@ public final class FillRequest implements Parcelable {
*/
public static final @RequestFlags int FLAG_VIEW_NOT_FOCUSED = 0x10;
+ // The flag value 0x20 has been defined in AutofillManager.
+
/** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 740ae260999f..970cb1888317 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,6 +41,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* Response for an {@link
@@ -598,7 +599,7 @@ public final class FillResponse implements Parcelable {
public Builder setHeader(@NonNull RemoteViews header) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mHeader = Preconditions.checkNotNull(header);
+ mHeader = Objects.requireNonNull(header);
return this;
}
@@ -630,7 +631,7 @@ public final class FillResponse implements Parcelable {
public Builder setFooter(@NonNull RemoteViews footer) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mFooter = Preconditions.checkNotNull(footer);
+ mFooter = Objects.requireNonNull(footer);
return this;
}
@@ -649,7 +650,7 @@ public final class FillResponse implements Parcelable {
public Builder setUserData(@NonNull UserData userData) {
throwIfDestroyed();
throwIfAuthenticationCalled();
- mUserData = Preconditions.checkNotNull(userData);
+ mUserData = Objects.requireNonNull(userData);
return this;
}
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 974f0ead9d19..e3171594c39e 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -33,6 +33,7 @@ import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -127,7 +128,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
@Deprecated
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
addOption(regex, resId);
}
@@ -143,7 +144,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
@NonNull CharSequence contentDescription) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
addOption(regex, resId, contentDescription);
}
@@ -177,7 +178,7 @@ public final class ImageTransformation extends InternalTransformation implements
*/
public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
@NonNull CharSequence contentDescription) {
- addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+ addOptionInternal(regex, resId, Objects.requireNonNull(contentDescription));
return this;
}
@@ -185,7 +186,7 @@ public final class ImageTransformation extends InternalTransformation implements
@Nullable CharSequence contentDescription) {
throwIfDestroyed();
- Preconditions.checkNotNull(regex);
+ Objects.requireNonNull(regex);
Preconditions.checkArgument(resId != 0);
mOptions.add(new Option(regex, resId, contentDescription));
diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java
index 2f098e282101..d626845b3b3e 100644
--- a/core/java/android/service/autofill/NegationValidator.java
+++ b/core/java/android/service/autofill/NegationValidator.java
@@ -22,7 +22,7 @@ import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Validator used to implement a {@code NOT} logical operation.
@@ -33,7 +33,7 @@ final class NegationValidator extends InternalValidator {
@NonNull private final InternalValidator mValidator;
NegationValidator(@NonNull InternalValidator validator) {
- mValidator = Preconditions.checkNotNull(validator);
+ mValidator = Objects.requireNonNull(validator);
}
@Override
diff --git a/core/java/android/service/autofill/RegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
index 8cb67d08e8a8..00c43473ce7f 100644
--- a/core/java/android/service/autofill/RegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -25,8 +25,7 @@ import android.os.Parcelable;
import android.util.Log;
import android.view.autofill.AutofillId;
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
import java.util.regex.Pattern;
/**
@@ -50,8 +49,8 @@ public final class RegexValidator extends InternalValidator implements Validator
* otherwise, it returns {@code false}.
*/
public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
- mId = Preconditions.checkNotNull(id);
- mRegex = Preconditions.checkNotNull(regex);
+ mId = Objects.requireNonNull(id);
+ mRegex = Objects.requireNonNull(regex);
}
/** @hide */
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 1753ecfa4e23..87a869f0ae17 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -23,7 +23,7 @@ import android.content.IntentSender;
import android.os.RemoteException;
import android.util.Log;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
/**
* Handles save requests from the {@link AutofillService} into the {@link Activity} being
@@ -70,7 +70,7 @@ public final class SaveCallback {
* or {@link #onFailure(CharSequence)} was already called.
*/
public void onSuccess(@NonNull IntentSender intentSender) {
- onSuccessInternal(Preconditions.checkNotNull(intentSender));
+ onSuccessInternal(Objects.requireNonNull(intentSender));
}
private void onSuccessInternal(@Nullable IntentSender intentSender) {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 619bfa225cf1..8edfde8c3914 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -39,6 +39,7 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Objects;
/**
* Information used to indicate that an {@link AutofillService} is interested on saving the
@@ -769,7 +770,7 @@ public final class SaveInfo implements Parcelable {
*/
public @NonNull Builder setTriggerId(@NonNull AutofillId id) {
throwIfDestroyed();
- mTriggerId = Preconditions.checkNotNull(id);
+ mTriggerId = Objects.requireNonNull(id);
return this;
}
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
index 5dd07c491980..3ad5352a1282 100644
--- a/core/java/android/service/autofill/SaveRequest.java
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -22,10 +22,9 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.internal.util.Preconditions;
-
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* This class represents a request to an {@link AutofillService
@@ -41,7 +40,7 @@ public final class SaveRequest implements Parcelable {
/** @hide */
public SaveRequest(@NonNull ArrayList<FillContext> fillContexts,
@Nullable Bundle clientState, @Nullable ArrayList<String> datasetIds) {
- mFillContexts = Preconditions.checkNotNull(fillContexts, "fillContexts");
+ mFillContexts = Objects.requireNonNull(fillContexts, "fillContexts");
mClientState = clientState;
mDatasetIds = datasetIds;
}
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
index cc48fcbe734e..5bafa7a1ff54 100644
--- a/core/java/android/service/autofill/TextValueSanitizer.java
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -26,8 +26,7 @@ import android.os.Parcelable;
import android.util.Slog;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -57,8 +56,8 @@ public final class TextValueSanitizer extends InternalSanitizer implements
* group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
*/
public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
- mRegex = Preconditions.checkNotNull(regex);
- mSubst = Preconditions.checkNotNull(subst);
+ mRegex = Objects.requireNonNull(regex);
+ mSubst = Objects.requireNonNull(subst);
}
/** @hide */
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index eaffc92c1d6b..e9d520365f3e 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -43,6 +43,7 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Defines the user data used for
@@ -108,7 +109,7 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
@Nullable
@Override
public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
if (mCategoryAlgorithms == null || !mCategoryAlgorithms.containsKey(categoryId)) {
return null;
}
@@ -296,7 +297,7 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
public Builder setFieldClassificationAlgorithmForCategory(@NonNull String categoryId,
@Nullable String name, @Nullable Bundle args) {
throwIfDestroyed();
- Preconditions.checkNotNull(categoryId);
+ Objects.requireNonNull(categoryId);
if (mCategoryAlgorithms == null) {
mCategoryAlgorithms = new ArrayMap<>(getMaxCategoryCount());
}
@@ -368,13 +369,13 @@ public final class UserData implements FieldClassificationUserData, Parcelable {
}
private String checkNotEmpty(@NonNull String name, @Nullable String value) {
- Preconditions.checkNotNull(value);
+ Objects.requireNonNull(value);
Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
return value;
}
private void checkValidValue(@Nullable String value) {
- Preconditions.checkNotNull(value);
+ Objects.requireNonNull(value);
final int length = value.length();
Preconditions.checkArgumentInRange(length, getMinValueLength(),
getMaxValueLength(), "value length (" + length + ")");
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 7cd674e847fd..fd0520d87af8 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -26,9 +26,8 @@ import android.util.Pair;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
-
import java.util.List;
+import java.util.Objects;
/**
* Object used to interact with the autofill system.
@@ -52,7 +51,7 @@ public final class FillController {
* automatically {@link FillWindow#destroy() destroyed}.
*/
public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> values) {
- Preconditions.checkNotNull(values);
+ Objects.requireNonNull(values);
if (sDebug) {
Log.d(TAG, "autofill() with " + values.size() + " values");
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index d4f7e114c291..0ce040d7f862 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -36,12 +36,12 @@ import android.view.WindowManager;
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.Objects;
/**
* Handle to a window used to display the augmented autofill UI.
@@ -103,9 +103,9 @@ public final class FillWindow implements AutoCloseable {
Log.d(TAG, "Updating " + area + " + with " + rootView);
}
// TODO(b/123100712): add test case for null
- Preconditions.checkNotNull(area);
- Preconditions.checkNotNull(area.proxy);
- Preconditions.checkNotNull(rootView);
+ Objects.requireNonNull(area);
+ Objects.requireNonNull(area.proxy);
+ Objects.requireNonNull(rootView);
// TODO(b/123100712): must check the area is a valid object returned by
// SmartSuggestionParams, throw IAE if not
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 3c44cfd4c7f6..737c95fadccd 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -54,7 +54,6 @@ import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FrameworkStatsLog;
-import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,6 +61,7 @@ import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -544,8 +544,8 @@ public abstract class ContentCaptureService extends Service {
@Override
public void onAccept(@NonNull Executor executor,
@NonNull DataShareReadAdapter adapter) {
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(executor);
DataShareReadAdapterDelegate delegate =
new DataShareReadAdapterDelegate(executor, adapter,
@@ -661,9 +661,9 @@ public abstract class ContentCaptureService extends Service {
DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter,
LocalDataShareAdapterResourceManager resourceManager) {
- Preconditions.checkNotNull(executor);
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(resourceManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(resourceManager);
resourceManager.initializeForDelegate(this, adapter, executor);
mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 4fd36e590d18..91042bfa3402 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -91,7 +91,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
= "android.service.notification.NotificationAssistantService";
/**
- * Data type: int, the feedback rating score provided by user
+ * Data type: int, the feedback rating score provided by user. The score can be any integer
+ * value depends on the experimental and feedback UX design.
*/
public static final String FEEDBACK_RATING = "feedback.rating";
@@ -129,7 +130,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* A notification was posted by an app. Called before post.
*
* <p>Note: this method is only called if you don't override
- * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)} or
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
*
* @param sbn the new notification
* @return an adjustment or null to take no action, within 200ms.
@@ -139,6 +141,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/**
* A notification was posted by an app. Called before post.
*
+ * <p>Note: this method is only called if you don't override
+ * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel, RankingMap)}.</p>
+ *
* @param sbn the new notification
* @param channel the channel the notification was posted to
* @return an adjustment or null to take no action, within 200ms.
@@ -282,7 +287,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* @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
+ * @param feedback the received feedback, such as {@link #FEEDBACK_RATING rating score}
*/
public void onNotificationFeedbackReceived(@NonNull String key, @NonNull RankingMap rankingMap,
@NonNull Bundle feedback) {
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 314c97db4e7b..1e5ff3a536ce 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -20,6 +20,8 @@ import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Calendar;
import java.util.Objects;
import java.util.TimeZone;
@@ -92,16 +94,22 @@ public class ScheduleCalendar {
*/
public long getNextChangeTime(long now) {
if (mSchedule == null) return 0;
- final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute);
- final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute);
+ final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute, true);
+ final long nextEnd = getNextTime(now, mSchedule.endHour, mSchedule.endMinute, false);
long nextScheduleTime = Math.min(nextStart, nextEnd);
return nextScheduleTime;
}
- private long getNextTime(long now, int hr, int min) {
- final long time = getTime(now, hr, min);
- return time <= now ? addDays(time, 1) : time;
+ private long getNextTime(long now, int hr, int min, boolean adjust) {
+ // The adjust parameter indicates whether to potentially adjust the time to the closest
+ // actual time if the indicated time is one skipped due to daylight time.
+ final long time = adjust ? getClosestActualTime(now, hr, min) : getTime(now, hr, min);
+ if (time <= now) {
+ final long tomorrow = addDays(time, 1);
+ return adjust ? getClosestActualTime(tomorrow, hr, min) : getTime(tomorrow, hr, min);
+ }
+ return time;
}
private long getTime(long millis, int hour, int min) {
@@ -119,7 +127,7 @@ public class ScheduleCalendar {
*/
public boolean isInSchedule(long time) {
if (mSchedule == null || mDays.size() == 0) return false;
- final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute);
+ final long start = getClosestActualTime(time, mSchedule.startHour, mSchedule.startMinute);
long end = getTime(time, mSchedule.endHour, mSchedule.endMinute);
if (end <= start) {
end = addDays(end, 1);
@@ -134,7 +142,7 @@ public class ScheduleCalendar {
*/
public boolean isAlarmInSchedule(long alarm, long now) {
if (mSchedule == null || mDays.size() == 0) return false;
- final long start = getTime(alarm, mSchedule.startHour, mSchedule.startMinute);
+ final long start = getClosestActualTime(alarm, mSchedule.startHour, mSchedule.startMinute);
long end = getTime(alarm, mSchedule.endHour, mSchedule.endMinute);
if (end <= start) {
end = addDays(end, 1);
@@ -186,4 +194,41 @@ public class ScheduleCalendar {
mCalendar.add(Calendar.DATE, days);
return mCalendar.getTimeInMillis();
}
+
+ /**
+ * This function returns the closest "actual" time to the provided hour/minute relative to the
+ * reference time. For most times this will behave exactly the same as getTime, but for any time
+ * during the hour skipped forward for daylight savings time (for instance, 02:xx when the
+ * clock is set to 03:00 after 01:59), this method will return the time when the clock changes
+ * (in this example, 03:00).
+ *
+ * Assumptions made in this implementation:
+ * - Time is moved forward on an hour boundary (minute 0) by exactly 1hr when clocks shift
+ * - a lenient Calendar implementation will interpret 02:xx on a day when 2-3AM is skipped
+ * as 03:xx
+ * - The skipped hour is never 11PM / 23:00.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public long getClosestActualTime(long refTime, int hour, int min) {
+ long resTime = getTime(refTime, hour, min);
+ if (!mCalendar.getTimeZone().observesDaylightTime()) {
+ // Do nothing if the timezone doesn't observe daylight time at all.
+ return resTime;
+ }
+
+ // Approach to identifying whether the time is "skipped": get the result from starting with
+ // refTime and setting hour and minute, then re-extract the hour and minute of the resulting
+ // moment in time. If the hour is exactly one more than the passed-in hour and the minute is
+ // the same, then the provided hour is likely a skipped one. If the time doesn't fall into
+ // this category, return the unmodified time instead.
+ mCalendar.setTimeInMillis(resTime);
+ int resHr = mCalendar.get(Calendar.HOUR_OF_DAY);
+ int resMin = mCalendar.get(Calendar.MINUTE);
+ if (resHr == hour + 1 && resMin == min) {
+ return getTime(refTime, resHr, 0);
+ }
+ return resTime;
+ }
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index ee8353a9f203..45bdb110634c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -19,9 +19,11 @@ package android.service.notification;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -79,7 +81,7 @@ public class ZenModeConfig implements Parcelable {
public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
public static final int MAX_SOURCE = SOURCE_STAR;
- private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
+ private static final int DEFAULT_SOURCE = SOURCE_STAR;
private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
public static final String MANUAL_RULE_ID = "MANUAL_RULE";
@@ -103,14 +105,17 @@ public class ZenModeConfig implements Parcelable {
private static final boolean DEFAULT_ALLOW_MEDIA = true;
private static final boolean DEFAULT_ALLOW_SYSTEM = false;
private static final boolean DEFAULT_ALLOW_CALLS = true;
- private static final boolean DEFAULT_ALLOW_MESSAGES = false;
+ private static final boolean DEFAULT_ALLOW_MESSAGES = true;
private static final boolean DEFAULT_ALLOW_REMINDERS = false;
private static final boolean DEFAULT_ALLOW_EVENTS = false;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
- private static final boolean DEFAULT_ALLOW_CONV = false;
- private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_NONE;
+ private static final boolean DEFAULT_ALLOW_CONV = true;
+ private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
- private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
+ // Default setting here is 010011101 = 157
+ private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
+ SUPPRESSED_EFFECT_SCREEN_OFF | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_LIGHTS | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_AMBIENT;
public static final int XML_VERSION = 8;
public static final String ZEN_TAG = "zen";
@@ -568,16 +573,22 @@ public class ZenModeConfig implements Parcelable {
// migrate old suppressed visual effects fields, if they still exist in the xml
Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
- if (allowWhenScreenOff != null) {
+ Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
+ if (allowWhenScreenOff != null || allowWhenScreenOn != null) {
+ // If either setting exists, then reset the suppressed visual effects field
+ // to 0 (all allowed) so that only the relevant bits are disallowed by
+ // the migrated settings.
readSuppressedEffects = true;
+ rt.suppressedVisualEffects = 0;
+ }
+ if (allowWhenScreenOff != null) {
if (!allowWhenScreenOff) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
- | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+ | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_AMBIENT;
}
}
- Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
if (allowWhenScreenOn != null) {
- readSuppressedEffects = true;
if (!allowWhenScreenOn) {
rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
}
@@ -1001,7 +1012,7 @@ public class ZenModeConfig implements Parcelable {
builder.showBadges(
(suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0);
builder.showInAmbientDisplay(
- (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0);
+ (suppressedVisualEffects & SUPPRESSED_EFFECT_AMBIENT) == 0);
builder.showInNotificationList(
(suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0);
}
@@ -1085,7 +1096,7 @@ public class ZenModeConfig implements Parcelable {
boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
ZenPolicy.VISUAL_EFFECT_AMBIENT,
- isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
+ isVisualEffectAllowed(SUPPRESSED_EFFECT_AMBIENT,
defaultPolicy));
if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
@@ -1120,7 +1131,7 @@ public class ZenModeConfig implements Parcelable {
}
if (suppressAmbient) {
- suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
+ suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
}
if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 1fb18fab3775..29d430d8e20f 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -49,10 +49,9 @@ import android.view.textclassifier.TextLanguage;
import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -127,8 +126,8 @@ public abstract class TextClassifierService extends Service {
public void onSuggestSelection(
TextClassificationSessionId sessionId,
TextSelection.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestSelection(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
@@ -138,8 +137,8 @@ public abstract class TextClassifierService extends Service {
public void onClassifyText(
TextClassificationSessionId sessionId,
TextClassification.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onClassifyText(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -148,8 +147,8 @@ public abstract class TextClassifierService extends Service {
public void onGenerateLinks(
TextClassificationSessionId sessionId,
TextLinks.Request request, ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onGenerateLinks(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -158,7 +157,7 @@ public abstract class TextClassifierService extends Service {
public void onSelectionEvent(
TextClassificationSessionId sessionId,
SelectionEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
mMainThreadHandler.post(
() -> TextClassifierService.this.onSelectionEvent(sessionId, event));
}
@@ -167,7 +166,7 @@ public abstract class TextClassifierService extends Service {
public void onTextClassifierEvent(
TextClassificationSessionId sessionId,
TextClassifierEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
mMainThreadHandler.post(
() -> TextClassifierService.this.onTextClassifierEvent(sessionId, event));
}
@@ -177,8 +176,8 @@ public abstract class TextClassifierService extends Service {
TextClassificationSessionId sessionId,
TextLanguage.Request request,
ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onDetectLanguage(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -188,8 +187,8 @@ public abstract class TextClassifierService extends Service {
TextClassificationSessionId sessionId,
ConversationActions.Request request,
ITextClassifierCallback callback) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
mMainThreadHandler.post(() -> TextClassifierService.this.onSuggestConversationActions(
sessionId, request, mCancellationSignal, new ProxyCallback<>(callback)));
}
@@ -197,8 +196,8 @@ public abstract class TextClassifierService extends Service {
@Override
public void onCreateTextClassificationSession(
TextClassificationContext context, TextClassificationSessionId sessionId) {
- Preconditions.checkNotNull(context);
- Preconditions.checkNotNull(sessionId);
+ Objects.requireNonNull(context);
+ Objects.requireNonNull(sessionId);
mMainThreadHandler.post(
() -> TextClassifierService.this.onCreateTextClassificationSession(
context, sessionId));
@@ -487,7 +486,7 @@ public abstract class TextClassifierService extends Service {
private ITextClassifierCallback mTextClassifierCallback;
private ProxyCallback(ITextClassifierCallback textClassifierCallback) {
- mTextClassifierCallback = Preconditions.checkNotNull(textClassifierCallback);
+ mTextClassifierCallback = Objects.requireNonNull(textClassifierCallback);
}
@Override
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index a2b22e8d8a7a..b516b0254f5c 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -114,9 +114,15 @@ import java.util.Objects;
*
* <p>Threading:
*
- * <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously
- * to the system server. Calls to {@link #onStartUpdates(long)} and {@link #onStopUpdates()} will
- * occur on a single thread.
+ * <p>Outgoing calls to {@code report} methods can be made on any thread and will be delivered
+ * asynchronously to the system server. Incoming calls to {@link TimeZoneProviderService}-defined
+ * service methods like {@link #onStartUpdates(long)} and {@link #onStopUpdates()} are also
+ * asynchronous with respect to the system server caller and will be delivered to this service using
+ * a single thread. {@link Service} lifecycle method calls like {@link #onCreate()} and {@link
+ * #onDestroy()} can occur on a different thread from those made to {@link
+ * TimeZoneProviderService}-defined service methods, so implementations must be defensive and not
+ * assume an ordering between them, e.g. a call to {@link #onStopUpdates()} can occur after {@link
+ * #onDestroy()} and should be handled safely.
*
* @hide
*/
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index 93c006aff435..756a70c129d1 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -29,6 +29,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.content.pm.ParceledListSlice;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,7 +39,6 @@ import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.util.ArraySet;
import android.util.Log;
import android.view.translation.ITranslationDirectManager;
import android.view.translation.ITranslationServiceCallback;
@@ -51,6 +51,7 @@ import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@@ -366,10 +367,11 @@ public abstract class TranslationService extends Service {
+ "format compatibility");
}
- final ArraySet<TranslationCapability> capabilities = new ArraySet<>(values);
final Bundle bundle = new Bundle();
- bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES,
- capabilities.toArray(new TranslationCapability[0]));
+ final ParceledListSlice<TranslationCapability> listSlice =
+ new ParceledListSlice<>(Arrays.asList(
+ values.toArray(new TranslationCapability[0])));
+ bundle.putParcelable(TranslationManager.EXTRA_CAPABILITIES, listSlice);
resultReceiver.send(STATUS_SYNC_CALL_SUCCESS, bundle);
}
});
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 725e20f2a74d..d46265e52d68 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -68,7 +68,6 @@ import com.android.internal.app.IVoiceInteractorCallback;
import com.android.internal.app.IVoiceInteractorRequest;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
@@ -77,6 +76,7 @@ import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1363,9 +1363,9 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor resultExecutor,
@NonNull Consumer<List<DirectAction>> callback) {
- Preconditions.checkNotNull(activityId);
- Preconditions.checkNotNull(resultExecutor);
- Preconditions.checkNotNull(callback);
+ Objects.requireNonNull(activityId);
+ Objects.requireNonNull(resultExecutor);
+ Objects.requireNonNull(callback);
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
@@ -1444,8 +1444,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
if (mToken == null) {
throw new IllegalStateException("Can't call before onCreate()");
}
- Preconditions.checkNotNull(resultExecutor);
- Preconditions.checkNotNull(resultListener);
+ Objects.requireNonNull(resultExecutor);
+ Objects.requireNonNull(resultListener);
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f4770727e31f..4bf60495f3af 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,8 @@
package android.service.wallpaper;
+import static android.app.WallpaperManager.COMMAND_FREEZE;
+import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
@@ -42,12 +44,14 @@ import android.content.res.TypedArray;
import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
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.HardwareBuffer;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Build;
@@ -56,6 +60,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -73,6 +78,7 @@ import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.MotionEvent;
import android.view.PixelCopy;
import android.view.Surface;
@@ -199,6 +205,12 @@ public abstract class WallpaperService extends Service {
boolean mVisible;
boolean mReportedVisible;
boolean mDestroyed;
+ // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
+ // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once
+ // mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through
+ // doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper
+ // host receives onVisibilityChanged(false) callback.
+ private boolean mFrozenRequested = false;
// Current window state.
boolean mCreated;
@@ -224,10 +236,9 @@ public abstract class WallpaperService extends Service {
final ClientWindowFrames mWinFrames = new ClientWindowFrames();
final Rect mDispatchedContentInsets = new Rect();
final Rect mDispatchedStableInsets = new Rect();
- final Rect mFinalSystemInsets = new Rect();
- final Rect mFinalStableInsets = new Rect();
DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
final InsetsState mInsetsState = new InsetsState();
+ final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
private final Point mSurfaceSize = new Point();
@@ -264,6 +275,8 @@ public abstract class WallpaperService extends Service {
SurfaceControl mSurfaceControl = new SurfaceControl();
SurfaceControl mBbqSurfaceControl;
BLASTBufferQueue mBlastBufferQueue;
+ private SurfaceControl mScreenshotSurfaceControl;
+ private Point mScreenshotSize = new Point();
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
@@ -997,8 +1010,8 @@ public abstract class WallpaperService extends Service {
InputChannel inputChannel = new InputChannel();
if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
- mDisplay.getDisplayId(), mInsetsState, inputChannel, mInsetsState,
- mTempControls) < 0) {
+ mDisplay.getDisplayId(), mRequestedVisibilities, inputChannel,
+ mInsetsState, mTempControls) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
}
@@ -1349,11 +1362,15 @@ public abstract class WallpaperService extends Service {
if (!mDestroyed) {
mVisible = visible;
reportVisibility();
- if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+ if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
}
}
void reportVisibility() {
+ if (mScreenshotSurfaceControl != null && mVisible) {
+ if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change");
+ return;
+ }
if (!mDestroyed) {
mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
@@ -1370,6 +1387,10 @@ public abstract class WallpaperService extends Service {
updateSurface(true, false, false);
}
onVisibilityChanged(visible);
+ if (mReportedVisible && mFrozenRequested) {
+ if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update");
+ freeze();
+ }
}
}
}
@@ -1830,6 +1851,9 @@ public abstract class WallpaperService extends Service {
void doCommand(WallpaperCommand cmd) {
Bundle result;
if (!mDestroyed) {
+ if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) {
+ updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action));
+ }
result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
cmd.extras, cmd.sync);
} else {
@@ -1844,6 +1868,159 @@ public abstract class WallpaperService extends Service {
}
}
+ private void updateFrozenState(boolean frozenRequested) {
+ if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null
+ // Procees the unfreeze command in case the wallaper became static while
+ // being paused.
+ && frozenRequested) {
+ if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers");
+ return;
+ }
+ mFrozenRequested = frozenRequested;
+ boolean isFrozen = mScreenshotSurfaceControl != null;
+ if (mFrozenRequested == isFrozen) {
+ return;
+ }
+ if (mFrozenRequested) {
+ freeze();
+ } else {
+ unfreeze();
+ }
+ }
+
+ private void freeze() {
+ if (!mReportedVisible || mDestroyed) {
+ // Screenshot can't be taken until visibility is reported to the wallpaper host.
+ return;
+ }
+ if (!showScreenshotOfWallpaper()) {
+ return;
+ }
+ // Prevent a wallpaper host from rendering wallpaper behind a screeshot.
+ doVisibilityChanged(false);
+ // Remember that visibility is requested since it's not guaranteed that
+ // mWindow#dispatchAppVisibility will be called when letterboxed application with
+ // wallpaper background transitions to the Home screen.
+ mVisible = true;
+ }
+
+ private void unfreeze() {
+ cleanUpScreenshotSurfaceControl();
+ if (mVisible) {
+ doVisibilityChanged(true);
+ }
+ }
+
+ private void cleanUpScreenshotSurfaceControl() {
+ // TODO(b/194399558): Add crossfade transition.
+ if (mScreenshotSurfaceControl != null) {
+ new SurfaceControl.Transaction()
+ .remove(mScreenshotSurfaceControl)
+ .show(mBbqSurfaceControl)
+ .apply();
+ mScreenshotSurfaceControl = null;
+ }
+ }
+
+ void scaleAndCropScreenshot() {
+ if (mScreenshotSurfaceControl == null) {
+ return;
+ }
+ if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) {
+ Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize);
+ return;
+ }
+ // Don't scale down and using the same scaling factor for both dimensions to
+ // avoid stretching wallpaper image.
+ float scaleFactor = Math.max(1, Math.max(
+ ((float) mSurfaceSize.x) / mScreenshotSize.x,
+ ((float) mSurfaceSize.y) / mScreenshotSize.y));
+ int diffX = ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x;
+ int diffY = ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y;
+ if (DEBUG) {
+ Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor
+ + " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize
+ + " mScreenshotSize=" + mScreenshotSize);
+ }
+ new SurfaceControl.Transaction()
+ .setMatrix(
+ mScreenshotSurfaceControl,
+ /* dsdx= */ scaleFactor, /* dtdx= */ 0,
+ /* dtdy= */ 0, /* dsdy= */ scaleFactor)
+ .setWindowCrop(
+ mScreenshotSurfaceControl,
+ new Rect(
+ /* left= */ diffX / 2,
+ /* top= */ diffY / 2,
+ /* right= */ diffX / 2 + mScreenshotSize.x,
+ /* bottom= */ diffY / 2 + mScreenshotSize.y))
+ .setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2)
+ .apply();
+ }
+
+ private boolean showScreenshotOfWallpaper() {
+ if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) {
+ if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid");
+ return false;
+ }
+
+ final Rect bounds = new Rect(0, 0, mSurfaceSize.x, mSurfaceSize.y);
+ if (bounds.isEmpty()) {
+ Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty");
+ return false;
+ }
+
+ if (mScreenshotSurfaceControl != null) {
+ Log.e(TAG, "Screenshot is unexpectedly not null");
+ // Destroying previous screenshot since it can have different size.
+ cleanUpScreenshotSurfaceControl();
+ }
+
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(
+ new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ // Needed because SurfaceFlinger#validateScreenshotPermissions
+ // uses this parameter to check whether a caller only attempts
+ // to screenshot itself when call doesn't come from the system.
+ .setUid(Process.myUid())
+ .setChildrenOnly(false)
+ .setSourceCrop(bounds)
+ .build());
+
+ if (screenshotBuffer == null) {
+ Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null");
+ return false;
+ }
+
+ final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+
+ // TODO(b/194399558): Add crossfade transition.
+ mScreenshotSurfaceControl = new SurfaceControl.Builder()
+ .setName("Wallpaper snapshot for engine " + this)
+ .setFormat(hardwareBuffer.getFormat())
+ .setParent(mSurfaceControl)
+ .setSecure(screenshotBuffer.containsSecureLayers())
+ .setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper")
+ .setBLASTLayer()
+ .build();
+
+ mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
+
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
+
+ t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
+ t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
+ // Place on top everything else.
+ t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
+ t.show(mScreenshotSurfaceControl);
+ t.hide(mBbqSurfaceControl);
+ t.apply();
+
+ return true;
+ }
+
void reportSurfaceDestroyed() {
if (mSurfaceCreated) {
mSurfaceCreated = false;
@@ -2166,6 +2343,7 @@ public abstract class WallpaperService extends Service {
final boolean reportDraw = message.arg1 != 0;
mEngine.updateSurface(true, false, reportDraw);
mEngine.doOffsetsChanged(true);
+ mEngine.scaleAndCropScreenshot();
} break;
case MSG_WINDOW_MOVED: {
// Do nothing. What does it mean for a Wallpaper to move?
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1f11d10052fe..1a7ec7f99c95 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -782,7 +782,7 @@ public class TextLine {
int spanStart = runStart;
int spanLimit;
- if (mSpanned == null) {
+ if (mSpanned == null || runStart == runLimit) {
spanLimit = runLimit;
} else {
int target = after ? offset + 1 : offset;
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4edff27d0ced..f50b51bbdb35 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -27,6 +27,7 @@ import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
/**
* ArrayMap is a generic key->value mapping data structure that is
@@ -980,6 +981,28 @@ public final class ArrayMap<K, V> implements Map<K, V> {
}
/**
+ * Performs the given action for all elements in the stored order. This implementation overrides
+ * the default implementation to avoid iterating using the {@link #entrySet()} and iterates in
+ * the key-value order consistent with {@link #keyAt(int)} and {@link #valueAt(int)}.
+ *
+ * @param action The action to be performed for each element
+ */
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null) {
+ throw new NullPointerException("action must not be null");
+ }
+
+ final int size = mSize;
+ for (int i = 0; i < size; ++i) {
+ if (size != mSize) {
+ throw new ConcurrentModificationException();
+ }
+ action.accept(keyAt(i), valueAt(i));
+ }
+ }
+
+ /**
* Perform a {@link #put(Object, Object)} of all key/value pairs in <var>map</var>
* @param map The map whose contents are to be retrieved.
*/
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index f53548a41177..b5c75b9276b7 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -28,6 +28,7 @@ import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -747,6 +748,23 @@ public final class ArraySet<E> implements Collection<E>, Set<E> {
return mSize;
}
+ /**
+ * Performs the given action for all elements in the stored order. This implementation overrides
+ * the default implementation to avoid using the {@link #iterator()}.
+ *
+ * @param action The action to be performed for each element
+ */
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ if (action == null) {
+ throw new NullPointerException("action must not be null");
+ }
+
+ for (int i = 0; i < mSize; ++i) {
+ action.accept(valueAt(i));
+ }
+ }
+
@Override
public Object[] toArray() {
Object[] result = new Object[mSize];
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6c3c38359957..3d39fbeea7d8 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -51,6 +51,9 @@ public class FeatureFlagUtils {
/** @hide */
public static final String SETTINGS_ENABLE_SECURITY_HUB = "settings_enable_security_hub";
+ /** @hide */
+ public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -72,12 +75,14 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
+ DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
static {
PERSISTENT_FLAGS = new HashSet<>();
PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
+ PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
}
/**
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index 3287c279c87f..590ba1a8b589 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -157,4 +157,28 @@ public class SparseArrayMap<K, V> {
}
}
}
+
+ /**
+ * @param <K> Any class
+ * @param <V> Any class
+ * @hide
+ */
+ public interface TriConsumer<K, V> {
+ /** Consume the int-K-V tuple. */
+ void accept(int key, K mapKey, V value);
+ }
+
+ /**
+ * Iterate through all int-K pairs and operate on all of the values.
+ * @hide
+ */
+ public void forEach(@NonNull TriConsumer<K, V> consumer) {
+ for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) {
+ final int i = mData.keyAt(iIdx);
+ final ArrayMap<K, V> data = mData.valueAt(i);
+ for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) {
+ consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx));
+ }
+ }
+ }
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index f74990a82327..c7d9b9c4ab3e 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -209,7 +209,7 @@ public class ApkSignatureSchemeV2Verifier {
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
- verityDigest, apk.length(), signatureInfo);
+ verityDigest, apk.getChannel().size(), signatureInfo);
}
return new VerifiedSigner(
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 4d1402a0d6d6..b07b5223d296 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -211,7 +211,7 @@ public class ApkSignatureSchemeV3Verifier {
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
byte[] verityDigest = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
verityRootHash = ApkSigningBlockUtils.parseVerityDigestAndVerifySourceLength(
- verityDigest, apk.length(), signatureInfo);
+ verityDigest, apk.getChannel().size(), signatureInfo);
}
return new VerifiedSigner(result.first, result.second, verityRootHash, contentDigests);
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 73f7543ba819..35c602ae622b 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -23,11 +23,12 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFIC
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.os.Build;
import android.os.Trace;
import android.util.jar.StrictJarFile;
@@ -52,7 +53,7 @@ import java.util.zip.ZipEntry;
/**
* Facade class that takes care of the details of APK verification on
- * behalf of PackageParser.
+ * behalf of ParsingPackageUtils.
*
* @hide for internal use only.
*/
@@ -62,90 +63,85 @@ public class ApkSignatureVerifier {
/**
* Verifies the provided APK and returns the certificates associated with each signer.
- *
- * @throws PackageParserException if the APK's signature failed to verify.
*/
- public static PackageParser.SigningDetails verify(String apkPath,
- @SignatureSchemeVersion int minSignatureSchemeVersion)
- throws PackageParserException {
- return verifySignatures(apkPath, minSignatureSchemeVersion, true);
+ public static ParseResult<SigningDetails> verify(ParseInput input, String apkPath,
+ @SignatureSchemeVersion int minSignatureSchemeVersion) {
+ return verifySignatures(input, apkPath, minSignatureSchemeVersion, true /* verifyFull */);
}
/**
* Returns the certificates associated with each signer for the given APK without verification.
* This method is dangerous and should not be used, unless the caller is absolutely certain the
* APK is trusted.
- *
- * @throws PackageParserException if there was a problem collecting certificates.
*/
- public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
- String apkPath, int minSignatureSchemeVersion)
- throws PackageParserException {
- return verifySignatures(apkPath, minSignatureSchemeVersion, false);
+ public static ParseResult<SigningDetails> unsafeGetCertsWithoutVerification(
+ ParseInput input, String apkPath, int minSignatureSchemeVersion) {
+ return verifySignatures(input, apkPath, minSignatureSchemeVersion, false /* verifyFull */);
}
/**
* Verifies the provided APK using all allowed signing schemas.
* @return the certificates associated with each signer.
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
- * @throws PackageParserException if there was a problem collecting certificates
*/
- private static PackageParser.SigningDetails verifySignatures(String apkPath,
- @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
- throws PackageParserException {
- return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
- verifyFull).signingDetails;
+ private static ParseResult<SigningDetails> verifySignatures(ParseInput input, String apkPath,
+ @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull) {
+ final ParseResult<SigningDetailsWithDigests> result =
+ verifySignaturesInternal(input, apkPath, minSignatureSchemeVersion, verifyFull);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(result.getResult().signingDetails);
}
/**
* Verifies the provided APK using all allowed signing schemas.
* @return the certificates associated with each signer and content digests.
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
- * @throws PackageParserException if there was a problem collecting certificates
* @hide
*/
- public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath,
- @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
- throws PackageParserException {
+ public static ParseResult<SigningDetailsWithDigests> verifySignaturesInternal(ParseInput input,
+ String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+ boolean verifyFull) {
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
- // V3 and before are older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ // V4 and before are older than the requested minimum signing version
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// first try v4
try {
- return verifyV4Signature(apkPath, minSignatureSchemeVersion, verifyFull);
+ return verifyV4Signature(input, apkPath, minSignatureSchemeVersion, verifyFull);
} catch (SignatureNotFoundException e) {
// not signed with v4, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V4) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v4 signature in package " + apkPath, e);
}
}
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
// V3 and before are older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
- return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
+ return verifyV3AndBelowSignatures(input, apkPath, minSignatureSchemeVersion, verifyFull);
}
- private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath,
- @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
- throws PackageParserException {
+ private static ParseResult<SigningDetailsWithDigests> verifyV3AndBelowSignatures(
+ ParseInput input, String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+ boolean verifyFull) {
// try v3
try {
- return verifyV3Signature(apkPath, verifyFull);
+ return verifyV3Signature(input, apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
// not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
}
@@ -153,18 +149,18 @@ public class ApkSignatureVerifier {
// redundant, protective version check
if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
// V2 and before are older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// try v2
try {
- return verifyV2Signature(apkPath, verifyFull);
+ return verifyV2Signature(input, apkPath, verifyFull);
} catch (SignatureNotFoundException e) {
// not signed with v2, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v2 signature in package " + apkPath, e);
}
}
@@ -172,13 +168,13 @@ public class ApkSignatureVerifier {
// redundant, protective version check
if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
// V1 and is older than the requested minimum signing version
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + apkPath);
}
// v2 didn't work, try jarsigner
- return verifyV1Signature(apkPath, verifyFull);
+ return verifyV1Signature(input, apkPath, verifyFull);
}
/**
@@ -187,11 +183,10 @@ public class ApkSignatureVerifier {
* @param verifyFull whether to verify (V4 vs V3) or just collect certificates.
* @return the certificates associated with each signer.
* @throws SignatureNotFoundException if there are no V4 signatures in the APK
- * @throws PackageParserException if there was a problem collecting certificates
*/
- private static SigningDetailsWithDigests verifyV4Signature(String apkPath,
- @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
- throws SignatureNotFoundException, PackageParserException {
+ private static ParseResult<SigningDetailsWithDigests> verifyV4Signature(ParseInput input,
+ String apkPath, @SignatureSchemeVersion int minSignatureSchemeVersion,
+ boolean verifyFull) throws SignatureNotFoundException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
try {
ApkSignatureSchemeV4Verifier.VerifiedSigner vSigner =
@@ -259,14 +254,14 @@ public class ApkSignatureVerifier {
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V4, pastSignerSigs),
- vSigner.contentDigests);
+ vSigner.contentDigests));
} catch (SignatureNotFoundException e) {
throw e;
} catch (Exception e) {
// APK Signature Scheme v4 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v4", e);
} finally {
@@ -280,10 +275,9 @@ public class ApkSignatureVerifier {
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @return the certificates associated with each signer.
* @throws SignatureNotFoundException if there are no V3 signatures in the APK
- * @throws PackageParserException if there was a problem collecting certificates
*/
- private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull)
- throws SignatureNotFoundException, PackageParserException {
+ private static ParseResult<SigningDetailsWithDigests> verifyV3Signature(ParseInput input,
+ String apkPath, boolean verifyFull) throws SignatureNotFoundException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
try {
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
@@ -301,14 +295,14 @@ public class ApkSignatureVerifier {
pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
}
}
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+ return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
- vSigner.contentDigests);
+ vSigner.contentDigests));
} catch (SignatureNotFoundException e) {
throw e;
} catch (Exception e) {
// APK Signature Scheme v3 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v3", e);
} finally {
@@ -322,23 +316,22 @@ public class ApkSignatureVerifier {
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
* @return the certificates associated with each signer.
* @throws SignatureNotFoundException if there are no V2 signatures in the APK
- * @throws PackageParserException if there was a problem collecting certificates
*/
- private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull)
- throws SignatureNotFoundException, PackageParserException {
+ private static ParseResult<SigningDetailsWithDigests> verifyV2Signature(ParseInput input,
+ String apkPath, boolean verifyFull) throws SignatureNotFoundException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
try {
ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner =
ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
Certificate[][] signerCerts = vSigner.certs;
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
+ return input.success(new SigningDetailsWithDigests(new SigningDetails(signerSigs,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests));
} catch (SignatureNotFoundException e) {
throw e;
} catch (Exception e) {
// APK Signature Scheme v2 signature found but did not verify
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
+ " using APK Signature Scheme v2", e);
} finally {
@@ -350,10 +343,9 @@ public class ApkSignatureVerifier {
* Verifies the provided APK using JAR schema.
* @return the certificates associated with each signer.
* @param verifyFull whether to verify all contents of this APK or just collect certificates.
- * @throws PackageParserException if there was a problem collecting certificates
*/
- private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull)
- throws PackageParserException {
+ private static ParseResult<SigningDetailsWithDigests> verifyV1Signature(ParseInput input,
+ String apkPath, boolean verifyFull) {
StrictJarFile jarFile = null;
try {
@@ -375,12 +367,17 @@ public class ApkSignatureVerifier {
final ZipEntry manifestEntry = jarFile.findEntry(
ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
}
- lastCerts = loadCertificates(jarFile, manifestEntry);
+ final ParseResult<Certificate[][]> result =
+ loadCertificates(input, jarFile, manifestEntry);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ lastCerts = result.getResult();
if (ArrayUtils.isEmpty(lastCerts)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+ apkPath + " has no certificates at entry "
+ ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
}
@@ -401,9 +398,15 @@ public class ApkSignatureVerifier {
}
for (ZipEntry entry : toVerify) {
- final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
+ final Certificate[][] entryCerts;
+ final ParseResult<Certificate[][]> ret =
+ loadCertificates(input, jarFile, entry);
+ if (ret.isError()) {
+ return input.error(ret);
+ }
+ entryCerts = ret.getResult();
if (ArrayUtils.isEmpty(entryCerts)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Package " + apkPath + " has no certificates at entry "
+ entry.getName());
}
@@ -411,20 +414,20 @@ public class ApkSignatureVerifier {
// make sure all entries use the same signing certs
final Signature[] entrySigs = convertToSignatures(entryCerts);
if (!Signature.areExactMatch(lastSigs, entrySigs)) {
- throw new PackageParserException(
+ return input.error(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Package " + apkPath + " has mismatched certificates at entry "
+ entry.getName());
}
}
}
- return new SigningDetailsWithDigests(
- new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
+ return input.success(new SigningDetailsWithDigests(
+ new SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null));
} catch (GeneralSecurityException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+ return input.error(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to collect certificates from " + apkPath, e);
} catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath, e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -432,17 +435,17 @@ public class ApkSignatureVerifier {
}
}
- private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
- throws PackageParserException {
+ private static ParseResult<Certificate[][]> loadCertificates(ParseInput input,
+ StrictJarFile jarFile, ZipEntry entry) {
InputStream is = null;
try {
// We must read the stream for the JarEntry to retrieve
// its certificates.
is = jarFile.getInputStream(entry);
readFullyIgnoringContents(is);
- return jarFile.getCertificateChains(entry);
+ return input.success(jarFile.getCertificateChains(entry));
} catch (IOException | RuntimeException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed reading " + entry.getName() + " in " + jarFile, e);
} finally {
IoUtils.closeQuietly(is);
@@ -576,7 +579,7 @@ public class ApkSignatureVerifier {
* @hide for internal use only.
*/
public static class SigningDetailsWithDigests {
- public final PackageParser.SigningDetails signingDetails;
+ public final SigningDetails signingDetails;
/**
* APK Signature Schemes v2/v3/v4 might contain multiple content digests.
@@ -587,7 +590,7 @@ public class ApkSignatureVerifier {
*/
public final Map<Integer, byte[]> contentDigests;
- SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+ SigningDetailsWithDigests(SigningDetails signingDetails,
Map<Integer, byte[]> contentDigests) {
this.signingDetails = signingDetails;
this.contentDigests = contentDigests;
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 7e6175c03d35..6a24de25a08c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -368,7 +368,7 @@ public final class ApkSigningBlockUtils {
SignatureInfo signatureInfo) throws SecurityException {
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
- apk.length(), signatureInfo);
+ apk.getChannel().size(), signatureInfo);
VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index b0a5992230bd..c7c465d30dad 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -90,7 +90,7 @@ public abstract class VerityBuilder {
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
- long dataSize = apk.length() - signingBlockSize;
+ long dataSize = apk.getChannel().size() - signingBlockSize;
int[] levelOffset = calculateVerityLevelOffset(dataSize);
int merkleTreeSize = levelOffset[levelOffset.length - 1];
@@ -108,7 +108,7 @@ public abstract class VerityBuilder {
@NonNull SignatureInfo signatureInfo, @NonNull ByteBuffer footerOutput)
throws IOException {
footerOutput.order(ByteOrder.LITTLE_ENDIAN);
- generateApkVerityHeader(footerOutput, apk.length(), DEFAULT_SALT);
+ generateApkVerityHeader(footerOutput, apk.getChannel().size(), DEFAULT_SALT);
long signingBlockSize =
signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
generateApkVerityExtensions(footerOutput, signatureInfo.apkSigningBlockOffset,
@@ -339,11 +339,11 @@ public abstract class VerityBuilder {
eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
consumeByChunk(digester,
DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField,
- apk.length() - offsetAfterEocdCdOffsetField),
+ apk.getChannel().size() - offsetAfterEocdCdOffsetField),
MMAP_REGION_SIZE_BYTES);
// 5. Pad 0s up to the nearest 4096-byte block before hashing.
- int lastIncompleteChunkSize = (int) (apk.length() % CHUNK_SIZE_BYTES);
+ int lastIncompleteChunkSize = (int) (apk.getChannel().size() % CHUNK_SIZE_BYTES);
if (lastIncompleteChunkSize != 0) {
digester.consume(ByteBuffer.allocate(CHUNK_SIZE_BYTES - lastIncompleteChunkSize));
}
diff --git a/core/java/android/util/apk/ZipUtils.java b/core/java/android/util/apk/ZipUtils.java
index fa5477e4190b..3ca3fc0f6338 100644
--- a/core/java/android/util/apk/ZipUtils.java
+++ b/core/java/android/util/apk/ZipUtils.java
@@ -63,7 +63,8 @@ abstract class ZipUtils {
// exactly the remaining bytes in the buffer. The search is bounded because the maximum
// size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
- long fileSize = zip.length();
+ // TODO(b/193592496) RandomAccessFile#length
+ long fileSize = zip.getChannel().size();
if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
return null;
}
@@ -110,7 +111,8 @@ abstract class ZipUtils {
throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize);
}
- long fileSize = zip.length();
+ // TODO(b/193592496) RandomAccessFile#length
+ long fileSize = zip.getChannel().size();
if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
// No space for EoCD record in the file.
return null;
diff --git a/core/java/android/uwb/AdapterStateListener.java b/core/java/android/uwb/AdapterStateListener.java
deleted file mode 100644
index 7e82cc6318f7..000000000000
--- a/core/java/android/uwb/AdapterStateListener.java
+++ /dev/null
@@ -1,199 +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 android.annotation.NonNull;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.uwb.UwbManager.AdapterStateCallback;
-import android.uwb.UwbManager.AdapterStateCallback.State;
-import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
- private static final String TAG = "Uwb.StateListener";
-
- private final IUwbAdapter mAdapter;
- private boolean mIsRegistered = false;
-
- private final Map<AdapterStateCallback, Executor> mCallbackMap = new HashMap<>();
-
- @StateChangedReason
- private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
- @State
- private int mAdapterState = AdapterStateCallback.STATE_DISABLED;
-
- public AdapterStateListener(@NonNull IUwbAdapter adapter) {
- mAdapter = adapter;
- }
-
- /**
- * Register an {@link AdapterStateCallback} with this {@link AdapterStateListener}
- *
- * @param executor an {@link Executor} to execute given callback
- * @param callback user implementation of the {@link AdapterStateCallback}
- */
- public void register(@NonNull Executor executor, @NonNull AdapterStateCallback callback) {
- synchronized (this) {
- if (mCallbackMap.containsKey(callback)) {
- return;
- }
-
- mCallbackMap.put(callback, executor);
-
- if (!mIsRegistered) {
- try {
- mAdapter.registerAdapterStateCallbacks(this);
- mIsRegistered = true;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to register adapter state callback");
- throw e.rethrowFromSystemServer();
- }
- } else {
- sendCurrentState(callback);
- }
- }
- }
-
- /**
- * Unregister the specified {@link AdapterStateCallback}
- *
- * @param callback user implementation of the {@link AdapterStateCallback}
- */
- public void unregister(@NonNull AdapterStateCallback callback) {
- synchronized (this) {
- if (!mCallbackMap.containsKey(callback)) {
- return;
- }
-
- mCallbackMap.remove(callback);
-
- if (mCallbackMap.isEmpty() && mIsRegistered) {
- try {
- mAdapter.unregisterAdapterStateCallbacks(this);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to unregister AdapterStateCallback with service");
- throw e.rethrowFromSystemServer();
- }
- mIsRegistered = false;
- }
- }
- }
-
- /**
- * Sets the adapter enabled state
- *
- * @param isEnabled value of new adapter state
- */
- public void setEnabled(boolean isEnabled) {
- synchronized (this) {
- try {
- mAdapter.setEnabled(isEnabled);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to set adapter state");
- throw e.rethrowFromSystemServer();
- }
-
- }
- }
-
- /**
- * Gets the adapter enabled state
- *
- * @return integer representing adapter enabled state
- */
- public int getAdapterState() {
- synchronized (this) {
- try {
- return mAdapter.getAdapterState();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to get adapter state");
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- private void sendCurrentState(@NonNull AdapterStateCallback callback) {
- synchronized (this) {
- Executor executor = mCallbackMap.get(callback);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- executor.execute(() -> callback.onStateChanged(
- mAdapterState, mAdapterStateChangeReason));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- @Override
- public void onAdapterStateChanged(int state, int reason) {
- synchronized (this) {
- @StateChangedReason int localReason =
- convertToStateChangedReason(reason);
- @State int localState = convertToState(state);
- mAdapterStateChangeReason = localReason;
- mAdapterState = localState;
- for (AdapterStateCallback cb : mCallbackMap.keySet()) {
- sendCurrentState(cb);
- }
- }
- }
-
- private static @StateChangedReason int convertToStateChangedReason(
- @StateChangeReason int reason) {
- switch (reason) {
- case StateChangeReason.ALL_SESSIONS_CLOSED:
- return AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED;
-
- case StateChangeReason.SESSION_STARTED:
- return AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED;
-
- case StateChangeReason.SYSTEM_POLICY:
- return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY;
-
- case StateChangeReason.SYSTEM_BOOT:
- return AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT;
-
- case StateChangeReason.UNKNOWN:
- default:
- return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
- }
- }
-
- private static @State int convertToState(@AdapterState int state) {
- switch (state) {
- case AdapterState.STATE_ENABLED_INACTIVE:
- return AdapterStateCallback.STATE_ENABLED_INACTIVE;
-
- case AdapterState.STATE_ENABLED_ACTIVE:
- return AdapterStateCallback.STATE_ENABLED_ACTIVE;
-
- case AdapterState.STATE_DISABLED:
- default:
- return AdapterStateCallback.STATE_DISABLED;
- }
- }
-}
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
deleted file mode 100644
index 3d603737c48c..000000000000
--- a/core/java/android/uwb/AngleMeasurement.java
+++ /dev/null
@@ -1,157 +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 android.annotation.FloatRange;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Angle measurement
- *
- * <p>The actual angle is interpreted as:
- * {@link #getRadians()} +/- {@link #getErrorRadians()} ()} at {@link #getConfidenceLevel()}
- *
- * @hide
- */
-@SystemApi
-public final class AngleMeasurement implements Parcelable {
- private final double mRadians;
- private final double mErrorRadians;
- private final double mConfidenceLevel;
-
- /**
- * Constructs a new {@link AngleMeasurement} object
- *
- * @param radians the angle in radians
- * @param errorRadians the error of the angle measurement in radians
- * @param confidenceLevel confidence level of the angle measurement
- *
- * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
- * allowed range
- */
- public AngleMeasurement(
- @FloatRange(from = -Math.PI, to = +Math.PI) double radians,
- @FloatRange(from = 0.0, to = +Math.PI) double errorRadians,
- @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
- if (radians < -Math.PI || radians > Math.PI) {
- throw new IllegalArgumentException("Invalid radians: " + radians);
- }
- mRadians = radians;
-
- if (errorRadians < 0.0 || errorRadians > Math.PI) {
- throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
- }
- mErrorRadians = errorRadians;
-
- if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
- throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
- }
- mConfidenceLevel = confidenceLevel;
- }
-
- /**
- * Angle measurement in radians
- *
- * @return angle in radians
- */
- @FloatRange(from = -Math.PI, to = +Math.PI)
- public double getRadians() {
- return mRadians;
- }
-
- /**
- * Error of angle measurement in radians
- *
- * <p>Must be a positive value
- *
- * @return angle measurement error in radians
- */
- @FloatRange(from = 0.0, to = +Math.PI)
- public double getErrorRadians() {
- return mErrorRadians;
- }
-
- /**
- * Angle measurement confidence level expressed as a value between
- * 0.0 to 1.0.
- *
- * <p>A value of 0.0 indicates there is no confidence in the measurement. A value of 1.0
- * indicates there is maximum confidence in the measurement.
- *
- * @return the confidence level of the angle measurement
- */
- @FloatRange(from = 0.0, to = 1.0)
- public double getConfidenceLevel() {
- return mConfidenceLevel;
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof AngleMeasurement) {
- AngleMeasurement other = (AngleMeasurement) obj;
- return mRadians == other.getRadians()
- && mErrorRadians == other.getErrorRadians()
- && mConfidenceLevel == other.getConfidenceLevel();
- }
- return false;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return Objects.hash(mRadians, mErrorRadians, mConfidenceLevel);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeDouble(mRadians);
- dest.writeDouble(mErrorRadians);
- dest.writeDouble(mConfidenceLevel);
- }
-
- public static final @android.annotation.NonNull Creator<AngleMeasurement> CREATOR =
- new Creator<AngleMeasurement>() {
- @Override
- public AngleMeasurement createFromParcel(Parcel in) {
- return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
- }
-
- @Override
- public AngleMeasurement[] newArray(int size) {
- return new AngleMeasurement[size];
- }
- };
-}
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
deleted file mode 100644
index db04ad16c191..000000000000
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ /dev/null
@@ -1,169 +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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Represents an angle of arrival measurement between two devices using Ultra Wideband
- *
- * @hide
- */
-@SystemApi
-public final class AngleOfArrivalMeasurement implements Parcelable {
- private final AngleMeasurement mAzimuthAngleMeasurement;
- private final AngleMeasurement mAltitudeAngleMeasurement;
-
- private AngleOfArrivalMeasurement(@NonNull AngleMeasurement azimuthAngleMeasurement,
- @Nullable AngleMeasurement altitudeAngleMeasurement) {
- mAzimuthAngleMeasurement = azimuthAngleMeasurement;
- mAltitudeAngleMeasurement = altitudeAngleMeasurement;
- }
-
- /**
- * Azimuth angle measurement
- * <p>Azimuth {@link AngleMeasurement} of remote device in horizontal coordinate system, this is
- * the angle clockwise from the meridian when viewing above the north pole.
- *
- * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
- *
- * <p>On an Android device, azimuth north is defined as the angle perpendicular away from the
- * back of the device when holding it in portrait mode upright.
- *
- * <p>Azimuth angle must be supported when Angle of Arrival is supported
- *
- * @return the azimuth {@link AngleMeasurement}
- */
- @NonNull
- public AngleMeasurement getAzimuth() {
- return mAzimuthAngleMeasurement;
- }
-
- /**
- * Altitude angle measurement
- * <p>Altitude {@link AngleMeasurement} of remote device in horizontal coordinate system, this
- * is the angle above the equator when the north pole is up.
- *
- * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system
- *
- * <p>On an Android device, altitude is defined as the angle vertical from ground when holding
- * the device in portrait mode upright.
- *
- * @return altitude {@link AngleMeasurement} or null when this is not available
- */
- @Nullable
- public AngleMeasurement getAltitude() {
- return mAltitudeAngleMeasurement;
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof AngleOfArrivalMeasurement) {
- AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj;
- return mAzimuthAngleMeasurement.equals(other.getAzimuth())
- && mAltitudeAngleMeasurement.equals(other.getAltitude());
- }
- return false;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return Objects.hash(mAzimuthAngleMeasurement, mAltitudeAngleMeasurement);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelable(mAzimuthAngleMeasurement, flags);
- dest.writeParcelable(mAltitudeAngleMeasurement, flags);
- }
-
- public static final @android.annotation.NonNull Creator<AngleOfArrivalMeasurement> CREATOR =
- new Creator<AngleOfArrivalMeasurement>() {
- @Override
- public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
- Builder builder =
- new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
-
- builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
-
- return builder.build();
- }
-
- @Override
- public AngleOfArrivalMeasurement[] newArray(int size) {
- return new AngleOfArrivalMeasurement[size];
- }
- };
-
- /**
- * Builder class for {@link AngleOfArrivalMeasurement}.
- */
- public static final class Builder {
- private final AngleMeasurement mAzimuthAngleMeasurement;
- private AngleMeasurement mAltitudeAngleMeasurement = null;
-
- /**
- * Constructs an {@link AngleOfArrivalMeasurement} object
- *
- * @param azimuthAngle the azimuth angle of the measurement
- */
- public Builder(@NonNull AngleMeasurement azimuthAngle) {
- mAzimuthAngleMeasurement = azimuthAngle;
- }
-
- /**
- * Set the altitude angle
- *
- * @param altitudeAngle altitude angle
- */
- @NonNull
- public Builder setAltitude(@NonNull AngleMeasurement altitudeAngle) {
- mAltitudeAngleMeasurement = altitudeAngle;
- return this;
- }
-
- /**
- * Build the {@link AngleOfArrivalMeasurement} object
- */
- @NonNull
- public AngleOfArrivalMeasurement build() {
- return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
- mAltitudeAngleMeasurement);
- }
- }
-}
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
deleted file mode 100644
index 98565536e64a..000000000000
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ /dev/null
@@ -1,214 +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 android.annotation.FloatRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * A data point for the distance measurement
- *
- * <p>The actual distance is interpreted as:
- * {@link #getMeters()} +/- {@link #getErrorMeters()} at {@link #getConfidenceLevel()}
- *
- * @hide
- */
-@SystemApi
-public final class DistanceMeasurement implements Parcelable {
- private final double mMeters;
- private final double mErrorMeters;
- private final double mConfidenceLevel;
-
- private DistanceMeasurement(double meters, double errorMeters, double confidenceLevel) {
- mMeters = meters;
- mErrorMeters = errorMeters;
- mConfidenceLevel = confidenceLevel;
- }
-
- /**
- * Distance measurement in meters
- *
- * @return distance in meters
- */
- public double getMeters() {
- return mMeters;
- }
-
- /**
- * Error of distance measurement in meters
- * <p>Must be positive
- *
- * @return error of distance measurement in meters
- */
- @FloatRange(from = 0.0)
- public double getErrorMeters() {
- return mErrorMeters;
- }
-
- /**
- * Distance measurement confidence level expressed as a value between 0.0 to 1.0.
- *
- * <p>A value of 0.0 indicates no confidence in the measurement. A value of 1.0 represents
- * maximum confidence in the measurement
- *
- * @return confidence level
- */
- @FloatRange(from = 0.0, to = 1.0)
- public double getConfidenceLevel() {
- return mConfidenceLevel;
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof DistanceMeasurement) {
- DistanceMeasurement other = (DistanceMeasurement) obj;
- return mMeters == other.getMeters()
- && mErrorMeters == other.getErrorMeters()
- && mConfidenceLevel == other.getConfidenceLevel();
- }
- return false;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return Objects.hash(mMeters, mErrorMeters, mConfidenceLevel);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeDouble(mMeters);
- dest.writeDouble(mErrorMeters);
- dest.writeDouble(mConfidenceLevel);
- }
-
- public static final @android.annotation.NonNull Creator<DistanceMeasurement> CREATOR =
- new Creator<DistanceMeasurement>() {
- @Override
- public DistanceMeasurement createFromParcel(Parcel in) {
- Builder builder = new Builder();
- builder.setMeters(in.readDouble());
- builder.setErrorMeters(in.readDouble());
- builder.setConfidenceLevel(in.readDouble());
- return builder.build();
- }
-
- @Override
- public DistanceMeasurement[] newArray(int size) {
- return new DistanceMeasurement[size];
- }
- };
-
- /**
- * Builder to get a {@link DistanceMeasurement} object.
- */
- public static final class Builder {
- private double mMeters = Double.NaN;
- private double mErrorMeters = Double.NaN;
- private double mConfidenceLevel = Double.NaN;
-
- /**
- * Set the distance measurement in meters
- *
- * @param meters distance in meters
- * @throws IllegalArgumentException if meters is NaN
- */
- @NonNull
- public Builder setMeters(double meters) {
- if (Double.isNaN(meters)) {
- throw new IllegalArgumentException("meters cannot be NaN");
- }
- mMeters = meters;
- return this;
- }
-
- /**
- * Set the distance error in meters
- *
- * @param errorMeters distance error in meters
- * @throws IllegalArgumentException if error is negative or NaN
- */
- @NonNull
- public Builder setErrorMeters(@FloatRange(from = 0.0) double errorMeters) {
- if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
- throw new IllegalArgumentException(
- "errorMeters must be >= 0.0 and not NaN: " + errorMeters);
- }
- mErrorMeters = errorMeters;
- return this;
- }
-
- /**
- * Set the confidence level
- *
- * @param confidenceLevel the confidence level in the distance measurement
- * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
- */
- @NonNull
- public Builder setConfidenceLevel(
- @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
- if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
- throw new IllegalArgumentException(
- "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
- }
- mConfidenceLevel = confidenceLevel;
- return this;
- }
-
- /**
- * Builds the {@link DistanceMeasurement} object
- *
- * @throws IllegalStateException if meters, error, or confidence are not set
- */
- @NonNull
- public DistanceMeasurement build() {
- if (Double.isNaN(mMeters)) {
- throw new IllegalStateException("Meters cannot be NaN");
- }
-
- if (Double.isNaN(mErrorMeters)) {
- throw new IllegalStateException("Error meters cannot be NaN");
- }
-
- if (Double.isNaN(mConfidenceLevel)) {
- throw new IllegalStateException("Confidence level cannot be NaN");
- }
-
- return new DistanceMeasurement(mMeters, mErrorMeters, mConfidenceLevel);
- }
- }
-}
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
deleted file mode 100644
index d87935003e03..000000000000
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ /dev/null
@@ -1,191 +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 android.content.AttributionSource;
-import android.os.PersistableBundle;
-import android.uwb.IUwbAdapterStateCallbacks;
-import android.uwb.IUwbRangingCallbacks;
-import android.uwb.SessionHandle;
-
-/**
- * @hide
- */
-interface IUwbAdapter {
- /*
- * Register the callbacks used to notify the framework of events and data
- *
- * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
- * function must be called immediately following registration with the current
- * state of the UWB adapter.
- *
- * @param callbacks callback to provide range and status updates to the framework
- */
- void registerAdapterStateCallbacks(in IUwbAdapterStateCallbacks adapterStateCallbacks);
-
- /*
- * Unregister the callbacks used to notify the framework of events and data
- *
- * Calling this function with an unregistered callback is a no-op
- *
- * @param callbacks callback to unregister
- */
- void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
-
- /**
- * Get the accuracy of the ranging timestamps
- *
- * @return accuracy of the ranging timestamps in nanoseconds
- */
- long getTimestampResolutionNanos();
-
- /**
- * Provides the capabilities and features of the device
- *
- * @return specification specific capabilities and features of the device
- */
- PersistableBundle getSpecificationInfo();
-
- /**
- * Request to open a new ranging session
- *
- * This function does not start the ranging session, but all necessary
- * components must be initialized and ready to start a new ranging
- * session prior to calling IUwbAdapterCallback#onRangingOpened.
- *
- * IUwbAdapterCallbacks#onRangingOpened must be called within
- * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being
- * called if the ranging session is opened successfully.
- *
- * IUwbAdapterCallbacks#onRangingOpenFailed must be called within
- * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
- * if the ranging session fails to be opened.
- *
- * If the provided sessionHandle is already open for the calling client, then
- * #onRangingOpenFailed must be called and the new session must not be opened.
- *
- * @param attributionSource AttributionSource to use for permission enforcement.
- * @param sessionHandle the session handle to open ranging for
- * @param rangingCallbacks the callbacks used to deliver ranging information
- * @param parameters the configuration to use for ranging
- */
- void openRanging(in AttributionSource attributionSource,
- in SessionHandle sessionHandle,
- in IUwbRangingCallbacks rangingCallbacks,
- in PersistableBundle parameters);
-
- /**
- * Request to start ranging
- *
- * IUwbAdapterCallbacks#onRangingStarted must be called within
- * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
- * called if the ranging session starts successfully.
- *
- * IUwbAdapterCallbacks#onRangingStartFailed must be called within
- * RANGING_SESSION_START_THRESHOLD_MS milliseconds of #startRanging being
- * called if the ranging session fails to be started.
- *
- * @param sessionHandle the session handle to start ranging for
- * @param parameters additional configuration required to start ranging
- */
- void startRanging(in SessionHandle sessionHandle,
- in PersistableBundle parameters);
-
- /**
- * Request to reconfigure ranging
- *
- * IUwbAdapterCallbacks#onRangingReconfigured must be called after
- * successfully reconfiguring the session.
- *
- * IUwbAdapterCallbacks#onRangingReconfigureFailed must be called after
- * failing to reconfigure the session.
- *
- * A session must not be modified by a failed call to #reconfigureRanging.
- *
- * @param sessionHandle the session handle to start ranging for
- * @param parameters the parameters to reconfigure and their new values
- */
- void reconfigureRanging(in SessionHandle sessionHandle,
- in PersistableBundle parameters);
-
- /**
- * Request to stop ranging
- *
- * IUwbAdapterCallbacks#onRangingStopped must be called after
- * successfully stopping the session.
- *
- * IUwbAdapterCallbacks#onRangingStopFailed must be called after failing
- * to stop the session.
- *
- * @param sessionHandle the session handle to stop ranging for
- */
- void stopRanging(in SessionHandle sessionHandle);
-
- /**
- * Close ranging for the session associated with the given handle
- *
- * Calling with an invalid handle or a handle that has already been closed
- * is a no-op.
- *
- * IUwbAdapterCallbacks#onRangingClosed must be called within
- * RANGING_SESSION_CLOSE_THRESHOLD_MS of #closeRanging being called.
- *
- * @param sessionHandle the session handle to close ranging for
- */
- void closeRanging(in SessionHandle sessionHandle);
-
- /**
- * Disables or enables UWB for a user
- *
- * The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
- * function must be called immediately following state change.
- *
- * @param enabled value representing intent to disable or enable UWB. If
- * true, any subsequent calls to #openRanging will be allowed. If false,
- * all active ranging sessions will be closed and subsequent calls to
- * #openRanging will be disallowed.
- */
- void setEnabled(boolean enabled);
-
- /**
- * Returns the current enabled/disabled UWB state.
- *
- * Possible values are:
- * IUwbAdapterState#STATE_DISABLED
- * IUwbAdapterState#STATE_ENABLED_ACTIVE
- * IUwbAdapterState#STATE_ENABLED_INACTIVE
- *
- * @return value representing enabled/disabled UWB state.
- */
- int getAdapterState();
-
- /**
- * The maximum allowed time to open a ranging session.
- */
- const int RANGING_SESSION_OPEN_THRESHOLD_MS = 3000; // Value TBD
-
- /**
- * The maximum allowed time to start a ranging session.
- */
- const int RANGING_SESSION_START_THRESHOLD_MS = 3000; // Value TBD
-
- /**
- * The maximum allowed time to notify the framework that a session has been
- * closed.
- */
- const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
-}
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
deleted file mode 100644
index 555bafee8b1e..000000000000
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ /dev/null
@@ -1,136 +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 android.os.PersistableBundle;
-import android.uwb.RangingChangeReason;
-import android.uwb.RangingReport;
-import android.uwb.SessionHandle;
-
-/**
- * @hide
- */
-oneway interface IUwbRangingCallbacks {
- /**
- * Called when the ranging session has been opened
- *
- * @param sessionHandle the session the callback is being invoked for
- */
- void onRangingOpened(in SessionHandle sessionHandle);
-
- /**
- * Called when a ranging session fails to start
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session failed to start
- * @param parameters protocol specific parameters
- */
- void onRangingOpenFailed(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Called when ranging has started
- *
- * May output parameters generated by the lower layers that must be sent to the
- * remote device(s). The PersistableBundle must be constructed using the UWB
- * support library.
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param rangingOutputParameters parameters generated by the lower layer that
- * should be sent to the remote device.
- */
- void onRangingStarted(in SessionHandle sessionHandle,
- in PersistableBundle parameters);
-
- /**
- * Called when a ranging session fails to start
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session failed to start
- * @param parameters protocol specific parameters
- */
- void onRangingStartFailed(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Called when ranging has been reconfigured
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param parameters the updated ranging configuration
- */
- void onRangingReconfigured(in SessionHandle sessionHandle,
- in PersistableBundle parameters);
-
- /**
- * Called when a ranging session fails to be reconfigured
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session failed to reconfigure
- * @param parameters protocol specific parameters
- */
- void onRangingReconfigureFailed(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Called when the ranging session has been stopped
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session was stopped
- * @param parameters protocol specific parameters
- */
-
- void onRangingStopped(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Called when a ranging session fails to stop
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session failed to stop
- * @param parameters protocol specific parameters
- */
- void onRangingStopFailed(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Called when a ranging session is closed
- *
- * @param sessionHandle the session the callback is being invoked for
- * @param reason the reason the session was closed
- * @param parameters protocol specific parameters
- */
- void onRangingClosed(in SessionHandle sessionHandle,
- RangingChangeReason reason,
- in PersistableBundle parameters);
-
- /**
- * Provides a new RangingResult to the framework
- *
- * The reported timestamp for a ranging measurement must be calculated as the
- * time which the ranging round that generated this measurement concluded.
- *
- * @param sessionHandle an identifier to associate the ranging results with a
- * session that is active
- * @param result the ranging report
- */
- void onRangingResult(in SessionHandle sessionHandle, in RangingReport result);
-}
diff --git a/core/java/android/uwb/OWNERS b/core/java/android/uwb/OWNERS
deleted file mode 100644
index 17936ae7bb73..000000000000
--- a/core/java/android/uwb/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-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/RangingChangeReason.aidl b/core/java/android/uwb/RangingChangeReason.aidl
deleted file mode 100644
index 19d4b3949d07..000000000000
--- a/core/java/android/uwb/RangingChangeReason.aidl
+++ /dev/null
@@ -1,63 +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;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum RangingChangeReason {
- /**
- * Unknown reason
- */
- UNKNOWN,
-
- /**
- * A local API call triggered the change, such as a call to
- * IUwbAdapter.closeRanging.
- */
- LOCAL_API,
-
- /**
- * The maximum number of sessions has been reached. This may be generated for
- * an active session if a higher priority session begins.
- */
- MAX_SESSIONS_REACHED,
-
- /**
- * The system state has changed resulting in the session changing (e.g. the
- * user disables UWB, or the user's locale changes and an active channel is no
- * longer permitted to be used).
- */
- SYSTEM_POLICY,
-
- /**
- * The remote device has requested to change the session
- */
- REMOTE_REQUEST,
-
- /**
- * The session changed for a protocol specific reason
- */
- PROTOCOL_SPECIFIC,
-
- /**
- * The provided parameters were invalid
- */
- BAD_PARAMETERS,
-}
-
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
deleted file mode 100644
index 6bba79600598..000000000000
--- a/core/java/android/uwb/RangingManager.java
+++ /dev/null
@@ -1,256 +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 android.annotation.NonNull;
-import android.content.AttributionSource;
-import android.os.CancellationSignal;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Hashtable;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public class RangingManager extends android.uwb.IUwbRangingCallbacks.Stub {
- private static final String TAG = "Uwb.RangingManager";
-
- private final IUwbAdapter mAdapter;
- private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
- private int mNextSessionId = 1;
-
- public RangingManager(IUwbAdapter adapter) {
- mAdapter = adapter;
- }
-
- /**
- * Open a new ranging session
- *
- * @param attributionSource Attribution source to use for the enforcement of
- * {@link android.Manifest.permission#ULTRAWIDEBAND_RANGING} runtime
- * permission.
- * @param params the parameters that define the ranging session
- * @param executor {@link Executor} to run callbacks
- * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
- * that is being opened.
- * @return a {@link CancellationSignal} that may be used to cancel the opening of the
- * {@link RangingSession}.
- */
- public CancellationSignal openSession(@NonNull AttributionSource attributionSource,
- @NonNull PersistableBundle params,
- @NonNull Executor executor,
- @NonNull RangingSession.Callback callbacks) {
- synchronized (this) {
- SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
- RangingSession session =
- new RangingSession(executor, callbacks, mAdapter, sessionHandle);
- mRangingSessionTable.put(sessionHandle, session);
- try {
- mAdapter.openRanging(attributionSource, sessionHandle, this, params);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- CancellationSignal cancellationSignal = new CancellationSignal();
- cancellationSignal.setOnCancelListener(() -> session.close());
- return cancellationSignal;
- }
- }
-
- private boolean hasSession(SessionHandle sessionHandle) {
- return mRangingSessionTable.containsKey(sessionHandle);
- }
-
- @Override
- public void onRangingOpened(SessionHandle sessionHandle) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG,
- "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingOpened();
- }
- }
-
- @Override
- public void onRangingOpenFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
- PersistableBundle parameters) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG,
- "onRangingOpenedFailed - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingOpenFailed(convertToReason(reason), parameters);
- mRangingSessionTable.remove(sessionHandle);
- }
- }
-
- @Override
- public void onRangingReconfigured(SessionHandle sessionHandle, PersistableBundle parameters) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG,
- "onRangingReconfigured - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingReconfigured(parameters);
- }
- }
-
- @Override
- public void onRangingReconfigureFailed(SessionHandle sessionHandle,
- @RangingChangeReason int reason, PersistableBundle params) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingReconfigureFailed(convertToReason(reason), params);
- }
- }
-
-
- @Override
- public void onRangingStarted(SessionHandle sessionHandle, PersistableBundle parameters) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG,
- "onRangingStarted - received unexpected SessionHandle: " + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStarted(parameters);
- }
- }
-
- @Override
- public void onRangingStartFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
- PersistableBundle params) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStartFailed(convertToReason(reason), params);
- }
- }
-
- @Override
- public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
- PersistableBundle params) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStopped(convertToReason(reason), params);
- }
- }
-
- @Override
- public void onRangingStopFailed(SessionHandle sessionHandle, @RangingChangeReason int reason,
- PersistableBundle parameters) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStopFailed - received unexpected SessionHandle: "
- + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStopFailed(convertToReason(reason), parameters);
- }
- }
-
- @Override
- public void onRangingClosed(SessionHandle sessionHandle, @RangingChangeReason int reason,
- PersistableBundle params) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingClosed - received unexpected SessionHandle: " + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingClosed(convertToReason(reason), params);
- mRangingSessionTable.remove(sessionHandle);
- }
- }
-
- @Override
- public void onRangingResult(SessionHandle sessionHandle, RangingReport result) {
- synchronized (this) {
- if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingResult - received unexpected SessionHandle: " + sessionHandle);
- return;
- }
-
- RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingResult(result);
- }
- }
-
- @RangingSession.Callback.Reason
- private static int convertToReason(@RangingChangeReason int reason) {
- switch (reason) {
- case RangingChangeReason.LOCAL_API:
- return RangingSession.Callback.REASON_LOCAL_REQUEST;
-
- case RangingChangeReason.MAX_SESSIONS_REACHED:
- return RangingSession.Callback.REASON_MAX_SESSIONS_REACHED;
-
- case RangingChangeReason.SYSTEM_POLICY:
- return RangingSession.Callback.REASON_SYSTEM_POLICY;
-
- case RangingChangeReason.REMOTE_REQUEST:
- return RangingSession.Callback.REASON_REMOTE_REQUEST;
-
- case RangingChangeReason.PROTOCOL_SPECIFIC:
- return RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR;
-
- case RangingChangeReason.BAD_PARAMETERS:
- return RangingSession.Callback.REASON_BAD_PARAMETERS;
-
- case RangingChangeReason.UNKNOWN:
- default:
- return RangingSession.Callback.REASON_UNKNOWN;
- }
- }
-}
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
deleted file mode 100644
index 249e2b746d0d..000000000000
--- a/core/java/android/uwb/RangingMeasurement.java
+++ /dev/null
@@ -1,308 +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 android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.SystemClock;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Representation of a ranging measurement between the local device and a remote device
- *
- * @hide
- */
-@SystemApi
-public final class RangingMeasurement implements Parcelable {
- private final UwbAddress mRemoteDeviceAddress;
- private final @Status int mStatus;
- private final long mElapsedRealtimeNanos;
- private final DistanceMeasurement mDistanceMeasurement;
- private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement;
-
- private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status,
- long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement,
- @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
- mRemoteDeviceAddress = remoteDeviceAddress;
- mStatus = status;
- mElapsedRealtimeNanos = elapsedRealtimeNanos;
- mDistanceMeasurement = distanceMeasurement;
- mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
- }
-
- /**
- * Get the remote device's {@link UwbAddress}
- *
- * @return the remote device's {@link UwbAddress}
- */
- @NonNull
- public UwbAddress getRemoteDeviceAddress() {
- return mRemoteDeviceAddress;
- }
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- RANGING_STATUS_SUCCESS,
- RANGING_STATUS_FAILURE_OUT_OF_RANGE,
- RANGING_STATUS_FAILURE_UNKNOWN_ERROR})
- public @interface Status {}
-
- /**
- * Ranging attempt was successful for this device
- */
- public static final int RANGING_STATUS_SUCCESS = 0;
-
- /**
- * Ranging failed for this device because it is out of range
- */
- public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1;
-
- /**
- * Ranging failed for this device because of unknown error
- */
- public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1;
-
- /**
- * Get the status of this ranging measurement
- *
- * <p>Possible values are
- * {@link #RANGING_STATUS_SUCCESS},
- * {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE},
- * {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}.
- *
- * @return the status of the ranging measurement
- */
- @Status
- public int getStatus() {
- return mStatus;
- }
-
- /**
- * Timestamp of this ranging measurement in time since boot nanos in the same namespace as
- * {@link SystemClock#elapsedRealtimeNanos()}
- *
- * @return timestamp of ranging measurement in nanoseconds
- */
- @SuppressLint("MethodNameUnits")
- public long getElapsedRealtimeNanos() {
- return mElapsedRealtimeNanos;
- }
-
- /**
- * Get the distance measurement
- *
- * @return a {@link DistanceMeasurement} or null if {@link #getStatus()} !=
- * {@link #RANGING_STATUS_SUCCESS}
- */
- @Nullable
- public DistanceMeasurement getDistanceMeasurement() {
- return mDistanceMeasurement;
- }
-
- /**
- * Get the angle of arrival measurement
- *
- * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} !=
- * {@link #RANGING_STATUS_SUCCESS}
- */
- @Nullable
- public AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
- return mAngleOfArrivalMeasurement;
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof RangingMeasurement) {
- RangingMeasurement other = (RangingMeasurement) obj;
- return mRemoteDeviceAddress.equals(other.getRemoteDeviceAddress())
- && mStatus == other.getStatus()
- && mElapsedRealtimeNanos == other.getElapsedRealtimeNanos()
- && mDistanceMeasurement.equals(other.getDistanceMeasurement())
- && mAngleOfArrivalMeasurement.equals(other.getAngleOfArrivalMeasurement());
- }
- return false;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return Objects.hash(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
- mDistanceMeasurement, mAngleOfArrivalMeasurement);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelable(mRemoteDeviceAddress, flags);
- dest.writeInt(mStatus);
- dest.writeLong(mElapsedRealtimeNanos);
- dest.writeParcelable(mDistanceMeasurement, flags);
- dest.writeParcelable(mAngleOfArrivalMeasurement, flags);
- }
-
- public static final @android.annotation.NonNull Creator<RangingMeasurement> CREATOR =
- new Creator<RangingMeasurement>() {
- @Override
- public RangingMeasurement createFromParcel(Parcel in) {
- Builder builder = new Builder();
- builder.setRemoteDeviceAddress(
- in.readParcelable(UwbAddress.class.getClassLoader()));
- builder.setStatus(in.readInt());
- builder.setElapsedRealtimeNanos(in.readLong());
- builder.setDistanceMeasurement(
- in.readParcelable(DistanceMeasurement.class.getClassLoader()));
- builder.setAngleOfArrivalMeasurement(
- in.readParcelable(AngleOfArrivalMeasurement.class.getClassLoader()));
- return builder.build();
- }
-
- @Override
- public RangingMeasurement[] newArray(int size) {
- return new RangingMeasurement[size];
- }
- };
-
- /**
- * Builder for a {@link RangingMeasurement} object.
- */
- public static final class Builder {
- private UwbAddress mRemoteDeviceAddress = null;
- private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR;
- private long mElapsedRealtimeNanos = -1L;
- private DistanceMeasurement mDistanceMeasurement = null;
- private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null;
-
- /**
- * Set the remote device address that this measurement is for
- *
- * @param remoteDeviceAddress remote device's address
- */
- @NonNull
- public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) {
- mRemoteDeviceAddress = remoteDeviceAddress;
- return this;
- }
-
- /**
- * Set the status of ranging measurement
- *
- * @param status the status of the ranging measurement
- */
- @NonNull
- public Builder setStatus(@Status int status) {
- mStatus = status;
- return this;
- }
-
- /**
- * Set the elapsed realtime in nanoseconds when the ranging measurement occurred
- *
- * @param elapsedRealtimeNanos time the ranging measurement occurred
- */
- @NonNull
- public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
- if (elapsedRealtimeNanos < 0) {
- throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0");
- }
- mElapsedRealtimeNanos = elapsedRealtimeNanos;
- return this;
- }
-
- /**
- * Set the {@link DistanceMeasurement}
- *
- * @param distanceMeasurement the distance measurement for this ranging measurement
- */
- @NonNull
- public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) {
- mDistanceMeasurement = distanceMeasurement;
- return this;
- }
-
- /**
- * Set the {@link AngleOfArrivalMeasurement}
- *
- * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging
- * measurement
- */
- @NonNull
- public Builder setAngleOfArrivalMeasurement(
- @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) {
- mAngleOfArrivalMeasurement = angleOfArrivalMeasurement;
- return this;
- }
-
- /**
- * Build the {@link RangingMeasurement} object
- *
- * @throws IllegalStateException if a distance or angle of arrival measurement is provided
- * but the measurement was not successful, if the
- * elapsedRealtimeNanos of the measurement is invalid, or
- * if no remote device address is set
- */
- @NonNull
- public RangingMeasurement build() {
- if (mStatus != RANGING_STATUS_SUCCESS) {
- if (mDistanceMeasurement != null) {
- throw new IllegalStateException(
- "Distance Measurement must be null if ranging is not successful");
- }
-
- if (mAngleOfArrivalMeasurement != null) {
- throw new IllegalStateException(
- "Angle of Arrival must be null if ranging is not successful");
- }
- }
-
- if (mRemoteDeviceAddress == null) {
- throw new IllegalStateException("No remote device address was set");
- }
-
- if (mElapsedRealtimeNanos < 0) {
- throw new IllegalStateException(
- "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos);
- }
-
- return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos,
- mDistanceMeasurement, mAngleOfArrivalMeasurement);
- }
- }
-}
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
deleted file mode 100644
index 7a2df8617700..000000000000
--- a/core/java/android/uwb/RangingReport.java
+++ /dev/null
@@ -1,160 +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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * This class contains the UWB ranging data
- *
- * @hide
- */
-@SystemApi
-public final class RangingReport implements Parcelable {
- private final List<RangingMeasurement> mRangingMeasurements;
-
- private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements) {
- mRangingMeasurements = rangingMeasurements;
- }
-
- /**
- * Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval
- * <p>The underlying UWB adapter may choose to do multiple measurements in each ranging
- * interval.
- *
- * <p>The entries in the {@link List} are ordered in ascending order based on
- * {@link RangingMeasurement#getElapsedRealtimeNanos()}
- *
- * @return a {@link List} of {@link RangingMeasurement} objects
- */
- @NonNull
- public List<RangingMeasurement> getMeasurements() {
- return mRangingMeasurements;
- }
-
- /**
- * @hide
- */
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof RangingReport) {
- RangingReport other = (RangingReport) obj;
- return mRangingMeasurements.equals(other.getMeasurements());
- }
-
- return false;
- }
-
- /**
- * @hide
- */
- @Override
- public int hashCode() {
- return Objects.hash(mRangingMeasurements);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeTypedList(mRangingMeasurements);
- }
-
- public static final @android.annotation.NonNull Creator<RangingReport> CREATOR =
- new Creator<RangingReport>() {
- @Override
- public RangingReport createFromParcel(Parcel in) {
- Builder builder = new Builder();
- builder.addMeasurements(in.createTypedArrayList(RangingMeasurement.CREATOR));
- return builder.build();
- }
-
- @Override
- public RangingReport[] newArray(int size) {
- return new RangingReport[size];
- }
- };
-
- /**
- * Builder for {@link RangingReport} object
- */
- public static final class Builder {
- List<RangingMeasurement> mMeasurements = new ArrayList<>();
-
- /**
- * Add a single {@link RangingMeasurement}
- *
- * @param rangingMeasurement a ranging measurement
- */
- @NonNull
- public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) {
- mMeasurements.add(rangingMeasurement);
- return this;
- }
-
- /**
- * Add a {@link List} of {@link RangingMeasurement}s
- *
- * @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add
- */
- @NonNull
- public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) {
- mMeasurements.addAll(rangingMeasurements);
- return this;
- }
-
- /**
- * Build the {@link RangingReport} object
- *
- * @throws IllegalStateException if measurements are not in monotonically increasing order
- */
- @NonNull
- public RangingReport build() {
- // Verify that all measurement timestamps are monotonically increasing
- RangingMeasurement prevMeasurement = null;
- for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) {
- RangingMeasurement curMeasurement = mMeasurements.get(curIndex);
- if (prevMeasurement != null
- && (prevMeasurement.getElapsedRealtimeNanos()
- > curMeasurement.getElapsedRealtimeNanos())) {
- throw new IllegalStateException(
- "Timestamp (" + curMeasurement.getElapsedRealtimeNanos()
- + ") at index " + curIndex + " is less than previous timestamp ("
- + prevMeasurement.getElapsedRealtimeNanos() + ")");
- }
- prevMeasurement = curMeasurement;
- }
- return new RangingReport(mMeasurements);
- }
- }
-}
-
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
deleted file mode 100644
index 345b69df84d5..000000000000
--- a/core/java/android/uwb/RangingSession.java
+++ /dev/null
@@ -1,496 +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 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;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides a way to control an active UWB ranging session.
- * <p>It also defines the required {@link RangingSession.Callback} that must be implemented
- * in order to be notified of UWB ranging results and status events related to the
- * {@link RangingSession}.
- *
- * <p>To get an instance of {@link RangingSession}, first use
- * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)} to request to open a
- * session. Once the session is opened, a {@link RangingSession} object is provided through
- * {@link RangingSession.Callback#onOpened(RangingSession)}. If opening a session fails, the failure
- * is reported through {@link RangingSession.Callback#onOpenFailed(int, PersistableBundle)} with the
- * failure reason.
- *
- * @hide
- */
-@SystemApi
-public final class RangingSession implements AutoCloseable {
- private static final String TAG = "Uwb.RangingSession";
- private final SessionHandle mSessionHandle;
- private final IUwbAdapter mAdapter;
- private final Executor mExecutor;
- private final Callback mCallback;
-
- private enum State {
- /**
- * The state of the {@link RangingSession} until
- * {@link RangingSession.Callback#onOpened(RangingSession)} is invoked
- */
- INIT,
-
- /**
- * The {@link RangingSession} is initialized and ready to begin ranging
- */
- IDLE,
-
- /**
- * The {@link RangingSession} is actively ranging
- */
- ACTIVE,
-
- /**
- * The {@link RangingSession} is closed and may not be used for ranging.
- */
- CLOSED
- }
-
- private State mState = State.INIT;
-
- /**
- * Interface for receiving {@link RangingSession} events
- */
- public interface Callback {
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- REASON_UNKNOWN,
- REASON_LOCAL_REQUEST,
- REASON_REMOTE_REQUEST,
- REASON_BAD_PARAMETERS,
- REASON_GENERIC_ERROR,
- REASON_MAX_SESSIONS_REACHED,
- REASON_SYSTEM_POLICY,
- REASON_PROTOCOL_SPECIFIC_ERROR})
- @interface Reason {}
-
- /**
- * Indicates that the session was closed or failed to open due to an unknown reason
- */
- int REASON_UNKNOWN = 0;
-
- /**
- * Indicates that the session was closed or failed to open because
- * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called
- */
- int REASON_LOCAL_REQUEST = 1;
-
- /**
- * Indicates that the session was closed or failed to open due to an explicit request from
- * the remote device.
- */
- int REASON_REMOTE_REQUEST = 2;
-
- /**
- * Indicates that the session was closed or failed to open due to erroneous parameters
- */
- int REASON_BAD_PARAMETERS = 3;
-
- /**
- * Indicates an error on this device besides the error code already listed
- */
- int REASON_GENERIC_ERROR = 4;
-
- /**
- * Indicates that the number of currently open sessions is equal to
- * {@link UwbManager#getMaxSimultaneousSessions()} and additional sessions may not be
- * opened.
- */
- int REASON_MAX_SESSIONS_REACHED = 5;
-
- /**
- * Indicates that the local system policy caused the change, such
- * as privacy policy, power management policy, permissions, and more.
- */
- int REASON_SYSTEM_POLICY = 6;
-
- /**
- * Indicates a protocol specific error. The associated {@link PersistableBundle} should be
- * consulted for additional information.
- */
- int REASON_PROTOCOL_SPECIFIC_ERROR = 7;
-
- /**
- * Invoked when {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
- * is successful
- *
- * @param session the newly opened {@link RangingSession}
- */
- void onOpened(@NonNull RangingSession session);
-
- /**
- * Invoked if {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}}
- * fails
- *
- * @param reason the failure reason
- * @param params protocol specific parameters
- */
- void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params);
-
- /**
- * Invoked when {@link RangingSession#start(PersistableBundle)} is successful
- * @param sessionInfo session specific parameters from the lower layers
- */
- void onStarted(@NonNull PersistableBundle sessionInfo);
-
- /**
- * Invoked when {@link RangingSession#start(PersistableBundle)} fails
- *
- * @param reason the failure reason
- * @param params protocol specific parameters
- */
- void onStartFailed(@Reason int reason, @NonNull PersistableBundle params);
-
- /**
- * Invoked when a request to reconfigure the session succeeds
- *
- * @param params the updated ranging configuration
- */
- void onReconfigured(@NonNull PersistableBundle params);
-
- /**
- * Invoked when a request to reconfigure the session fails
- *
- * @param reason reason the session failed to be reconfigured
- * @param params protocol specific failure reasons
- */
- void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params);
-
- /**
- * Invoked when a request to stop the session succeeds
- *
- * @param reason reason for the session stop
- * @param parameters protocol specific parameters related to the stop reason
- */
- void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
-
- /**
- * Invoked when a request to stop the session fails
- *
- * @param reason reason the session failed to be stopped
- * @param params protocol specific failure reasons
- */
- void onStopFailed(@Reason int reason, @NonNull PersistableBundle params);
-
- /**
- * Invoked when session is either closed spontaneously, or per user request via
- * {@link RangingSession#close()} or {@link AutoCloseable#close()}.
- *
- * @param reason reason for the session closure
- * @param parameters protocol specific parameters related to the close reason
- */
- void onClosed(@Reason int reason, @NonNull PersistableBundle parameters);
-
- /**
- * Called once per ranging interval even when a ranging measurement fails
- *
- * @param rangingReport ranging report for this interval's measurements
- */
- void onReportReceived(@NonNull RangingReport rangingReport);
- }
-
- /**
- * @hide
- */
- public RangingSession(Executor executor, Callback callback, IUwbAdapter adapter,
- SessionHandle sessionHandle) {
- mState = State.INIT;
- mExecutor = executor;
- mCallback = callback;
- mAdapter = adapter;
- mSessionHandle = sessionHandle;
- }
-
- /**
- * @hide
- */
- public boolean isOpen() {
- return mState == State.IDLE || mState == State.ACTIVE;
- }
-
- /**
- * Begins ranging for the session.
- *
- * <p>On successfully starting a ranging session,
- * {@link RangingSession.Callback#onStarted(PersistableBundle)} is invoked.
- *
- * <p>On failure to start the session,
- * {@link RangingSession.Callback#onStartFailed(int, PersistableBundle)} is invoked.
- *
- * @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();
- }
-
- try {
- mAdapter.startRanging(mSessionHandle, params);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attempts to reconfigure the session with the given parameters
- * <p>This call may be made when the session is open.
- *
- * <p>On successfully reconfiguring the session
- * {@link RangingSession.Callback#onReconfigured(PersistableBundle)} is invoked.
- *
- * <p>On failure to reconfigure the session,
- * {@link RangingSession.Callback#onReconfigureFailed(int, PersistableBundle)} is invoked.
- *
- * @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();
- }
-
- try {
- mAdapter.reconfigureRanging(mSessionHandle, params);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Stops actively ranging
- *
- * <p>A session that has been stopped may be resumed by calling
- * {@link RangingSession#start(PersistableBundle)} without the need to open a new session.
- *
- * <p>Stopping a {@link RangingSession} is useful when the lower layers should not discard
- * the parameters of the session, or when a session needs to be able to be resumed quickly.
- *
- * <p>If the {@link RangingSession} is no longer needed, use {@link RangingSession#close()} to
- * completely close the session and allow lower layers of the stack to perform necessarily
- * cleanup.
- *
- * <p>Stopped sessions may be closed by the system at any time. In such a case,
- * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} is invoked.
- *
- * <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();
- }
-
- try {
- mAdapter.stopRanging(mSessionHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Close the ranging session
- *
- * <p>After calling this function, in order resume ranging, a new {@link RangingSession} must
- * be opened by calling
- * {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}.
- *
- * <p>If this session is currently ranging, it will stop and close the session.
- * <p>If the session is in the process of being opened, it will attempt to stop the session from
- * being opened.
- * <p>If the session is already closed, the registered
- * {@link Callback#onClosed(int, PersistableBundle)} callback will still be invoked.
- *
- * <p>{@link Callback#onClosed(int, PersistableBundle)} will be invoked using the same callback
- * object given to {@link UwbManager#openRangingSession(PersistableBundle, Executor, Callback)}
- * when the {@link RangingSession} was opened. The callback will be invoked after each call to
- * {@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(
- Callback.REASON_LOCAL_REQUEST, new PersistableBundle()));
- return;
- }
-
- try {
- mAdapter.closeRanging(mSessionHandle);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * @hide
- */
- public void onRangingOpened() {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingOpened invoked for a closed session");
- return;
- }
-
- mState = State.IDLE;
- executeCallback(() -> mCallback.onOpened(this));
- }
-
- /**
- * @hide
- */
- public void onRangingOpenFailed(@Callback.Reason int reason,
- @NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingOpenFailed invoked for a closed session");
- return;
- }
-
- mState = State.CLOSED;
- executeCallback(() -> mCallback.onOpenFailed(reason, params));
- }
-
- /**
- * @hide
- */
- public void onRangingStarted(@NonNull PersistableBundle parameters) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingStarted invoked for a closed session");
- return;
- }
-
- mState = State.ACTIVE;
- executeCallback(() -> mCallback.onStarted(parameters));
- }
-
- /**
- * @hide
- */
- public void onRangingStartFailed(@Callback.Reason int reason,
- @NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingStartFailed invoked for a closed session");
- return;
- }
-
- executeCallback(() -> mCallback.onStartFailed(reason, params));
- }
-
- /**
- * @hide
- */
- public void onRangingReconfigured(@NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingReconfigured invoked for a closed session");
- return;
- }
-
- executeCallback(() -> mCallback.onReconfigured(params));
- }
-
- /**
- * @hide
- */
- public void onRangingReconfigureFailed(@Callback.Reason int reason,
- @NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingReconfigureFailed invoked for a closed session");
- return;
- }
-
- executeCallback(() -> mCallback.onReconfigureFailed(reason, params));
- }
-
- /**
- * @hide
- */
- public void onRangingStopped(@Callback.Reason int reason,
- @NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingStopped invoked for a closed session");
- return;
- }
-
- mState = State.IDLE;
- executeCallback(() -> mCallback.onStopped(reason, params));
- }
-
- /**
- * @hide
- */
- public void onRangingStopFailed(@Callback.Reason int reason,
- @NonNull PersistableBundle params) {
- if (mState == State.CLOSED) {
- Log.w(TAG, "onRangingStopFailed invoked for a closed session");
- return;
- }
-
- executeCallback(() -> mCallback.onStopFailed(reason, params));
- }
-
- /**
- * @hide
- */
- public void onRangingClosed(@Callback.Reason int reason,
- @NonNull PersistableBundle parameters) {
- mState = State.CLOSED;
- executeCallback(() -> mCallback.onClosed(reason, parameters));
- }
-
- /**
- * @hide
- */
- public void onRangingResult(@NonNull RangingReport report) {
- if (!isOpen()) {
- Log.w(TAG, "onRangingResult invoked for non-open session");
- return;
- }
-
- executeCallback(() -> mCallback.onReportReceived(report));
- }
-
- /**
- * @hide
- */
- private void executeCallback(@NonNull Runnable runnable) {
- final long identity = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(runnable);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-}
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
deleted file mode 100644
index b23f5ad603ff..000000000000
--- a/core/java/android/uwb/SessionHandle.java
+++ /dev/null
@@ -1,86 +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 android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * @hide
- */
-public final class SessionHandle implements Parcelable {
- private final int mId;
-
- public SessionHandle(int id) {
- mId = id;
- }
-
- protected SessionHandle(Parcel in) {
- mId = in.readInt();
- }
-
- public static final Creator<SessionHandle> CREATOR = new Creator<SessionHandle>() {
- @Override
- public SessionHandle createFromParcel(Parcel in) {
- return new SessionHandle(in);
- }
-
- @Override
- public SessionHandle[] newArray(int size) {
- return new SessionHandle[size];
- }
- };
-
- public int getId() {
- return mId;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mId);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof SessionHandle) {
- SessionHandle other = (SessionHandle) obj;
- return mId == other.mId;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(mId);
- }
-
- @Override
- public String toString() {
- return "SessionHandle [id=" + mId + "]";
- }
-}
diff --git a/core/java/android/uwb/StateChangeReason.aidl b/core/java/android/uwb/StateChangeReason.aidl
deleted file mode 100644
index 28eaf9f2b24e..000000000000
--- a/core/java/android/uwb/StateChangeReason.aidl
+++ /dev/null
@@ -1,50 +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;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum StateChangeReason {
- /**
- * The state changed for an unknown reason
- */
- UNKNOWN,
-
- /**
- * The adapter state changed because a session started.
- */
- SESSION_STARTED,
-
-
- /**
- * The adapter state changed because all sessions were closed.
- */
- ALL_SESSIONS_CLOSED,
-
- /**
- * The adapter state changed because of a device system change.
- */
- SYSTEM_POLICY,
-
- /**
- * Used to signal the first adapter state message after boot
- */
- SYSTEM_BOOT,
-}
-
diff --git a/core/java/android/uwb/TEST_MAPPING b/core/java/android/uwb/TEST_MAPPING
deleted file mode 100644
index 08ed2c7b71d9..000000000000
--- a/core/java/android/uwb/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "presubmit": [
- {
- "name": "UwbManagerTests"
- },
- {
- "name": "CtsUwbTestCases"
- }
- ]
-}
diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java
deleted file mode 100644
index 22883be88760..000000000000
--- a/core/java/android/uwb/UwbAddress.java
+++ /dev/null
@@ -1,131 +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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Arrays;
-
-/**
- * A class representing a UWB address
- *
- * @hide
- */
-@SystemApi
-public final class UwbAddress implements Parcelable {
- public static final int SHORT_ADDRESS_BYTE_LENGTH = 2;
- public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8;
-
- private final byte[] mAddressBytes;
-
- private UwbAddress(byte[] address) {
- mAddressBytes = address;
- }
-
- /**
- * Create a {@link UwbAddress} from a byte array.
- *
- * <p>If the provided array is {@link #SHORT_ADDRESS_BYTE_LENGTH} bytes, a short address is
- * created. If the provided array is {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes, then an
- * extended address is created.
- *
- * @param address a byte array to convert to a {@link UwbAddress}
- * @return a {@link UwbAddress} created from the input byte array
- * @throws IllegalArgumentException when the length is not one of
- * {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes
- */
- @NonNull
- public static UwbAddress fromBytes(@NonNull byte[] address) {
- if (address.length != SHORT_ADDRESS_BYTE_LENGTH
- && address.length != EXTENDED_ADDRESS_BYTE_LENGTH) {
- throw new IllegalArgumentException("Invalid UwbAddress length " + address.length);
- }
- return new UwbAddress(address);
- }
-
- /**
- * Get the address as a byte array
- *
- * @return the byte representation of this {@link UwbAddress}
- */
- @NonNull
- public byte[] toBytes() {
- return mAddressBytes;
- }
-
- /**
- * The length of the address in bytes
- * <p>Possible values are {@link #SHORT_ADDRESS_BYTE_LENGTH} and
- * {@link #EXTENDED_ADDRESS_BYTE_LENGTH}.
- */
- public int size() {
- return mAddressBytes.length;
- }
-
- @NonNull
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder("0x");
- for (byte addressByte : mAddressBytes) {
- builder.append(String.format("%02X", addressByte));
- }
- return builder.toString();
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (obj instanceof UwbAddress) {
- return Arrays.equals(mAddressBytes, ((UwbAddress) obj).toBytes());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Arrays.hashCode(mAddressBytes);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mAddressBytes.length);
- dest.writeByteArray(mAddressBytes);
- }
-
- public static final @android.annotation.NonNull Creator<UwbAddress> CREATOR =
- new Creator<UwbAddress>() {
- @Override
- public UwbAddress createFromParcel(Parcel in) {
- byte[] address = new byte[in.readInt()];
- in.readByteArray(address);
- return UwbAddress.fromBytes(address);
- }
-
- @Override
- public UwbAddress[] newArray(int size) {
- return new UwbAddress[size];
- }
- };
-}
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
deleted file mode 100644
index f7406ae277de..000000000000
--- a/core/java/android/uwb/UwbManager.java
+++ /dev/null
@@ -1,308 +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 android.Manifest.permission;
-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;
-import android.content.Context;
-import android.os.CancellationSignal;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.concurrent.Executor;
-
-/**
- * This class provides a way to perform Ultra Wideband (UWB) operations such as querying the
- * device's capabilities and determining the distance and angle between the local device and a
- * remote device.
- *
- * <p>To get a {@link UwbManager}, call the <code>Context.getSystemService(UwbManager.class)</code>.
- *
- * @hide
- */
-@SystemApi
-@SystemService(Context.UWB_SERVICE)
-public final class UwbManager {
- private static final String SERVICE_NAME = Context.UWB_SERVICE;
-
- private final Context mContext;
- private final IUwbAdapter mUwbAdapter;
- private final AdapterStateListener mAdapterStateListener;
- private final RangingManager mRangingManager;
-
- /**
- * Interface for receiving UWB adapter state changes
- */
- public interface AdapterStateCallback {
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- STATE_CHANGED_REASON_SESSION_STARTED,
- STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
- STATE_CHANGED_REASON_SYSTEM_POLICY,
- STATE_CHANGED_REASON_SYSTEM_BOOT,
- STATE_CHANGED_REASON_ERROR_UNKNOWN})
- @interface StateChangedReason {}
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- STATE_ENABLED_INACTIVE,
- STATE_ENABLED_ACTIVE,
- STATE_DISABLED})
- @interface State {}
-
- /**
- * Indicates that the state change was due to opening of first UWB session
- */
- int STATE_CHANGED_REASON_SESSION_STARTED = 0;
-
- /**
- * Indicates that the state change was due to closure of all UWB sessions
- */
- int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1;
-
- /**
- * Indicates that the state change was due to changes in system policy
- */
- int STATE_CHANGED_REASON_SYSTEM_POLICY = 2;
-
- /**
- * Indicates that the current state is due to a system boot
- */
- int STATE_CHANGED_REASON_SYSTEM_BOOT = 3;
-
- /**
- * Indicates that the state change was due to some unknown error
- */
- int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4;
-
- /**
- * Indicates that UWB is disabled on device
- */
- int STATE_DISABLED = 0;
- /**
- * Indicates that UWB is enabled on device but has no active ranging sessions
- */
- int STATE_ENABLED_INACTIVE = 1;
-
- /**
- * Indicates that UWB is enabled and has active ranging session
- */
- int STATE_ENABLED_ACTIVE = 2;
-
- /**
- * Invoked when underlying UWB adapter's state is changed
- * <p>Invoked with the adapter's current state after registering an
- * {@link AdapterStateCallback} using
- * {@link UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback)}.
- *
- * <p>Possible reasons for the state to change are
- * {@link #STATE_CHANGED_REASON_SESSION_STARTED},
- * {@link #STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED},
- * {@link #STATE_CHANGED_REASON_SYSTEM_POLICY},
- * {@link #STATE_CHANGED_REASON_SYSTEM_BOOT},
- * {@link #STATE_CHANGED_REASON_ERROR_UNKNOWN}.
- *
- * <p>Possible values for the UWB state are
- * {@link #STATE_ENABLED_INACTIVE},
- * {@link #STATE_ENABLED_ACTIVE},
- * {@link #STATE_DISABLED}.
- *
- * @param state the UWB state; inactive, active or disabled
- * @param reason the reason for the state change
- */
- void onStateChanged(@State int state, @StateChangedReason int reason);
- }
-
- /**
- * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
- *
- * @param ctx Context of the client.
- * @param adapter an instance of an {@link android.uwb.IUwbAdapter}
- */
- private UwbManager(@NonNull Context ctx, @NonNull IUwbAdapter adapter) {
- mContext = ctx;
- mUwbAdapter = adapter;
- mAdapterStateListener = new AdapterStateListener(adapter);
- mRangingManager = new RangingManager(adapter);
- }
-
- /**
- * @hide
- */
- public static UwbManager getInstance(@NonNull Context ctx) {
- IBinder b = ServiceManager.getService(SERVICE_NAME);
- if (b == null) {
- return null;
- }
-
- IUwbAdapter adapter = IUwbAdapter.Stub.asInterface(b);
- if (adapter == null) {
- return null;
- }
-
- return new UwbManager(ctx, adapter);
- }
-
- /**
- * Register an {@link AdapterStateCallback} to listen for UWB adapter state changes
- * <p>The provided callback will be invoked by the given {@link Executor}.
- *
- * <p>When first registering a callback, the callbacks's
- * {@link AdapterStateCallback#onStateChanged(int, int)} is immediately invoked to indicate
- * the current state of the underlying UWB adapter with the most recent
- * {@link AdapterStateCallback.StateChangedReason} that caused the change.
- *
- * @param executor an {@link Executor} to execute given callback
- * @param callback user implementation of the {@link AdapterStateCallback}
- */
- @RequiresPermission(permission.UWB_PRIVILEGED)
- public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AdapterStateCallback callback) {
- mAdapterStateListener.register(executor, callback);
- }
-
- /**
- * Unregister the specified {@link AdapterStateCallback}
- * <p>The same {@link AdapterStateCallback} object used when calling
- * {@link #registerAdapterStateCallback(Executor, AdapterStateCallback)} must be used.
- *
- * <p>Callbacks are automatically unregistered when application process goes away
- *
- * @param callback user implementation of the {@link AdapterStateCallback}
- */
- @RequiresPermission(permission.UWB_PRIVILEGED)
- public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) {
- mAdapterStateListener.unregister(callback);
- }
-
- /**
- * Get a {@link PersistableBundle} with the supported UWB protocols and parameters.
- * <p>The {@link PersistableBundle} should be parsed using a support library
- *
- * <p>Android reserves the '^android.*' namespace</p>
- *
- * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters
- */
- @NonNull
- @RequiresPermission(permission.UWB_PRIVILEGED)
- public PersistableBundle getSpecificationInfo() {
- try {
- return mUwbAdapter.getSpecificationInfo();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the timestamp resolution for events in nanoseconds
- * <p>This value defines the maximum error of all timestamps for events reported to
- * {@link RangingSession.Callback}.
- *
- * @return the timestamp resolution in nanoseconds
- */
- @SuppressLint("MethodNameUnits")
- @RequiresPermission(permission.UWB_PRIVILEGED)
- public long elapsedRealtimeResolutionNanos() {
- try {
- return mUwbAdapter.getTimestampResolutionNanos();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Open a {@link RangingSession} with the given parameters
- * <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
- * {@link RangingSession} object used to control ranging when the session is successfully
- * opened.
- *
- * <p>If a session cannot be opened, then
- * {@link RangingSession.Callback#onClosed(int, PersistableBundle)} will be invoked with the
- * appropriate {@link RangingSession.Callback.Reason}.
- *
- * <p>An open {@link RangingSession} will be automatically closed if client application process
- * dies.
- *
- * <p>A UWB support library must be used in order to construct the {@code parameter}
- * {@link PersistableBundle}.
- *
- * @param parameters the parameters that define the ranging session
- * @param executor {@link Executor} to run callbacks
- * @param callbacks {@link RangingSession.Callback} to associate with the
- * {@link RangingSession} that is being opened.
- *
- * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a
- * {@link RangingSession} that has been requested through {@link #openRangingSession}
- * but has not yet been made available by
- * {@link RangingSession.Callback#onOpened(RangingSession)}.
- */
- @NonNull
- @RequiresPermission(allOf = {
- permission.UWB_PRIVILEGED,
- permission.UWB_RANGING
- })
- public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull RangingSession.Callback callbacks) {
- return mRangingManager.openSession(
- mContext.getAttributionSource(), parameters, executor, callbacks);
- }
-
- /**
- * Returns the current enabled/disabled state for UWB.
- *
- * Possible values are:
- * AdapterStateCallback#STATE_DISABLED
- * AdapterStateCallback#STATE_ENABLED_INACTIVE
- * AdapterStateCallback#STATE_ENABLED_ACTIVE
- *
- * @return value representing current enabled/disabled state for UWB.
- * @hide
- */
- public @AdapterStateCallback.State int getAdapterState() {
- return mAdapterStateListener.getAdapterState();
- }
-
- /**
- * Disables or enables UWB for a user
- *
- * @param enabled value representing intent to disable or enable UWB. If true any subsequent
- * calls to IUwbAdapter#openRanging will be allowed. If false, all active ranging sessions will
- * be closed and subsequent calls to IUwbAdapter#openRanging will be disallowed.
- *
- * @hide
- */
- public void setUwbEnabled(boolean enabled) {
- mAdapterStateListener.setEnabled(enabled);
- }
-}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 199ecb3e5df2..d23200bd3c80 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -616,9 +616,12 @@ public final class AccessibilityInteractionController {
// focus instead fetching all provider nodes to do the search here.
AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
if (provider != null) {
- if (mViewRootImpl.mAccessibilityFocusedVirtualView != null) {
- focused = AccessibilityNodeInfo.obtain(
- mViewRootImpl.mAccessibilityFocusedVirtualView);
+ final AccessibilityNodeInfo focusNode =
+ mViewRootImpl.mAccessibilityFocusedVirtualView;
+ if (focusNode != null) {
+ final int virtualNodeId = AccessibilityNodeInfo
+ .getVirtualDescendantId(focusNode.getSourceNodeId());
+ focused = provider.createAccessibilityNodeInfo(virtualNodeId);
}
} else if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
focused = host.createAccessibilityNodeInfo();
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 7023e4bd0134..af76cb914b42 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -17,6 +17,7 @@
package android.view;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Handler;
import android.os.Looper;
/**
@@ -27,6 +28,13 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
private Choreographer mChoreographer;
private boolean mBatchingEnabled;
private boolean mBatchedInputScheduled;
+ private final Handler mHandler;
+ private final Runnable mConsumeBatchedInputEvents = new Runnable() {
+ @Override
+ public void run() {
+ consumeBatchedInputEvents(-1);
+ }
+ };
@UnsupportedAppUsage
public BatchedInputEventReceiver(
@@ -34,6 +42,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
super(inputChannel, looper);
mChoreographer = choreographer;
mBatchingEnabled = true;
+ mHandler = new Handler(looper);
}
@Override
@@ -60,7 +69,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
mBatchingEnabled = batchingEnabled;
if (!batchingEnabled) {
unscheduleBatchedInput();
- consumeBatchedInputEvents(-1);
+ mHandler.post(mConsumeBatchedInputEvents);
}
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 9cb0d1ff2c3f..e7ff978266a2 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1463,10 +1463,10 @@ public final class Display {
return false;
}
final Configuration config = mResources.getConfiguration();
- // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
- // if the caller is the recents component.
+ // TODO(b/179308296) Temporarily - never report max bounds to only Launcher if the feature
+ // is disabled.
return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
- && !isRecentsComponent();
+ && (mDisplayInfo.shouldConstrainMetricsForLauncher || !isRecentsComponent());
}
/**
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index e1a4402d8964..0257e55073dc 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -31,6 +31,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Path;
@@ -873,18 +874,147 @@ public final class DisplayCutout {
}
/**
+ * Gets the index of the given display unique id in {@link R.array#config_displayUniqueIdArray}
+ * which is used to get the related cutout configs for that display.
+ *
+ * For multi-display device, {@link R.array#config_displayUniqueIdArray} should be set for each
+ * display if there are different type of cutouts on each display.
+ * For single display device, {@link R.array#config_displayUniqueIdArray} should not to be set
+ * and the system will load the default configs for main built-in display.
+ */
+ private static int getDisplayCutoutConfigIndex(Resources res, String displayUniqueId) {
+ int index = -1;
+ if (displayUniqueId == null || displayUniqueId.isEmpty()) {
+ return index;
+ }
+ final String[] ids = res.getStringArray(R.array.config_displayUniqueIdArray);
+ final int size = ids.length;
+ for (int i = 0; i < size; i++) {
+ if (displayUniqueId.equals(ids[i])) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Gets the display cutout by the given display unique id.
+ *
+ * Loads the default config {@link R.string#config_mainBuiltInDisplayCutout) if
+ * {@link R.array#config_displayUniqueIdArray} is not set.
+ */
+ private static String getDisplayCutoutPath(Resources res, String displayUniqueId) {
+ final int index = getDisplayCutoutConfigIndex(res, displayUniqueId);
+ final String[] array = res.getStringArray(R.array.config_displayCutoutPathArray);
+ if (index >= 0 && index < array.length) {
+ return array[index];
+ }
+ return res.getString(R.string.config_mainBuiltInDisplayCutout);
+ }
+
+ /**
+ * Gets the display cutout approximation rect by the given display unique id.
+ *
+ * Loads the default config {@link R.string#config_mainBuiltInDisplayCutoutRectApproximation} if
+ * {@link R.array#config_displayUniqueIdArray} is not set.
+ */
+ private static String getDisplayCutoutApproximationRect(Resources res, String displayUniqueId) {
+ final int index = getDisplayCutoutConfigIndex(res, displayUniqueId);
+ final String[] array = res.getStringArray(
+ R.array.config_displayCutoutApproximationRectArray);
+ if (index >= 0 && index < array.length) {
+ return array[index];
+ }
+ return res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation);
+ }
+
+ /**
+ * Gets whether to mask a built-in display cutout of a display which is determined by the
+ * given display unique id.
+ *
+ * Loads the default config {@link R.bool#config_maskMainBuiltInDisplayCutout} if
+ * {@link R.array#config_displayUniqueIdArray} is not set.
+ *
+ * @hide
+ */
+ public static boolean getMaskBuiltInDisplayCutout(Resources res, String displayUniqueId) {
+ final int index = getDisplayCutoutConfigIndex(res, displayUniqueId);
+ final TypedArray array = res.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray);
+ boolean maskCutout;
+ if (index >= 0 && index < array.length()) {
+ maskCutout = array.getBoolean(index, false);
+ } else {
+ maskCutout = res.getBoolean(R.bool.config_maskMainBuiltInDisplayCutout);
+ }
+ array.recycle();
+ return maskCutout;
+ }
+
+ /**
+ * Gets whether to fill a built-in display cutout of a display which is determined by the
+ * given display unique id.
+ *
+ * Loads the default config{@link R.bool#config_fillMainBuiltInDisplayCutout} if
+ * {@link R.array#config_displayUniqueIdArray} is not set.
+ *
+ * @hide
+ */
+ public static boolean getFillBuiltInDisplayCutout(Resources res, String displayUniqueId) {
+ final int index = getDisplayCutoutConfigIndex(res, displayUniqueId);
+ final TypedArray array = res.obtainTypedArray(R.array.config_fillBuiltInDisplayCutoutArray);
+ boolean fillCutout;
+ if (index >= 0 && index < array.length()) {
+ fillCutout = array.getBoolean(index, false);
+ } else {
+ fillCutout = res.getBoolean(R.bool.config_fillMainBuiltInDisplayCutout);
+ }
+ array.recycle();
+ return fillCutout;
+ }
+
+ /**
+ * Gets the waterfall cutout by the given display unique id.
+ *
+ * Loads the default waterfall dimens if {@link R.array#config_displayUniqueIdArray} is not set.
+ * {@link R.dimen#waterfall_display_left_edge_size},
+ * {@link R.dimen#waterfall_display_top_edge_size},
+ * {@link R.dimen#waterfall_display_right_edge_size},
+ * {@link R.dimen#waterfall_display_bottom_edge_size}
+ */
+ private static Insets getWaterfallInsets(Resources res, String displayUniqueId) {
+ Insets insets;
+ final int index = getDisplayCutoutConfigIndex(res, displayUniqueId);
+ final TypedArray array = res.obtainTypedArray(R.array.config_waterfallCutoutArray);
+ if (index >= 0 && index < array.length() && array.getResourceId(index, 0) > 0) {
+ final int resourceId = array.getResourceId(index, 0);
+ final TypedArray waterfall = res.obtainTypedArray(resourceId);
+ insets = Insets.of(
+ waterfall.getDimensionPixelSize(0 /* waterfall left edge size */, 0),
+ waterfall.getDimensionPixelSize(1 /* waterfall top edge size */, 0),
+ waterfall.getDimensionPixelSize(2 /* waterfall right edge size */, 0),
+ waterfall.getDimensionPixelSize(3 /* waterfall bottom edge size */, 0));
+ waterfall.recycle();
+ } else {
+ insets = loadWaterfallInset(res);
+ }
+ array.recycle();
+ return insets;
+ }
+
+ /**
* Creates the display cutout according to
* @android:string/config_mainBuiltInDisplayCutoutRectApproximation, which is the closest
* rectangle-base approximation of the cutout.
*
* @hide
*/
- public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth,
- int displayHeight) {
- return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
- res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
+ public static DisplayCutout fromResourcesRectApproximation(Resources res,
+ String displayUniqueId, int displayWidth, int displayHeight) {
+ return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId),
+ getDisplayCutoutApproximationRect(res, displayUniqueId),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- loadWaterfallInset(res)).second;
+ getWaterfallInsets(res, displayUniqueId)).second;
}
/**
@@ -892,11 +1022,11 @@ public final class DisplayCutout {
*
* @hide
*/
- public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
- return pathAndDisplayCutoutFromSpec(
- res.getString(R.string.config_mainBuiltInDisplayCutout), null,
+ public static Path pathFromResources(Resources res, String displayUniqueId, int displayWidth,
+ int displayHeight) {
+ return pathAndDisplayCutoutFromSpec(getDisplayCutoutPath(res, displayUniqueId), null,
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- loadWaterfallInset(res)).first;
+ getWaterfallInsets(res, displayUniqueId)).first;
}
/**
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8e5f905e9c74..657251046551 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -306,6 +306,13 @@ public final class DisplayInfo implements Parcelable {
public float brightnessDefault;
/**
+ * @hide
+ * True if Display#getRealSize and getRealMetrics should be constrained for Launcher, false
+ * otherwise.
+ */
+ public boolean shouldConstrainMetricsForLauncher = false;
+
+ /**
* The {@link RoundedCorners} if present, otherwise {@code null}.
*/
@Nullable
@@ -381,7 +388,8 @@ public final class DisplayInfo implements Parcelable {
&& brightnessMinimum == other.brightnessMinimum
&& brightnessMaximum == other.brightnessMaximum
&& brightnessDefault == other.brightnessDefault
- && Objects.equals(roundedCorners, other.roundedCorners);
+ && Objects.equals(roundedCorners, other.roundedCorners)
+ && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
}
@Override
@@ -432,6 +440,7 @@ public final class DisplayInfo implements Parcelable {
brightnessMaximum = other.brightnessMaximum;
brightnessDefault = other.brightnessDefault;
roundedCorners = other.roundedCorners;
+ shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
}
public void readFromParcel(Parcel source) {
@@ -488,6 +497,7 @@ public final class DisplayInfo implements Parcelable {
for (int i = 0; i < numUserDisabledFormats; i++) {
userDisabledHdrTypes[i] = source.readInt();
}
+ shouldConstrainMetricsForLauncher = source.readBoolean();
}
@Override
@@ -542,6 +552,7 @@ public final class DisplayInfo implements Parcelable {
for (int i = 0; i < userDisabledHdrTypes.length; i++) {
dest.writeInt(userDisabledHdrTypes[i]);
}
+ dest.writeBoolean(shouldConstrainMetricsForLauncher);
}
@Override
@@ -796,6 +807,8 @@ public final class DisplayInfo implements Parcelable {
sb.append(brightnessMaximum);
sb.append(", brightnessDefault ");
sb.append(brightnessDefault);
+ sb.append(", shouldConstrainMetricsForLauncher ");
+ sb.append(shouldConstrainMetricsForLauncher);
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 9f63500fc853..ec613edeedb4 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -122,6 +122,29 @@ public class HapticFeedbackConstants {
public static final int REJECT = 17;
/**
+ * A haptic effect to provide texture while a rotary input device is being scrolled.
+ *
+ * @hide
+ */
+ public static final int ROTARY_SCROLL_TICK = 18;
+
+ /**
+ * A haptic effect to signal that a list element has been focused while scrolling using a rotary
+ * input device.
+ *
+ * @hide
+ */
+ public static final int ROTARY_SCROLL_ITEM_FOCUS = 19;
+
+ /**
+ * A haptic effect to signal reaching the scrolling limits of a list while scrolling using a
+ * rotary input device.
+ *
+ * @hide
+ */
+ public static final int ROTARY_SCROLL_LIMIT = 20;
+
+ /**
* The phone has booted with safe mode enabled.
* This is a private constant. Feel free to renumber as desired.
* @hide
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 802163617b3b..8c2348c5d438 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -53,6 +53,7 @@ import android.view.IWindowSessionCallback;
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.InputChannel;
@@ -720,14 +721,15 @@ interface IWindowManager
int displayId, in IDisplayWindowInsetsController displayWindowInsetsController);
/**
- * Called when a remote process modifies insets on a display window container.
+ * Called when a remote process updates the requested visibilities of insets on a display window
+ * container.
*/
- void modifyDisplayWindowInsets(int displayId, in InsetsState state);
+ void updateDisplayWindowRequestedVisibilities(int displayId, in InsetsVisibilities vis);
/**
* Called to get the expected window insets.
*
- * @return {@code true} if system bars are always comsumed.
+ * @return {@code true} if system bars are always consumed.
*/
boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
out InsetsState outInsetsState);
@@ -814,9 +816,10 @@ interface IWindowManager
* @param displayId The display associated with the window context
* @param options A bundle used to pass window-related options and choose the right DisplayArea
*
- * @return {@code true} if the WindowContext is attached to the DisplayArea successfully.
+ * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
+ * attached to the DisplayArea successfully. {@code null}, otherwise.
*/
- boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
+ Configuration attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
in Bundle options);
/**
@@ -865,4 +868,17 @@ interface IWindowManager
void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
boolean isTaskSnapshotSupported();
+
+ /**
+ * Returns the preferred display ID to show software keyboard.
+ *
+ * @see android.window.WindowProviderService#getLaunchedDisplayId
+ */
+ int getImeDisplayId();
+
+ /**
+ * Control if we should enable task snapshot features on this device.
+ * @hide
+ */
+ void setTaskSnapshotEnabled(boolean enabled);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7bad5cbfbdc3..a6abed020959 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -32,6 +32,7 @@ import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -46,12 +47,12 @@ import java.util.List;
*/
interface IWindowSession {
int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
- in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
+ in int viewVisibility, in int layerStackId, in InsetsVisibilities requestedVisibilities,
out InputChannel outInputChannel, out InsetsState insetsState,
out InsetsSourceControl[] activeControls);
int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
- in InsetsState requestedVisibility, out InputChannel outInputChannel,
+ in InsetsVisibilities requestedVisibilities, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out InsetsState insetsState);
@@ -285,10 +286,9 @@ interface IWindowSession {
oneway void updateTapExcludeRegion(IWindow window, in Region region);
/**
- * Called when the client has changed the local insets state, and now the server should reflect
- * that new state.
+ * Updates the requested visibilities of insets.
*/
- oneway void insetsModified(IWindow window, in InsetsState state);
+ oneway void updateRequestedVisibilities(IWindow window, in InsetsVisibilities visibilities);
/**
* Called when the system gesture exclusion has changed.
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 25dda5b2e0bb..2884d2279bfd 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -171,6 +171,16 @@ public abstract class InputEventReceiver {
}
/**
+ * Called when the display for the window associated with the input channel has entered or
+ * exited touch mode.
+ *
+ * @param isInTouchMode {@code true} if the display showing the window associated with the
+ * input channel entered touch mode.
+ */
+ public void onTouchModeChanged(boolean isInTouchMode) {
+ }
+
+ /**
* Called when a batched input event is pending.
*
* The batched input event will continue to accumulate additional movement
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 5a34a92a4b1a..4816d2c4950f 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -16,12 +16,11 @@
package android.view;
-import static android.view.Display.INVALID_DISPLAY;
-
import android.annotation.Nullable;
+import android.graphics.Matrix;
import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
import android.os.IBinder;
-import android.os.TouchOcclusionMode;
import java.lang.ref.WeakReference;
@@ -43,6 +42,12 @@ public final class InputWindowHandle {
// channel and the server input channel will both contain this token.
public IBinder token;
+ /**
+ * The {@link IWindow} handle if InputWindowHandle is associated with a window, null otherwise.
+ */
+ @Nullable
+ private IBinder windowToken;
+
// The window name.
public String name;
@@ -101,10 +106,6 @@ public final class InputWindowHandle {
// Display this input is on.
public int displayId;
- // If this value is set to a valid display ID, it indicates this window is a portal which
- // transports the touch of this window to the display indicated by portalToDisplayId.
- public int portalToDisplayId = INVALID_DISPLAY;
-
/**
* Crops the touchable region to the bounds of the surface provided.
*
@@ -122,6 +123,12 @@ public final class InputWindowHandle {
*/
public boolean replaceTouchableRegionWithCrop;
+ /**
+ * The transform that should be applied to the Window to get it from screen coordinates to
+ * window coordinates
+ */
+ public Matrix transform;
+
private native void nativeDispose();
public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -136,6 +143,9 @@ public final class InputWindowHandle {
.append(frameRight).append(",").append(frameBottom).append("]")
.append(", touchableRegion=").append(touchableRegion)
.append(", visible=").append(visible)
+ .append(", scaleFactor=").append(scaleFactor)
+ .append(", transform=").append(transform)
+ .append(", windowToken=").append(getWindow())
.toString();
}
@@ -167,4 +177,12 @@ public final class InputWindowHandle {
public void setTouchableRegionCrop(@Nullable SurfaceControl bounds) {
touchableRegionSurfaceControl = new WeakReference<>(bounds);
}
+
+ public void setWindowToken(IWindow iwindow) {
+ windowToken = iwindow.asBinder();
+ }
+
+ public IWindow getWindow() {
+ return IWindow.Stub.asInterface(windowToken);
+ }
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f915c9182d2..2ed705a007c4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -45,7 +45,6 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
@@ -61,6 +60,7 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.internal.inputmethod.ImeTracing;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -107,9 +107,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
boolean hasControl);
/**
- * Called when insets have been modified by the client and should be reported back to WM.
+ * Called when the requested visibilities of insets have been modified by the client.
+ * The visibilities should be reported back to WM.
+ *
+ * @param visibilities A collection of the requested visibilities.
*/
- void onInsetsModified(InsetsState insetsState);
+ void updateRequestedVisibilities(InsetsVisibilities visibilities);
/**
* @return Whether the host has any callbacks it wants to synchronize the animations with.
@@ -536,10 +539,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
/** The state dispatched from server */
private final InsetsState mLastDispatchedState = new InsetsState();
- // TODO: Use other class to represent the requested visibility of each type, because the
- // display frame and the frame in each source are not used.
/** The requested visibilities sent to server */
- private final InsetsState mRequestedState = new InsetsState();
+ private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
private final Rect mFrame = new Rect();
private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
@@ -801,7 +802,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
- boolean requestedStateStale = false;
+ boolean requestedVisibilityStale = false;
final int[] showTypes = new int[1];
final int[] hideTypes = new int[1];
@@ -822,20 +823,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final InsetsSourceConsumer consumer = getSourceConsumer(type);
consumer.setControl(control, showTypes, hideTypes);
- if (!requestedStateStale) {
+ if (!requestedVisibilityStale) {
final boolean requestedVisible = consumer.isRequestedVisible();
// We might have changed our requested visibilities while we don't have the control,
// so we need to update our requested state once we have control. Otherwise, our
// requested state at the server side might be incorrect.
final boolean requestedVisibilityChanged =
- requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type);
+ requestedVisible != mRequestedVisibilities.getVisibility(type);
// The IME client visibility will be reset by insets source provider while updating
// control, so if IME is requested visible, we need to send the request to server.
final boolean imeRequestedVisible = type == ITYPE_IME && requestedVisible;
- requestedStateStale = requestedVisibilityChanged || imeRequestedVisible;
+ requestedVisibilityStale = requestedVisibilityChanged || imeRequestedVisible;
}
}
@@ -861,7 +862,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
// InsetsSourceConsumer#setControl might change the requested visibility.
- updateRequestedVisibility();
+ updateRequestedVisibilities();
}
@Override
@@ -1015,7 +1016,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (types == 0) {
// nothing to animate.
listener.onCancelled(null);
- updateRequestedVisibility();
+ updateRequestedVisibilities();
if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
return;
}
@@ -1051,7 +1052,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
});
}
- updateRequestedVisibility();
+ updateRequestedVisibilities();
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
return;
}
@@ -1059,7 +1060,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (typesReady == 0) {
if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
listener.onCancelled(null);
- updateRequestedVisibility();
+ updateRequestedVisibilities();
return;
}
@@ -1091,7 +1092,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
} else {
hideDirectly(types, false /* animationFinished */, animationType, fromIme);
}
- updateRequestedVisibility();
+ updateRequestedVisibilities();
}
/**
@@ -1348,7 +1349,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
/**
* Sends the requested visibilities to window manager if any of them is changed.
*/
- private void updateRequestedVisibility() {
+ private void updateRequestedVisibilities() {
boolean changed = false;
for (int i = mRequestedVisibilityChanged.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mRequestedVisibilityChanged.valueAt(i);
@@ -1357,8 +1358,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
continue;
}
final boolean requestedVisible = consumer.isRequestedVisible();
- if (requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type)) {
- mRequestedState.getSource(type).setVisible(requestedVisible);
+ if (mRequestedVisibilities.getVisibility(type) != requestedVisible) {
+ mRequestedVisibilities.setVisibility(type, requestedVisible);
changed = true;
}
}
@@ -1366,11 +1367,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
if (!changed) {
return;
}
- mHost.onInsetsModified(mRequestedState);
+ mHost.updateRequestedVisibilities(mRequestedVisibilities);
}
- InsetsState getRequestedVisibility() {
- return mRequestedState;
+ InsetsVisibilities getRequestedVisibilities() {
+ return mRequestedVisibilities;
}
@VisibleForTesting
@@ -1425,7 +1426,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
}
- updateRequestedVisibility();
+ updateRequestedVisibilities();
if (fromIme) {
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0);
@@ -1441,7 +1442,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
}
- updateRequestedVisibility();
+ updateRequestedVisibilities();
if (fromIme) {
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0);
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ee33541d9f40..bf9de39124c9 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -36,13 +36,13 @@ import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.Log;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.ImeTracing;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 37101b757e66..f4444a145bf4 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -878,16 +878,5 @@ public class InsetsState implements Parcelable {
+ ", mSources= { " + joiner
+ " }";
}
-
- public @NonNull String toSourceVisibilityString() {
- StringJoiner joiner = new StringJoiner(", ");
- for (int i = 0; i < SIZE; i++) {
- InsetsSource source = mSources[i];
- if (source != null) {
- joiner.add(typeToString(i) + ": " + (source.isVisible() ? "visible" : "invisible"));
- }
- }
- return joiner.toString();
- }
}
diff --git a/core/java/android/view/InsetsVisibilities.aidl b/core/java/android/view/InsetsVisibilities.aidl
new file mode 100644
index 000000000000..bd573ea7bc35
--- /dev/null
+++ b/core/java/android/view/InsetsVisibilities.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;
+
+parcelable InsetsVisibilities;
diff --git a/core/java/android/view/InsetsVisibilities.java b/core/java/android/view/InsetsVisibilities.java
new file mode 100644
index 000000000000..30668bad3424
--- /dev/null
+++ b/core/java/android/view/InsetsVisibilities.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.StringJoiner;
+
+/**
+ * A collection of visibilities of insets. This is used for carrying the requested visibilities.
+ * @hide
+ */
+public class InsetsVisibilities implements Parcelable {
+
+ private static final int UNSPECIFIED = 0;
+ private static final int VISIBLE = 1;
+ private static final int INVISIBLE = -1;
+
+ private final int[] mVisibilities = new int[InsetsState.SIZE];
+
+ public InsetsVisibilities() {
+ }
+
+ public InsetsVisibilities(InsetsVisibilities other) {
+ set(other);
+ }
+
+ public InsetsVisibilities(Parcel in) {
+ in.readIntArray(mVisibilities);
+ }
+
+ /**
+ * Copies from another {@link InsetsVisibilities}.
+ *
+ * @param other an instance of {@link InsetsVisibilities}.
+ */
+ public void set(InsetsVisibilities other) {
+ System.arraycopy(other.mVisibilities, InsetsState.FIRST_TYPE, mVisibilities,
+ InsetsState.FIRST_TYPE, InsetsState.SIZE);
+ }
+
+ /**
+ * Sets a visibility to a type.
+ *
+ * @param type The {@link @InsetsState.InternalInsetsType}.
+ * @param visible {@code true} represents visible; {@code false} represents invisible.
+ */
+ public void setVisibility(@InsetsState.InternalInsetsType int type, boolean visible) {
+ mVisibilities[type] = visible ? VISIBLE : INVISIBLE;
+ }
+
+ /**
+ * Returns the specified insets visibility of the type. If it has never been specified,
+ * this returns the default visibility.
+ *
+ * @param type The {@link @InsetsState.InternalInsetsType}.
+ * @return The specified visibility or the default one if it is not specified.
+ */
+ public boolean getVisibility(@InsetsState.InternalInsetsType int type) {
+ final int visibility = mVisibilities[type];
+ return visibility == UNSPECIFIED
+ ? InsetsState.getDefaultVisibility(type)
+ : visibility == VISIBLE;
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner(", ");
+ for (int type = InsetsState.FIRST_TYPE; type <= InsetsState.LAST_TYPE; type++) {
+ final int visibility = mVisibilities[type];
+ if (visibility != UNSPECIFIED) {
+ joiner.add(InsetsState.typeToString(type) + ": "
+ + (visibility == VISIBLE ? "visible" : "invisible"));
+ }
+ }
+ return joiner.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mVisibilities);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof InsetsVisibilities)) {
+ return false;
+ }
+ return Arrays.equals(mVisibilities, ((InsetsVisibilities) other).mVisibilities);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeIntArray(mVisibilities);
+ }
+
+ public static final @NonNull Creator<InsetsVisibilities> CREATOR =
+ new Creator<InsetsVisibilities>() {
+
+ public InsetsVisibilities createFromParcel(Parcel in) {
+ return new InsetsVisibilities(in);
+ }
+
+ public InsetsVisibilities[] newArray(int size) {
+ return new InsetsVisibilities[size];
+ }
+ };
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index cda9b233576c..d12ed8f1595e 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -31,6 +31,8 @@ import android.util.Log;
import android.util.SparseIntArray;
import android.view.KeyCharacterMap.KeyData;
+import java.util.concurrent.TimeUnit;
+
/**
* Object used to report key and button events.
* <p>
@@ -1298,9 +1300,16 @@ public class KeyEvent extends InputEvent implements Parcelable {
private int mRepeatCount;
@UnsupportedAppUsage
private int mFlags;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ /**
+ * The time when the key initially was pressed, in nanoseconds. Only millisecond precision is
+ * exposed as public api, so this must always be converted to / from milliseconds when used.
+ */
private long mDownTime;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ /**
+ * The time when the current key event occurred. If mAction is ACTION_DOWN, then this is equal
+ * to mDownTime. Only millisecond precision is exposed as public api, so this must always be
+ * converted to / from milliseconds when used.
+ */
private long mEventTime;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private String mCharacters;
@@ -1400,8 +1409,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat) {
mId = nativeNextId();
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
@@ -1425,8 +1434,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
public KeyEvent(long downTime, long eventTime, int action,
int code, int repeat, int metaState) {
mId = nativeNextId();
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
@@ -1454,8 +1463,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
int code, int repeat, int metaState,
int deviceId, int scancode) {
mId = nativeNextId();
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
@@ -1485,8 +1494,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
int code, int repeat, int metaState,
int deviceId, int scancode, int flags) {
mId = nativeNextId();
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
@@ -1518,8 +1527,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source) {
mId = nativeNextId();
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = action;
mKeyCode = code;
mRepeatCount = repeat;
@@ -1545,8 +1554,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
public KeyEvent(long time, String characters, int deviceId, int flags) {
mId = nativeNextId();
- mDownTime = time;
- mEventTime = time;
+ mDownTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(time, TimeUnit.MILLISECONDS);
mCharacters = characters;
mAction = ACTION_MULTIPLE;
mKeyCode = KEYCODE_UNKNOWN;
@@ -1592,7 +1601,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
mId = nativeNextId(); // Not an exact copy so assign a new ID.
mDownTime = origEvent.mDownTime;
- mEventTime = eventTime;
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
mAction = origEvent.mAction;
mKeyCode = origEvent.mKeyCode;
mRepeatCount = newRepeat;
@@ -1626,14 +1635,14 @@ public class KeyEvent extends InputEvent implements Parcelable {
*
* @hide
*/
- public static KeyEvent obtain(int id, long downTime, long eventTime, int action,
+ private static KeyEvent obtain(int id, long downTimeNanos, long eventTimeNanos, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac,
String characters) {
KeyEvent ev = obtain();
ev.mId = id;
- ev.mDownTime = downTime;
- ev.mEventTime = eventTime;
+ ev.mDownTime = downTimeNanos;
+ ev.mEventTime = eventTimeNanos;
ev.mAction = action;
ev.mKeyCode = code;
ev.mRepeatCount = repeat;
@@ -1656,6 +1665,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scanCode, int flags, int source, int displayId, String characters) {
+ downTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ eventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState,
deviceId, scanCode, flags, source, displayId, null /* hmac */, characters);
}
@@ -1669,6 +1680,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static KeyEvent obtain(long downTime, long eventTime, int action,
int code, int repeat, int metaState,
int deviceId, int scancode, int flags, int source, String characters) {
+ // Do not convert downTime and eventTime here. We are calling the obtain method above,
+ // which will do the conversion. Just specify INVALID_DISPLAY and forward the request.
return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
flags, source, INVALID_DISPLAY, characters);
}
@@ -1768,7 +1781,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
int newRepeat, int newFlags) {
KeyEvent ret = new KeyEvent(event);
ret.mId = nativeNextId(); // Not an exact copy so assign a new ID.
- ret.mEventTime = eventTime;
+ ret.mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
ret.mRepeatCount = newRepeat;
ret.mFlags = newFlags;
return ret;
@@ -2617,8 +2630,8 @@ public class KeyEvent extends InputEvent implements Parcelable {
* @hide
*/
public final void setTime(long downTime, long eventTime) {
- mDownTime = downTime;
- mEventTime = eventTime;
+ mDownTime = TimeUnit.NANOSECONDS.convert(downTime, TimeUnit.MILLISECONDS);
+ mEventTime = TimeUnit.NANOSECONDS.convert(eventTime, TimeUnit.MILLISECONDS);
}
/**
@@ -2633,7 +2646,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
* {@link android.os.SystemClock#uptimeMillis} time base
*/
public final long getDownTime() {
- return mDownTime;
+ return TimeUnit.MILLISECONDS.convert(mDownTime, TimeUnit.NANOSECONDS);
}
/**
@@ -2645,7 +2658,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
@Override
public final long getEventTime() {
- return mEventTime;
+ return TimeUnit.MILLISECONDS.convert(mEventTime, TimeUnit.NANOSECONDS);
}
/**
@@ -2664,7 +2677,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
*/
@Override
public final long getEventTimeNano() {
- return mEventTime * 1000000L;
+ return mEventTime;
}
/**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 69ff64f3d6a5..40942ea7f551 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -4008,6 +4008,22 @@ public final class MotionEvent extends InputEvent implements Parcelable {
public float orientation;
/**
+ * The movement of x position of a motion event.
+ *
+ * @see MotionEvent#AXIS_RELATIVE_X
+ * @hide
+ */
+ public float relativeX;
+
+ /**
+ * The movement of y position of a motion event.
+ *
+ * @see MotionEvent#AXIS_RELATIVE_Y
+ * @hide
+ */
+ public float relativeY;
+
+ /**
* Clears the contents of this object.
* Resets all axes to zero.
*/
@@ -4023,6 +4039,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
toolMajor = 0;
toolMinor = 0;
orientation = 0;
+ relativeX = 0;
+ relativeY = 0;
}
/**
@@ -4053,6 +4071,8 @@ public final class MotionEvent extends InputEvent implements Parcelable {
toolMajor = other.toolMajor;
toolMinor = other.toolMinor;
orientation = other.orientation;
+ relativeX = other.relativeX;
+ relativeY = other.relativeY;
}
/**
@@ -4084,6 +4104,10 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return toolMinor;
case AXIS_ORIENTATION:
return orientation;
+ case AXIS_RELATIVE_X:
+ return relativeX;
+ case AXIS_RELATIVE_Y:
+ return relativeY;
default: {
if (axis < 0 || axis > 63) {
throw new IllegalArgumentException("Axis out of range.");
@@ -4137,6 +4161,12 @@ public final class MotionEvent extends InputEvent implements Parcelable {
case AXIS_ORIENTATION:
orientation = value;
break;
+ case AXIS_RELATIVE_X:
+ relativeX = value;
+ break;
+ case AXIS_RELATIVE_Y:
+ relativeY = value;
+ break;
default: {
if (axis < 0 || axis > 63) {
throw new IllegalArgumentException("Axis out of range.");
diff --git a/core/java/android/view/RemoteAnimationAdapter.java b/core/java/android/view/RemoteAnimationAdapter.java
index a78036fba094..e1cc60491f72 100644
--- a/core/java/android/view/RemoteAnimationAdapter.java
+++ b/core/java/android/view/RemoteAnimationAdapter.java
@@ -17,6 +17,7 @@
package android.view;
import android.app.ActivityOptions;
+import android.app.IApplicationThread;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -58,6 +59,9 @@ public class RemoteAnimationAdapter implements Parcelable {
private int mCallingPid;
private int mCallingUid;
+ /** @see #getCallingApplication */
+ private IApplicationThread mCallingApplication;
+
/**
* @param runner The interface that gets notified when we actually need to start the animation.
* @param duration The duration of the animation.
@@ -81,11 +85,19 @@ public class RemoteAnimationAdapter implements Parcelable {
this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
}
+ @UnsupportedAppUsage
+ public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
+ long statusBarTransitionDelay, IApplicationThread callingApplication) {
+ this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
+ mCallingApplication = callingApplication;
+ }
+
public RemoteAnimationAdapter(Parcel in) {
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
mDuration = in.readLong();
mStatusBarTransitionDelay = in.readLong();
mChangeNeedsSnapshot = in.readBoolean();
+ mCallingApplication = IApplicationThread.Stub.asInterface(in.readStrongBinder());
}
public IRemoteAnimationRunner getRunner() {
@@ -126,6 +138,15 @@ public class RemoteAnimationAdapter implements Parcelable {
return mCallingUid;
}
+ /**
+ * Gets the ApplicationThread that will run the animation. Instead it is intended to pass the
+ * calling information among client processes (eg. shell + launcher) through one-way binder
+ * calls (where binder itself doesn't track calling information).
+ */
+ public IApplicationThread getCallingApplication() {
+ return mCallingApplication;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -137,6 +158,7 @@ public class RemoteAnimationAdapter implements Parcelable {
dest.writeLong(mDuration);
dest.writeLong(mStatusBarTransitionDelay);
dest.writeBoolean(mChangeNeedsSnapshot);
+ dest.writeStrongInterface(mCallingApplication);
}
public static final @android.annotation.NonNull Creator<RemoteAnimationAdapter> CREATOR
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index cdc099b8e2ea..bd68401e6534 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -197,6 +197,12 @@ public class RemoteAnimationTarget implements Parcelable {
public ActivityManager.RunningTaskInfo taskInfo;
/**
+ * {@code true} if picture-in-picture permission is granted in {@link android.app.AppOpsManager}
+ */
+ @UnsupportedAppUsage
+ public boolean allowEnterPip;
+
+ /**
* The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used
* for non-app window.
*/
@@ -206,10 +212,11 @@ public class RemoteAnimationTarget implements Parcelable {
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
- SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo) {
+ SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo,
+ boolean allowEnterPip) {
this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
- startBounds, taskInfo, INVALID_WINDOW_TYPE);
+ startBounds, taskInfo, allowEnterPip, INVALID_WINDOW_TYPE);
}
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
@@ -217,7 +224,7 @@ public class RemoteAnimationTarget implements Parcelable {
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
SurfaceControl startLeash, Rect startBounds,
- ActivityManager.RunningTaskInfo taskInfo,
+ ActivityManager.RunningTaskInfo taskInfo, boolean allowEnterPip,
@WindowManager.LayoutParams.WindowType int windowType) {
this.mode = mode;
this.taskId = taskId;
@@ -235,6 +242,7 @@ public class RemoteAnimationTarget implements Parcelable {
this.startLeash = startLeash;
this.startBounds = startBounds == null ? null : new Rect(startBounds);
this.taskInfo = taskInfo;
+ this.allowEnterPip = allowEnterPip;
this.windowType = windowType;
}
@@ -255,6 +263,7 @@ public class RemoteAnimationTarget implements Parcelable {
startLeash = in.readTypedObject(SurfaceControl.CREATOR);
startBounds = in.readTypedObject(Rect.CREATOR);
taskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ allowEnterPip = in.readBoolean();
windowType = in.readInt();
}
@@ -281,6 +290,7 @@ public class RemoteAnimationTarget implements Parcelable {
dest.writeTypedObject(startLeash, 0 /* flags */);
dest.writeTypedObject(startBounds, 0 /* flags */);
dest.writeTypedObject(taskInfo, 0 /* flags */);
+ dest.writeBoolean(allowEnterPip);
dest.writeInt(windowType);
}
@@ -299,6 +309,7 @@ public class RemoteAnimationTarget implements Parcelable {
pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
pw.print(prefix); pw.print("leash="); pw.println(leash);
pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
+ pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
}
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
index 8808827b248a..758f9ab935cf 100644
--- a/core/java/android/view/ScrollCaptureResponse.java
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -53,6 +53,10 @@ public class ScrollCaptureResponse implements Parcelable {
@Nullable
private String mWindowTitle = null;
+ /** The package which owns the window. */
+ @Nullable
+ private String mPackageName = null;
+
/** Carries additional logging and debugging information when enabled. */
@NonNull
@DataClass.PluralOf("message")
@@ -77,7 +81,7 @@ public class ScrollCaptureResponse implements Parcelable {
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -97,6 +101,7 @@ public class ScrollCaptureResponse implements Parcelable {
@Nullable Rect windowBounds,
@Nullable Rect boundsInWindow,
@Nullable String windowTitle,
+ @Nullable String packageName,
@NonNull ArrayList<String> messages) {
this.mDescription = description;
com.android.internal.util.AnnotationValidations.validate(
@@ -105,6 +110,7 @@ public class ScrollCaptureResponse implements Parcelable {
this.mWindowBounds = windowBounds;
this.mBoundsInWindow = boundsInWindow;
this.mWindowTitle = windowTitle;
+ this.mPackageName = packageName;
this.mMessages = messages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessages);
@@ -153,6 +159,14 @@ public class ScrollCaptureResponse implements Parcelable {
}
/**
+ * The package name of the process the window is owned by.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
* Carries additional logging and debugging information when enabled.
*/
@DataClass.Generated.Member
@@ -172,6 +186,7 @@ public class ScrollCaptureResponse implements Parcelable {
"windowBounds = " + mWindowBounds + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"windowTitle = " + mWindowTitle + ", " +
+ "packageName = " + mPackageName + ", " +
"messages = " + mMessages +
" }";
}
@@ -187,12 +202,14 @@ public class ScrollCaptureResponse implements Parcelable {
if (mWindowBounds != null) flg |= 0x4;
if (mBoundsInWindow != null) flg |= 0x8;
if (mWindowTitle != null) flg |= 0x10;
+ if (mPackageName != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeString(mDescription);
if (mConnection != null) dest.writeStrongInterface(mConnection);
if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags);
if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags);
if (mWindowTitle != null) dest.writeString(mWindowTitle);
+ if (mPackageName != null) dest.writeString(mPackageName);
dest.writeStringList(mMessages);
}
@@ -213,6 +230,7 @@ public class ScrollCaptureResponse implements Parcelable {
Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
String windowTitle = (flg & 0x10) == 0 ? null : in.readString();
+ String packageName = (flg & 0x20) == 0 ? null : in.readString();
ArrayList<String> messages = new ArrayList<>();
in.readStringList(messages);
@@ -223,6 +241,7 @@ public class ScrollCaptureResponse implements Parcelable {
this.mWindowBounds = windowBounds;
this.mBoundsInWindow = boundsInWindow;
this.mWindowTitle = windowTitle;
+ this.mPackageName = packageName;
this.mMessages = messages;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMessages);
@@ -256,6 +275,7 @@ public class ScrollCaptureResponse implements Parcelable {
private @Nullable Rect mWindowBounds;
private @Nullable Rect mBoundsInWindow;
private @Nullable String mWindowTitle;
+ private @Nullable String mPackageName;
private @NonNull ArrayList<String> mMessages;
private long mBuilderFieldsSet = 0L;
@@ -319,12 +339,23 @@ public class ScrollCaptureResponse implements Parcelable {
}
/**
+ * The package name of the process the window is owned by.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mPackageName = value;
+ return this;
+ }
+
+ /**
* Carries additional logging and debugging information when enabled.
*/
@DataClass.Generated.Member
public @NonNull Builder setMessages(@NonNull ArrayList<String> value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20;
+ mBuilderFieldsSet |= 0x40;
mMessages = value;
return this;
}
@@ -340,7 +371,7 @@ public class ScrollCaptureResponse implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull ScrollCaptureResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x40; // Mark builder used
+ mBuilderFieldsSet |= 0x80; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mDescription = "";
@@ -358,6 +389,9 @@ public class ScrollCaptureResponse implements Parcelable {
mWindowTitle = null;
}
if ((mBuilderFieldsSet & 0x20) == 0) {
+ mPackageName = null;
+ }
+ if ((mBuilderFieldsSet & 0x40) == 0) {
mMessages = new ArrayList<>();
}
ScrollCaptureResponse o = new ScrollCaptureResponse(
@@ -366,12 +400,13 @@ public class ScrollCaptureResponse implements Parcelable {
mWindowBounds,
mBoundsInWindow,
mWindowTitle,
+ mPackageName,
mMessages);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x40) != 0) {
+ if ((mBuilderFieldsSet & 0x80) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -379,10 +414,10 @@ public class ScrollCaptureResponse implements Parcelable {
}
@DataClass.Generated(
- time = 1614833185795L,
- codegenVersion = "1.0.22",
+ time = 1628630366187L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ff2d2eb3d334..aaf53ee1cfc1 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -29,6 +29,7 @@ import android.graphics.Canvas;
import android.graphics.ColorSpace;
import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
@@ -408,6 +409,20 @@ public class Surface implements Parcelable {
}
/**
+ * Returns the default size of this Surface provided by the consumer of the surface.
+ * Should only be used by the producer of the surface.
+ *
+ * @hide
+ */
+ @NonNull
+ public Point getDefaultSize() {
+ synchronized (mLock) {
+ checkNotReleasedLocked();
+ return new Point(nativeGetWidth(mNativeObject), nativeGetHeight(mNativeObject));
+ }
+ }
+
+ /**
* Gets a {@link Canvas} for drawing into this surface.
*
* After drawing into the provided {@link Canvas}, the caller must
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8143cf953f19..d6186d71331e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -164,6 +164,8 @@ public final class SurfaceControl implements Parcelable {
IBinder displayToken, long nativeSurfaceObject);
private static native void nativeSetDisplayLayerStack(long transactionObj,
IBinder displayToken, int layerStack);
+ private static native void nativeSetDisplayFlags(long transactionObj,
+ IBinder displayToken, int flags);
private static native void nativeSetDisplayProjection(long transactionObj,
IBinder displayToken, int orientation,
int l, int t, int r, int b,
@@ -550,6 +552,15 @@ public final class SurfaceControl implements Parcelable {
*/
private static final int SURFACE_OPAQUE = 0x02;
+ /* flags used with setDisplayFlags() (keep in sync with DisplayDevice.h) */
+
+ /**
+ * DisplayDevice flag: This display's transform is sent to inputflinger and used for input
+ * dispatch. This flag is used to disambiguate displays which share a layerstack.
+ * @hide
+ */
+ public static final int DISPLAY_RECEIVES_INPUT = 0x01;
+
// Display power modes.
/**
* Display power mode off: used while blanking the screen.
@@ -3169,6 +3180,17 @@ public final class SurfaceControl implements Parcelable {
/**
* @hide
*/
+ public Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+ nativeSetDisplayFlags(mNativeObject, displayToken, flags);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
public Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c1956e45653b..a4e7a43cc934 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1604,12 +1604,21 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
+ setResizeBackgroundColor(mTmpTransaction, bgColor);
+ mTmpTransaction.apply();
+ }
+
+ /**
+ * Version of {@link #setResizeBackgroundColor(int)} that allows you to provide
+ * {@link SurfaceControl.Transaction}.
+ * @hide
+ */
+ public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) {
if (mBackgroundControl == null) {
return;
}
-
mBackgroundColor = bgColor;
- updateBackgroundColor(mTmpTransaction).apply();
+ updateBackgroundColor(t);
}
@UnsupportedAppUsage
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index fc0ec4c560d4..b55ae1e7e468 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -34,8 +34,8 @@ import android.util.AttributeSet;
import android.util.Log;
/**
- * <p>A TextureView can be used to display a content stream. Such a content
- * stream can for instance be a video or an OpenGL scene. The content stream
+ * <p>A TextureView can be used to display a content stream, such as that
+ * coming from a camera preview, a video, or an OpenGL scene. The content stream
* can come from the application's process as well as a remote process.</p>
*
* <p>TextureView can only be used in a hardware accelerated window. When
@@ -43,56 +43,81 @@ import android.util.Log;
*
* <p>Unlike {@link SurfaceView}, TextureView does not create a separate
* window but behaves as a regular View. This key difference allows a
- * TextureView to be moved, transformed, animated, etc. For instance, you
- * can make a TextureView semi-translucent by calling
- * <code>myView.setAlpha(0.5f)</code>.</p>
+ * TextureView to have translucency, arbitrary rotations, and complex
+ * clipping. For example, you can make a TextureView semi-translucent by
+ * calling <code>myView.setAlpha(0.5f)</code>.</p>
+ *
+ * <p>One implication of this integration of TextureView into the view
+ * hierarchy is that it may have slower performance than
+ * SurfaceView. TextureView contents must be copied, internally, from the
+ * underlying surface into the view displaying those contents. For
+ * that reason, SurfaceView is recommended as a more general solution
+ * to problems requiring rendering to surfaces.</p>
*
* <p>Using a TextureView is simple: all you need to do is get its
* {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
- * render content. The following example demonstrates how to render the
- * camera preview into a TextureView:</p>
+ * render content. The following example demonstrates how to render a video
+ * into a TextureView:</p>
*
* <pre>
- * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
- * private Camera mCamera;
+ * public class MyActivity extends Activity implements TextureView.SurfaceTextureListener {
+ * private MediaPlayer mMediaPlayer;
* private TextureView mTextureView;
*
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
+ * mMediaPlayer = new MediaPlayer();
+ *
* mTextureView = new TextureView(this);
* mTextureView.setSurfaceTextureListener(this);
- *
* setContentView(mTextureView);
* }
*
- * public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- * mCamera = Camera.open();
- *
- * try {
- * mCamera.setPreviewTexture(surface);
- * mCamera.startPreview();
- * } catch (IOException ioe) {
- * // Something bad happened
- * }
+ * public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture,
+ * int width, int height) {
+ * AssetFileDescriptor fileDescriptor = // get file descriptor
+ * mMediaPlayer.setDataSource(fileDescriptor);
+ * mMediaPlayer.setSurface(new Surface(surfaceTexture));
+ * mMediaPlayer.prepareAsync();
+ * mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ * &#64;Override
+ * public void onPrepared(MediaPlayer mp) {
+ * mMediaPlayer.start();
+ * }
+ * });
+ * } catch (IOException e) {
+ * e.printStackTrace();
+ * }
* }
*
- * public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- * // Ignored, Camera does all the work for us
- * }
+ * &#64;Override
+ * public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture,
+ * int width, int height) {
+ * // Handle size change depending on media needs
+ * }
*
- * public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- * mCamera.stopPreview();
- * mCamera.release();
- * return true;
- * }
+ * &#64;Override
+ * public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
+ * // Release unneeded resources
+ * mMediaPlayer.stop();
+ * mMediaPlayer.release();
+ * return true;
+ * }
+ *
+ * &#64;Override
+ * public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
+ * // Invoked every time there's a new video frame
+ * }
*
- * public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- * // Invoked every time there's a new Camera preview frame
- * }
* }
* </pre>
*
+ * <p>Similarly, TextureView can supply the surface needed for GL rendering or
+ * camera previews. Camera2 APIs require the surface created by TextureView,
+ * although developers are recommended to use the CameraX APIs instead, for which
+ * PreviewView creates its own TextureView or SurfaceView internally.</p>
+ *
* <p>A TextureView's SurfaceTexture can be obtained either by invoking
* {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
* It is important to know that a SurfaceTexture is available only after the
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index d839e3532b64..bee0709556e7 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.BLASTBufferQueue;
import android.graphics.FrameInfo;
import android.graphics.HardwareRenderer;
import android.graphics.Picture;
@@ -257,6 +258,76 @@ public final class ThreadedRenderer extends HardwareRenderer {
private boolean mEnabled;
private boolean mRequested = true;
+ /**
+ * This child class exists to break ownership cycles. ViewRootImpl owns a ThreadedRenderer
+ * which owns a WebViewOverlayProvider. WebViewOverlayProvider will in turn be set as
+ * the listener for HardwareRenderer callbacks. By keeping this a child class, there are
+ * no cycles in the chain. The ThreadedRenderer will remain GC-able if any callbacks are
+ * still outstanding, which will in turn release any JNI references to WebViewOverlayProvider.
+ */
+ private static final class WebViewOverlayProvider implements
+ PrepareSurfaceControlForWebviewCallback, ASurfaceTransactionCallback {
+ private static final boolean sOverlaysAreEnabled =
+ HardwareRenderer.isWebViewOverlaysEnabled();
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+ private boolean mHasWebViewOverlays = false;
+ private BLASTBufferQueue mBLASTBufferQueue;
+ private SurfaceControl mSurfaceControl;
+
+ public boolean setSurfaceControlOpaque(boolean opaque) {
+ synchronized (this) {
+ if (mHasWebViewOverlays) return false;
+ mTransaction.setOpaque(mSurfaceControl, opaque).apply();
+ }
+ return opaque;
+ }
+
+ public boolean shouldEnableOverlaySupport() {
+ return sOverlaysAreEnabled && mSurfaceControl != null && mBLASTBufferQueue != null;
+ }
+
+ public void setSurfaceControl(SurfaceControl surfaceControl) {
+ synchronized (this) {
+ mSurfaceControl = surfaceControl;
+ if (mSurfaceControl != null && mHasWebViewOverlays) {
+ mTransaction.setOpaque(surfaceControl, false).apply();
+ }
+ }
+ }
+
+ public void setBLASTBufferQueue(BLASTBufferQueue bufferQueue) {
+ synchronized (this) {
+ mBLASTBufferQueue = bufferQueue;
+ }
+ }
+
+ @Override
+ public void prepare() {
+ synchronized (this) {
+ mHasWebViewOverlays = true;
+ if (mSurfaceControl != null) {
+ mTransaction.setOpaque(mSurfaceControl, false).apply();
+ }
+ }
+ }
+
+ @Override
+ public boolean onMergeTransaction(long nativeTransactionObj,
+ long aSurfaceControlNativeObj, long frameNr) {
+ synchronized (this) {
+ if (mBLASTBufferQueue == null) {
+ return false;
+ } else {
+ mBLASTBufferQueue.mergeWithNextTransaction(nativeTransactionObj, frameNr);
+ return true;
+ }
+ }
+ }
+ }
+
+ private final WebViewOverlayProvider mWebViewOverlayProvider = new WebViewOverlayProvider();
+ private boolean mWebViewOverlaysEnabled = false;
+
@Nullable
private ArrayList<FrameDrawingCallback> mNextRtFrameCallbacks;
@@ -455,6 +526,56 @@ public final class ThreadedRenderer extends HardwareRenderer {
}
/**
+ * Whether or not the renderer owns the SurfaceControl's opacity. If true, use
+ * {@link #setSurfaceControlOpaque(boolean)} to update the opacity
+ */
+ public boolean rendererOwnsSurfaceControlOpacity() {
+ return mWebViewOverlayProvider.mSurfaceControl != null;
+ }
+
+ /**
+ * Sets the SurfaceControl's opacity that this HardwareRenderer is rendering onto. The renderer
+ * may opt to override the opacity, and will return the value that is ultimately set
+ *
+ * @return true if the surface is opaque, false otherwise
+ *
+ * @hide
+ */
+ public boolean setSurfaceControlOpaque(boolean opaque) {
+ return mWebViewOverlayProvider.setSurfaceControlOpaque(opaque);
+ }
+
+ private void updateWebViewOverlayCallbacks() {
+ boolean shouldEnable = mWebViewOverlayProvider.shouldEnableOverlaySupport();
+ if (shouldEnable != mWebViewOverlaysEnabled) {
+ mWebViewOverlaysEnabled = shouldEnable;
+ if (shouldEnable) {
+ setASurfaceTransactionCallback(mWebViewOverlayProvider);
+ setPrepareSurfaceControlForWebviewCallback(mWebViewOverlayProvider);
+ } else {
+ setASurfaceTransactionCallback(null);
+ setPrepareSurfaceControlForWebviewCallback(null);
+ }
+ }
+ }
+
+ @Override
+ public void setSurfaceControl(@Nullable SurfaceControl surfaceControl) {
+ super.setSurfaceControl(surfaceControl);
+ mWebViewOverlayProvider.setSurfaceControl(surfaceControl);
+ updateWebViewOverlayCallbacks();
+ }
+
+ /**
+ * Sets the BLASTBufferQueue being used for rendering. This is required to be specified
+ * for WebView overlay support
+ */
+ public void setBlastBufferQueue(@Nullable BLASTBufferQueue blastBufferQueue) {
+ mWebViewOverlayProvider.setBLASTBufferQueue(blastBufferQueue);
+ updateWebViewOverlayCallbacks();
+ }
+
+ /**
* Updates the light position based on the position of the window.
*
* @param attachInfo Information about the window.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f4223fb467f5..ffc98d2c8e90 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6432,7 +6432,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@Override
public String toString() {
- StringBuilder out = new StringBuilder(128);
+ StringBuilder out = new StringBuilder(256);
out.append(getClass().getName());
out.append('{');
out.append(Integer.toHexString(System.identityHashCode(this)));
@@ -19816,6 +19816,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Check if this view can be scrolled horizontally in a certain direction.
*
+ * <p>This is without regard to whether the view is enabled or not, or if it will scroll
+ * in response to user input or not.
+ *
* @param direction Negative to check scrolling left, positive to check scrolling right.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
@@ -19833,6 +19836,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Check if this view can be scrolled vertically in a certain direction.
*
+ * <p>This is without regard to whether the view is enabled or not, or if it will scroll
+ * in response to user input or not.
+ *
* @param direction Negative to check scrolling up, positive to check scrolling down.
* @return true if this view can be scrolled in the specified direction, false otherwise.
*/
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0a3d0da6da1e..495edab57abd 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -600,6 +600,8 @@ public class ViewConfiguration {
}
/**
+ * Used for both key and motion events.
+ *
* @return the duration in milliseconds before a press turns into
* a long press
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 679da313edcc..488f7c3fca8b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2723,7 +2723,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
continue;
}
childWithAccessibilityFocus = null;
- i = childrenCount - 1;
+ i = childrenCount;
}
if (!child.canReceivePointerEvents()
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3550a31f9038..0280be4f720b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -151,7 +151,6 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
@@ -192,6 +191,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
@@ -253,11 +253,11 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
- * Whether or not to report end-to-end input latency. Disabled temporarily as a
+ * Whether or not to report end-to-end input latency. Can be disabled temporarily as a
* risk mitigation against potential jank caused by acquiring a weak reference
- * per frame
+ * per frame.
*/
- private static final boolean ENABLE_INPUT_LATENCY_TRACKING = false;
+ private static final boolean ENABLE_INPUT_LATENCY_TRACKING = true;
/**
* Set this system property to true to force the view hierarchy to render
@@ -291,6 +291,21 @@ public final class ViewRootImpl implements ViewParent,
*/
private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
+ /**
+ * If set to {@code true}, the new logic to layout system bars as normal window and to use
+ * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will
+ * be applied.
+ */
+ private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets";
+
+ /**
+ * A flag to indicate to use the new generalized insets window logic, or the old hard-coded
+ * insets window layout logic.
+ * {@hide}
+ */
+ public static final boolean INSETS_LAYOUT_GENERALIZATION =
+ SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, true);
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -388,6 +403,8 @@ public final class ViewRootImpl implements ViewParent,
View mView;
View mAccessibilityFocusedHost;
+ // Accessibility-focused virtual view. The bounds and sourceNodeId of
+ // mAccessibilityFocusedVirtualView is up-to-date while other fields may be stale.
AccessibilityNodeInfo mAccessibilityFocusedVirtualView;
// True if the window currently has pointer capture enabled.
@@ -468,9 +485,6 @@ public final class ViewRootImpl implements ViewParent,
protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
- // Set to true if mSurfaceControl is used for Webview Overlay
- private boolean mIsForWebviewOverlay;
-
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -1118,7 +1132,7 @@ public final class ViewRootImpl implements ViewParent,
controlInsetsForCompatibility(mWindowAttributes);
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
- mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
+ mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);
if (mTranslator != null) {
mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
@@ -1369,42 +1383,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- /**
- * Register a callback to be executed when Webview overlay needs to merge a transaction.
- * This callback will be executed on RenderThread worker thread, and released inside native code
- * when CanvasContext is destroyed.
- */
- private void addASurfaceTransactionCallback() {
- HardwareRenderer.ASurfaceTransactionCallback callback = (nativeTransactionObj,
- nativeSurfaceControlObj,
- frameNr) -> {
- if (mBlastBufferQueue == null) {
- return false;
- } else {
- mBlastBufferQueue.mergeWithNextTransaction(nativeTransactionObj, frameNr);
- return true;
- }
- };
- mAttachInfo.mThreadedRenderer.setASurfaceTransactionCallback(callback);
- }
-
- /**
- * Register a callback to be executed when Webview overlay needs a surface control.
- * This callback will be executed on RenderThread worker thread, and released inside native code
- * when CanvasContext is destroyed.
- */
- private void addPrepareSurfaceControlForWebviewCallback() {
- HardwareRenderer.PrepareSurfaceControlForWebviewCallback callback = () -> {
- // make mSurfaceControl transparent, so child surface controls are visible
- if (mIsForWebviewOverlay) return;
- synchronized (ViewRootImpl.this) {
- mIsForWebviewOverlay = true;
- }
- mTransaction.setOpaque(mSurfaceControl, false).apply();
- };
- mAttachInfo.mThreadedRenderer.setPrepareSurfaceControlForWebviewCallback(callback);
- }
-
@UnsupportedAppUsage
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
@@ -1449,11 +1427,8 @@ public final class ViewRootImpl implements ViewParent,
if (mHardwareRendererObserver != null) {
mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
}
- if (HardwareRenderer.isWebViewOverlaysEnabled()) {
- addPrepareSurfaceControlForWebviewCallback();
- addASurfaceTransactionCallback();
- }
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
+ mAttachInfo.mThreadedRenderer.setBlastBufferQueue(mBlastBufferQueue);
}
}
}
@@ -2024,6 +1999,7 @@ public final class ViewRootImpl implements ViewParent,
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.setSurfaceControl(null);
+ mAttachInfo.mThreadedRenderer.setBlastBufferQueue(null);
}
}
@@ -2502,6 +2478,14 @@ public final class ViewRootImpl implements ViewParent,
|| lp.type == TYPE_VOLUME_OVERLAY;
}
+ private Rect getWindowBoundsInsetSystemBars() {
+ final Rect bounds = new Rect(
+ mContext.getResources().getConfiguration().windowConfiguration.getBounds());
+ bounds.inset(mInsetsController.getState().calculateInsets(
+ bounds, Type.systemBars(), false /* ignoreVisibility */));
+ return bounds;
+ }
+
int dipToPx(int dip) {
final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
return (int) (displayMetrics.density * dip + 0.5f);
@@ -2588,8 +2572,9 @@ public final class ViewRootImpl implements ViewParent,
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
// For wrap content, we have to remeasure later on anyways. Use size consistent with
// below so we get best use of the measure cache.
- desiredWindowWidth = dipToPx(config.screenWidthDp);
- desiredWindowHeight = dipToPx(config.screenHeightDp);
+ final Rect bounds = getWindowBoundsInsetSystemBars();
+ desiredWindowWidth = bounds.width();
+ desiredWindowHeight = bounds.height();
} else {
// After addToDisplay, the frame contains the frameHint from window manager, which
// for most windows is going to be the same size as the result of relayoutWindow.
@@ -2666,9 +2651,9 @@ public final class ViewRootImpl implements ViewParent,
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
- Configuration config = res.getConfiguration();
- desiredWindowWidth = dipToPx(config.screenWidthDp);
- desiredWindowHeight = dipToPx(config.screenHeightDp);
+ final Rect bounds = getWindowBoundsInsetSystemBars();
+ desiredWindowWidth = bounds.width();
+ desiredWindowHeight = bounds.height();
}
}
}
@@ -4813,6 +4798,9 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Get accessibility-focused virtual view. The bounds and sourceNodeId of the returned node is
+ * up-to-date while other fields may be stale.
+ *
* @hide
*/
@UnsupportedAppUsage
@@ -7794,11 +7782,8 @@ public final class ViewRootImpl implements ViewParent,
}
}
if (mAttachInfo.mThreadedRenderer != null) {
- if (HardwareRenderer.isWebViewOverlaysEnabled()) {
- addPrepareSurfaceControlForWebviewCallback();
- addASurfaceTransactionCallback();
- }
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
+ mAttachInfo.mThreadedRenderer.setBlastBufferQueue(mBlastBufferQueue);
}
} else {
destroySurface();
@@ -7844,11 +7829,10 @@ public final class ViewRootImpl implements ViewParent,
return;
}
- synchronized (this) {
- if (mIsForWebviewOverlay) {
- mIsSurfaceOpaque = false;
- return;
- }
+ final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
+ if (renderer != null && renderer.rendererOwnsSurfaceControlOpacity()) {
+ opaque = renderer.setSurfaceControlOpaque(opaque);
+ } else {
mTransaction.setOpaque(mSurfaceControl, opaque).apply();
}
@@ -9493,6 +9477,7 @@ public final class ViewRootImpl implements ViewParent,
ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder();
response.setWindowTitle(getTitle().toString());
+ response.setPackageName(mContext.getPackageName());
StringWriter writer = new StringWriter();
IndentingPrintWriter pw = new IndentingPrintWriter(writer);
@@ -9907,7 +9892,10 @@ public final class ViewRootImpl implements ViewParent,
if (!mUseMTRenderer) {
return;
}
- mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
+ // Only wait if it will report next draw.
+ if (mReportNextDraw) {
+ mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
+ }
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw);
}
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index ce882da1a6da..efffa2b05a1e 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -149,10 +149,10 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host {
}
@Override
- public void onInsetsModified(InsetsState insetsState) {
+ public void updateRequestedVisibilities(InsetsVisibilities vis) {
try {
if (mViewRoot.mAdded) {
- mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState);
+ mViewRoot.mWindowSession.updateRequestedVisibilities(mViewRoot.mWindow, vis);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to call insetsModified", e);
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 57dfc62b4f8f..1edbbba09eef 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -16,6 +16,7 @@
package android.view;
+import android.app.ActivityTaskManager;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Parcel;
@@ -51,6 +52,7 @@ public class WindowInfo implements Parcelable {
public boolean inPictureInPicture;
public boolean hasFlagWatchOutsideTouch;
public int displayId = Display.INVALID_DISPLAY;
+ public int taskId = ActivityTaskManager.INVALID_TASK_ID;
private WindowInfo() {
/* do nothing - hide constructor */
@@ -67,6 +69,7 @@ public class WindowInfo implements Parcelable {
public static WindowInfo obtain(WindowInfo other) {
WindowInfo window = obtain();
window.displayId = other.displayId;
+ window.taskId = other.taskId;
window.type = other.type;
window.layer = other.layer;
window.token = other.token;
@@ -103,6 +106,7 @@ public class WindowInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(displayId);
+ parcel.writeInt(taskId);
parcel.writeInt(type);
parcel.writeInt(layer);
parcel.writeStrongBinder(token);
@@ -129,6 +133,7 @@ public class WindowInfo implements Parcelable {
builder.append("WindowInfo[");
builder.append("title=").append(title);
builder.append(", displayId=").append(displayId);
+ builder.append(", taskId=").append(taskId);
builder.append(", type=").append(type);
builder.append(", layer=").append(layer);
builder.append(", token=").append(token);
@@ -146,6 +151,7 @@ public class WindowInfo implements Parcelable {
private void initFromParcel(Parcel parcel) {
displayId = parcel.readInt();
+ taskId = parcel.readInt();
type = parcel.readInt();
layer = parcel.readInt();
token = parcel.readStrongBinder();
@@ -169,6 +175,7 @@ public class WindowInfo implements Parcelable {
private void clear() {
displayId = Display.INVALID_DISPLAY;
+ taskId = ActivityTaskManager.INVALID_TASK_ID;
type = 0;
layer = 0;
token = null;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 55beae0f7b3d..47546848dcf3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -97,6 +97,7 @@ import android.content.ClipData;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
@@ -380,8 +381,11 @@ public interface WindowManager extends ViewManager {
int TRANSIT_CHANGE = 6;
/**
* The keyguard was visible and has been dismissed.
+ * @deprecated use {@link #TRANSIT_TO_BACK} + {@link #TRANSIT_FLAG_KEYGUARD_GOING_AWAY} for
+ * keyguard going away with Shell transition.
* @hide
*/
+ @Deprecated
int TRANSIT_KEYGUARD_GOING_AWAY = 7;
/**
* A window is appearing above a locked keyguard.
@@ -394,6 +398,16 @@ public interface WindowManager extends ViewManager {
*/
int TRANSIT_KEYGUARD_UNOCCLUDE = 9;
/**
+ * A window is starting to enter PiP.
+ * @hide
+ */
+ int TRANSIT_PIP = 10;
+ /**
+ * The screen is turning on.
+ * @hide
+ */
+ int TRANSIT_WAKE = 11;
+ /**
* The first slot for custom transition types. Callers (like Shell) can make use of custom
* transition types for dealing with special cases. These types are effectively ignored by
* Core and will just be passed along as part of TransitionInfo objects. An example is
@@ -402,7 +416,7 @@ public interface WindowManager extends ViewManager {
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 10;
+ int TRANSIT_FIRST_CUSTOM = 12;
/**
* @hide
@@ -418,6 +432,8 @@ public interface WindowManager extends ViewManager {
TRANSIT_KEYGUARD_GOING_AWAY,
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_KEYGUARD_UNOCCLUDE,
+ TRANSIT_PIP,
+ TRANSIT_WAKE,
TRANSIT_FIRST_CUSTOM
})
@Retention(RetentionPolicy.SOURCE)
@@ -467,6 +483,19 @@ public interface WindowManager extends ViewManager {
int TRANSIT_FLAG_KEYGUARD_LOCKED = 0x40;
/**
+ * Transition flag: Indicates that this transition is for recents animation.
+ * TODO(b/188669821): Remove once special-case logic moves to shell.
+ * @hide
+ */
+ int TRANSIT_FLAG_IS_RECENTS = 0x80;
+
+ /**
+ * Transition flag: Indicates that keyguard should go away with this transition.
+ * @hide
+ */
+ int TRANSIT_FLAG_KEYGUARD_GOING_AWAY = 0x100;
+
+ /**
* @hide
*/
@IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -476,7 +505,9 @@ public interface WindowManager extends ViewManager {
TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION,
TRANSIT_FLAG_APP_CRASHED,
TRANSIT_FLAG_OPEN_BEHIND,
- TRANSIT_FLAG_KEYGUARD_LOCKED
+ TRANSIT_FLAG_KEYGUARD_LOCKED,
+ TRANSIT_FLAG_IS_RECENTS,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY
})
@Retention(RetentionPolicy.SOURCE)
@interface TransitionFlags {}
@@ -922,6 +953,8 @@ public interface WindowManager extends ViewManager {
case TRANSIT_KEYGUARD_GOING_AWAY: return "KEYGUARD_GOING_AWAY";
case TRANSIT_KEYGUARD_OCCLUDE: return "KEYGUARD_OCCLUDE";
case TRANSIT_KEYGUARD_UNOCCLUDE: return "KEYGUARD_UNOCCLUDE";
+ case TRANSIT_PIP: return "PIP";
+ case TRANSIT_WAKE: return "WAKE";
case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
default:
if (type > TRANSIT_FIRST_CUSTOM) {
@@ -2072,6 +2105,7 @@ public interface WindowManager extends ViewManager {
* {@hide}
*/
@UnsupportedAppUsage
+ @TestApi
public static final int FLAG_SLIPPERY = 0x20000000;
/**
@@ -3468,6 +3502,22 @@ public interface WindowManager extends ViewManager {
public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
/**
+ * If specified, the insets provided by this window will be our window frame minus the
+ * insets specified by providedInternalInsets.
+ *
+ * @hide
+ */
+ public Insets providedInternalInsets = Insets.NONE;
+
+ /**
+ * {@link LayoutParams} to be applied to the window when layout with a assigned rotation.
+ * This will make layout during rotation change smoothly.
+ *
+ * @hide
+ */
+ public LayoutParams[] paramsForRotation;
+
+ /**
* Specifies types of insets that this window should avoid overlapping during layout.
*
* @param types which {@link WindowInsets.Type}s of insets that this window should avoid.
@@ -3566,6 +3616,18 @@ public interface WindowManager extends ViewManager {
return mFitInsetsIgnoringVisibility;
}
+ private void checkNonRecursiveParams() {
+ if (paramsForRotation == null) {
+ return;
+ }
+ for (int i = paramsForRotation.length - 1; i >= 0; i--) {
+ if (paramsForRotation[i].paramsForRotation != null) {
+ throw new IllegalArgumentException(
+ "Params cannot contain params recursively.");
+ }
+ }
+ }
+
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
@@ -3820,6 +3882,14 @@ public interface WindowManager extends ViewManager {
} else {
out.writeInt(0);
}
+ providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */);
+ if (paramsForRotation != null) {
+ checkNonRecursiveParams();
+ out.writeInt(paramsForRotation.length);
+ out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */);
+ } else {
+ out.writeInt(0);
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3891,6 +3961,12 @@ public interface WindowManager extends ViewManager {
providesInsetsTypes = new int[insetsTypesLength];
in.readIntArray(providesInsetsTypes);
}
+ providedInternalInsets = Insets.CREATOR.createFromParcel(in);
+ int paramsForRotationLength = in.readInt();
+ if (paramsForRotationLength > 0) {
+ paramsForRotation = new LayoutParams[paramsForRotationLength];
+ in.readTypedArray(paramsForRotation, LayoutParams.CREATOR);
+ }
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -4187,6 +4263,17 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
+ if (!providedInternalInsets.equals(o.providedInternalInsets)) {
+ providedInternalInsets = o.providedInternalInsets;
+ changes |= LAYOUT_CHANGED;
+ }
+
+ if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
+ paramsForRotation = o.paramsForRotation;
+ checkNonRecursiveParams();
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -4382,6 +4469,18 @@ public interface WindowManager extends ViewManager {
sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
}
}
+ if (!providedInternalInsets.equals(Insets.NONE)) {
+ sb.append(" providedInternalInsets=");
+ sb.append(providedInternalInsets);
+ }
+ if (paramsForRotation != null && paramsForRotation.length != 0) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" paramsForRotation=");
+ for (int i = 0; i < paramsForRotation.length; ++i) {
+ if (i > 0) sb.append(' ');
+ sb.append(paramsForRotation[i].toString());
+ }
+ }
sb.append('}');
return sb.toString();
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f800991944ac..a2d3e3447354 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -19,9 +19,12 @@ package android.view;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.window.WindowProviderService.isWindowProviderService;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -36,7 +39,9 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.window.WindowContext;
+import android.window.WindowProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
@@ -145,6 +150,7 @@ public final class WindowManagerImpl implements WindowManager {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
+ assertWindowContextTypeMatches(wparams.type);
// Only use the default token if we don't have a parent window and a token.
if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
wparams.token = mDefaultToken;
@@ -152,6 +158,34 @@ public final class WindowManagerImpl implements WindowManager {
wparams.mWindowContextToken = mWindowContextToken;
}
+ private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
+ if (!(mContext instanceof WindowProvider)) {
+ return;
+ }
+ // Don't need to check sub-window type because sub window should be allowed to be attached
+ // to the parent window.
+ if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
+ return;
+ }
+ final WindowProvider windowProvider = (WindowProvider) mContext;
+ if (windowProvider.getWindowType() == windowType) {
+ return;
+ }
+ IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
+ + " Window Context's window type is " + windowProvider.getWindowType()
+ + ", while LayoutParams' type is set to " + windowType + "."
+ + " Please create another Window Context via"
+ + " createWindowContext(getDisplay(), " + windowType + ", null)"
+ + " to add window with type:" + windowType);
+ if (!isWindowProviderService(windowProvider.getWindowContextOptions())) {
+ throw exception;
+ }
+ // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple
+ // window types. Usually it's because the Window Context is a WindowProviderService.
+ StrictMode.onIncorrectContextUsed("WindowContext's window type must"
+ + " match type in WindowManager.LayoutParams", exception);
+ }
+
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index bbef3e6aeefa..e634d601c1f1 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -209,4 +209,41 @@ public interface WindowManagerPolicyConstants {
return Integer.toString(why);
}
}
+
+ /**
+ * How much to multiply the policy's type layer, to reserve room
+ * for multiple windows of the same type and Z-ordering adjustment
+ * with TYPE_LAYER_OFFSET.
+ */
+ int TYPE_LAYER_MULTIPLIER = 10000;
+
+ /**
+ * Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
+ * or below others in the same layer.
+ */
+ int TYPE_LAYER_OFFSET = 1000;
+
+ /**
+ * How much to increment the layer for each window, to reserve room
+ * for effect surfaces between them.
+ */
+ int WINDOW_LAYER_MULTIPLIER = 5;
+
+ /**
+ * Animation thumbnail is as far as possible below the window above
+ * the thumbnail (or in other words as far as possible above the window
+ * below it).
+ */
+ int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
+
+ int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
+ int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
+ int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
+ int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+
+ /**
+ * Layers for screen rotation animation. We put these layers above
+ * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+ */
+ int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index ae54f51f35d1..c413a9ba7a27 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -135,7 +135,7 @@ public class WindowlessWindowManager implements IWindowSession {
*/
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility,
+ int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
@@ -181,10 +181,10 @@ public class WindowlessWindowManager implements IWindowSession {
*/
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+ int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
- return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
+ return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibilities,
outInputChannel, outInsetsState, outActiveControls);
}
@@ -454,7 +454,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public void insetsModified(android.view.IWindow window, android.view.InsetsState state) {
+ public void updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities) {
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f6d6fde6435f..40da86a2ee43 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -1298,6 +1298,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.readList(record.mText, null);
record.mSourceWindowId = parcel.readInt();
record.mSourceNodeId = parcel.readLong();
+ record.mSourceDisplayId = parcel.readInt();
record.mSealed = (parcel.readInt() == 1);
}
@@ -1364,6 +1365,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.writeList(record.mText);
parcel.writeInt(record.mSourceWindowId);
parcel.writeLong(record.mSourceNodeId);
+ parcel.writeInt(record.mSourceDisplayId);
parcel.writeInt(record.mSealed ? 1 : 0);
}
@@ -1402,6 +1404,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
if (DEBUG) {
builder.append("; SourceWindowId: 0x").append(Long.toHexString(mSourceWindowId));
builder.append("; SourceNodeId: 0x").append(Long.toHexString(mSourceNodeId));
+ builder.append("; SourceDisplayId: ").append(mSourceDisplayId);
}
for (int i = 0; i < getRecordCount(); i++) {
builder.append(" Record ").append(i).append(":");
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index dd81dd93380b..80ef6cff5570 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -16,6 +16,9 @@
package android.view.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -86,6 +89,7 @@ public final class AccessibilityInteractionClient
public static final int NO_ID = -1;
public static final String CALL_STACK = "call_stack";
+ public static final String IGNORE_CALL_STACK = "ignore_call_stack";
private static final String LOG_TAG = "AccessibilityInteractionClient";
@@ -121,6 +125,12 @@ public final class AccessibilityInteractionClient
private volatile int mInteractionId = -1;
private volatile int mCallingUid = Process.INVALID_UID;
+ // call stack for IAccessibilityInteractionConnectionCallback APIs. These callback APIs are
+ // shared by multiple requests APIs in IAccessibilityServiceConnection. To correctly log the
+ // request API which triggers the callback, we log trace entries for callback after the
+ // request API thread waiting for the callback returns. To log the correct callback stack in
+ // the request API thread, we save the callback stack in this member variables.
+ private List<StackTraceElement> mCallStackOfCallback;
private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
@@ -307,18 +317,30 @@ public final class AccessibilityInteractionClient
if (DEBUG) {
Log.i(LOG_TAG, "Window cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";bypassCache=false");
+ }
return window;
}
if (DEBUG) {
Log.i(LOG_TAG, "Window cache miss");
}
}
+
final long identityToken = Binder.clearCallingIdentity();
try {
window = connection.getWindow(accessibilityWindowId);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindow", "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId + ";bypassCache="
+ + bypassCache);
+ }
+
if (window != null) {
if (!bypassCache) {
sAccessibilityCache.addWindow(window);
@@ -368,6 +390,10 @@ public final class AccessibilityInteractionClient
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
+ if (shouldTraceClient()) {
+ logTraceClient(
+ connection, "getWindows cache", "connectionId=" + connectionId);
+ }
return windows;
}
if (DEBUG) {
@@ -379,6 +405,9 @@ public final class AccessibilityInteractionClient
} finally {
Binder.restoreCallingIdentity(identityToken);
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
+ }
if (windows != null) {
sAccessibilityCache.setWindowsOnAllDisplays(windows);
return windows;
@@ -472,6 +501,15 @@ public final class AccessibilityInteractionClient
Log.i(LOG_TAG, "Node cache hit for "
+ idToString(accessibilityWindowId, accessibilityNodeId));
}
+ if (shouldTraceClient()) {
+ logTraceClient(connection,
+ "findAccessibilityNodeInfoByAccessibilityId cache",
+ "connectionId=" + connectionId + ";accessibilityWindowId="
+ + accessibilityWindowId + ";accessibilityNodeId="
+ + accessibilityNodeId + ";bypassCache=" + bypassCache
+ + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
return cachedInfo;
}
if (DEBUG) {
@@ -488,6 +526,14 @@ public final class AccessibilityInteractionClient
prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
}
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";bypassCache="
+ + bypassCache + ";prefetchFlags=" + prefetchFlags + ";arguments="
+ + arguments);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -500,16 +546,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
AccessibilityNodeInfo info =
getFindAccessibilityNodeInfoResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId",
- "InteractionId:" + interactionId + ";Result: " + info
- + ";connectionId=" + connectionId
- + ";accessibilityWindowId="
- + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";bypassCache=" + bypassCache
- + ";prefetchFlags=" + prefetchFlags
- + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfoByAccessibilityId",
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionId + ";Result: " + info);
}
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_MASK) != 0
&& info != null) {
@@ -571,6 +611,14 @@ public final class AccessibilityInteractionClient
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
+ + viewId);
+ }
+
packageNames = connection.findAccessibilityNodeInfosByViewId(
accessibilityWindowId, accessibilityNodeId, viewId, interactionId, this,
Thread.currentThread().getId());
@@ -581,13 +629,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId="
- + viewId);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByViewId",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ":Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -630,6 +675,12 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -643,12 +694,10 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId="
- + interactionId + ":Result: " + infos + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findAccessibilityNodeInfosByText",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + infos);
}
if (infos != null) {
finalizeAndCacheAccessibilityNodeInfos(infos, connectionId,
@@ -690,6 +739,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "findFocus",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
+ + focusType);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -703,13 +759,9 @@ public final class AccessibilityInteractionClient
if (packageNames != null) {
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "findFocus", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType="
- + focusType);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "findFocus", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
return info;
@@ -747,6 +799,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "focusSearch",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
+ + direction);
+ }
final String[] packageNames;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -761,13 +820,9 @@ public final class AccessibilityInteractionClient
AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
interactionId);
finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "focusSearch", "InteractionId=" + interactionId
- + ":Result: " + info + ";connectionId=" + connectionId
- + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";direction="
- + direction);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "focusSearch", "InteractionId=" + interactionId
+ + ";connectionId=" + connectionId + ";Result:" + info);
}
return info;
}
@@ -803,6 +858,13 @@ public final class AccessibilityInteractionClient
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
final int interactionId = mInteractionIdCounter.getAndIncrement();
+ if (shouldTraceClient()) {
+ logTraceClient(connection, "performAccessibilityAction",
+ "InteractionId:" + interactionId + "connectionId=" + connectionId
+ + ";accessibilityWindowId=" + accessibilityWindowId
+ + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + action
+ + ";arguments=" + arguments);
+ }
final boolean success;
final long identityToken = Binder.clearCallingIdentity();
try {
@@ -816,13 +878,10 @@ public final class AccessibilityInteractionClient
if (success) {
final boolean result =
getPerformAccessibilityActionResultAndClear(interactionId);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
- logTrace(connection, "performAccessibilityAction", "InteractionId="
- + interactionId + ":Result: " + result + ";connectionId="
- + connectionId + ";accessibilityWindowId=" + accessibilityWindowId
- + ";accessibilityNodeId=" + accessibilityNodeId + ";action="
- + action + ";arguments=" + arguments);
+ if (shouldTraceCallback()) {
+ logTraceCallback(connection, "performAccessibilityAction",
+ "InteractionId=" + interactionId + ";connectionId=" + connectionId
+ + ";Result: " + result);
}
return result;
}
@@ -886,6 +945,8 @@ public final class AccessibilityInteractionClient
mFindAccessibilityNodeInfoResult = info;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -936,6 +997,8 @@ public final class AccessibilityInteractionClient
}
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -975,13 +1038,15 @@ public final class AccessibilityInteractionClient
finalizeAndCacheAccessibilityNodeInfos(
infos, connectionIdWaitingForPrefetchResultCopy, false,
packageNamesForNextPrefetchResultCopy);
- if (mAccessibilityManager != null
- && mAccessibilityManager.isAccessibilityTracingEnabled()) {
+ if (shouldTraceCallback()) {
logTrace(getConnection(connectionIdWaitingForPrefetchResultCopy),
"setPrefetchAccessibilityNodeInfoResult",
- "InteractionId:" + interactionId + ";Result: " + infos
- + ";connectionId=" + connectionIdWaitingForPrefetchResultCopy,
- Binder.getCallingUid());
+ "InteractionId:" + interactionId + ";connectionId="
+ + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos,
+ Binder.getCallingUid(),
+ Arrays.asList(Thread.currentThread().getStackTrace()),
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
}
} else if (DEBUG) {
Log.w(LOG_TAG, "Prefetching for interaction with id " + interactionId + " dropped "
@@ -1013,6 +1078,8 @@ public final class AccessibilityInteractionClient
mPerformAccessibilityActionResult = succeeded;
mInteractionId = interactionId;
mCallingUid = Binder.getCallingUid();
+ mCallStackOfCallback = new ArrayList<StackTraceElement>(
+ Arrays.asList(Thread.currentThread().getStackTrace()));
}
mInstanceLock.notifyAll();
}
@@ -1222,24 +1289,47 @@ public final class AccessibilityInteractionClient
return true;
}
+ private boolean shouldTraceClient() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionClientTraceEnabled();
+ }
+
+ private boolean shouldTraceCallback() {
+ return (mAccessibilityManager != null)
+ && mAccessibilityManager.isA11yInteractionConnectionCBTraceEnabled();
+ }
+
private void logTrace(
IAccessibilityServiceConnection connection, String method, String params,
- int callingUid) {
+ int callingUid, List<StackTraceElement> callStack, HashSet<String> ignoreSet,
+ long logTypes) {
try {
Bundle b = new Bundle();
- ArrayList<StackTraceElement> callStack = new ArrayList<StackTraceElement>(
- Arrays.asList(Thread.currentThread().getStackTrace()));
- b.putSerializable(CALL_STACK, callStack);
+ b.putSerializable(CALL_STACK, new ArrayList<StackTraceElement>(callStack));
+ if (ignoreSet != null) {
+ b.putSerializable(IGNORE_CALL_STACK, ignoreSet);
+ }
connection.logTrace(SystemClock.elapsedRealtimeNanos(),
- LOG_TAG + ".callback for " + method, params, Process.myPid(),
- Thread.currentThread().getId(), callingUid, b);
+ LOG_TAG + "." + method,
+ logTypes, params, Process.myPid(), Thread.currentThread().getId(),
+ callingUid, b);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to log trace. " + e);
}
}
- private void logTrace(
+ private void logTraceCallback(
+ IAccessibilityServiceConnection connection, String method, String params) {
+ logTrace(connection, method + " callback", params, mCallingUid, mCallStackOfCallback,
+ new HashSet<String>(Arrays.asList("getStackTrace")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK);
+ }
+
+ private void logTraceClient(
IAccessibilityServiceConnection connection, String method, String params) {
- logTrace(connection, method, params, mCallingUid);
+ logTrace(connection, method, params, Binder.getCallingUid(),
+ Arrays.asList(Thread.currentThread().getStackTrace()),
+ new HashSet<String>(Arrays.asList("getStackTrace", "logTraceClient")),
+ FLAGS_ACCESSIBILITY_INTERACTION_CLIENT);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index f9cdbd322c26..17fad7e57de7 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -111,7 +111,13 @@ public final class AccessibilityManager {
public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010;
/** @hide */
- public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020;
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED = 0x00000100;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED = 0x00000200;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED = 0x00000400;
+ /** @hide */
+ public static final int STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED = 0x00000800;
/** @hide */
public static final int DALTONIZER_DISABLED = -1;
@@ -235,8 +241,8 @@ public final class AccessibilityManager {
@UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
- // Whether accessibility tracing is enabled or not
- boolean mIsAccessibilityTracingEnabled = false;
+ // accessibility tracing state
+ int mAccessibilityTracingState = 0;
AccessibilityPolicy mAccessibilityPolicy;
@@ -1029,13 +1035,50 @@ public final class AccessibilityManager {
}
/**
- * Gets accessibility tracing enabled state.
+ * Gets accessibility interaction connection tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionConnectionTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction connection callback tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionConnectionCBTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility interaction client tracing enabled state.
+ *
+ * @hide
+ */
+ public boolean isA11yInteractionClientTraceEnabled() {
+ synchronized (mLock) {
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED) != 0);
+ }
+ }
+
+ /**
+ * Gets accessibility service tracing enabled state.
*
* @hide
*/
- public boolean isAccessibilityTracingEnabled() {
+ public boolean isA11yServiceTraceEnabled() {
synchronized (mLock) {
- return mIsAccessibilityTracingEnabled;
+ return ((mAccessibilityTracingState
+ & STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED) != 0);
}
}
@@ -1233,8 +1276,6 @@ public final class AccessibilityManager {
(stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
final boolean highTextContrastEnabled =
(stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
- final boolean accessibilityTracingEnabled =
- (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0;
final boolean wasEnabled = isEnabled();
final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
@@ -1257,7 +1298,7 @@ public final class AccessibilityManager {
notifyHighTextContrastStateChanged();
}
- updateAccessibilityTracingState(accessibilityTracingEnabled);
+ updateAccessibilityTracingState(stateFlags);
}
/**
@@ -1715,11 +1756,11 @@ public final class AccessibilityManager {
}
/**
- * Update mIsAccessibilityTracingEnabled.
+ * Update mAccessibilityTracingState.
*/
- private void updateAccessibilityTracingState(boolean enabled) {
+ private void updateAccessibilityTracingState(int stateFlag) {
synchronized (mLock) {
- mIsAccessibilityTracingEnabled = enabled;
+ mAccessibilityTracingState = stateFlag;
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index c3a4d3206462..f26abb2b46ef 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -20,8 +20,10 @@ import static com.android.internal.util.CollectionUtils.isEmpty;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcelable;
+import android.view.Display;
import android.view.View;
import java.util.ArrayList;
@@ -104,6 +106,7 @@ public class AccessibilityRecord {
@UnsupportedAppUsage
long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ int mSourceDisplayId = Display.INVALID_DISPLAY;
CharSequence mClassName;
CharSequence mContentDescription;
@@ -202,6 +205,27 @@ public class AccessibilityRecord {
}
/**
+ * Sets the display id.
+ *
+ * @param displayId The displayId id.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setDisplayId(int displayId) {
+ mSourceDisplayId = displayId;
+ }
+
+ /**
+ * Gets the id of the display from which the event comes from.
+ *
+ * @return The display id.
+ */
+ public int getDisplayId() {
+ return mSourceDisplayId;
+ }
+
+ /**
* Sets the window id.
*
* @param windowId The window id.
@@ -886,6 +910,7 @@ public class AccessibilityRecord {
mText.addAll(record.mText);
mSourceWindowId = record.mSourceWindowId;
mSourceNodeId = record.mSourceNodeId;
+ mSourceDisplayId = record.mSourceDisplayId;
mConnectionId = record.mConnectionId;
}
@@ -914,6 +939,7 @@ public class AccessibilityRecord {
mText.clear();
mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ mSourceDisplayId = Display.INVALID_DISPLAY;
mConnectionId = UNDEFINED;
}
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index edcb59a79c70..76e226163ca1 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -19,6 +19,7 @@ package android.view.accessibility;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Parcel;
@@ -114,6 +115,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
private int mBooleanProperties;
private int mId = UNDEFINED_WINDOW_ID;
private int mParentId = UNDEFINED_WINDOW_ID;
+ private int mTaskId = ActivityTaskManager.INVALID_TASK_ID;
private Region mRegionInScreen = new Region();
private LongArray mChildIds;
private CharSequence mTitle;
@@ -307,6 +309,28 @@ public final class AccessibilityWindowInfo implements Parcelable {
}
/**
+ * Gets the task ID.
+ *
+ * @return The task ID.
+ *
+ * @hide
+ */
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * Sets the task ID.
+ *
+ * @param taskId The task ID.
+ *
+ * @hide
+ */
+ public void setTaskId(int taskId) {
+ mTaskId = taskId;
+ }
+
+ /**
* Sets the unique id of the IAccessibilityServiceConnection over which
* this instance can send requests to the system.
*
@@ -578,6 +602,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
parcel.writeInt(mBooleanProperties);
parcel.writeInt(mId);
parcel.writeInt(mParentId);
+ parcel.writeInt(mTaskId);
mRegionInScreen.writeToParcel(parcel, flags);
parcel.writeCharSequence(mTitle);
parcel.writeLong(mAnchorId);
@@ -608,6 +633,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mBooleanProperties = other.mBooleanProperties;
mId = other.mId;
mParentId = other.mParentId;
+ mTaskId = other.mTaskId;
mRegionInScreen.set(other.mRegionInScreen);
mTitle = other.mTitle;
mAnchorId = other.mAnchorId;
@@ -631,6 +657,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mBooleanProperties = parcel.readInt();
mId = parcel.readInt();
mParentId = parcel.readInt();
+ mTaskId = parcel.readInt();
mRegionInScreen = Region.CREATOR.createFromParcel(parcel);
mTitle = parcel.readCharSequence();
mAnchorId = parcel.readLong();
@@ -676,6 +703,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
builder.append("title=").append(mTitle);
builder.append(", displayId=").append(mDisplayId);
builder.append(", id=").append(mId);
+ builder.append(", taskId=").append(mTaskId);
builder.append(", type=").append(typeToString(mType));
builder.append(", layer=").append(mLayer);
builder.append(", region=").append(mRegionInScreen);
@@ -719,6 +747,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mBooleanProperties = 0;
mId = UNDEFINED_WINDOW_ID;
mParentId = UNDEFINED_WINDOW_ID;
+ mTaskId = ActivityTaskManager.INVALID_TASK_ID;
mRegionInScreen.setEmpty();
mChildIds = null;
mConnectionId = UNDEFINED_WINDOW_ID;
diff --git a/core/java/android/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java
new file mode 100644
index 000000000000..70564446986f
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillClientController.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.Application;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A controller to manage the autofill requests for the {@link Activity}.
+ *
+ * @hide
+ */
+public final class AutofillClientController implements AutofillManager.AutofillClient {
+
+ private static final String TAG = "AutofillClientController";
+
+ private static final String LOG_TAG = "autofill_client";
+ public static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+
+ public static final String LAST_AUTOFILL_ID = "android:lastAutofillId";
+ public static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
+ public static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
+
+ /** The last autofill id that was returned from {@link #getNextAutofillId()} */
+ public int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
+
+ @NonNull
+ private final Activity mActivity;
+ /** The autofill manager. Always access via {@link #getAutofillManager()}. */
+ @Nullable
+ private AutofillManager mAutofillManager;
+ /** The autofill dropdown fill ui. */
+ @Nullable
+ private AutofillPopupWindow mAutofillPopupWindow;
+ private boolean mAutoFillResetNeeded;
+ private boolean mAutoFillIgnoreFirstResumePause;
+
+ /**
+ * AutofillClientController constructor.
+ */
+ public AutofillClientController(Activity activity) {
+ mActivity = activity;
+ }
+
+ private AutofillManager getAutofillManager() {
+ if (mAutofillManager == null) {
+ mAutofillManager = mActivity.getSystemService(AutofillManager.class);
+ }
+ return mAutofillManager;
+ }
+
+ // ------------------ Called for Activity events ------------------
+
+ /**
+ * Called when the Activity is attached.
+ */
+ public void onActivityAttached(Application application) {
+ mActivity.setAutofillOptions(application.getAutofillOptions());
+ }
+
+ /**
+ * Called when the {@link Activity#onCreate(Bundle)} is called.
+ */
+ public void onActivityCreated(@NonNull Bundle savedInstanceState) {
+ mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false);
+ mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID, View.LAST_APP_AUTOFILL_ID);
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onCreate(savedInstanceState);
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onStart()} is called.
+ */
+ public void onActivityStarted() {
+ if (mAutoFillResetNeeded) {
+ getAutofillManager().onVisibleForAutofill();
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onResume()} is called.
+ */
+ public void onActivityResumed() {
+ enableAutofillCompatibilityIfNeeded();
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ View focus = mActivity.getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // TODO(b/148815880): Bring up keyboard if resumed from inline authentication.
+ // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
+ // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
+ // window visibility after recreation is INVISIBLE in onResume() and next frame
+ // ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
+ // So we cannot call View.notifyEnterOrExited() which will do nothing
+ // when View.isVisibleToUser() is false.
+ getAutofillManager().notifyViewEntered(focus);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the Activity is performing resume.
+ */
+ public void onActivityPerformResume(boolean followedByPause) {
+ if (mAutoFillResetNeeded) {
+ // When Activity is destroyed in paused state, and relaunch activity, there will be
+ // extra onResume and onPause event, ignore the first onResume and onPause.
+ // see ActivityThread.handleRelaunchActivity()
+ mAutoFillIgnoreFirstResumePause = followedByPause;
+ if (mAutoFillIgnoreFirstResumePause && DEBUG) {
+ Slog.v(TAG, "autofill will ignore first pause when relaunching " + this);
+ }
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onPause()} is called.
+ */
+ public void onActivityPaused() {
+ if (mAutoFillResetNeeded) {
+ if (!mAutoFillIgnoreFirstResumePause) {
+ if (DEBUG) Log.v(TAG, "autofill notifyViewExited " + this);
+ View focus = mActivity.getCurrentFocus();
+ if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ getAutofillManager().notifyViewExited(focus);
+ }
+ } else {
+ // reset after first pause()
+ if (DEBUG) Log.v(TAG, "autofill got first pause " + this);
+ mAutoFillIgnoreFirstResumePause = false;
+ }
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onStop()} is called.
+ */
+ public void onActivityStopped(Intent intent, boolean changingConfigurations) {
+ if (mAutoFillResetNeeded) {
+ // If stopped without changing the configurations, the response should expire.
+ getAutofillManager().onInvisibleForAutofill(!changingConfigurations);
+ } else if (intent != null
+ && intent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
+ && intent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
+ restoreAutofillSaveUi(intent);
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onDestroy()} is called.
+ */
+ public void onActivityDestroyed() {
+ if (mActivity.isFinishing() && mAutoFillResetNeeded) {
+ getAutofillManager().onActivityFinishing();
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onSaveInstanceState(Bundle)} is called.
+ */
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
+ if (mAutoFillResetNeeded) {
+ outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
+ getAutofillManager().onSaveInstanceState(outState);
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#finish()} is called.
+ */
+ public void onActivityFinish(Intent intent) {
+ // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
+ // be restored now.
+ if (intent != null && intent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+ restoreAutofillSaveUi(intent);
+ }
+ }
+
+ /**
+ * Called when the {@link Activity#onBackPressed()} is called.
+ */
+ public void onActivityBackPressed(Intent intent) {
+ // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
+ // be restored now.
+ if (intent != null && intent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
+ restoreAutofillSaveUi(intent);
+ }
+ }
+
+ /**
+ * Called when the Activity is dispatching the result.
+ */
+ public void onDispatchActivityResult(int requestCode, int resultCode, Intent data) {
+ Intent resultData = (resultCode == Activity.RESULT_OK) ? data : null;
+ getAutofillManager().onAuthenticationResult(requestCode, resultData,
+ mActivity.getCurrentFocus());
+ }
+
+ /**
+ * Called when the {@link Activity#startActivity(Intent, Bundle)} is called.
+ */
+ public void onStartActivity(Intent startIntent, Intent cachedIntent) {
+ if (cachedIntent != null
+ && cachedIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)
+ && cachedIntent.hasExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY)) {
+ if (TextUtils.equals(mActivity.getPackageName(),
+ startIntent.resolveActivity(mActivity.getPackageManager()).getPackageName())) {
+ // Apply Autofill restore mechanism on the started activity by startActivity()
+ final IBinder token =
+ cachedIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
+ // Remove restore ability from current activity
+ cachedIntent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
+ cachedIntent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY);
+ // Put restore token
+ startIntent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token);
+ startIntent.putExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY, true);
+ }
+ }
+ }
+
+ /**
+ * Restore the autofill save ui.
+ */
+ public void restoreAutofillSaveUi(Intent intent) {
+ final IBinder token =
+ intent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
+ // Make only restore Autofill once
+ intent.removeExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN);
+ intent.removeExtra(AutofillManager.EXTRA_RESTORE_CROSS_ACTIVITY);
+ getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
+ token);
+ }
+
+ /**
+ * Enable autofill compatibility mode for the Activity if the compatibility mode is enabled
+ * for the package.
+ */
+ public void enableAutofillCompatibilityIfNeeded() {
+ if (mActivity.isAutofillCompatibilityEnabled()) {
+ final AutofillManager afm = mActivity.getSystemService(AutofillManager.class);
+ if (afm != null) {
+ afm.enableCompatibilityMode();
+ }
+ }
+ }
+
+ /**
+ * Prints autofill related information for the Activity.
+ */
+ public void dumpAutofillManager(String prefix, PrintWriter writer) {
+ final AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.dump(prefix, writer);
+ writer.print(prefix); writer.print("Autofill Compat Mode: ");
+ writer.println(mActivity.isAutofillCompatibilityEnabled());
+ } else {
+ writer.print(prefix); writer.println("No AutofillManager");
+ }
+ }
+
+ /**
+ * Returns the next autofill ID that is unique in the activity
+ *
+ * <p>All IDs will be bigger than {@link View#LAST_APP_AUTOFILL_ID}. All IDs returned
+ * will be unique.
+ */
+ public int getNextAutofillId() {
+ if (mLastAutofillId == Integer.MAX_VALUE - 1) {
+ mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
+ }
+
+ mLastAutofillId++;
+
+ return mLastAutofillId;
+ }
+
+ // ------------------ AutofillClient implementation ------------------
+
+ @Override
+ public AutofillId autofillClientGetNextAutofillId() {
+ return new AutofillId(getNextAutofillId());
+ }
+
+ @Override
+ public boolean autofillClientIsCompatibilityModeEnabled() {
+ return mActivity.isAutofillCompatibilityEnabled();
+ }
+
+ @Override
+ public boolean autofillClientIsVisibleForAutofill() {
+ return !mActivity.isVisibleForAutofill();
+ }
+
+ @Override
+ public ComponentName autofillClientGetComponentName() {
+ return mActivity.getComponentName();
+ }
+
+ @Override
+ public IBinder autofillClientGetActivityToken() {
+ return mActivity.getActivityToken();
+ }
+
+ @Override
+ public boolean[] autofillClientGetViewVisibility(AutofillId[] autofillIds) {
+ final int autofillIdCount = autofillIds.length;
+ final boolean[] visible = new boolean[autofillIdCount];
+ for (int i = 0; i < autofillIdCount; i++) {
+ final AutofillId autofillId = autofillIds[i];
+ final View view = autofillClientFindViewByAutofillIdTraversal(autofillId);
+ if (view != null) {
+ if (!autofillId.isVirtualInt()) {
+ visible[i] = view.isVisibleToUser();
+ } else {
+ visible[i] = view.isVisibleToUserForAutofill(autofillId.getVirtualChildIntId());
+ }
+ }
+ }
+ if (android.view.autofill.Helper.sVerbose) {
+ Log.v(TAG, "autofillClientGetViewVisibility(): " + Arrays.toString(visible));
+ }
+ return visible;
+ }
+
+ @Override
+ public View autofillClientFindViewByAccessibilityIdTraversal(int viewId, int windowId) {
+ final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance()
+ .getRootViews(mActivity.getActivityToken());
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+ if (rootView != null && rootView.getAccessibilityWindowId() == windowId) {
+ final View view = rootView.findViewByAccessibilityIdTraversal(viewId);
+ if (view != null) {
+ return view;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
+ final ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+
+ if (rootView != null) {
+ final View view = rootView.findViewByAutofillIdTraversal(autofillId.getViewId());
+ if (view != null) {
+ return view;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public View[] autofillClientFindViewsByAutofillIdTraversal(AutofillId[] autofillIds) {
+ final View[] views = new View[autofillIds.length];
+ final ArrayList<ViewRootImpl> roots =
+ WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+
+ for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
+ final View rootView = roots.get(rootNum).getView();
+
+ if (rootView != null) {
+ final int viewCount = autofillIds.length;
+ for (int viewNum = 0; viewNum < viewCount; viewNum++) {
+ if (views[viewNum] == null) {
+ views[viewNum] = rootView.findViewByAutofillIdTraversal(
+ autofillIds[viewNum].getViewId());
+ }
+ }
+ }
+ }
+ return views;
+ }
+
+ @Override
+ public boolean autofillClientIsFillUiShowing() {
+ return mAutofillPopupWindow != null && mAutofillPopupWindow.isShowing();
+ }
+
+ @Override
+ public boolean autofillClientRequestHideFillUi() {
+ if (mAutofillPopupWindow == null) {
+ return false;
+ }
+ mAutofillPopupWindow.dismiss();
+ mAutofillPopupWindow = null;
+ return true;
+ }
+
+ @Override
+ public boolean autofillClientRequestShowFillUi(@NonNull View anchor, int width,
+ int height, @Nullable Rect anchorBounds, IAutofillWindowPresenter presenter) {
+ final boolean wasShowing;
+
+ if (mAutofillPopupWindow == null) {
+ wasShowing = false;
+ mAutofillPopupWindow = new AutofillPopupWindow(presenter);
+ } else {
+ wasShowing = mAutofillPopupWindow.isShowing();
+ }
+ mAutofillPopupWindow.update(anchor, 0, 0, width, height, anchorBounds);
+
+ return !wasShowing && mAutofillPopupWindow.isShowing();
+ }
+
+ @Override
+ public void autofillClientDispatchUnhandledKey(View anchor, KeyEvent keyEvent) {
+ ViewRootImpl rootImpl = anchor.getViewRootImpl();
+ if (rootImpl != null) {
+ // don't care if anchorView is current focus, for example a custom view may only receive
+ // touchEvent, not focusable but can still trigger autofill window. The Key handling
+ // might be inside parent of the custom view.
+ rootImpl.dispatchKeyFromAutofill(keyEvent);
+ }
+ }
+
+ @Override
+ public boolean isDisablingEnterExitEventForAutofill() {
+ return mAutoFillIgnoreFirstResumePause || !mActivity.isResumed();
+ }
+
+ @Override
+ public void autofillClientResetableStateAvailable() {
+ mAutoFillResetNeeded = true;
+ }
+
+ @Override
+ public void autofillClientRunOnUiThread(Runnable action) {
+ mActivity.runOnUiThread(action);
+ }
+
+ @Override
+ public void autofillClientAuthenticate(int authenticationId, IntentSender intent,
+ Intent fillInIntent, boolean authenticateInline) {
+ try {
+ mActivity.startIntentSenderForResult(intent, AUTO_FILL_AUTH_WHO_PREFIX,
+ authenticationId, fillInIntent, 0, 0, null);
+ } catch (IntentSender.SendIntentException e) {
+ Log.e(TAG, "authenticate() failed for intent:" + intent, e);
+ }
+ }
+}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index d0651472bd16..11220561b00c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -25,6 +25,7 @@ import static android.view.autofill.Helper.sVerbose;
import static android.view.autofill.Helper.toList;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -45,16 +46,21 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
import android.service.autofill.FillEventHistory;
+import android.service.autofill.IFillCallback;
import android.service.autofill.UserData;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -74,6 +80,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@@ -83,7 +90,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.SyncResultReceiver;
import org.xmlpull.v1.XmlPullParserException;
@@ -99,6 +105,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
import sun.misc.Cleaner;
@@ -167,6 +174,12 @@ import sun.misc.Cleaner;
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
+ * <p>There is another choice for the application to provide it's datasets to the Autofill framework
+ * by setting an {@link AutofillRequestCallback} through
+ * {@link #setAutofillRequestCallback(Executor, AutofillRequestCallback)}. The application can use
+ * its callback instead of the default {@link AutofillService}. See
+ * {@link AutofillRequestCallback} for more details.
+ *
* <h3 id="additional-notes">Additional notes</h3>
*
* <p>It is safe to call <code>AutofillManager</code> methods from any thread.
@@ -292,6 +305,7 @@ public final class AutofillManager {
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY = 0x8;
+ /** @hide */ public static final int FLAG_ENABLED_CLIENT_SUGGESTIONS = 0x20;
// NOTE: flag below is used by the session start receiver only, hence it can have values above
/** @hide */ public static final int RECEIVER_FLAG_SESSION_FOR_AUGMENTED_AUTOFILL_ONLY = 0x1;
@@ -592,6 +606,11 @@ public final class AutofillManager {
@GuardedBy("mLock")
private boolean mEnabledForAugmentedAutofillOnly;
+ @GuardedBy("mLock")
+ @Nullable private AutofillRequestCallback mAutofillRequestCallback;
+ @GuardedBy("mLock")
+ @Nullable private Executor mRequestCallbackExecutor;
+
/** @hide */
public interface AutofillClient {
/**
@@ -726,7 +745,7 @@ public final class AutofillManager {
* @hide
*/
public AutofillManager(Context context, IAutoFillManager service) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
+ mContext = Objects.requireNonNull(context, "context cannot be null");
mService = service;
mOptions = context.getAutofillOptions();
@@ -1836,6 +1855,32 @@ public final class AutofillManager {
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ /**
+ * Sets the client's suggestions callback for autofill.
+ *
+ * @see AutofillRequestCallback
+ *
+ * @param executor specifies the thread upon which the callbacks will be invoked.
+ * @param callback which handles autofill request to provide client's suggestions.
+ */
+ public void setAutofillRequestCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull AutofillRequestCallback callback) {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = executor;
+ mAutofillRequestCallback = callback;
+ }
+ }
+
+ /**
+ * clears the client's suggestions callback for autofill.
+ */
+ public void clearAutofillRequestCallback() {
+ synchronized (mLock) {
+ mRequestCallbackExecutor = null;
+ mAutofillRequestCallback = null;
+ }
+ }
+
@GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
@@ -1896,6 +1941,13 @@ public final class AutofillManager {
}
}
+ if (mAutofillRequestCallback != null) {
+ if (sDebug) {
+ Log.d(TAG, "startSession with the client suggestions provider");
+ }
+ flags |= FLAG_ENABLED_CLIENT_SUGGESTIONS;
+ }
+
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, clientActivity,
@@ -2245,6 +2297,28 @@ public final class AutofillManager {
}
}
+ private void onFillRequest(InlineSuggestionsRequest request,
+ CancellationSignal cancellationSignal, FillCallback callback) {
+ final AutofillRequestCallback autofillRequestCallback;
+ final Executor executor;
+ synchronized (mLock) {
+ autofillRequestCallback = mAutofillRequestCallback;
+ executor = mRequestCallbackExecutor;
+ }
+ if (autofillRequestCallback != null && executor != null) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() ->
+ autofillRequestCallback.onFillRequest(
+ request, cancellationSignal, callback));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ callback.onSuccess(null);
+ }
+ }
+
/** @hide */
public static final int SET_STATE_FLAG_ENABLED = 0x01;
/** @hide */
@@ -3627,6 +3701,23 @@ public final class AutofillManager {
afm.post(() -> afm.requestShowSoftInput(id));
}
}
+
+ @Override
+ public void requestFillFromClient(int id, InlineSuggestionsRequest request,
+ IFillCallback callback) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ ICancellationSignal transport = CancellationSignal.createTransport();
+ try {
+ callback.onCancellable(transport);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error requesting a cancellation", e);
+ }
+
+ afm.onFillRequest(request, CancellationSignal.fromTransport(transport),
+ new FillCallback(callback, id));
+ }
+ }
}
private static final class AugmentedAutofillManagerClient
diff --git a/core/java/android/view/autofill/AutofillRequestCallback.java b/core/java/android/view/autofill/AutofillRequestCallback.java
new file mode 100644
index 000000000000..e632a5849471
--- /dev/null
+++ b/core/java/android/view/autofill/AutofillRequestCallback.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CancellationSignal;
+import android.service.autofill.FillCallback;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * <p>This class is used to provide some input suggestions to the Autofill framework.
+ *
+ * <P>When the user is requested to input something, Autofill will try to query input suggestions
+ * for the user choosing. If the application want to provide some internal input suggestions,
+ * implements this callback and register via
+ * {@link AutofillManager#setAutofillRequestCallback(java.util.concurrent.Executor,
+ * AutofillRequestCallback)}. Autofill will callback the
+ * {@link #onFillRequest(InlineSuggestionsRequest, CancellationSignal, FillCallback)} to request
+ * input suggestions.
+ *
+ * <P>To make sure the callback to take effect, must register before the autofill session starts.
+ * If the autofill session is started, calls {@link AutofillManager#cancel()} to finish current
+ * session, and then the callback will be used at the next restarted session.
+ *
+ * <P>To create a {@link android.service.autofill.FillResponse}, application should fetch
+ * {@link AutofillId}s from its view structure. Below is an example:
+ * <pre class="prettyprint">
+ * AutofillId usernameId = findViewById(R.id.username).getAutofillId();
+ * AutofillId passwordId = findViewById(R.id.password).getAutofillId();
+ * </pre>
+ * To learn more about creating a {@link android.service.autofill.FillResponse}, read
+ * <a href="/guide/topics/text/autofill-services#fill">Fill out client views</a>.
+ *
+ * <P>To fallback to the default {@link android.service.autofill.AutofillService}, just respond
+ * a null of the {@link android.service.autofill.FillResponse}. And then Autofill will do a fill
+ * request with the default {@link android.service.autofill.AutofillService}. Or clear the callback
+ * from {@link AutofillManager} via {@link AutofillManager#clearAutofillRequestCallback()}. If the
+ * client would like to keep no suggestions for the field, respond with an empty
+ * {@link android.service.autofill.FillResponse} which has no dataset.
+ *
+ * <P>IMPORTANT: This should not be used for displaying anything other than input suggestions, or
+ * the keyboard may choose to block your app from the inline strip.
+ */
+public interface AutofillRequestCallback {
+ /**
+ * Called by the Android system to decide if a screen can be autofilled by the callback.
+ *
+ * @param inlineSuggestionsRequest the {@link InlineSuggestionsRequest request} to handle if
+ * currently inline suggestions are supported and can be displayed.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param callback object used to notify the result of the request.
+ */
+ void onFillRequest(@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
+}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 1f833f66c257..64507aac54cb 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -24,9 +24,11 @@ import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
import android.os.IBinder;
+import android.service.autofill.IFillCallback;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.KeyEvent;
import com.android.internal.os.IResultReceiver;
@@ -140,4 +142,10 @@ oneway interface IAutoFillManagerClient {
* Requests to show the soft input method if the focus is on the given id.
*/
void requestShowSoftInput(in AutofillId id);
+
+ /**
+ * Requests to determine if a screen can be autofilled by the client app.
+ */
+ void requestFillFromClient(int id, in InlineSuggestionsRequest request,
+ in IFillCallback callback);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java
index 1adef94255e0..027c8d20ccc6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureCondition.java
+++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java
@@ -23,10 +23,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.DebugUtils;
-import com.android.internal.util.Preconditions;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Defines a condition for when content capture should be allowed.
@@ -61,7 +60,7 @@ public final class ContentCaptureCondition implements Parcelable {
* the {@code LocusId} used in the {@link ContentCaptureContext}).
*/
public ContentCaptureCondition(@NonNull LocusId locusId, @Flags int flags) {
- this.mLocusId = Preconditions.checkNotNull(locusId);
+ this.mLocusId = Objects.requireNonNull(locusId);
this.mFlags = flags;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 9998fbc02d12..71b8003954e6 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -276,7 +276,7 @@ public final class ContentCaptureContext implements Parcelable {
* @param id id associated with this context.
*/
public Builder(@NonNull LocusId id) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
}
/**
@@ -291,7 +291,7 @@ public final class ContentCaptureContext implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle extras) {
- mExtras = Preconditions.checkNotNull(extras);
+ mExtras = Objects.requireNonNull(extras);
throwIfDestroyed();
return this;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ce6d034c585e..ae45c6e034e1 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -33,13 +33,12 @@ import android.util.Log;
import android.view.autofill.AutofillId;
import android.view.inputmethod.BaseInputConnection;
-import com.android.internal.util.Preconditions;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/** @hide */
@SystemApi
@@ -173,13 +172,13 @@ public final class ContentCaptureEvent implements Parcelable {
/** @hide */
public ContentCaptureEvent setAutofillId(@NonNull AutofillId id) {
- mId = Preconditions.checkNotNull(id);
+ mId = Objects.requireNonNull(id);
return this;
}
/** @hide */
public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
- mIds = Preconditions.checkNotNull(ids);
+ mIds = Objects.requireNonNull(ids);
return this;
}
@@ -189,7 +188,7 @@ public final class ContentCaptureEvent implements Parcelable {
* @hide
*/
public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (mIds == null) {
mIds = new ArrayList<>();
if (mId == null) {
@@ -253,7 +252,7 @@ public final class ContentCaptureEvent implements Parcelable {
/** @hide */
@NonNull
public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
- mNode = Preconditions.checkNotNull(node);
+ mNode = Objects.requireNonNull(node);
return this;
}
@@ -425,7 +424,7 @@ public final class ContentCaptureEvent implements Parcelable {
* @hide
*/
public void mergeEvent(@NonNull ContentCaptureEvent event) {
- Preconditions.checkNotNull(event);
+ Objects.requireNonNull(event);
final int eventType = event.getType();
if (mType != eventType) {
Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType) + ") cannot be merged "
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9241c3074ddd..8514f6fb4ccf 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -49,7 +49,6 @@ import android.view.WindowManager;
import android.view.contentcapture.ContentCaptureSession.FlushReason;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.SyncResultReceiver;
import java.io.PrintWriter;
@@ -59,6 +58,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -398,9 +398,9 @@ public final class ContentCaptureManager {
/** @hide */
public ContentCaptureManager(@NonNull Context context,
@NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) {
- mContext = Preconditions.checkNotNull(context, "context cannot be null");
- mService = Preconditions.checkNotNull(service, "service cannot be null");
- mOptions = Preconditions.checkNotNull(options, "options cannot be null");
+ mContext = Objects.requireNonNull(context, "context cannot be null");
+ mService = Objects.requireNonNull(service, "service cannot be null");
+ mOptions = Objects.requireNonNull(options, "options cannot be null");
ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel);
@@ -679,7 +679,7 @@ public final class ContentCaptureManager {
* @param request object specifying what user data should be removed.
*/
public void removeData(@NonNull DataRemovalRequest request) {
- Preconditions.checkNotNull(request);
+ Objects.requireNonNull(request);
try {
mService.removeData(request);
@@ -703,9 +703,9 @@ public final class ContentCaptureManager {
public void shareData(@NonNull DataShareRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull DataShareWriteAdapter dataShareWriteAdapter) {
- Preconditions.checkNotNull(request);
- Preconditions.checkNotNull(dataShareWriteAdapter);
- Preconditions.checkNotNull(executor);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(dataShareWriteAdapter);
+ Objects.requireNonNull(executor);
try {
mService.shareData(request,
@@ -840,9 +840,9 @@ public final class ContentCaptureManager {
private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter,
LocalDataShareAdapterResourceManager resourceManager) {
- Preconditions.checkNotNull(executor);
- Preconditions.checkNotNull(adapter);
- Preconditions.checkNotNull(resourceManager);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(adapter);
+ Objects.requireNonNull(resourceManager);
resourceManager.initializeForDelegate(this, adapter, executor);
mResourceManagerReference = new WeakReference<>(resourceManager);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index cc47f09d4e8d..955269160e49 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -41,6 +41,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Session used when notifying the Android system about events associated with views.
@@ -231,7 +232,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
// Used by ChildCOntentCaptureSession
ContentCaptureSession(@NonNull ContentCaptureContext initialContext) {
this();
- mClientContext = Preconditions.checkNotNull(initialContext);
+ mClientContext = Objects.requireNonNull(initialContext);
}
/** @hide */
@@ -362,7 +363,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param node node that has been added.
*/
public final void notifyViewAppeared(@NonNull ViewStructure node) {
- Preconditions.checkNotNull(node);
+ Objects.requireNonNull(node);
if (!isContentCaptureEnabled()) return;
if (!(node instanceof ViewNode.ViewStructureImpl)) {
@@ -383,7 +384,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param id id of the node that has been removed.
*/
public final void notifyViewDisappeared(@NonNull AutofillId id) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
internalNotifyViewDisappeared(id);
@@ -424,7 +425,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @param text new text.
*/
public final void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- Preconditions.checkNotNull(id);
+ Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
@@ -438,7 +439,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* Notifies the Intelligence Service that the insets of a view have changed.
*/
public final void notifyViewInsetsChanged(@NonNull Insets viewInsets) {
- Preconditions.checkNotNull(viewInsets);
+ Objects.requireNonNull(viewInsets);
if (!isContentCaptureEnabled()) return;
@@ -534,7 +535,7 @@ public abstract class ContentCaptureSession implements AutoCloseable {
* @throws IllegalArgumentException if the {@code parentId} is a virtual child id.
*/
public @NonNull AutofillId newAutofillId(@NonNull AutofillId hostId, long virtualChildId) {
- Preconditions.checkNotNull(hostId);
+ Objects.requireNonNull(hostId);
Preconditions.checkArgument(hostId.isNonVirtual(), "hostId cannot be virtual: %s", hostId);
return new AutofillId(hostId, virtualChildId, mId);
}
diff --git a/core/java/android/view/contentcapture/DataRemovalRequest.java b/core/java/android/view/contentcapture/DataRemovalRequest.java
index f403dacee13e..3d861137d670 100644
--- a/core/java/android/view/contentcapture/DataRemovalRequest.java
+++ b/core/java/android/view/contentcapture/DataRemovalRequest.java
@@ -29,6 +29,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Class used by apps to request the content capture service to remove data associated with
@@ -143,7 +144,7 @@ public final class DataRemovalRequest implements Parcelable {
public Builder addLocusId(@NonNull LocusId locusId, @Flags int flags) {
throwIfDestroyed();
Preconditions.checkState(!mForEverything, "Already is for everything");
- Preconditions.checkNotNull(locusId);
+ Objects.requireNonNull(locusId);
// felipeal: check flags
if (mLocusIds == null) {
diff --git a/core/java/android/view/contentcapture/DataShareRequest.java b/core/java/android/view/contentcapture/DataShareRequest.java
index 78c0ef9568ba..b2eabf113777 100644
--- a/core/java/android/view/contentcapture/DataShareRequest.java
+++ b/core/java/android/view/contentcapture/DataShareRequest.java
@@ -24,7 +24,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
-import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
/** Container class representing a request to share data with Content Capture service. */
@DataClass(
@@ -47,7 +48,7 @@ public final class DataShareRequest implements Parcelable {
/** Constructs a request to share data with the Content Capture Service. */
public DataShareRequest(@Nullable LocusId locusId, @NonNull String mimeType) {
- Preconditions.checkNotNull(mimeType);
+ Objects.requireNonNull(mimeType);
mPackageName = ActivityThread.currentActivityThread().getApplication().getPackageName();
mLocusId = locusId;
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index c882c6e3200d..1b4a00f81e44 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -34,7 +34,7 @@ import android.view.ViewStructure.HtmlInfo.Builder;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
//TODO(b/122484602): add javadocs / implement Parcelable / implement
//TODO(b/122484602): for now it's extending ViewNode directly as it needs most of its properties,
@@ -659,7 +659,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
/** @hide */
@TestApi
public ViewStructureImpl(@NonNull View view) {
- mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId();
+ mNode.mAutofillId = Objects.requireNonNull(view).getAutofillId();
final ViewParent parent = view.getParent();
if (parent instanceof View) {
mNode.mParentAutofillId = ((View) parent).getAutofillId();
@@ -669,7 +669,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
/** @hide */
@TestApi
public ViewStructureImpl(@NonNull AutofillId parentId, long virtualId, int sessionId) {
- mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+ mNode.mParentAutofillId = Objects.requireNonNull(parentId);
mNode.mAutofillId = new AutofillId(parentId, virtualId, sessionId);
}
@@ -830,7 +830,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setTextIdEntry(@NonNull String entryName) {
- mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
+ mNode.mTextIdEntry = Objects.requireNonNull(entryName);
}
@Override
@@ -840,7 +840,7 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setHintIdEntry(String entryName) {
- mNode.mHintIdEntry = Preconditions.checkNotNull(entryName);
+ mNode.mHintIdEntry = Objects.requireNonNull(entryName);
}
@Override
@@ -913,13 +913,13 @@ public final class ViewNode extends AssistStructure.ViewNode {
@Override
public void setAutofillId(AutofillId id) {
- mNode.mAutofillId = Preconditions.checkNotNull(id);
+ mNode.mAutofillId = Objects.requireNonNull(id);
}
@Override
public void setAutofillId(AutofillId parentId, int virtualId) {
- mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+ mNode.mParentAutofillId = Objects.requireNonNull(parentId);
mNode.mAutofillId = new AutofillId(parentId, virtualId);
}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 1eb1a9345d94..e1e175512edc 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -111,6 +111,22 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
/**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mServiceSupported;
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ private boolean mClientSupported;
+
+ /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -204,6 +220,14 @@ public final class InlineSuggestionsRequest implements Parcelable {
return Bundle.EMPTY;
}
+ private static boolean defaultServiceSupported() {
+ return true;
+ }
+
+ private static boolean defaultClientSupported() {
+ return true;
+ }
+
/** @hide */
abstract static class BaseBuilder {
abstract Builder setInlinePresentationSpecs(
@@ -216,15 +240,25 @@ public final class InlineSuggestionsRequest implements Parcelable {
abstract Builder setHostDisplayId(int value);
}
+ /** @hide */
+ public boolean isServiceSupported() {
+ return mServiceSupported;
+ }
+
+ /** @hide */
+ public boolean isClientSupported() {
+ return mClientSupported;
+ }
+
- // Code below generated by codegen v1.0.23.
+ // 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/inputmethod/InlineSuggestionsRequest.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -240,7 +274,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
@NonNull Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId,
- @Nullable InlinePresentationSpec inlineTooltipPresentationSpec) {
+ @Nullable InlinePresentationSpec inlineTooltipPresentationSpec,
+ boolean serviceSupported,
+ boolean clientSupported) {
this.mMaxSuggestionCount = maxSuggestionCount;
this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
@@ -257,6 +293,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -340,7 +378,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)
*/
@DataClass.Generated.Member
public @Nullable InlinePresentationSpec getInlineTooltipPresentationSpec() {
@@ -361,7 +401,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
"extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
"hostDisplayId = " + mHostDisplayId + ", " +
- "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec +
+ "inlineTooltipPresentationSpec = " + mInlineTooltipPresentationSpec + ", " +
+ "serviceSupported = " + mServiceSupported + ", " +
+ "clientSupported = " + mClientSupported +
" }";
}
@@ -385,7 +427,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
&& extrasEquals(that.mExtras)
&& java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
&& mHostDisplayId == that.mHostDisplayId
- && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec);
+ && java.util.Objects.equals(mInlineTooltipPresentationSpec, that.mInlineTooltipPresentationSpec)
+ && mServiceSupported == that.mServiceSupported
+ && mClientSupported == that.mClientSupported;
}
@Override
@@ -403,6 +447,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
_hash = 31 * _hash + mHostDisplayId;
_hash = 31 * _hash + java.util.Objects.hashCode(mInlineTooltipPresentationSpec);
+ _hash = 31 * _hash + Boolean.hashCode(mServiceSupported);
+ _hash = 31 * _hash + Boolean.hashCode(mClientSupported);
return _hash;
}
@@ -413,6 +459,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
int flg = 0;
+ if (mServiceSupported) flg |= 0x100;
+ if (mClientSupported) flg |= 0x200;
if (mHostInputToken != null) flg |= 0x20;
if (mInlineTooltipPresentationSpec != null) flg |= 0x80;
dest.writeInt(flg);
@@ -438,6 +486,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
int flg = in.readInt();
+ boolean serviceSupported = (flg & 0x100) != 0;
+ boolean clientSupported = (flg & 0x200) != 0;
int maxSuggestionCount = in.readInt();
List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
@@ -464,6 +514,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
this.mHostInputToken = hostInputToken;
this.mHostDisplayId = hostDisplayId;
this.mInlineTooltipPresentationSpec = inlineTooltipPresentationSpec;
+ this.mServiceSupported = serviceSupported;
+ this.mClientSupported = clientSupported;
onConstructed();
}
@@ -497,6 +549,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
private @Nullable IBinder mHostInputToken;
private int mHostDisplayId;
private @Nullable InlinePresentationSpec mInlineTooltipPresentationSpec;
+ private boolean mServiceSupported;
+ private boolean mClientSupported;
private long mBuilderFieldsSet = 0L;
@@ -629,7 +683,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
/**
- * Specifies the UI specification for the inline suggestion tooltip in the response.
+ * The {@link InlinePresentationSpec} for the inline suggestion tooltip in the response.
+ *
+ * @see android.service.autofill.InlinePresentation#createTooltipPresentation(Slice, InlinePresentationSpec)s
*/
@DataClass.Generated.Member
public @NonNull Builder setInlineTooltipPresentationSpec(@NonNull InlinePresentationSpec value) {
@@ -639,10 +695,38 @@ public final class InlineSuggestionsRequest implements Parcelable {
return this;
}
+ /**
+ * Whether the IME supports inline suggestions from the default Autofill service that
+ * provides the input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setServiceSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mServiceSupported = value;
+ return this;
+ }
+
+ /**
+ * Whether the IME supports inline suggestions from the application that provides the
+ * input view.
+ *
+ * Note: The default value is {@code true}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientSupported(boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mClientSupported = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull InlineSuggestionsRequest build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x100; // Mark builder used
+ mBuilderFieldsSet |= 0x400; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -665,6 +749,12 @@ public final class InlineSuggestionsRequest implements Parcelable {
if ((mBuilderFieldsSet & 0x80) == 0) {
mInlineTooltipPresentationSpec = defaultInlineTooltipPresentationSpec();
}
+ if ((mBuilderFieldsSet & 0x100) == 0) {
+ mServiceSupported = defaultServiceSupported();
+ }
+ if ((mBuilderFieldsSet & 0x200) == 0) {
+ mClientSupported = defaultClientSupported();
+ }
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
mInlinePresentationSpecs,
@@ -673,12 +763,14 @@ public final class InlineSuggestionsRequest implements Parcelable {
mExtras,
mHostInputToken,
mHostDisplayId,
- mInlineTooltipPresentationSpec);
+ mInlineTooltipPresentationSpec,
+ mServiceSupported,
+ mClientSupported);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x100) != 0) {
+ if ((mBuilderFieldsSet & 0x400) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -686,10 +778,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1621415989607L,
- codegenVersion = "1.0.23",
+ time = 1615798784918L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate @android.annotation.Nullable android.widget.inline.InlinePresentationSpec mInlineTooltipPresentationSpec\nprivate boolean mServiceSupported\nprivate boolean mClientSupported\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.widget.inline.InlinePresentationSpec defaultInlineTooltipPresentationSpec()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nprivate static boolean defaultServiceSupported()\nprivate static boolean defaultClientSupported()\npublic boolean isServiceSupported()\npublic boolean isClientSupported()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 5f036a348808..5185dc2543c7 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -843,9 +843,14 @@ public interface InputConnection {
/**
* Called back when the connected IME switches between fullscreen and normal modes.
*
- * <p>Note: On {@link android.os.Build.VERSION_CODES#O} and later devices, input methods are no
- * longer allowed to directly call this method at any time. To signal this event in the target
- * application, input methods should always call
+ * <p><p><strong>Editor authors:</strong> There is a bug on
+ * {@link android.os.Build.VERSION_CODES#O} and later devices that this method is called back
+ * on the main thread even when {@link #getHandler()} is overridden. This bug is fixed in
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.</p>
+ *
+ * <p><p><strong>IME authors:</strong> On {@link android.os.Build.VERSION_CODES#O} and later
+ * devices, input methods are no longer allowed to directly call this method at any time.
+ * To signal this event in the target application, input methods should always call
* {@link InputMethodService#updateFullscreenMode()} instead. This approach should work on API
* {@link android.os.Build.VERSION_CODES#N_MR1} and prior devices.</p>
*
@@ -927,14 +932,20 @@ public interface InputConnection {
boolean requestCursorUpdates(int cursorUpdateMode);
/**
- * Called by the {@link InputMethodManager} to enable application developers to specify a
- * dedicated {@link Handler} on which incoming IPC method calls from input methods will be
- * dispatched.
+ * Called by the system to enable application developers to specify a dedicated thread on which
+ * {@link InputConnection} methods are called back.
*
- * <p>Note: This does nothing when called from input methods.</p>
+ * <p><strong>Editor authors</strong>: although you can return your custom subclasses of
+ * {@link Handler}, the system only uses {@link android.os.Looper} returned from
+ * {@link Handler#getLooper()}. You cannot intercept or cancel {@link InputConnection}
+ * callbacks by implementing this method.</p>
+ *
+ * <p><strong>IME authors</strong>: This method is not intended to be called from the IME. You
+ * will always receive {@code null}.</p>
*
* @return {@code null} to use the default {@link Handler}.
*/
+ @Nullable
Handler getHandler();
/**
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index d2db0df6c597..5b2068ff16cd 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -96,8 +96,6 @@ public interface InputMethod {
*
* @param token special token for the system to identify
* {@link InputMethodService}
- * @param displayId The id of the display that current IME shown.
- * Used for {{@link #updateInputMethodDisplay(int)}}
* @param privilegedOperations IPC endpoint to do some privileged
* operations that are allowed only to the
* current IME.
@@ -105,9 +103,8 @@ public interface InputMethod {
* @hide
*/
@MainThread
- default void initializeInternal(IBinder token, int displayId,
+ default void initializeInternal(IBinder token,
IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
- updateInputMethodDisplay(displayId);
attachToken(token);
}
@@ -143,16 +140,6 @@ public interface InputMethod {
public void attachToken(IBinder token);
/**
- * Update context display according to given displayId.
- *
- * @param displayId The id of the display that need to update for context.
- * @hide
- */
- @MainThread
- default void updateInputMethodDisplay(int displayId) {
- }
-
- /**
* Bind a new application environment in to the input method, so that it
* can later start and stop input processing.
* Typically this method is called when this input method is enabled in an
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 9b463bb9538f..96198c64fa56 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -418,8 +418,9 @@ public final class InputMethodInfo implements Parcelable {
}
/**
- * Return a unique ID for this input method. The ID is generated from
- * the package and class name implementing the method.
+ * @return a unique ID for this input method, which is guaranteed to be the same as the result
+ * of {@code getComponent().flattenToShortString()}.
+ * @see ComponentName#unflattenFromString(String)
*/
public String getId() {
return mId;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 42d77cd09689..139b69c218f5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,7 +18,6 @@ package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.util.imetracing.ImeTracing.PROTO_ARG;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
@@ -73,7 +72,6 @@ import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.ImeFocusController;
@@ -88,19 +86,19 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.RemoteInputConnectionImpl;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.os.SomeArgs;
-import com.android.internal.view.IInputConnectionWrapper;
-import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InputBindResult;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -367,7 +365,7 @@ public final class InputMethodManager {
final H mH;
// Our generic input connection if the current target does not have its own.
- final IInputContext mIInputContext;
+ private final RemoteInputConnectionImpl mFallbackInputConnection;
private final int mDisplayId;
@@ -409,8 +407,7 @@ public final class InputMethodManager {
/**
* The InputConnection that was last retrieved from the served view.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- IInputConnectionWrapper mServedInputConnectionWrapper;
+ RemoteInputConnectionImpl mServedInputConnection;
/**
* The completions that were last provided by the served view.
*/
@@ -699,8 +696,8 @@ public final class InputMethodManager {
*/
@Override
public void finishComposingText() {
- if (mServedInputConnectionWrapper != null) {
- mServedInputConnectionWrapper.finishComposingText();
+ if (mServedInputConnection != null) {
+ mServedInputConnection.finishComposingText();
}
}
@@ -754,9 +751,9 @@ public final class InputMethodManager {
return false;
}
- return mServedInputConnectionWrapper != null
- && mServedInputConnectionWrapper.isActive()
- && mServedInputConnectionWrapper.getServedView() == view;
+ return mServedInputConnection != null
+ && mServedInputConnection.isActive()
+ && mServedInputConnection.getServedView() == view;
}
}
}
@@ -921,12 +918,9 @@ public final class InputMethodManager {
// that this happened and make sure our own editor's
// state is reset.
mRestartOnNextWindowFocus = true;
- try {
- // Note that finishComposingText() is allowed to run
- // even when we are not active.
- mIInputContext.finishComposingText();
- } catch (RemoteException e) {
- }
+ // Note that finishComposingText() is allowed to run
+ // even when we are not active.
+ mFallbackInputConnection.finishComposingText();
}
// Check focus again in case that "onWindowFocus" is called before
// handling this message.
@@ -956,15 +950,15 @@ public final class InputMethodManager {
}
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
- InputConnection ic = null;
+ RemoteInputConnectionImpl ic = null;
synchronized (mH) {
- mFullscreenMode = fullscreen;
- if (mServedInputConnectionWrapper != null) {
- ic = mServedInputConnectionWrapper.getInputConnection();
+ if (mFullscreenMode != fullscreen && mServedInputConnection != null) {
+ ic = mServedInputConnection;
+ mFullscreenMode = fullscreen;
}
}
if (ic != null) {
- ic.reportFullscreenMode(fullscreen);
+ ic.dispatchReportFullscreenMode(fullscreen);
}
return;
}
@@ -1033,8 +1027,6 @@ public final class InputMethodManager {
}
};
- final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
-
/**
* For layoutlib to clean up static objects inside {@link InputMethodManager}.
*/
@@ -1083,7 +1075,7 @@ public final class InputMethodManager {
// 1) doing so has no effect for A and 2) doing so is sufficient for B.
final long identity = Binder.clearCallingIdentity();
try {
- service.addClient(imm.mClient, imm.mIInputContext, displayId);
+ service.addClient(imm.mClient, imm.mFallbackInputConnection, displayId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
} finally {
@@ -1128,7 +1120,8 @@ public final class InputMethodManager {
mMainLooper = looper;
mH = new H(looper);
mDisplayId = displayId;
- mIInputContext = new IInputConnectionWrapper(looper, mDummyInputConnection, this, null);
+ mFallbackInputConnection = new RemoteInputConnectionImpl(looper,
+ new BaseInputConnection(this, false), this, null);
}
/**
@@ -1205,18 +1198,6 @@ public final class InputMethodManager {
}
}
- /** @hide */
- @UnsupportedAppUsage
- public IInputMethodClient getClient() {
- return mClient;
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public IInputContext getInputContext() {
- return mIInputContext;
- }
-
/**
* Returns the list of installed input methods.
*
@@ -1400,8 +1381,7 @@ public final class InputMethodManager {
public boolean isAcceptingText() {
checkFocus();
synchronized (mH) {
- return mServedInputConnectionWrapper != null
- && mServedInputConnectionWrapper.getInputConnection() != null;
+ return mServedInputConnection != null && !mServedInputConnection.isFinished();
}
}
@@ -1455,9 +1435,9 @@ public final class InputMethodManager {
*/
void clearConnectionLocked() {
mCurrentTextBoxAttribute = null;
- if (mServedInputConnectionWrapper != null) {
- mServedInputConnectionWrapper.deactivate();
- mServedInputConnectionWrapper = null;
+ if (mServedInputConnection != null) {
+ mServedInputConnection.deactivate();
+ mServedInputConnection = null;
}
}
@@ -1953,11 +1933,11 @@ public final class InputMethodManager {
mCurrentTextBoxAttribute = tba;
mServedConnecting = false;
- if (mServedInputConnectionWrapper != null) {
- mServedInputConnectionWrapper.deactivate();
- mServedInputConnectionWrapper = null;
+ if (mServedInputConnection != null) {
+ mServedInputConnection.deactivate();
+ mServedInputConnection = null;
}
- IInputConnectionWrapper servedContext;
+ RemoteInputConnectionImpl servedInputConnection;
final int missingMethodFlags;
if (ic != null) {
mCursorSelStart = tba.initialSelStart;
@@ -1974,14 +1954,14 @@ public final class InputMethodManager {
} else {
icHandler = ic.getHandler();
}
- servedContext = new IInputConnectionWrapper(
+ servedInputConnection = new RemoteInputConnectionImpl(
icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
} else {
- servedContext = null;
+ servedInputConnection = null;
missingMethodFlags = 0;
icHandler = null;
}
- mServedInputConnectionWrapper = servedContext;
+ mServedInputConnection = servedInputConnection;
if (DEBUG) {
Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
@@ -1991,7 +1971,7 @@ public final class InputMethodManager {
try {
res = mService.startInputOrWindowGainedFocus(
startInputReason, mClient, windowGainingFocus, startInputFlags,
- softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
+ softInputMode, windowFlags, tba, servedInputConnection, missingMethodFlags,
view.getContext().getApplicationInfo().targetSdkVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -2154,6 +2134,7 @@ public final class InputMethodManager {
* @hide
*/
public boolean requestImeShow(IBinder windowToken) {
+ checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
if (servedView == null || servedView.getWindowToken() != windowToken) {
@@ -3086,7 +3067,7 @@ public final class InputMethodManager {
p.println(" mService=" + mService);
p.println(" mMainLooper=" + mMainLooper);
- p.println(" mIInputContext=" + mIInputContext);
+ p.println(" mFallbackInputConnection=" + mFallbackInputConnection);
p.println(" mActive=" + mActive
+ " mRestartOnNextWindowFocus=" + mRestartOnNextWindowFocus
+ " mBindSequence=" + mBindSequence
@@ -3107,7 +3088,7 @@ public final class InputMethodManager {
} else {
p.println(" mCurrentTextBoxAttribute: null");
}
- p.println(" mServedInputConnectionWrapper=" + mServedInputConnectionWrapper);
+ p.println(" mServedInputConnection=" + mServedInputConnection);
p.println(" mCompletions=" + Arrays.toString(mCompletions));
p.println(" mCursorRect=" + mCursorRect);
p.println(" mCursorSelStart=" + mCursorSelStart
@@ -3192,7 +3173,7 @@ public final class InputMethodManager {
}
for (String arg : args) {
- if (arg.equals(PROTO_ARG)) {
+ if (arg.equals(ImeTracing.PROTO_ARG)) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
dumpDebug(proto, null /* icProto */);
proto.flush();
@@ -3211,7 +3192,7 @@ public final class InputMethodManager {
* @hide
*/
@GuardedBy("mH")
- public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) {
+ public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) {
if (mCurrentInputMethodSession == null) {
return;
}
@@ -3233,11 +3214,11 @@ public final class InputMethodManager {
if (mImeInsetsConsumer != null) {
mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER);
}
- if (mServedInputConnectionWrapper != null) {
- mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION);
+ if (mServedInputConnection != null) {
+ mServedInputConnection.dumpDebug(proto, INPUT_CONNECTION);
}
if (icProto != null) {
- proto.write(INPUT_CONNECTION_CALL, icProto.getBytes());
+ proto.write(INPUT_CONNECTION_CALL, icProto);
}
}
}
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index 1e8253db888a..604979b1ac78 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -27,10 +27,10 @@ import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
/**
* Represents the result of language detection of a piece of text.
@@ -168,7 +168,7 @@ public final class TextLanguage implements Parcelable {
public Builder putLocale(
@NonNull ULocale locale,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
- Preconditions.checkNotNull(locale);
+ Objects.requireNonNull(locale);
mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore);
return this;
}
@@ -187,7 +187,7 @@ public final class TextLanguage implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
return this;
}
@@ -316,7 +316,7 @@ public final class TextLanguage implements Parcelable {
* @param text the text to process.
*/
public Builder(@NonNull CharSequence text) {
- mText = Preconditions.checkNotNull(text);
+ mText = Objects.requireNonNull(text);
}
/**
@@ -324,7 +324,7 @@ public final class TextLanguage implements Parcelable {
*/
@NonNull
public Builder setExtras(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mBundle = Objects.requireNonNull(bundle);
return this;
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index ae927cf60565..e31eabb083e5 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -20,6 +20,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -114,6 +115,7 @@ public final class TextServicesManager {
* @throws ServiceNotFoundException When {@link TextServicesManager} is not available.
* @hide
*/
+ @UserHandleAware
@NonNull
public static TextServicesManager createInstance(@NonNull Context context)
throws ServiceNotFoundException {
@@ -178,6 +180,7 @@ public final class TextServicesManager {
* languages in settings will be returned.
* @return a spell checker session of the spell checker
*/
+ @UserHandleAware
@Nullable
public SpellCheckerSession newSpellCheckerSession(@Nullable Bundle bundle,
@Nullable Locale locale,
@@ -208,6 +211,7 @@ public final class TextServicesManager {
* @param listener a spell checker session lister for getting results from the spell checker.
* @return The spell checker session of the spell checker.
*/
+ @UserHandleAware
@Nullable
public SpellCheckerSession newSpellCheckerSession(
@NonNull SpellCheckerSessionParams params,
@@ -283,6 +287,7 @@ public final class TextServicesManager {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553,
publicAlternatives = "Use {@link #getEnabledSpellCheckerInfos()} instead.")
+ @UserHandleAware
public SpellCheckerInfo[] getEnabledSpellCheckers() {
try {
final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers(mUserId);
@@ -300,6 +305,7 @@ public final class TextServicesManager {
*
* @return The list of currently enabled spell checkers.
*/
+ @UserHandleAware
@NonNull
public List<SpellCheckerInfo> getEnabledSpellCheckerInfos() {
final SpellCheckerInfo[] enabledSpellCheckers = getEnabledSpellCheckers();
@@ -312,6 +318,7 @@ public final class TextServicesManager {
*
* @return The current active spell checker info.
*/
+ @UserHandleAware
@Nullable
public SpellCheckerInfo getCurrentSpellCheckerInfo() {
try {
@@ -328,6 +335,7 @@ public final class TextServicesManager {
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
publicAlternatives = "Use {@link #getCurrentSpellCheckerInfo()} instead.")
+ @UserHandleAware
@Nullable
public SpellCheckerInfo getCurrentSpellChecker() {
return getCurrentSpellCheckerInfo();
@@ -343,6 +351,7 @@ public final class TextServicesManager {
* @hide
*/
@UnsupportedAppUsage
+ @UserHandleAware
@Nullable
public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
boolean allowImplicitlySelectedSubtype) {
@@ -358,6 +367,7 @@ public final class TextServicesManager {
*
* @return {@code true} if spell checker is enabled, {@code false} otherwise.
*/
+ @UserHandleAware
public boolean isSpellCheckerEnabled() {
try {
return mService.isSpellCheckerEnabled(mUserId);
@@ -366,6 +376,7 @@ public final class TextServicesManager {
}
}
+ @UserHandleAware
void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
try {
mService.finishSpellCheckerService(mUserId, listener);
diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java
index 54c455c19621..6610375d46df 100644
--- a/core/java/android/view/translation/TranslationManager.java
+++ b/core/java/android/view/translation/TranslationManager.java
@@ -23,12 +23,12 @@ import android.annotation.SystemService;
import android.annotation.WorkerThread;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Looper;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SynchronousResultReceiver;
import android.util.ArrayMap;
@@ -271,13 +271,10 @@ public final class TranslationManager {
if (result.resultCode != STATUS_SYNC_CALL_SUCCESS) {
return Collections.emptySet();
}
- Parcelable[] parcelables = result.bundle.getParcelableArray(EXTRA_CAPABILITIES);
- ArraySet<TranslationCapability> capabilities = new ArraySet();
- for (Parcelable obj : parcelables) {
- if (obj instanceof TranslationCapability) {
- capabilities.add((TranslationCapability) obj);
- }
- }
+ ParceledListSlice<TranslationCapability> listSlice =
+ result.bundle.getParcelable(EXTRA_CAPABILITIES);
+ ArraySet<TranslationCapability> capabilities =
+ new ArraySet<>(listSlice == null ? null : listSlice.getList());
return capabilities;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/webkit/WebViewLibraryLoader.java b/core/java/android/webkit/WebViewLibraryLoader.java
index be49fc434c79..91412d7e8631 100644
--- a/core/java/android/webkit/WebViewLibraryLoader.java
+++ b/core/java/android/webkit/WebViewLibraryLoader.java
@@ -178,12 +178,23 @@ public class WebViewLibraryLoader {
*/
static void reserveAddressSpaceInZygote() {
System.loadLibrary("webviewchromium_loader");
- boolean is64Bit = VMRuntime.getRuntime().is64Bit();
- // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
- // On 32-bit it's fairly scarce and we should keep it to a realistic number that
- // permits some future growth but doesn't hog space: we use 130MB which is roughly
- // what was calculated on older OS versions in practice.
- long addressSpaceToReserve = is64Bit ? 1 * 1024 * 1024 * 1024 : 130 * 1024 * 1024;
+ long addressSpaceToReserve;
+ if (VMRuntime.getRuntime().is64Bit()) {
+ // On 64-bit address space is really cheap and we can reserve 1GB which is plenty.
+ addressSpaceToReserve = 1 * 1024 * 1024 * 1024;
+ } else if (VMRuntime.getRuntime().vmInstructionSet().equals("arm")) {
+ // On 32-bit the address space is fairly scarce, hence we should keep it to a realistic
+ // number that permits some future growth but doesn't hog space. For ARM we use 130MB
+ // which is roughly what was calculated on older OS versions. The size has been
+ // growing gradually, but a few efforts have offset it back to the size close to the
+ // original.
+ addressSpaceToReserve = 130 * 1024 * 1024;
+ } else {
+ // The number below was obtained for a binary used for x86 emulators, allowing some
+ // natural growth.
+ addressSpaceToReserve = 190 * 1024 * 1024;
+ }
+
sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
if (sAddressSpaceReserved) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index f9f823b70810..26579c5dec68 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -480,6 +480,17 @@ public interface WebViewProvider {
return false;
}
+ /**
+ * @see View#onApplyWindowInsets(WindowInsets).
+ *
+ * <p>This is the entry point for the WebView implementation to override. It returns
+ * {@code null} when the WebView implementation hasn't implemented the WindowInsets support
+ * on S yet. In this case, the {@link View#onApplyWindowInsets()} super method will be
+ * called instead.
+ *
+ * @param insets Insets to apply
+ * @return The supplied insets with any applied insets consumed.
+ */
@SuppressWarnings("unused")
@Nullable
default WindowInsets onApplyWindowInsets(@Nullable WindowInsets insets) {
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 721260e8cafe..a45a91e84126 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2543,33 +2543,32 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return;
}
- boolean isItemEnabled = view.isEnabled() && isEnabled();
+ boolean isItemActionable = isEnabled();
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof AbsListView.LayoutParams) {
- isItemEnabled &= ((AbsListView.LayoutParams) lp).isEnabled;
+ isItemActionable &= ((AbsListView.LayoutParams) lp).isEnabled;
}
- info.setEnabled(isItemEnabled);
-
if (position == getSelectedItemPosition()) {
info.setSelected(true);
- addAccessibilityActionIfEnabled(info, isItemEnabled,
+ addAccessibilityActionIfEnabled(info, isItemActionable,
AccessibilityAction.ACTION_CLEAR_SELECTION);
} else {
- addAccessibilityActionIfEnabled(info, isItemEnabled,
+ addAccessibilityActionIfEnabled(info, isItemActionable,
AccessibilityAction.ACTION_SELECT);
}
if (isItemClickable(view)) {
- addAccessibilityActionIfEnabled(info, isItemEnabled, AccessibilityAction.ACTION_CLICK);
+ addAccessibilityActionIfEnabled(info, isItemActionable,
+ AccessibilityAction.ACTION_CLICK);
// A disabled item is a separator which should not be clickable.
- info.setClickable(isItemEnabled);
+ info.setClickable(isItemActionable);
}
if (isLongClickable()) {
- addAccessibilityActionIfEnabled(info, isItemEnabled,
+ addAccessibilityActionIfEnabled(info, isItemActionable,
AccessibilityAction.ACTION_LONG_CLICK);
- info.setLongClickable(true);
+ info.setLongClickable(isItemActionable);
}
}
@@ -5938,36 +5937,37 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
boolean handled = false;
boolean okToSend = true;
switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_NUMPAD_ENTER:
- okToSend = false;
- break;
- case KeyEvent.KEYCODE_BACK:
- if (mFiltered && mPopup != null && mPopup.isShowing()) {
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.getRepeatCount() == 0) {
- KeyEvent.DispatcherState state = getKeyDispatcherState();
- if (state != null) {
- state.startTracking(event, this);
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_NUMPAD_ENTER:
+ okToSend = false;
+ break;
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_ESCAPE:
+ if (mFiltered && mPopup != null && mPopup.isShowing()) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ state.startTracking(event, this);
+ }
+ handled = true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP
+ && event.isTracking() && !event.isCanceled()) {
+ handled = true;
+ mTextFilter.setText("");
}
- handled = true;
- } else if (event.getAction() == KeyEvent.ACTION_UP
- && event.isTracking() && !event.isCanceled()) {
- handled = true;
- mTextFilter.setText("");
}
- }
- okToSend = false;
- break;
- case KeyEvent.KEYCODE_SPACE:
- // Only send spaces once we are filtered
- okToSend = mFiltered;
- break;
+ okToSend = false;
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ // Only send spaces once we are filtered
+ okToSend = mFiltered;
+ break;
}
if (okToSend) {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index d8f7f4c5f326..f3dfda5bdf82 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -412,24 +412,29 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
void refreshChildren() {
- if (mAdapter == null) return;
+ final int adapterCount = mAdapter == null ? 0 : getCount();
for (int i = mCurrentWindowStart; i <= mCurrentWindowEnd; i++) {
int index = modulo(i, getWindowSize());
- int adapterCount = getCount();
- // get the fresh child from the adapter
- final View updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
+ final View updatedChild;
+ if (i < adapterCount) {
+ // get the fresh child from the adapter
+ updatedChild = mAdapter.getView(modulo(i, adapterCount), null, this);
- if (updatedChild.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ if (updatedChild.getImportantForAccessibility()
+ == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ updatedChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ } else {
+ updatedChild = null;
}
if (mViewsMap.containsKey(index)) {
final FrameLayout fl = (FrameLayout) mViewsMap.get(index).view;
- // add the new child to the frame, if it exists
+ // flush out the old child
+ fl.removeAllViewsInLayout();
if (updatedChild != null) {
- // flush out the old child
- fl.removeAllViewsInLayout();
+ // add the new child to the frame, if it exists
fl.addView(updatedChild);
}
}
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index ce29eea9fd0c..cfae95dc414f 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -788,8 +788,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing()
- && !mPopup.isDropDownAlwaysVisible()) {
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ && isPopupShowing() && !mPopup.isDropDownAlwaysVisible()) {
// special case for the back key, we do not even try to send it
// to the drop down list but instead, consume it immediately
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0c4da88bb058..62585c1a40d1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -126,13 +126,13 @@ import android.widget.TextView.Drawables;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.view.FloatingActionMode;
-import com.android.internal.widget.EditableInputConnection;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 8aa557bab4e3..f292c610f842 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -136,6 +136,7 @@ public class ImageView extends View {
private int[] mState = null;
private boolean mMergeState = false;
+ private boolean mHasLevelSet = false;
private int mLevel = 0;
@UnsupportedAppUsage
private int mDrawableWidth;
@@ -798,6 +799,7 @@ public class ImageView extends View {
@android.view.RemotableViewMethod
public void setImageLevel(int level) {
mLevel = level;
+ mHasLevelSet = true;
if (mDrawable != null) {
mDrawable.setLevel(level);
resizeFromDrawable();
@@ -1069,7 +1071,9 @@ public class ImageView extends View {
: isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown();
d.setVisible(visible, true);
}
- d.setLevel(mLevel);
+ if (mHasLevelSet) {
+ d.setLevel(mLevel);
+ }
mDrawableWidth = d.getIntrinsicWidth();
mDrawableHeight = d.getIntrinsicHeight();
applyImageTint();
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 6232480f8620..7766f1a0b350 100755
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1071,7 +1071,8 @@ public class ListPopupWindow implements ShowableListMenu {
* @see #setModal(boolean)
*/
public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
+ if ((keyCode == KeyEvent.KEYCODE_BACK
+ || keyCode == KeyEvent.KEYCODE_ESCAPE) && isShowing()) {
// special case for the back key, we do not even try to send it
// to the drop down list but instead, consume it immediately
final View anchorView = mDropDownAnchorView;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index e7e148abb95a..decd9a547edb 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -2521,7 +2521,8 @@ public class PopupWindow {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ || event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE) {
if (getKeyDispatcherState() == null) {
return super.dispatchKeyEvent(event);
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 1b76ebf7c8c6..356d059ba5dc 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -245,8 +245,6 @@ public class ProgressBar extends View {
private boolean mAggregatedIsVisible;
- private CharSequence mCustomStateDescription = null;
-
private final ArrayList<RefreshData> mRefreshData = new ArrayList<RefreshData>();
private ObjectAnimator mLastProgressAnimator;
@@ -685,6 +683,9 @@ public class ProgressBar extends View {
swapCurrentDrawable(mProgressDrawable);
stopAnimation();
}
+
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
}
@@ -1622,18 +1623,21 @@ public class ProgressBar extends View {
@Override
@RemotableViewMethod
public void setStateDescription(@Nullable CharSequence stateDescription) {
- mCustomStateDescription = stateDescription;
- if (stateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
- } else {
- super.setStateDescription(stateDescription);
- }
+ // Assume the previous custom state description is different from default state description.
+ // Otherwise when the argument is null to restore the default state description, we will
+ // send out a state description changed event even though the state description presented to
+ // the user doesn't change. Since mStateDescription in View is private, we can't prevent
+ // this event from sending out.
+ super.setStateDescription(stateDescription);
}
void onProgressRefresh(float scale, boolean fromUser, int progress) {
if (AccessibilityManager.getInstance(mContext).isEnabled()
- && mCustomStateDescription == null) {
- super.setStateDescription(formatStateDescription(mProgress));
+ && getStateDescription() == null && !isIndeterminate()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION);
+ sendAccessibilityEventUnchecked(event);
}
}
@@ -2356,7 +2360,15 @@ public class ProgressBar extends View {
AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, getMin(), getMax(),
getProgress());
info.setRangeInfo(rangeInfo);
- info.setStateDescription(formatStateDescription(mProgress));
+ }
+
+ // Only set the default state description when custom state descripton is null.
+ if (getStateDescription() == null) {
+ if (isIndeterminate()) {
+ info.setStateDescription(getResources().getString(R.string.in_progress));
+ } else {
+ info.setStateDescription(formatStateDescription(mProgress));
+ }
}
}
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 693b13bbf224..3ad7b46d503a 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -509,7 +509,8 @@ public class ScrollView extends FrameLayout {
mTempRect.setEmpty();
if (!canScroll()) {
- if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
+ if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK
+ && event.getKeyCode() != KeyEvent.KEYCODE_ESCAPE) {
View currentFocused = findFocus();
if (currentFocused == this) currentFocused = null;
View nextFocused = FocusFinder.getInstance().findNextFocus(this,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f5c1bcf2de42..69a5e3904715 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -200,13 +200,14 @@ import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.EditableInputConnection;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
-import com.android.internal.widget.EditableInputConnection;
import libcore.util.EmptyArray;
@@ -6318,6 +6319,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
text = TextUtils.stringOrSpannedString(text);
}
+ @AccessibilityUtils.A11yTextChangeType int a11yTextChangeType = AccessibilityUtils.NONE;
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ a11yTextChangeType = AccessibilityUtils.textOrSpanChanged(text, mText);
+ }
+
if (mAutoLinkMask != 0) {
Spannable s2;
@@ -6337,6 +6343,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* setText() again to try to upgrade the buffer type.
*/
setTextInternal(text);
+ if (a11yTextChangeType == AccessibilityUtils.NONE) {
+ a11yTextChangeType = AccessibilityUtils.PARCELABLE_SPAN;
+ }
// Do not change the movement method for text that support text selection as it
// would prevent an arbitrary cursor displacement.
@@ -6401,7 +6410,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
- notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ if (a11yTextChangeType == AccessibilityUtils.TEXT) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
+ } else if (a11yTextChangeType == AccessibilityUtils.PARCELABLE_SPAN) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java
new file mode 100644
index 000000000000..9a079751553f
--- /dev/null
+++ b/core/java/android/window/ConfigurationHelper.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.window;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * A helper class to maintain {@link android.content.res.Configuration} related methods used both
+ * in {@link android.app.Activity} and {@link WindowContext}.
+ *
+ * @hide
+ */
+public class ConfigurationHelper {
+ private ConfigurationHelper() {}
+
+ /** Ask text layout engine to free its caches if there is a locale change. */
+ public static void freeTextLayoutCachesIfNeeded(int configDiff) {
+ if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ Canvas.freeTextLayoutCaches();
+ }
+ }
+
+ /**
+ * A helper method to filter out {@link ActivityInfo#CONFIG_SCREEN_SIZE} if the
+ * {@link Configuration#diffPublicOnly(Configuration) diff} of two {@link Configuration}
+ * doesn't cross the boundary.
+ *
+ * @see SizeConfigurationBuckets#filterDiff(int, Configuration, Configuration,
+ * SizeConfigurationBuckets)
+ */
+ public static int diffPublicWithSizeBuckets(@Nullable Configuration currentConfig,
+ @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
+ // If current configuration is null, it is definitely different from updated Configuration.
+ if (currentConfig == null) {
+ return 0xffffffff;
+ }
+ int publicDiff = currentConfig.diffPublicOnly(newConfig);
+ return SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig, newConfig, buckets);
+ }
+
+ /**
+ * Returns {@code true} if the {@link android.content.res.Resources} associated with
+ * a {@code token} needs to be updated.
+ *
+ * @param token A {@link Context#getActivityToken() activity token} or
+ * {@link Context#getWindowContextToken() window context token}
+ * @param config The original {@link Configuration}
+ * @param newConfig The updated Configuration
+ * @param displayChanged a flag to indicate there's a display change
+ * @param configChanged a flag to indicate there's a Configuration change.
+ *
+ * @see ResourcesManager#updateResourcesForActivity(IBinder, Configuration, int)
+ */
+ public static boolean shouldUpdateResources(IBinder token, @Nullable Configuration config,
+ @NonNull Configuration newConfig, @NonNull Configuration overrideConfig,
+ boolean displayChanged, @Nullable Boolean configChanged) {
+ // The configuration has not yet been initialized. We should update it.
+ if (config == null) {
+ return true;
+ }
+ // If the token associated context is moved to another display, we should update the
+ // ResourcesKey.
+ if (displayChanged) {
+ return true;
+ }
+ // If the new config is the same as the config this Activity is already running with and
+ // the override config also didn't change, then don't update the Resources
+ if (!ResourcesManager.getInstance().isSameResourcesOverrideConfig(token, overrideConfig)) {
+ return true;
+ }
+ // If there's a update on WindowConfiguration#mBounds or maxBounds, we should update the
+ // Resources to make WindowMetrics API report the updated result.
+ if (shouldUpdateWindowMetricsBounds(config, newConfig)) {
+ return true;
+ }
+ return configChanged == null ? config.diff(newConfig) != 0 : configChanged;
+ }
+
+ /**
+ * Returns {@code true} if {@code displayId} is different from {@code newDisplayId}.
+ * Note that {@link Display#INVALID_DISPLAY} means no difference.
+ */
+ public static boolean isDifferentDisplay(int displayId, int newDisplayId) {
+ return newDisplayId != INVALID_DISPLAY && displayId != newDisplayId;
+ }
+
+ // TODO(b/173090263): Remove this method after the improvement of AssetManager and ResourcesImpl
+ // constructions.
+ /**
+ * Returns {@code true} if the metrics reported by {@link android.view.WindowMetrics} APIs
+ * should be updated.
+ *
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+ private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig,
+ @NonNull Configuration newConfig) {
+ final Rect currentBounds = currentConfig.windowConfiguration.getBounds();
+ final Rect newBounds = newConfig.windowConfiguration.getBounds();
+
+ final Rect currentMaxBounds = currentConfig.windowConfiguration.getMaxBounds();
+ final Rect newMaxBounds = newConfig.windowConfiguration.getMaxBounds();
+
+ return !currentBounds.equals(newBounds) || !currentMaxBounds.equals(newMaxBounds);
+ }
+}
diff --git a/core/java/android/window/DisplayAreaInfo.java b/core/java/android/window/DisplayAreaInfo.java
index 358467ff599f..1a7aab6852b6 100644
--- a/core/java/android/window/DisplayAreaInfo.java
+++ b/core/java/android/window/DisplayAreaInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.content.res.Configuration;
@@ -43,8 +45,17 @@ public final class DisplayAreaInfo implements Parcelable {
*/
public final int displayId;
+ /**
+ * The feature id of this display area.
+ */
public final int featureId;
+ /**
+ * The feature id of the root display area this display area is associated with.
+ * @hide
+ */
+ public int rootDisplayAreaId = FEATURE_UNDEFINED;
+
public DisplayAreaInfo(@NonNull WindowContainerToken token, int displayId, int featureId) {
this.token = token;
this.displayId = displayId;
@@ -56,6 +67,7 @@ public final class DisplayAreaInfo implements Parcelable {
configuration.readFromParcel(in);
displayId = in.readInt();
featureId = in.readInt();
+ rootDisplayAreaId = in.readInt();
}
@Override
@@ -64,6 +76,7 @@ public final class DisplayAreaInfo implements Parcelable {
configuration.writeToParcel(dest, flags);
dest.writeInt(displayId);
dest.writeInt(featureId);
+ dest.writeInt(rootDisplayAreaId);
}
@NonNull
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 878439906de2..6758a3b411a2 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -34,6 +34,15 @@ import java.util.concurrent.Executor;
public class DisplayAreaOrganizer extends WindowOrganizer {
/**
+ * Key to specify the {@link com.android.server.wm.RootDisplayArea} to attach a window to.
+ * It will be used by the function passed in from
+ * {@link com.android.server.wm.DisplayAreaPolicyBuilder#setSelectRootForWindowFunc(BiFunction)}
+ * to find the Root DA to attach the window.
+ * @hide
+ */
+ public static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
+
+ /**
* The value in display area indicating that no value has been set.
*/
public static final int FEATURE_UNDEFINED = -1;
@@ -256,6 +265,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
}
};
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
private IDisplayAreaOrganizerController getController() {
try {
return getWindowOrganizerController().getDisplayAreaOrganizerController();
@@ -263,5 +273,4 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
return null;
}
}
-
}
diff --git a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
index 02aa1a93a35f..7864c245310e 100644
--- a/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
+++ b/core/java/android/window/IRemoteTransitionFinishedCallback.aidl
@@ -16,14 +16,18 @@
package android.window;
+import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
/**
* Interface to be invoked by the controlling process when a remote transition has finished.
*
* @see IRemoteTransition
+ * @param wct An optional WindowContainerTransaction to apply before the transition finished.
+ * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup
+ * transaction. This is applied by shell.Transitions (before submitting the wct).
* {@hide}
*/
interface IRemoteTransitionFinishedCallback {
- void onTransitionFinished(in WindowContainerTransaction wct);
+ void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct);
}
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
new file mode 100644
index 000000000000..5eb432e785ee
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -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 android.window;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+/** @hide */
+oneway interface ITaskFragmentOrganizer {
+ void onTaskFragmentAppeared(in TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
+ void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
+
+ /**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ */
+ void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken Token set through {@link
+ * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exceptionBundle Bundle containing the exception. Should be created with
+ * {@link TaskFragmentOrganizer#putExceptionInBundle}.
+ */
+ void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle exceptionBundle);
+}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
new file mode 100644
index 000000000000..0ca8a864dba5
--- /dev/null
+++ b/core/java/android/window/ITaskFragmentOrganizerController.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.window;
+
+import android.window.ITaskFragmentOrganizer;
+
+/** @hide */
+interface ITaskFragmentOrganizerController {
+
+ /**
+ * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ */
+ void registerOrganizer(in ITaskFragmentOrganizer organizer);
+
+ /**
+ * Unregisters a previously registered TaskFragmentOrganizer.
+ */
+ void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
+}
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 1223d72f643e..e65fcdd7b13b 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -19,7 +19,9 @@ package android.window;
import android.view.SurfaceControl;
import android.os.IBinder;
+import android.view.RemoteAnimationAdapter;
import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
@@ -60,6 +62,17 @@ interface IWindowOrganizerController {
in @nullable WindowContainerTransaction t);
/**
+ * Starts a legacy transition.
+ * @param type The transition type.
+ * @param adapter The animation to use.
+ * @param syncCallback A sync callback for the contents of `t`
+ * @param t Operations that are part of the transition.
+ * @return sync-id or -1 if this no-op'd because a transition is already running.
+ */
+ int startLegacyTransition(int type, in RemoteAnimationAdapter adapter,
+ in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t);
+
+ /**
* Finishes a transition. This must be called for all created transitions.
* @param transitionToken Which transition to finish
* @param t Changes to make before finishing but in the same SF Transaction. Can be null.
@@ -77,6 +90,9 @@ interface IWindowOrganizerController {
/** @return An interface enabling the management of display area organizers. */
IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
+ /** @return An interface enabling the management of task fragment organizers. */
+ ITaskFragmentOrganizerController getTaskFragmentOrganizerController();
+
/**
* Registers a transition player with Core. There is only one of these at a time and calling
* this will replace the existing one if set.
diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java
index 7422f2449a8d..f474f0a76cc6 100644
--- a/core/java/android/window/SizeConfigurationBuckets.java
+++ b/core/java/android/window/SizeConfigurationBuckets.java
@@ -16,6 +16,7 @@
package android.window;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
@@ -25,6 +26,7 @@ import android.content.res.Configuration;
import android.os.Parcelable;
import android.util.SparseIntArray;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
import java.util.Arrays;
@@ -54,10 +56,24 @@ public final class SizeConfigurationBuckets implements Parcelable {
@Nullable
private final int[] mSmallest;
+ /** Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets */
+ @Nullable
+ private final int[] mScreenLayoutSize;
+
+ /**
+ * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+ * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+ * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
+ */
+ private final boolean mScreenLayoutLongSet;
+
public SizeConfigurationBuckets(Configuration[] sizeConfigurations) {
SparseIntArray horizontal = new SparseIntArray();
SparseIntArray vertical = new SparseIntArray();
SparseIntArray smallest = new SparseIntArray();
+ SparseIntArray screenLayoutSize = new SparseIntArray();
+ int curScreenLayoutSize;
+ boolean screenLayoutLongSet = false;
for (int i = sizeConfigurations.length - 1; i >= 0; i--) {
Configuration config = sizeConfigurations[i];
if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
@@ -69,23 +85,42 @@ public final class SizeConfigurationBuckets implements Parcelable {
if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
smallest.put(config.smallestScreenWidthDp, 0);
}
+ if ((curScreenLayoutSize = config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+ != Configuration.SCREENLAYOUT_SIZE_UNDEFINED) {
+ screenLayoutSize.put(curScreenLayoutSize, 0);
+ }
+ if (!screenLayoutLongSet && (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK)
+ != Configuration.SCREENLAYOUT_LONG_UNDEFINED) {
+ screenLayoutLongSet = true;
+ }
}
mHorizontal = horizontal.copyKeys();
mVertical = vertical.copyKeys();
mSmallest = smallest.copyKeys();
+ mScreenLayoutSize = screenLayoutSize.copyKeys();
+ mScreenLayoutLongSet = screenLayoutLongSet;
}
/**
* Get the changes between two configurations but don't count changes in sizes if they don't
- * cross boundaries that are important to the app.
+ * cross boundaries that are important to the app.
*
* This is a static helper to deal with null `buckets`. When no buckets have been specified,
* this actually filters out all 3 size-configs. This is legacy behavior.
*/
- public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig,
- @Nullable SizeConfigurationBuckets buckets) {
+ public static int filterDiff(int diff, @NonNull Configuration oldConfig,
+ @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
+ final boolean nonSizeLayoutFieldsUnchanged =
+ areNonSizeLayoutFieldsUnchanged(oldConfig.screenLayout, newConfig.screenLayout);
if (buckets == null) {
- return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
+ // Only unflip CONFIG_SCREEN_LAYOUT if non-size-related attributes of screen layout do
+ // not change.
+ if (nonSizeLayoutFieldsUnchanged) {
+ return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE
+ | CONFIG_SCREEN_LAYOUT);
+ } else {
+ return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
+ }
}
if ((diff & CONFIG_SCREEN_SIZE) != 0) {
final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp,
@@ -103,6 +138,13 @@ public final class SizeConfigurationBuckets implements Parcelable {
diff &= ~CONFIG_SMALLEST_SCREEN_SIZE;
}
}
+ if ((diff & CONFIG_SCREEN_LAYOUT) != 0 && nonSizeLayoutFieldsUnchanged) {
+ if (!buckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig)
+ && !buckets.crossesScreenLayoutLongThreshold(oldConfig.screenLayout,
+ newConfig.screenLayout)) {
+ diff &= ~CONFIG_SCREEN_LAYOUT;
+ }
+ }
return diff;
}
@@ -119,6 +161,61 @@ public final class SizeConfigurationBuckets implements Parcelable {
}
/**
+ * Returns whether a screen layout size threshold has been crossed.
+ */
+ @VisibleForTesting
+ public boolean crossesScreenLayoutSizeThreshold(@NonNull Configuration firstConfig,
+ @NonNull Configuration secondConfig) {
+ // If both the old and new screen layout are equal (both can be undefined), then no
+ // threshold is crossed.
+ if ((firstConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+ == (secondConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)) {
+ return false;
+ }
+ // Any time the new layout size is smaller than the old layout size, the activity has
+ // crossed a size threshold because layout size represents the smallest possible size the
+ // activity can occupy.
+ if (!secondConfig.isLayoutSizeAtLeast(firstConfig.screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK)) {
+ return true;
+ }
+ // If the new layout size is at least as large as the old layout size, then check if the new
+ // layout size has crossed a threshold.
+ if (mScreenLayoutSize != null) {
+ for (int screenLayoutSize : mScreenLayoutSize) {
+ if (firstConfig.isLayoutSizeAtLeast(screenLayoutSize)
+ != secondConfig.isLayoutSizeAtLeast(screenLayoutSize)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean crossesScreenLayoutLongThreshold(int firstScreenLayout,
+ int secondScreenLayout) {
+ final int firstScreenLayoutLongValue = firstScreenLayout
+ & Configuration.SCREENLAYOUT_LONG_MASK;
+ final int secondScreenLayoutLongValue = secondScreenLayout
+ & Configuration.SCREENLAYOUT_LONG_MASK;
+ return mScreenLayoutLongSet && firstScreenLayoutLongValue != secondScreenLayoutLongValue;
+ }
+
+ /**
+ * Returns whether non-size related screen layout attributes have changed. If true, then
+ * {@link ActivityInfo#CONFIG_SCREEN_LAYOUT} should not be filtered out in
+ * {@link SizeConfigurationBuckets#filterDiff()} because the non-size related attributes
+ * do not have a bucket range like the size-related attributes of screen layout.
+ */
+ @VisibleForTesting
+ public static boolean areNonSizeLayoutFieldsUnchanged(int oldScreenLayout,
+ int newScreenLayout) {
+ final int nonSizeRelatedFields = Configuration.SCREENLAYOUT_LAYOUTDIR_MASK
+ | Configuration.SCREENLAYOUT_ROUND_MASK | Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+ return (oldScreenLayout & nonSizeRelatedFields) == (newScreenLayout & nonSizeRelatedFields);
+ }
+
+ /**
* The purpose of this method is to decide whether the activity needs to be relaunched upon
* changing its size. In most cases the activities don't need to be relaunched, if the resize
* is small, all the activity content has to do is relayout itself within new bounds. There are
@@ -132,7 +229,8 @@ public final class SizeConfigurationBuckets implements Parcelable {
* it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
* of the threshold.
*/
- private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
+ @VisibleForTesting
+ public static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
int secondDp) {
if (thresholds == null) {
return false;
@@ -150,12 +248,13 @@ public final class SizeConfigurationBuckets implements Parcelable {
@Override
public String toString() {
return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " "
- + Arrays.toString(mSmallest);
+ + Arrays.toString(mSmallest) + " " + Arrays.toString(mScreenLayoutSize) + " "
+ + mScreenLayoutLongSet;
}
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -177,15 +276,25 @@ public final class SizeConfigurationBuckets implements Parcelable {
* Vertical (screenHeightDp) buckets
* @param smallest
* Smallest (smallestScreenWidthDp) buckets
+ * @param screenLayoutSize
+ * Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets
+ * @param screenLayoutLongSet
+ * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+ * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+ * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
*/
@DataClass.Generated.Member
public SizeConfigurationBuckets(
@Nullable int[] horizontal,
@Nullable int[] vertical,
- @Nullable int[] smallest) {
+ @Nullable int[] smallest,
+ @Nullable int[] screenLayoutSize,
+ boolean screenLayoutLongSet) {
this.mHorizontal = horizontal;
this.mVertical = vertical;
this.mSmallest = smallest;
+ this.mScreenLayoutSize = screenLayoutSize;
+ this.mScreenLayoutLongSet = screenLayoutLongSet;
// onConstructed(); // You can define this method to get a callback
}
@@ -214,6 +323,24 @@ public final class SizeConfigurationBuckets implements Parcelable {
return mSmallest;
}
+ /**
+ * Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets
+ */
+ @DataClass.Generated.Member
+ public @Nullable int[] getScreenLayoutSize() {
+ return mScreenLayoutSize;
+ }
+
+ /**
+ * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+ * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+ * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
+ */
+ @DataClass.Generated.Member
+ public boolean isScreenLayoutLongSet() {
+ return mScreenLayoutLongSet;
+ }
+
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
@@ -221,13 +348,16 @@ public final class SizeConfigurationBuckets implements Parcelable {
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
+ if (mScreenLayoutLongSet) flg |= 0x10;
if (mHorizontal != null) flg |= 0x1;
if (mVertical != null) flg |= 0x2;
if (mSmallest != null) flg |= 0x4;
+ if (mScreenLayoutSize != null) flg |= 0x8;
dest.writeByte(flg);
if (mHorizontal != null) dest.writeIntArray(mHorizontal);
if (mVertical != null) dest.writeIntArray(mVertical);
if (mSmallest != null) dest.writeIntArray(mSmallest);
+ if (mScreenLayoutSize != null) dest.writeIntArray(mScreenLayoutSize);
}
@Override
@@ -242,13 +372,17 @@ public final class SizeConfigurationBuckets implements Parcelable {
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
+ boolean screenLayoutLongSet = (flg & 0x10) != 0;
int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray();
int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray();
int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray();
+ int[] screenLayoutSize = (flg & 0x8) == 0 ? null : in.createIntArray();
this.mHorizontal = horizontal;
this.mVertical = vertical;
this.mSmallest = smallest;
+ this.mScreenLayoutSize = screenLayoutSize;
+ this.mScreenLayoutLongSet = screenLayoutLongSet;
// onConstructed(); // You can define this method to get a callback
}
@@ -268,10 +402,10 @@ public final class SizeConfigurationBuckets implements Parcelable {
};
@DataClass.Generated(
- time = 1615845864280L,
- codegenVersion = "1.0.22",
+ time = 1628273704583L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java",
- inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\nprivate static boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)")
+ inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\nprivate final @android.annotation.Nullable int[] mScreenLayoutSize\nprivate final boolean mScreenLayoutLongSet\npublic static int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate boolean crossesHorizontalSizeThreshold(int,int)\nprivate boolean crossesVerticalSizeThreshold(int,int)\nprivate boolean crossesSmallestSizeThreshold(int,int)\npublic @com.android.internal.annotations.VisibleForTesting boolean crossesScreenLayoutSizeThreshold(android.content.res.Configuration,android.content.res.Configuration)\nprivate boolean crossesScreenLayoutLongThreshold(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean areNonSizeLayoutFieldsUnchanged(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.aidl b/core/java/android/window/TaskFragmentAppearedInfo.aidl
new file mode 100644
index 000000000000..3729c09168a6
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAppearedInfo.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.window;
+
+/**
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
+ */
+parcelable TaskFragmentAppearedInfo;
diff --git a/core/java/android/window/TaskFragmentAppearedInfo.java b/core/java/android/window/TaskFragmentAppearedInfo.java
new file mode 100644
index 000000000000..89d9a9508a71
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAppearedInfo.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Data object for the TaskFragment info provided when a TaskFragment is presented to an organizer.
+ * @hide
+ */
+@TestApi
+public final class TaskFragmentAppearedInfo implements Parcelable {
+
+ @NonNull
+ private final TaskFragmentInfo mTaskFragmentInfo;
+
+ @NonNull
+ private final SurfaceControl mLeash;
+
+ /** @hide */
+ public TaskFragmentAppearedInfo(
+ @NonNull TaskFragmentInfo taskFragmentInfo, @NonNull SurfaceControl leash) {
+ mTaskFragmentInfo = taskFragmentInfo;
+ mLeash = leash;
+ }
+
+ @NonNull
+ public TaskFragmentInfo getTaskFragmentInfo() {
+ return mTaskFragmentInfo;
+ }
+
+ @NonNull
+ public SurfaceControl getLeash() {
+ return mLeash;
+ }
+
+ private TaskFragmentAppearedInfo(Parcel in) {
+ mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+ mLeash = in.readTypedObject(SurfaceControl.CREATOR);
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mTaskFragmentInfo, flags);
+ dest.writeTypedObject(mLeash, flags);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentAppearedInfo> CREATOR =
+ new Creator<TaskFragmentAppearedInfo>() {
+ @Override
+ public TaskFragmentAppearedInfo createFromParcel(Parcel in) {
+ return new TaskFragmentAppearedInfo(in);
+ }
+
+ @Override
+ public TaskFragmentAppearedInfo[] newArray(int size) {
+ return new TaskFragmentAppearedInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentAppearedInfo{"
+ + " taskFragmentInfo=" + mTaskFragmentInfo
+ + "}";
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/window/TaskFragmentCreationParams.aidl b/core/java/android/window/TaskFragmentCreationParams.aidl
new file mode 100644
index 000000000000..fde50892640b
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.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.window;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+parcelable TaskFragmentCreationParams;
diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java
new file mode 100644
index 000000000000..81ab7836435d
--- /dev/null
+++ b/core/java/android/window/TaskFragmentCreationParams.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WindowingMode;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data object for options to create TaskFragment with.
+ * @hide
+ */
+@TestApi
+public final class TaskFragmentCreationParams implements Parcelable {
+
+ /** The organizer that will organize this TaskFragment. */
+ @NonNull
+ private final TaskFragmentOrganizerToken mOrganizer;
+
+ /**
+ * Unique token assigned from the client organizer to identify the {@link TaskFragmentInfo} when
+ * a new TaskFragment is created with this option.
+ */
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ /**
+ * Activity token used to identify the leaf Task to create the TaskFragment in. It has to belong
+ * to the same app as the root Activity of the target Task.
+ */
+ @NonNull
+ private final IBinder mOwnerToken;
+
+ /** The initial bounds of the TaskFragment. Fills parent if empty. */
+ @NonNull
+ private final Rect mInitialBounds = new Rect();
+
+ /** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */
+ @WindowingMode
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+ private TaskFragmentCreationParams(
+ @NonNull TaskFragmentOrganizerToken organizer,
+ @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) {
+ mOrganizer = organizer;
+ mFragmentToken = fragmentToken;
+ mOwnerToken = ownerToken;
+ }
+
+ @NonNull
+ public TaskFragmentOrganizerToken getOrganizer() {
+ return mOrganizer;
+ }
+
+ @NonNull
+ public IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ @NonNull
+ public IBinder getOwnerToken() {
+ return mOwnerToken;
+ }
+
+ @NonNull
+ public Rect getInitialBounds() {
+ return mInitialBounds;
+ }
+
+ @WindowingMode
+ public int getWindowingMode() {
+ return mWindowingMode;
+ }
+
+ private TaskFragmentCreationParams(Parcel in) {
+ mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in);
+ mFragmentToken = in.readStrongBinder();
+ mOwnerToken = in.readStrongBinder();
+ mInitialBounds.readFromParcel(in);
+ mWindowingMode = in.readInt();
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mOrganizer.writeToParcel(dest, flags);
+ dest.writeStrongBinder(mFragmentToken);
+ dest.writeStrongBinder(mOwnerToken);
+ mInitialBounds.writeToParcel(dest, flags);
+ dest.writeInt(mWindowingMode);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentCreationParams> CREATOR =
+ new Creator<TaskFragmentCreationParams>() {
+ @Override
+ public TaskFragmentCreationParams createFromParcel(Parcel in) {
+ return new TaskFragmentCreationParams(in);
+ }
+
+ @Override
+ public TaskFragmentCreationParams[] newArray(int size) {
+ return new TaskFragmentCreationParams[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentCreationParams{"
+ + " organizer=" + mOrganizer
+ + " fragmentToken=" + mFragmentToken
+ + " ownerToken=" + mOwnerToken
+ + " initialBounds=" + mInitialBounds
+ + " windowingMode=" + mWindowingMode
+ + "}";
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Builder to construct the options to create TaskFragment with. */
+ public static final class Builder {
+
+ @NonNull
+ private final TaskFragmentOrganizerToken mOrganizer;
+
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ @NonNull
+ private final IBinder mOwnerToken;
+
+ @NonNull
+ private final Rect mInitialBounds = new Rect();
+
+ @WindowingMode
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+
+ public Builder(@NonNull TaskFragmentOrganizerToken organizer,
+ @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) {
+ mOrganizer = organizer;
+ mFragmentToken = fragmentToken;
+ mOwnerToken = ownerToken;
+ }
+
+ /** Sets the initial bounds for the TaskFragment. */
+ @NonNull
+ public Builder setInitialBounds(@NonNull Rect bounds) {
+ mInitialBounds.set(bounds);
+ return this;
+ }
+
+ /** Sets the initial windowing mode for the TaskFragment. */
+ @NonNull
+ public Builder setWindowingMode(@WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
+ /** Constructs the options to create TaskFragment with. */
+ @NonNull
+ public TaskFragmentCreationParams build() {
+ final TaskFragmentCreationParams result = new TaskFragmentCreationParams(
+ mOrganizer, mFragmentToken, mOwnerToken);
+ result.mInitialBounds.set(mInitialBounds);
+ result.mWindowingMode = mWindowingMode;
+ return result;
+ }
+ }
+}
diff --git a/core/java/android/window/TaskFragmentInfo.aidl b/core/java/android/window/TaskFragmentInfo.aidl
new file mode 100644
index 000000000000..461a7364803f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.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.window;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+parcelable TaskFragmentInfo;
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
new file mode 100644
index 000000000000..dac420b6190d
--- /dev/null
+++ b/core/java/android/window/TaskFragmentInfo.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 android.window;
+
+import static android.app.WindowConfiguration.WindowingMode;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Stores information about a particular TaskFragment.
+ * @hide
+ */
+@TestApi
+public final class TaskFragmentInfo implements Parcelable {
+
+ /**
+ * Client assigned unique token in {@link TaskFragmentCreationParams#getFragmentToken()} to
+ * create this TaskFragment with.
+ */
+ @NonNull
+ private final IBinder mFragmentToken;
+
+ @NonNull
+ private final WindowContainerToken mToken;
+
+ @NonNull
+ private final Configuration mConfiguration = new Configuration();
+
+ /** Whether the TaskFragment contains any child Window Container. */
+ private final boolean mIsEmpty;
+
+ /** Whether the TaskFragment contains any running Activity. */
+ private final boolean mHasRunningActivity;
+
+ /** Whether this TaskFragment is visible on the window hierarchy. */
+ private final boolean mIsVisible;
+
+ /**
+ * List of Activity tokens that are children of this TaskFragment. It only contains Activities
+ * that belong to the organizer process for security.
+ */
+ @NonNull
+ private final List<IBinder> mActivities = new ArrayList<>();
+
+ /** Relative position of the fragment's top left corner in the parent container. */
+ private final Point mPositionInParent;
+
+ /** @hide */
+ public TaskFragmentInfo(
+ @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
+ @NonNull Configuration configuration, boolean isEmpty, boolean hasRunningActivity,
+ boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent) {
+ mFragmentToken = requireNonNull(fragmentToken);
+ mToken = requireNonNull(token);
+ mConfiguration.setTo(configuration);
+ mIsEmpty = isEmpty;
+ mHasRunningActivity = hasRunningActivity;
+ mIsVisible = isVisible;
+ mActivities.addAll(activities);
+ mPositionInParent = requireNonNull(positionInParent);
+ }
+
+ @NonNull
+ public IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ @NonNull
+ public WindowContainerToken getToken() {
+ return mToken;
+ }
+
+ @NonNull
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ public boolean isEmpty() {
+ return mIsEmpty;
+ }
+
+ public boolean hasRunningActivity() {
+ return mHasRunningActivity;
+ }
+
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
+ @NonNull
+ public List<IBinder> getActivities() {
+ return mActivities;
+ }
+
+ /** Returns the relative position of the fragment's top left corner in the parent container. */
+ @NonNull
+ public Point getPositionInParent() {
+ return mPositionInParent;
+ }
+
+ @WindowingMode
+ public int getWindowingMode() {
+ return mConfiguration.windowConfiguration.getWindowingMode();
+ }
+
+ /**
+ * Returns {@code true} if the parameters that are important for task fragment organizers are
+ * equal between this {@link TaskFragmentInfo} and {@param that}.
+ */
+ public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) {
+ if (that == null) {
+ return false;
+ }
+
+ return mFragmentToken.equals(that.mFragmentToken)
+ && mToken.equals(that.mToken)
+ && mIsEmpty == that.mIsEmpty
+ && mHasRunningActivity == that.mHasRunningActivity
+ && mIsVisible == that.mIsVisible
+ && getWindowingMode() == that.getWindowingMode()
+ && mActivities.equals(that.mActivities)
+ && mPositionInParent.equals(that.mPositionInParent);
+ }
+
+ private TaskFragmentInfo(Parcel in) {
+ mFragmentToken = in.readStrongBinder();
+ mToken = in.readTypedObject(WindowContainerToken.CREATOR);
+ mConfiguration.readFromParcel(in);
+ mIsEmpty = in.readBoolean();
+ mHasRunningActivity = in.readBoolean();
+ mIsVisible = in.readBoolean();
+ in.readBinderList(mActivities);
+ mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mFragmentToken);
+ dest.writeTypedObject(mToken, flags);
+ mConfiguration.writeToParcel(dest, flags);
+ dest.writeBoolean(mIsEmpty);
+ dest.writeBoolean(mHasRunningActivity);
+ dest.writeBoolean(mIsVisible);
+ dest.writeBinderList(mActivities);
+ dest.writeTypedObject(mPositionInParent, flags);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentInfo> CREATOR =
+ new Creator<TaskFragmentInfo>() {
+ @Override
+ public TaskFragmentInfo createFromParcel(Parcel in) {
+ return new TaskFragmentInfo(in);
+ }
+
+ @Override
+ public TaskFragmentInfo[] newArray(int size) {
+ return new TaskFragmentInfo[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentInfo{"
+ + " fragmentToken=" + mFragmentToken
+ + " token=" + mToken
+ + " isEmpty=" + mIsEmpty
+ + " hasRunningActivity=" + mHasRunningActivity
+ + " isVisible=" + mIsVisible
+ + " positionInParent=" + mPositionInParent
+ + "}";
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
new file mode 100644
index 000000000000..f22f0b231ec9
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Interface for WindowManager to delegate control of {@code TaskFragment}.
+ * @hide
+ */
+@TestApi
+public class TaskFragmentOrganizer extends WindowOrganizer {
+
+ /**
+ * Key to the exception in {@link Bundle} in {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ */
+ private static final String KEY_ERROR_CALLBACK_EXCEPTION = "fragment_exception";
+
+ /**
+ * Creates a {@link Bundle} with an exception that can be passed to
+ * {@link ITaskFragmentOrganizer#onTaskFragmentError}.
+ * @hide
+ */
+ public static Bundle putExceptionInBundle(@NonNull Throwable exception) {
+ final Bundle exceptionBundle = new Bundle();
+ exceptionBundle.putSerializable(KEY_ERROR_CALLBACK_EXCEPTION, exception);
+ return exceptionBundle;
+ }
+
+ /**
+ * Callbacks from WM Core are posted on this executor.
+ */
+ private final Executor mExecutor;
+
+ public TaskFragmentOrganizer(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * Gets the executor to run callbacks on.
+ */
+ @NonNull
+ public Executor getExecutor() {
+ return mExecutor;
+ }
+
+ /**
+ * Registers a TaskFragmentOrganizer to manage TaskFragments.
+ */
+ @CallSuper
+ public void registerOrganizer() {
+ try {
+ getController().registerOrganizer(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a previously registered TaskFragmentOrganizer.
+ */
+ @CallSuper
+ public void unregisterOrganizer() {
+ try {
+ getController().unregisterOrganizer(mInterface);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Called when a TaskFragment is created and organized by this organizer. */
+ public void onTaskFragmentAppeared(
+ @NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}
+
+ /** Called when the status of an organized TaskFragment is changed. */
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+ /** Called when an organized TaskFragment is removed. */
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {}
+
+ /**
+ * Called when the parent leaf Task of organized TaskFragments is changed.
+ * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
+ * transaction.
+ *
+ * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
+ * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
+ * bounds.
+ */
+ public void onTaskFragmentParentInfoChanged(
+ @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
+
+ /**
+ * Called when the {@link WindowContainerTransaction} created with
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
+ *
+ * @param errorCallbackToken token set in
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param exception exception from the server side.
+ */
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
+
+ @Override
+ public void applyTransaction(@NonNull WindowContainerTransaction t) {
+ t.setTaskFragmentOrganizer(mInterface);
+ super.applyTransaction(t);
+ }
+
+ // Suppress the lint because it is not a registration method.
+ @SuppressWarnings("ExecutorRegistration")
+ @Override
+ public int applySyncTransaction(@NonNull WindowContainerTransaction t,
+ @NonNull WindowContainerTransactionCallback callback) {
+ t.setTaskFragmentOrganizer(mInterface);
+ return super.applySyncTransaction(t, callback);
+ }
+
+ private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
+ @Override
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
+ }
+
+ @Override
+ public void onTaskFragmentParentInfoChanged(
+ @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
+ mExecutor.execute(
+ () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
+ fragmentToken, parentConfig));
+ }
+
+ @Override
+ public void onTaskFragmentError(
+ @NonNull IBinder errorCallbackToken, @NonNull Bundle exceptionBundle) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTaskFragmentError(
+ errorCallbackToken,
+ (Throwable) exceptionBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION)));
+ }
+ };
+
+ private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface);
+
+ @NonNull
+ public TaskFragmentOrganizerToken getOrganizerToken() {
+ return mToken;
+ }
+
+ private ITaskFragmentOrganizerController getController() {
+ try {
+ return getWindowOrganizerController().getTaskFragmentOrganizerController();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/window/TaskFragmentOrganizerToken.java b/core/java/android/window/TaskFragmentOrganizerToken.java
new file mode 100644
index 000000000000..d5216a6444d9
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOrganizerToken.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.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Interface to communicate between window manager and {@link TaskFragmentOrganizer}.
+ * <p>
+ * Window manager will dispatch TaskFragment information updates via this interface.
+ * It is necessary because {@link ITaskFragmentOrganizer} aidl interface can not be used as a
+ * {@link TestApi}.
+ * @hide
+ */
+@TestApi
+public final class TaskFragmentOrganizerToken implements Parcelable {
+ private final ITaskFragmentOrganizer mRealToken;
+
+ TaskFragmentOrganizerToken(ITaskFragmentOrganizer realToken) {
+ mRealToken = realToken;
+ }
+
+ /** @hide */
+ public IBinder asBinder() {
+ return mRealToken.asBinder();
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongInterface(mRealToken);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentOrganizerToken> CREATOR =
+ new Creator<TaskFragmentOrganizerToken>() {
+ @Override
+ public TaskFragmentOrganizerToken createFromParcel(Parcel in) {
+ final ITaskFragmentOrganizer realToken =
+ ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
+ // The TaskFragmentOrganizerToken may be null for TaskOrganizer or
+ // DisplayAreaOrganizer.
+ if (realToken == null) {
+ return null;
+ }
+ return new TaskFragmentOrganizerToken(realToken);
+ }
+
+ @Override
+ public TaskFragmentOrganizerToken[] newArray(int size) {
+ return new TaskFragmentOrganizerToken[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return mRealToken.asBinder().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "TaskFragmentOrganizerToken{" + mRealToken + "}";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TaskFragmentOrganizerToken)) {
+ return false;
+ }
+ return mRealToken.asBinder() == ((TaskFragmentOrganizerToken) obj).asBinder();
+ }
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index c7c91cdd0941..6f250fc0ce7a 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -222,6 +222,7 @@ public class TaskOrganizer extends WindowOrganizer {
}
}
+
/**
* Restarts the top activity in the given task by killing its process if it is visible.
* @hide
@@ -289,6 +290,7 @@ public class TaskOrganizer extends WindowOrganizer {
}
};
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
private ITaskOrganizerController getController() {
try {
return getWindowOrganizerController().getTaskOrganizerController();
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 141f47b130d1..db15145b0a1e 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -17,12 +17,17 @@
package android.window;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.view.WindowManager.TransitionType;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.WindowManager;
/**
* A parcelable filter that can be used for rerouting transitions to a remote. This is a local
@@ -33,11 +38,29 @@ import android.os.Parcelable;
*/
public final class TransitionFilter implements Parcelable {
+ /** The associated requirement doesn't care about the z-order. */
+ public static final int CONTAINER_ORDER_ANY = 0;
+ /** The associated requirement only matches the top-most (z-order) container. */
+ public static final int CONTAINER_ORDER_TOP = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "CONTAINER_ORDER_" }, value = {
+ CONTAINER_ORDER_ANY,
+ CONTAINER_ORDER_TOP,
+ })
+ public @interface ContainerOrder {}
+
/**
* When non-null: this is a list of transition types that this filter applies to. This filter
* will fail for transitions that aren't one of these types.
*/
- @Nullable public int[] mTypeSet = null;
+ @Nullable public @TransitionType int[] mTypeSet = null;
+
+ /** All flags must be set on a transition. */
+ public @WindowManager.TransitionFlags int mFlags = 0;
+
+ /** All flags must NOT be set on a transition. */
+ public @WindowManager.TransitionFlags int mNotFlags = 0;
/**
* A list of required changes. To pass, a transition must meet all requirements.
@@ -49,6 +72,8 @@ public final class TransitionFilter implements Parcelable {
private TransitionFilter(Parcel in) {
mTypeSet = in.createIntArray();
+ mFlags = in.readInt();
+ mNotFlags = in.readInt();
mRequirements = in.createTypedArray(Requirement.CREATOR);
}
@@ -65,10 +90,19 @@ public final class TransitionFilter implements Parcelable {
}
if (!typePass) return false;
}
+ if ((info.getFlags() & mFlags) != mFlags) {
+ return false;
+ }
+ if ((info.getFlags() & mNotFlags) != 0) {
+ return false;
+ }
// Make sure info meets all of the requirements.
if (mRequirements != null) {
for (int i = 0; i < mRequirements.length; ++i) {
- if (!mRequirements[i].matches(info)) return false;
+ final boolean matches = mRequirements[i].matches(info);
+ if (matches == mRequirements[i].mNot) {
+ return false;
+ }
}
}
return true;
@@ -78,6 +112,8 @@ public final class TransitionFilter implements Parcelable {
/** @hide */
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeIntArray(mTypeSet);
+ dest.writeInt(mFlags);
+ dest.writeInt(mNotFlags);
dest.writeTypedArray(mRequirements, flags);
}
@@ -107,10 +143,12 @@ public final class TransitionFilter implements Parcelable {
sb.append("{types=[");
if (mTypeSet != null) {
for (int i = 0; i < mTypeSet.length; ++i) {
- sb.append((i == 0 ? "" : ",") + mTypeSet[i]);
+ sb.append((i == 0 ? "" : ",") + WindowManager.transitTypeToString(mTypeSet[i]));
}
}
- sb.append("] checks=[");
+ sb.append("] flags=0x" + Integer.toHexString(mFlags));
+ sb.append("] notFlags=0x" + Integer.toHexString(mNotFlags));
+ sb.append(" checks=[");
if (mRequirements != null) {
for (int i = 0; i < mRequirements.length; ++i) {
sb.append((i == 0 ? "" : ",") + mRequirements[i]);
@@ -125,30 +163,56 @@ public final class TransitionFilter implements Parcelable {
*/
public static final class Requirement implements Parcelable {
public int mActivityType = ACTIVITY_TYPE_UNDEFINED;
+
+ /** This only matches if the change is independent of its parents. */
+ public boolean mMustBeIndependent = true;
+
+ /** If this matches, the parent filter will fail */
+ public boolean mNot = false;
+
public int[] mModes = null;
+ /** Matches only if all the flags here are set on the change. */
+ public @TransitionInfo.ChangeFlags int mFlags = 0;
+
+ /** If this needs to be a task. */
+ public boolean mMustBeTask = false;
+
+ public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
+ public ComponentName mTopActivity;
+
public Requirement() {
}
private Requirement(Parcel in) {
mActivityType = in.readInt();
+ mMustBeIndependent = in.readBoolean();
+ mNot = in.readBoolean();
mModes = in.createIntArray();
+ mFlags = in.readInt();
+ mMustBeTask = in.readBoolean();
+ mOrder = in.readInt();
+ mTopActivity = in.readTypedObject(ComponentName.CREATOR);
}
/** Go through changes and find if at-least one change matches this filter */
boolean matches(@NonNull TransitionInfo info) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (!TransitionInfo.isIndependent(change, info)) {
+ if (mMustBeIndependent && !TransitionInfo.isIndependent(change, info)) {
// Only look at independent animating windows.
continue;
}
+ if (mOrder == CONTAINER_ORDER_TOP && i > 0) {
+ continue;
+ }
if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
if (change.getTaskInfo() == null
|| change.getTaskInfo().getActivityType() != mActivityType) {
continue;
}
}
+ if (!matchesTopActivity(change.getTaskInfo())) continue;
if (mModes != null) {
boolean pass = false;
for (int m = 0; m < mModes.length; ++m) {
@@ -159,24 +223,44 @@ public final class TransitionFilter implements Parcelable {
}
if (!pass) continue;
}
+ if ((change.getFlags() & mFlags) != mFlags) {
+ continue;
+ }
+ if (mMustBeTask && change.getTaskInfo() == null) {
+ continue;
+ }
return true;
}
return false;
}
+ private boolean matchesTopActivity(ActivityManager.RunningTaskInfo info) {
+ if (mTopActivity == null) return true;
+ if (info == null) return false;
+ final ComponentName component = info.topActivity;
+ return mTopActivity.equals(component);
+ }
+
/** Check if the request matches this filter. It may generate false positives */
boolean matches(@NonNull TransitionRequestInfo request) {
- // Can't check modes since the transition hasn't been built at this point.
+ // Can't check modes/order since the transition hasn't been built at this point.
if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
return request.getTriggerTask() != null
- && request.getTriggerTask().getActivityType() == mActivityType;
+ && request.getTriggerTask().getActivityType() == mActivityType
+ && matchesTopActivity(request.getTriggerTask());
}
@Override
/** @hide */
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mActivityType);
+ dest.writeBoolean(mMustBeIndependent);
+ dest.writeBoolean(mNot);
dest.writeIntArray(mModes);
+ dest.writeInt(mFlags);
+ dest.writeBoolean(mMustBeTask);
+ dest.writeInt(mOrder);
+ dest.writeTypedObject(mTopActivity, flags);
}
@NonNull
@@ -202,14 +286,31 @@ public final class TransitionFilter implements Parcelable {
@Override
public String toString() {
StringBuilder out = new StringBuilder();
- out.append("{atype=" + WindowConfiguration.activityTypeToString(mActivityType));
+ out.append('{');
+ if (mNot) out.append("NOT ");
+ out.append("atype=" + WindowConfiguration.activityTypeToString(mActivityType));
+ out.append(" independent=" + mMustBeIndependent);
out.append(" modes=[");
if (mModes != null) {
for (int i = 0; i < mModes.length; ++i) {
out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i]));
}
}
- return out.append("]}").toString();
+ out.append("]").toString();
+ out.append(" flags=" + TransitionInfo.flagsToString(mFlags));
+ out.append(" mustBeTask=" + mMustBeTask);
+ out.append(" order=" + containerOrderToString(mOrder));
+ out.append(" topActivity=").append(mTopActivity);
+ out.append("}");
+ return out.toString();
+ }
+ }
+
+ private static String containerOrderToString(int order) {
+ switch (order) {
+ case CONTAINER_ORDER_ANY: return "ANY";
+ case CONTAINER_ORDER_TOP: return "TOP";
}
+ return "UNKNOWN(" + order + ")";
}
}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 23b8ee4a019f..c2ffc03b6119 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -16,13 +16,23 @@
package android.window;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionFlags;
+import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
import android.annotation.IntDef;
@@ -31,11 +41,11 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import java.util.ArrayList;
import java.util.List;
@@ -80,8 +90,22 @@ public final class TransitionInfo implements Parcelable {
/** The container has voice session. */
public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4;
+ /** The container is the display. */
+ public static final int FLAG_IS_DISPLAY = 1 << 5;
+
+ /** The container can show on top of lock screen. */
+ public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6;
+
+ /**
+ * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is
+ * used to prevent seamless rotation.
+ * TODO(b/194540864): Once we can include all windows in transition, then replace this with
+ * something like FLAG_IS_SYSTEM_ALERT instead. Then we can do mixed rotations.
+ */
+ public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 5;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 8;
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
@@ -91,20 +115,24 @@ public final class TransitionInfo implements Parcelable {
FLAG_TRANSLUCENT,
FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT,
FLAG_IS_VOICE_INTERACTION,
+ FLAG_IS_DISPLAY,
+ FLAG_OCCLUDES_KEYGUARD,
+ FLAG_DISPLAY_HAS_ALERT_WINDOWS,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
- private final @WindowManager.TransitionOldType int mType;
- private final @WindowManager.TransitionFlags int mFlags;
+ private final @TransitionType int mType;
+ private final @TransitionFlags int mFlags;
private final ArrayList<Change> mChanges = new ArrayList<>();
private SurfaceControl mRootLeash;
private final Point mRootOffset = new Point();
+ private AnimationOptions mOptions;
+
/** @hide */
- public TransitionInfo(@WindowManager.TransitionOldType int type,
- @WindowManager.TransitionFlags int flags) {
+ public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) {
mType = type;
mFlags = flags;
}
@@ -116,6 +144,7 @@ public final class TransitionInfo implements Parcelable {
mRootLeash = new SurfaceControl();
mRootLeash.readFromParcel(in);
mRootOffset.readFromParcel(in);
+ mOptions = in.readTypedObject(AnimationOptions.CREATOR);
}
@Override
@@ -126,6 +155,7 @@ public final class TransitionInfo implements Parcelable {
dest.writeList(mChanges);
mRootLeash.writeToParcel(dest, flags);
mRootOffset.writeToParcel(dest, flags);
+ dest.writeTypedObject(mOptions, flags);
}
@NonNull
@@ -154,7 +184,11 @@ public final class TransitionInfo implements Parcelable {
mRootOffset.set(offsetLeft, offsetTop);
}
- public int getType() {
+ public void setAnimationOptions(AnimationOptions options) {
+ mOptions = options;
+ }
+
+ public @TransitionType int getType() {
return mType;
}
@@ -182,6 +216,14 @@ public final class TransitionInfo implements Parcelable {
return mRootOffset;
}
+ public AnimationOptions getAnimationOptions() {
+ return mOptions;
+ }
+
+ /**
+ * @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom
+ * in Z (meaning index 0 is the top-most container).
+ */
@NonNull
public List<Change> getChanges() {
return mChanges;
@@ -208,10 +250,17 @@ public final class TransitionInfo implements Parcelable {
mChanges.add(change);
}
+ /**
+ * Whether this transition includes keyguard going away.
+ */
+ public boolean isKeyguardGoingAway() {
+ return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("{t=" + transitTypeToString(mType) + " f=" + Integer.toHexString(mFlags)
+ sb.append("{t=" + transitTypeToString(mType) + " f=0x" + Integer.toHexString(mFlags)
+ " ro=" + mRootOffset + " c=[");
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
@@ -257,6 +306,15 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "IS_VOICE_INTERACTION");
}
+ if ((flags & FLAG_IS_DISPLAY) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "IS_DISPLAY");
+ }
+ if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "OCCLUDES_KEYGUARD");
+ }
+ if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
}
@@ -302,8 +360,10 @@ public final class TransitionInfo implements Parcelable {
private final Rect mEndAbsBounds = new Rect();
private final Point mEndRelOffset = new Point();
private ActivityManager.RunningTaskInfo mTaskInfo = null;
+ private boolean mAllowEnterPip;
private int mStartRotation = ROTATION_UNDEFINED;
private int mEndRotation = ROTATION_UNDEFINED;
+ private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
mContainer = container;
@@ -321,8 +381,10 @@ public final class TransitionInfo implements Parcelable {
mEndAbsBounds.readFromParcel(in);
mEndRelOffset.readFromParcel(in);
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+ mAllowEnterPip = in.readBoolean();
mStartRotation = in.readInt();
mEndRotation = in.readInt();
+ mRotationAnimation = in.readInt();
}
/** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -363,12 +425,25 @@ public final class TransitionInfo implements Parcelable {
mTaskInfo = taskInfo;
}
+ /** Sets the allowEnterPip flag which represents AppOpsManager check on PiP permission */
+ public void setAllowEnterPip(boolean allowEnterPip) {
+ mAllowEnterPip = allowEnterPip;
+ }
+
/** Sets the start and end rotation of this container. */
public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) {
mStartRotation = start;
mEndRotation = end;
}
+ /**
+ * Sets the app-requested animation type for rotation. Will be one of the
+ * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams};
+ */
+ public void setRotationAnimation(int anim) {
+ mRotationAnimation = anim;
+ }
+
/** @return the container that is changing. May be null if non-remotable (eg. activity) */
@Nullable
public WindowContainerToken getContainer() {
@@ -432,6 +507,10 @@ public final class TransitionInfo implements Parcelable {
return mTaskInfo;
}
+ public boolean getAllowEnterPip() {
+ return mAllowEnterPip;
+ }
+
public int getStartRotation() {
return mStartRotation;
}
@@ -440,6 +519,11 @@ public final class TransitionInfo implements Parcelable {
return mEndRotation;
}
+ /** @return the rotation animation. */
+ public int getRotationAnimation() {
+ return mRotationAnimation;
+ }
+
/** @hide */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -452,8 +536,10 @@ public final class TransitionInfo implements Parcelable {
mEndAbsBounds.writeToParcel(dest, flags);
mEndRelOffset.writeToParcel(dest, flags);
dest.writeTypedObject(mTaskInfo, flags);
+ dest.writeBoolean(mAllowEnterPip);
dest.writeInt(mStartRotation);
dest.writeInt(mEndRotation);
+ dest.writeInt(mRotationAnimation);
}
@NonNull
@@ -481,7 +567,149 @@ public final class TransitionInfo implements Parcelable {
return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
- + mStartRotation + "->" + mEndRotation + "}";
+ + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
+ }
+ }
+
+ /** Represents animation options during a transition */
+ public static final class AnimationOptions implements Parcelable {
+
+ private int mType;
+ private int mEnterResId;
+ private int mExitResId;
+ private boolean mOverrideTaskTransition;
+ private String mPackageName;
+ private final Rect mTransitionBounds = new Rect();
+ private HardwareBuffer mThumbnail;
+
+ private AnimationOptions(int type) {
+ mType = type;
+ }
+
+ public AnimationOptions(Parcel in) {
+ mType = in.readInt();
+ mEnterResId = in.readInt();
+ mExitResId = in.readInt();
+ mOverrideTaskTransition = in.readBoolean();
+ mPackageName = in.readString();
+ mTransitionBounds.readFromParcel(in);
+ mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
+ }
+
+ public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
+ int exitResId, boolean overrideTaskTransition) {
+ AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
+ options.mPackageName = packageName;
+ options.mEnterResId = enterResId;
+ options.mExitResId = exitResId;
+ options.mOverrideTaskTransition = overrideTaskTransition;
+ return options;
+ }
+
+ public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width,
+ int height) {
+ AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP);
+ options.mTransitionBounds.set(startX, startY, startX + width, startY + height);
+ return options;
+ }
+
+ public static AnimationOptions makeThumnbnailAnimOptions(HardwareBuffer srcThumb,
+ int startX, int startY, boolean scaleUp) {
+ AnimationOptions options = new AnimationOptions(
+ scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN);
+ options.mTransitionBounds.set(startX, startY, startX, startY);
+ options.mThumbnail = srcThumb;
+ return options;
+ }
+
+ public static AnimationOptions makeCrossProfileAnimOptions() {
+ AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS);
+ return options;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getEnterResId() {
+ return mEnterResId;
+ }
+
+ public int getExitResId() {
+ return mExitResId;
+ }
+
+ public boolean getOverrideTaskTransition() {
+ return mOverrideTaskTransition;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Rect getTransitionBounds() {
+ return mTransitionBounds;
+ }
+
+ public HardwareBuffer getThumbnail() {
+ return mThumbnail;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mEnterResId);
+ dest.writeInt(mExitResId);
+ dest.writeBoolean(mOverrideTaskTransition);
+ dest.writeString(mPackageName);
+ mTransitionBounds.writeToParcel(dest, flags);
+ dest.writeTypedObject(mThumbnail, flags);
+ }
+
+ @NonNull
+ public static final Creator<AnimationOptions> CREATOR =
+ new Creator<AnimationOptions>() {
+ @Override
+ public AnimationOptions createFromParcel(Parcel in) {
+ return new AnimationOptions(in);
+ }
+
+ @Override
+ public AnimationOptions[] newArray(int size) {
+ return new AnimationOptions[size];
+ }
+ };
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ private static String typeToString(int mode) {
+ switch(mode) {
+ case ANIM_CUSTOM: return "ANIM_CUSTOM";
+ case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
+ case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
+ case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
+ case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
+ default: return "<unknown:" + mode + ">";
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{ AnimationOtions type= " + typeToString(mType) + " package=" + mPackageName
+ + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
}
}
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index c0af57214e5e..342df4f9eee3 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -19,7 +19,9 @@ package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.PendingIntent;
import android.app.WindowConfiguration;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -34,6 +36,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Represents a collection of operations on some WindowContainers that should be applied all at
@@ -48,11 +51,19 @@ public final class WindowContainerTransaction implements Parcelable {
// Flat list because re-order operations are order-dependent
private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
+ @Nullable
+ private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
public WindowContainerTransaction() {}
private WindowContainerTransaction(Parcel in) {
in.readMap(mChanges, null /* loader */);
in.readList(mHierarchyOps, null /* loader */);
+ mErrorCallbackToken = in.readStrongBinder();
+ mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
}
private Change getOrCreateChange(IBinder token) {
@@ -325,7 +336,8 @@ public final class WindowContainerTransaction implements Parcelable {
/**
* Sets to containers adjacent to each other. Containers below two visible adjacent roots will
- * be made invisible. This currently only applies to Task containers created by organizer.
+ * be made invisible. This currently only applies to TaskFragment containers created by
+ * organizer.
* @param root1 the first root.
* @param root2 the second root.
*/
@@ -378,6 +390,176 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Sends a pending intent in sync.
+ * @param sender The PendingIntent sender.
+ * @param intent The fillIn intent to patch over the sender's base intent.
+ * @param options bundle containing ActivityOptions for the task's top activity.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent,
+ @Nullable Bundle options) {
+ mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
+ .setLaunchOptions(options)
+ .setPendingIntent(sender)
+ .setActivityIntent(intent)
+ .build());
+ return this;
+ }
+
+ /**
+ * Creates a new TaskFragment with the given options.
+ * @param taskFragmentOptions the options used to create the TaskFragment.
+ */
+ @NonNull
+ public WindowContainerTransaction createTaskFragment(
+ @NonNull TaskFragmentCreationParams taskFragmentOptions) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT)
+ .setTaskFragmentCreationOptions(taskFragmentOptions)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Deletes an existing TaskFragment. Any remaining activities below it will be destroyed.
+ * @param taskFragment the TaskFragment to be removed.
+ */
+ @NonNull
+ public WindowContainerTransaction deleteTaskFragment(
+ @NonNull WindowContainerToken taskFragment) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT)
+ .setContainer(taskFragment.asBinder())
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Starts an activity in the TaskFragment.
+ * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+ * {@link TaskFragmentCreationParams#getFragmentToken()}.
+ * @param callerToken the activity token that initialized the activity launch.
+ * @param activityIntent intent to start the activity.
+ * @param activityOptions ActivityOptions to start the activity with.
+ * @see android.content.Context#startActivity(Intent, Bundle).
+ */
+ @NonNull
+ public WindowContainerTransaction startActivityInTaskFragment(
+ @NonNull IBinder fragmentToken, @NonNull IBinder callerToken,
+ @NonNull Intent activityIntent, @Nullable Bundle activityOptions) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT)
+ .setContainer(fragmentToken)
+ .setReparentContainer(callerToken)
+ .setActivityIntent(activityIntent)
+ .setLaunchOptions(activityOptions)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Moves an activity into the TaskFragment.
+ * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+ * {@link TaskFragmentCreationParams#getFragmentToken()}.
+ * @param activityToken activity to be reparented.
+ */
+ @NonNull
+ public WindowContainerTransaction reparentActivityToTaskFragment(
+ @NonNull IBinder fragmentToken, @NonNull IBinder activityToken) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT)
+ .setReparentContainer(fragmentToken)
+ .setContainer(activityToken)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Reparents all children of one TaskFragment to another.
+ * @param oldParent children of this TaskFragment will be reparented.
+ * @param newParent the new parent TaskFragment to move the children to. If {@code null}, the
+ * children will be moved to the leaf Task.
+ */
+ @NonNull
+ public WindowContainerTransaction reparentChildren(
+ @NonNull WindowContainerToken oldParent,
+ @Nullable WindowContainerToken newParent) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
+ .setContainer(oldParent.asBinder())
+ .setReparentContainer(newParent != null ? newParent.asBinder() : null)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * Sets to TaskFragments adjacent to each other. Containers below two visible adjacent
+ * TaskFragments will be made invisible. This is similar to
+ * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with
+ * fragmentTokens when that TaskFragments haven't been created (but will be created in the same
+ * {@link WindowContainerTransaction}).
+ * To reset it, pass {@code null} for {@code fragmentToken2}.
+ * @param fragmentToken1 client assigned unique token to create TaskFragment with specified
+ * in {@link TaskFragmentCreationParams#getFragmentToken()}.
+ * @param fragmentToken2 client assigned unique token to create TaskFragment with specified
+ * in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is
+ * {@code null}, the transaction will reset the adjacent TaskFragment.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setAdjacentTaskFragments(
+ @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
+ .setContainer(fragmentToken1)
+ .setReparentContainer(fragmentToken2)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
+ * When this {@link WindowContainerTransaction} failed to finish on the server side, it will
+ * trigger callback with this {@param errorCallbackToken}.
+ * @param errorCallbackToken client provided token that will be passed back as parameter in
+ * the callback if there is an error on the server side.
+ * @see ITaskFragmentOrganizer#onTaskFragmentError
+ */
+ @NonNull
+ public WindowContainerTransaction setErrorCallbackToken(@NonNull IBinder errorCallbackToken) {
+ if (mErrorCallbackToken != null) {
+ throw new IllegalStateException("Can't set multiple error token for one transaction.");
+ }
+ mErrorCallbackToken = errorCallbackToken;
+ return this;
+ }
+
+ /**
+ * Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
+ * When this is set, the server side will not check for the permission of
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
+ * contains operations that are allowed for this organizer, such as modifying TaskFragments that
+ * are organized by this organizer.
+ * @hide
+ */
+ @NonNull
+ WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
+ if (mTaskFragmentOrganizer != null) {
+ throw new IllegalStateException("Can't set multiple organizers for one transaction.");
+ }
+ mTaskFragmentOrganizer = organizer;
+ return this;
+ }
+
+ /**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
* other in an unusable state. When false, other is left alone, but
@@ -398,6 +580,23 @@ public final class WindowContainerTransaction implements Parcelable {
mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
: new HierarchyOp(other.mHierarchyOps.get(i)));
}
+ if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
+ != other.mErrorCallbackToken) {
+ throw new IllegalArgumentException("Can't merge two WCTs with different error token");
+ }
+ final IBinder taskFragmentOrganizerAsBinder = mTaskFragmentOrganizer != null
+ ? mTaskFragmentOrganizer.asBinder()
+ : null;
+ final IBinder otherTaskFragmentOrganizerAsBinder = other.mTaskFragmentOrganizer != null
+ ? other.mTaskFragmentOrganizer.asBinder()
+ : null;
+ if (!Objects.equals(taskFragmentOrganizerAsBinder, otherTaskFragmentOrganizerAsBinder)) {
+ throw new IllegalArgumentException(
+ "Can't merge two WCTs from different TaskFragmentOrganizers");
+ }
+ mErrorCallbackToken = mErrorCallbackToken != null
+ ? mErrorCallbackToken
+ : other.mErrorCallbackToken;
}
/** @hide */
@@ -415,10 +614,26 @@ public final class WindowContainerTransaction implements Parcelable {
return mHierarchyOps;
}
+ /** @hide */
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
+ /** @hide */
+ @Nullable
+ public ITaskFragmentOrganizer getTaskFragmentOrganizer() {
+ return mTaskFragmentOrganizer;
+ }
+
@Override
@NonNull
public String toString() {
- return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
+ return "WindowContainerTransaction {"
+ + " changes = " + mChanges
+ + " hops = " + mHierarchyOps
+ + " errorCallbackToken=" + mErrorCallbackToken
+ + " taskFragmentOrganizer=" + mTaskFragmentOrganizer
+ " }";
}
@@ -427,6 +642,8 @@ public final class WindowContainerTransaction implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeMap(mChanges);
dest.writeList(mHierarchyOps);
+ dest.writeStrongBinder(mErrorCallbackToken);
+ dest.writeStrongInterface(mTaskFragmentOrganizer);
}
@Override
@@ -705,6 +922,13 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
+ public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7;
+ public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8;
+ public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
+ public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
+ public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
+ public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
+ public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -713,75 +937,101 @@ public final class WindowContainerTransaction implements Parcelable {
private final int mType;
// Container we are performing the operation on.
- private final IBinder mContainer;
+ @Nullable
+ private IBinder mContainer;
// If this is same as mContainer, then only change position, don't reparent.
- private final IBinder mReparent;
+ @Nullable
+ private IBinder mReparent;
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
- private final boolean mToTop;
+ private boolean mToTop;
- final private int[] mWindowingModes;
- final private int[] mActivityTypes;
+ @Nullable
+ private int[] mWindowingModes;
- private final Bundle mLaunchOptions;
+ @Nullable
+ private int[] mActivityTypes;
+
+ @Nullable
+ private Bundle mLaunchOptions;
+
+ @Nullable
+ private Intent mActivityIntent;
+
+ // Used as options for WindowContainerTransaction#createTaskFragment().
+ @Nullable
+ private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+
+ @Nullable
+ private PendingIntent mPendingIntent;
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
- container, reparent, null, null, toTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
+ .setContainer(container)
+ .setReparentContainer(reparent)
+ .setToTop(toTop)
+ .build();
}
public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_REORDER,
- container, container, null, null, toTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
+ .setContainer(container)
+ .setReparentContainer(container)
+ .setToTop(toTop)
+ .build();
}
public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
- currentParent, newParent, windowingModes, activityTypes, onTop, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
+ .setContainer(currentParent)
+ .setReparentContainer(newParent)
+ .setWindowingModes(windowingModes)
+ .setActivityTypes(activityTypes)
+ .setToTop(onTop)
+ .build();
}
public static HierarchyOp createForSetLaunchRoot(IBinder container,
int[] windowingModes, int[] activityTypes) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
- container, null, windowingModes, activityTypes, false, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)
+ .setContainer(container)
+ .setWindowingModes(windowingModes)
+ .setActivityTypes(activityTypes)
+ .build();
}
public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
- root1, root2, null, null, false, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
+ .setContainer(root1)
+ .setReparentContainer(root2)
+ .build();
}
/** Create a hierarchy op for launching a task. */
public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
final Bundle fullOptions = options == null ? new Bundle() : options;
fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
- return new HierarchyOp(HIERARCHY_OP_TYPE_LAUNCH_TASK, null, null, null, null, true,
- fullOptions);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+ .setToTop(true)
+ .setLaunchOptions(fullOptions)
+ .build();
}
/** Create a hierarchy op for setting launch adjacent flag root. */
public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
boolean clearRoot) {
- return new HierarchyOp(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT, container, null,
- null, null, clearRoot, null);
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT)
+ .setContainer(container)
+ .setToTop(clearRoot)
+ .build();
}
-
- private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,
- int[] windowingModes, int[] activityTypes, boolean toTop,
- @Nullable Bundle launchOptions) {
+ /** Only creates through {@link Builder}. */
+ private HierarchyOp(int type) {
mType = type;
- mContainer = container;
- mReparent = reparent;
- mWindowingModes = windowingModes != null ?
- Arrays.copyOf(windowingModes, windowingModes.length) : null;
- mActivityTypes = activityTypes != null ?
- Arrays.copyOf(activityTypes, activityTypes.length) : null;
- mToTop = toTop;
- mLaunchOptions = launchOptions;
}
public HierarchyOp(@NonNull HierarchyOp copy) {
@@ -792,6 +1042,9 @@ public final class WindowContainerTransaction implements Parcelable {
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
+ mActivityIntent = copy.mActivityIntent;
+ mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
+ mPendingIntent = copy.mPendingIntent;
}
protected HierarchyOp(Parcel in) {
@@ -802,6 +1055,9 @@ public final class WindowContainerTransaction implements Parcelable {
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
+ mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
}
public int getType() {
@@ -827,6 +1083,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mReparent;
}
+ @NonNull
+ public IBinder getCallingActivity() {
+ return mReparent;
+ }
+
public boolean getToTop() {
return mToTop;
}
@@ -844,6 +1105,21 @@ public final class WindowContainerTransaction implements Parcelable {
return mLaunchOptions;
}
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public TaskFragmentCreationParams getTaskFragmentCreationOptions() {
+ return mTaskFragmentCreationOptions;
+ }
+
+ @Nullable
+ public PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
@Override
public String toString() {
switch (mType) {
@@ -868,6 +1144,22 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
+ "}";
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ return "{CreateTaskFragment: options=" + mTaskFragmentCreationOptions + "}";
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+ return "{DeleteTaskFragment: taskFragment=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ return "{StartActivityInTaskFragment: fragmentToken=" + mContainer + " intent="
+ + mActivityIntent + " options=" + mLaunchOptions + "}";
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ return "{ReparentActivityToTaskFragment: fragmentToken=" + mReparent
+ + " activity=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ return "{ReparentChildren: oldParent=" + mContainer + " newParent=" + mReparent
+ + "}";
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
+ return "{SetAdjacentTaskFragments: container=" + mContainer
+ + " adjacentContainer=" + mReparent + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
@@ -884,6 +1176,9 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
+ dest.writeTypedObject(mPendingIntent, flags);
}
@Override
@@ -902,5 +1197,105 @@ public final class WindowContainerTransaction implements Parcelable {
return new HierarchyOp[size];
}
};
+
+ private static class Builder {
+
+ private final int mType;
+
+ @Nullable
+ private IBinder mContainer;
+
+ @Nullable
+ private IBinder mReparent;
+
+ private boolean mToTop;
+
+ @Nullable
+ private int[] mWindowingModes;
+
+ @Nullable
+ private int[] mActivityTypes;
+
+ @Nullable
+ private Bundle mLaunchOptions;
+
+ @Nullable
+ private Intent mActivityIntent;
+
+ @Nullable
+ private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+
+ @Nullable
+ private PendingIntent mPendingIntent;
+
+ Builder(int type) {
+ mType = type;
+ }
+
+ Builder setContainer(@Nullable IBinder container) {
+ mContainer = container;
+ return this;
+ }
+
+ Builder setReparentContainer(@Nullable IBinder reparentContainer) {
+ mReparent = reparentContainer;
+ return this;
+ }
+
+ Builder setToTop(boolean toTop) {
+ mToTop = toTop;
+ return this;
+ }
+
+ Builder setWindowingModes(@Nullable int[] windowingModes) {
+ mWindowingModes = windowingModes;
+ return this;
+ }
+
+ Builder setActivityTypes(@Nullable int[] activityTypes) {
+ mActivityTypes = activityTypes;
+ return this;
+ }
+
+ Builder setLaunchOptions(@Nullable Bundle launchOptions) {
+ mLaunchOptions = launchOptions;
+ return this;
+ }
+
+ Builder setActivityIntent(@Nullable Intent activityIntent) {
+ mActivityIntent = activityIntent;
+ return this;
+ }
+
+ Builder setPendingIntent(@Nullable PendingIntent sender) {
+ mPendingIntent = sender;
+ return this;
+ }
+
+ Builder setTaskFragmentCreationOptions(
+ @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) {
+ mTaskFragmentCreationOptions = taskFragmentCreationOptions;
+ return this;
+ }
+
+ HierarchyOp build() {
+ final HierarchyOp hierarchyOp = new HierarchyOp(mType);
+ hierarchyOp.mContainer = mContainer;
+ hierarchyOp.mReparent = mReparent;
+ hierarchyOp.mWindowingModes = mWindowingModes != null
+ ? Arrays.copyOf(mWindowingModes, mWindowingModes.length)
+ : null;
+ hierarchyOp.mActivityTypes = mActivityTypes != null
+ ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
+ : null;
+ hierarchyOp.mToTop = mToTop;
+ hierarchyOp.mLaunchOptions = mLaunchOptions;
+ hierarchyOp.mActivityIntent = mActivityIntent;
+ hierarchyOp.mPendingIntent = mPendingIntent;
+ hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+
+ return hierarchyOp;
+ }
+ }
}
}
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 901625b0732c..cfccb712127e 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,7 +26,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.IBinder;
+import android.view.Display;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -43,23 +43,33 @@ import java.lang.ref.Reference;
* @hide
*/
@UiContext
-public class WindowContext extends ContextWrapper {
+public class WindowContext extends ContextWrapper implements WindowProvider {
private final WindowManager mWindowManager;
- private final @WindowManager.LayoutParams.WindowType int mType;
- private final @Nullable Bundle mOptions;
+ @WindowManager.LayoutParams.WindowType
+ private final int mType;
+ @Nullable
+ private final Bundle mOptions;
private final ComponentCallbacksController mCallbacksController =
new ComponentCallbacksController();
private final WindowContextController mController;
/**
- * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
- * the token.
+ * Default implementation of {@link WindowContext}
+ * <p>
+ * Note that the users should call {@link Context#createWindowContext(Display, int, Bundle)}
+ * to create a {@link WindowContext} instead of using this constructor
+ * </p><p>
+ * Example usage:
+ * <pre class="prettyprint">
+ * Bundle options = new Bundle();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * Context windowContext = context.createWindowContext(display, windowType, options);
+ * </pre></p>
*
- * @param base Base {@link Context} for this new instance.
- * @param type Window type to be used with this context.
+ * @param base Base {@link Context} for this new instance.
+ * @param type Window type to be used with this context.
* @param options A bundle used to pass window-related options.
- *
- * @hide
+ * @see DisplayAreaInfo#rootDisplayAreaId
*/
public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
super(base);
@@ -67,7 +77,7 @@ public class WindowContext extends ContextWrapper {
mType = type;
mOptions = options;
mWindowManager = createWindowContextWindowManager(this);
- IBinder token = getWindowContextToken();
+ WindowTokenClient token = (WindowTokenClient) getWindowContextToken();
mController = new WindowContextController(token);
Reference.reachabilityFence(this);
@@ -105,10 +115,13 @@ public class WindowContext extends ContextWrapper {
@Override
public void destroy() {
- mCallbacksController.clearCallbacks();
- // Called to the base ContextImpl to do final clean-up.
- getBaseContext().destroy();
- Reference.reachabilityFence(this);
+ try {
+ mCallbacksController.clearCallbacks();
+ // Called to the base ContextImpl to do final clean-up.
+ getBaseContext().destroy();
+ } finally {
+ Reference.reachabilityFence(this);
+ }
}
@Override
@@ -125,4 +138,15 @@ public class WindowContext extends ContextWrapper {
void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
mCallbacksController.dispatchConfigurationChanged(newConfig);
}
+
+ @Override
+ public int getWindowType() {
+ return mType;
+ }
+
+ @Nullable
+ @Override
+ public Bundle getWindowContextOptions() {
+ return mOptions;
+ }
}
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index d84f571931fd..5aa623388574 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -19,6 +19,7 @@ package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,7 +47,7 @@ public class WindowContextController {
@VisibleForTesting
public boolean mAttachedToDisplayArea;
@NonNull
- private final IBinder mToken;
+ private final WindowTokenClient mToken;
/**
* Window Context Controller constructor
@@ -54,14 +55,13 @@ public class WindowContextController {
* @param token The token used to attach to a window manager node. It is usually from
* {@link Context#getWindowContextToken()}.
*/
- public WindowContextController(@NonNull IBinder token) {
- mToken = token;
- mWms = WindowManagerGlobal.getWindowManagerService();
+ public WindowContextController(@NonNull WindowTokenClient token) {
+ this(token, WindowManagerGlobal.getWindowManagerService());
}
/** Used for test only. DO NOT USE it in production code. */
@VisibleForTesting
- public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) {
+ public WindowContextController(@NonNull WindowTokenClient token, IWindowManager mockWms) {
mToken = token;
mWms = mockWms;
}
@@ -81,8 +81,15 @@ public class WindowContextController {
+ "a DisplayArea once.");
}
try {
- mAttachedToDisplayArea = mWms.attachWindowContextToDisplayArea(mToken, type, displayId,
- options);
+ final Configuration configuration = mWms.attachWindowContextToDisplayArea(mToken, type,
+ displayId, options);
+ if (configuration != null) {
+ mAttachedToDisplayArea = true;
+ // Send the DisplayArea's configuration to WindowContext directly instead of
+ // waiting for dispatching from WMS.
+ mToken.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/window/WindowInfosListener.java b/core/java/android/window/WindowInfosListener.java
new file mode 100644
index 000000000000..4376e3eb572e
--- /dev/null
+++ b/core/java/android/window/WindowInfosListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.view.InputWindowHandle;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Listener for getting {@link InputWindowHandle} updates from SurfaceFlinger.
+ * @hide
+ */
+public abstract class WindowInfosListener {
+ private final long mNativeListener;
+
+ public WindowInfosListener() {
+ NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced(
+ WindowInfosListener.class.getClassLoader(), nativeGetFinalizer());
+
+ mNativeListener = nativeCreate(this);
+ registry.registerNativeAllocation(this, mNativeListener);
+ }
+
+ /**
+ * Called when WindowInfos in SurfaceFlinger have changed.
+ * @param windowHandles Reverse Z ordered array of window information that was on screen,
+ * where the first value is the topmost window.
+ */
+ public abstract void onWindowInfosChanged(InputWindowHandle[] windowHandles);
+
+ /**
+ * Register the WindowInfosListener.
+ */
+ public void register() {
+ nativeRegister(mNativeListener);
+ }
+
+ /**
+ * Unregisters the WindowInfosListener.
+ */
+ public void unregister() {
+ nativeUnregister(mNativeListener);
+ }
+
+ private static native long nativeCreate(WindowInfosListener thiz);
+ private static native void nativeRegister(long ptr);
+ private static native void nativeUnregister(long ptr);
+ private static native long nativeGetFinalizer();
+}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 544d42240079..e9b8174567a8 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -25,6 +25,7 @@ import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Singleton;
+import android.view.RemoteAnimationAdapter;
/**
* Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -36,9 +37,16 @@ public class WindowOrganizer {
/**
* Apply multiple WindowContainer operations at once.
+ *
+ * Note that using this API requires the caller to hold
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
+ * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
+ * created by itself.
+ *
* @param t The transaction to apply.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
+ conditional = true)
public void applyTransaction(@NonNull WindowContainerTransaction t) {
try {
if (!t.isEmpty()) {
@@ -51,6 +59,12 @@ public class WindowOrganizer {
/**
* Apply multiple WindowContainer operations at once.
+ *
+ * Note that using this API requires the caller to hold
+ * {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
+ * {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
+ * created by itself.
+ *
* @param t The transaction to apply.
* @param callback This transaction will use the synchronization scheme described in
* BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
@@ -58,7 +72,8 @@ public class WindowOrganizer {
* @return An ID for the sync operation which will later be passed to transactionReady callback.
* This lets the caller differentiate overlapping sync operations.
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
+ conditional = true)
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
@NonNull WindowContainerTransactionCallback callback) {
try {
@@ -111,6 +126,27 @@ public class WindowOrganizer {
}
/**
+ * Start a legacy transition.
+ * @param type The type of the transition. This is ignored if a transitionToken is provided.
+ * @param adapter An existing transition to start. If null, a new transition is created.
+ * @param t The set of window operations that are part of this transition.
+ * @return true on success, false if a transition was already running.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
+ @NonNull
+ public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
+ @NonNull WindowContainerTransactionCallback syncCallback,
+ @NonNull WindowContainerTransaction t) {
+ try {
+ return getWindowOrganizerController().startLegacyTransition(
+ type, adapter, syncCallback.mInterface, t);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Register an ITransitionPlayer to handle transition animations.
* @hide
*/
@@ -123,7 +159,6 @@ public class WindowOrganizer {
}
}
- @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
IWindowOrganizerController getWindowOrganizerController() {
return IWindowOrganizerControllerSingleton.get();
}
diff --git a/core/java/android/window/WindowProvider.java b/core/java/android/window/WindowProvider.java
new file mode 100644
index 000000000000..b078b9362b90
--- /dev/null
+++ b/core/java/android/window/WindowProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.os.Bundle;
+import android.view.WindowManager.LayoutParams.WindowType;
+
+/**
+ * An interface to provide a non-activity window.
+ * Examples are {@link WindowContext} and {@link WindowProviderService}.
+ *
+ * @hide
+ */
+public interface WindowProvider {
+ /** @hide */
+ String KEY_IS_WINDOW_PROVIDER_SERVICE = "android.windowContext.isWindowProviderService";
+
+ /** Gets the window type of this provider */
+ @WindowType
+ int getWindowType();
+
+ /** Gets the launch options of this provider */
+ @Nullable
+ Bundle getWindowContextOptions();
+}
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index b8619fbcf334..f8484d15344e 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -36,27 +36,45 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.view.WindowManagerImpl;
-// TODO(b/159767464): handle #onConfigurationChanged(Configuration)
/**
* A {@link Service} responsible for showing a non-activity window, such as software keyboards or
* accessibility overlay windows. This {@link Service} has similar behavior to
* {@link WindowContext}, but is represented as {@link Service}.
*
* @see android.inputmethodservice.InputMethodService
- * @see android.accessibilityservice.AccessibilityService
*
* @hide
*/
@TestApi
@UiContext
-public abstract class WindowProviderService extends Service {
+public abstract class WindowProviderService extends Service implements WindowProvider {
+ private final Bundle mOptions;
private final WindowTokenClient mWindowToken = new WindowTokenClient();
private final WindowContextController mController = new WindowContextController(mWindowToken);
private WindowManager mWindowManager;
+ private boolean mInitialized;
/**
- * Returns the type of this {@link WindowProviderService}.
+ * Returns {@code true} if the {@code windowContextOptions} declares that it is a
+ * {@link WindowProviderService}.
+ *
+ * @hide
+ */
+ public static boolean isWindowProviderService(@Nullable Bundle windowContextOptions) {
+ if (windowContextOptions == null) {
+ return false;
+ }
+ return (windowContextOptions.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false));
+ }
+
+ public WindowProviderService() {
+ mOptions = new Bundle();
+ mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+ }
+
+ /**
+ * Returns the window type of this {@link WindowProviderService}.
* Each inheriting class must implement this method to provide the type of the window. It is
* used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)}
*
@@ -68,15 +86,24 @@ public abstract class WindowProviderService extends Service {
@SuppressLint("OnNameExpected")
// Suppress the lint because it is not a callback and users should provide window type
// so we cannot make it final.
- public abstract @WindowType int getWindowType();
+ @WindowType
+ @Override
+ public abstract int getWindowType();
/**
* Returns the option of this {@link WindowProviderService}.
- * Default is {@code null}. The inheriting class can implement this method to provide the
- * customization {@code option} of the window. It is used similar to {@code options} of
- * {@link Context#createWindowContext(int, Bundle)}
- *
- * @see Context#createWindowContext(int, Bundle)
+ * <p>
+ * The inheriting class can implement this method to provide the customization {@code option} of
+ * the window, but must be based on this method's returned value.
+ * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)}
+ * </p>
+ * <pre class="prettyprint">
+ * public Bundle getWindowContextOptions() {
+ * final Bundle options = super.getWindowContextOptions();
+ * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+ * return options;
+ * }
+ * </pre>
*
* @hide
*/
@@ -85,8 +112,24 @@ public abstract class WindowProviderService extends Service {
// Suppress the lint because it is not a callback and users may override this API to provide
// launch option. Also, the return value of this API is null by default.
@Nullable
+ @CallSuper
+ @Override
public Bundle getWindowContextOptions() {
- return null;
+ return mOptions;
+ }
+
+ /**
+ * Returns the display ID to launch this {@link WindowProviderService}.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint({"OnNameExpected"})
+ // Suppress the lint because it is not a callback and users may override this API to provide
+ // display.
+ @NonNull
+ public int getInitialDisplayId() {
+ return DEFAULT_DISPLAY;
}
/**
@@ -104,19 +147,22 @@ public abstract class WindowProviderService extends Service {
public final Context createServiceBaseContext(ActivityThread mainThread,
LoadedApk packageInfo) {
final Context context = super.createServiceBaseContext(mainThread, packageInfo);
- // Always associate with the default display at initialization.
- final Display defaultDisplay = context.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY);
- return context.createTokenContext(mWindowToken, defaultDisplay);
+ final Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(getInitialDisplayId());
+ return context.createTokenContext(mWindowToken, display);
}
- @CallSuper
+ /** @hide */
@Override
- public void onCreate() {
- super.onCreate();
- mWindowToken.attachContext(this);
- mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions());
- mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+ protected void attachBaseContext(Context newBase) {
+ super.attachBaseContext(newBase);
+ if (!mInitialized) {
+ mWindowToken.attachContext(this);
+ mController.attachToDisplayArea(getWindowType(), getDisplayId(),
+ getWindowContextOptions());
+ mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this);
+ mInitialized = true;
+ }
}
@SuppressLint("OnNameExpected")
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 6abf5575d353..f3e3859b4256 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -15,14 +15,25 @@
*/
package android.window;
+import static android.window.ConfigurationHelper.diffPublicWithSizeBuckets;
+import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
+import static android.window.ConfigurationHelper.isDifferentDisplay;
+import static android.window.ConfigurationHelper.shouldUpdateResources;
+
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.inputmethodservice.AbstractInputMethodService;
+import android.os.Build;
import android.os.Bundle;
+import android.os.Debug;
import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.WeakReference;
@@ -33,11 +44,13 @@ import java.lang.ref.WeakReference;
* {@link Context#getWindowContextToken() the token of non-Activity UI Contexts}.
*
* @see WindowContext
- * @see android.view.IWindowManager#registerWindowContextListener(IBinder, int, int, Bundle)
+ * @see android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
*
* @hide
*/
public class WindowTokenClient extends IWindowToken.Stub {
+ private static final String TAG = WindowTokenClient.class.getSimpleName();
+
/**
* Attached {@link Context} for this window token to update configuration and resources.
* Initialized by {@link #attachContext(Context)}.
@@ -46,12 +59,16 @@ public class WindowTokenClient extends IWindowToken.Stub {
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+ private final Configuration mConfiguration = new Configuration();
+
+ private boolean mShouldDumpConfigForIme;
+
/**
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
* <p>This method must be called before invoking
- * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
- * Bundle, boolean)}.<p/>
+ * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
+ * Bundle)}.<p/>
*
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
@@ -61,25 +78,87 @@ public class WindowTokenClient extends IWindowToken.Stub {
throw new IllegalStateException("Context is already attached.");
}
mContextRef = new WeakReference<>(context);
+ mConfiguration.setTo(context.getResources().getConfiguration());
+ mShouldDumpConfigForIme = Build.IS_DEBUGGABLE
+ && context instanceof AbstractInputMethodService;
}
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * @param newConfig the updated {@link Configuration}
+ * @param newDisplayId the updated {@link android.view.Display} ID
+ */
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
+ }
+
+ // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
+ // are inherited from WindowProvider.
+ /**
+ * Called when {@link Configuration} updates from the server side receive.
+ *
+ * Similar to {@link #onConfigurationChanged(Configuration, int)}, but adds a flag to control
+ * whether to dispatch configuration update or not.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
+ boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
if (context == null) {
return;
}
- final int currentDisplayId = context.getDisplayId();
- final boolean displayChanged = newDisplayId != currentDisplayId;
- final Configuration config = context.getResources().getConfiguration();
- final boolean configChanged = config.diff(newConfig) != 0;
- if (displayChanged || configChanged) {
+ final boolean displayChanged = isDifferentDisplay(context.getDisplayId(), newDisplayId);
+ final boolean shouldUpdateResources = shouldUpdateResources(this, mConfiguration,
+ newConfig, newConfig /* overrideConfig */, displayChanged,
+ null /* configChanged */);
+
+ if (!shouldUpdateResources && mShouldDumpConfigForIme) {
+ Log.d(TAG, "Configuration not dispatch to IME because configuration is up"
+ + " to date. Current config=" + context.getResources().getConfiguration()
+ + ", reported config=" + mConfiguration
+ + ", updated config=" + newConfig);
+ }
+
+ if (shouldUpdateResources) {
// TODO(ag/9789103): update resource manager logic to track non-activity tokens
mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
- if (context instanceof WindowContext) {
+
+ if (shouldReportConfigChange && context instanceof WindowContext) {
+ final WindowContext windowContext = (WindowContext) context;
ActivityThread.currentActivityThread().getHandler().post(
- () -> ((WindowContext) context).dispatchConfigurationChanged(newConfig));
+ () -> windowContext.dispatchConfigurationChanged(newConfig));
+ }
+
+ // Dispatch onConfigurationChanged only if there's a significant public change to
+ // make it compatible with the original behavior.
+ final Configuration[] sizeConfigurations = context.getResources()
+ .getSizeConfigurations();
+ final SizeConfigurationBuckets buckets = sizeConfigurations != null
+ ? new SizeConfigurationBuckets(sizeConfigurations) : null;
+ final int diff = diffPublicWithSizeBuckets(mConfiguration, newConfig, buckets);
+
+ if (shouldReportConfigChange && diff != 0
+ && context instanceof WindowProviderService) {
+ final WindowProviderService windowProviderService = (WindowProviderService) context;
+ ActivityThread.currentActivityThread().getHandler().post(
+ () -> windowProviderService.onConfigurationChanged(newConfig));
+ }
+ freeTextLayoutCachesIfNeeded(diff);
+ if (mShouldDumpConfigForIme) {
+ if (!shouldReportConfigChange) {
+ Log.d(TAG, "Only apply configuration update to Resources because "
+ + "shouldReportConfigChange is false.\n" + Debug.getCallers(5));
+ } else if (diff == 0) {
+ Log.d(TAG, "Configuration not dispatch to IME because configuration has no "
+ + " public difference with updated config. "
+ + " Current config=" + context.getResources().getConfiguration()
+ + ", reported config=" + mConfiguration
+ + ", updated config=" + newConfig);
+ }
}
+ mConfiguration.setTo(newConfig);
}
if (displayChanged) {
context.updateDisplay(newDisplayId);
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index c57afbc67494..179ac8b1c528 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.accessibility.util;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
@@ -30,6 +31,7 @@ import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
@@ -152,19 +154,29 @@ public final class AccessibilityStatsLogUtils {
convertToLoggingMagnificationMode(mode));
}
- private static boolean isFloatingMenuEnabled(Context context) {
+ private static boolean isAccessibilityFloatingMenuEnabled(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
== ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
}
+ private static boolean isAccessibilityGestureEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_GESTURE;
+ }
+
private static int convertToLoggingShortcutType(Context context,
@ShortcutType int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON:
- return isFloatingMenuEnabled(context)
- ? ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU
- : ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ if (isAccessibilityFloatingMenuEnabled(context)) {
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+ } else if (isAccessibilityGestureEnabled(context)) {
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+ } else {
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ }
case ACCESSIBILITY_SHORTCUT_KEY:
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
}
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 4b4e20f9181b..6a976efa5ef9 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -20,16 +20,23 @@ import static com.android.internal.accessibility.common.ShortcutConstants.Access
import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.ParcelableSpan;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.ArraySet;
import android.view.accessibility.AccessibilityManager;
+import libcore.util.EmptyArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -39,7 +46,25 @@ import java.util.Set;
* Collection of utilities for accessibility service.
*/
public final class AccessibilityUtils {
- private AccessibilityUtils() {}
+ private AccessibilityUtils() {
+ }
+
+ /** @hide */
+ @IntDef(value = {
+ NONE,
+ TEXT,
+ PARCELABLE_SPAN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface A11yTextChangeType {
+ }
+
+ /** Specifies no content has been changed for accessibility. */
+ public static final int NONE = 0;
+ /** Specifies some readable sequence has been changed. */
+ public static final int TEXT = 1;
+ /** Specifies some parcelable spans has been changed. */
+ public static final int PARCELABLE_SPAN = 2;
/**
* Returns the set of enabled accessibility services for userId. If there are no
@@ -168,4 +193,55 @@ public final class AccessibilityUtils {
Settings.Secure.USER_SETUP_COMPLETE, /* def= */ 0, UserHandle.USER_CURRENT)
!= /* false */ 0;
}
+
+ /**
+ * Returns the text change type for accessibility. It only cares about readable sequence changes
+ * or {@link ParcelableSpan} changes which are able to pass via IPC.
+ *
+ * @param before The CharSequence before changing
+ * @param after The CharSequence after changing
+ * @return Returns {@code TEXT} for readable sequence changes or {@code PARCELABLE_SPAN} for
+ * ParcelableSpan changes. Otherwise, returns {@code NONE}.
+ */
+ @A11yTextChangeType
+ public static int textOrSpanChanged(CharSequence before, CharSequence after) {
+ if (!TextUtils.equals(before, after)) {
+ return TEXT;
+ }
+ if (before instanceof Spanned || after instanceof Spanned) {
+ if (!parcelableSpansEquals(before, after)) {
+ return PARCELABLE_SPAN;
+ }
+ }
+ return NONE;
+ }
+
+ private static boolean parcelableSpansEquals(CharSequence before, CharSequence after) {
+ Object[] spansA = EmptyArray.OBJECT;
+ Object[] spansB = EmptyArray.OBJECT;
+ Spanned a = null;
+ Spanned b = null;
+ if (before instanceof Spanned) {
+ a = (Spanned) before;
+ spansA = a.getSpans(0, a.length(), ParcelableSpan.class);
+ }
+ if (after instanceof Spanned) {
+ b = (Spanned) after;
+ spansB = b.getSpans(0, b.length(), ParcelableSpan.class);
+ }
+ if (spansA.length != spansB.length) {
+ return false;
+ }
+ for (int i = 0; i < spansA.length; ++i) {
+ final Object thisSpan = spansA[i];
+ final Object otherSpan = spansB[i];
+ if ((thisSpan.getClass() != otherSpan.getClass())
+ || (a.getSpanStart(thisSpan) != b.getSpanStart(otherSpan))
+ || (a.getSpanEnd(thisSpan) != b.getSpanEnd(otherSpan))
+ || (a.getSpanFlags(thisSpan) != b.getSpanFlags(otherSpan))) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 6bf46457a1de..1d13b73fc186 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -145,7 +145,7 @@ interface IBatteryStats {
void noteBleScanStarted(in WorkSource ws, boolean isUnoptimized);
void noteBleScanStopped(in WorkSource ws, boolean isUnoptimized);
- void noteResetBleScan();
+ void noteBleScanReset();
void noteBleScanResults(in WorkSource ws, int numNewResults);
/** {@hide} */
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index c16ee42f4926..7d2cb50a9b4e 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -789,6 +789,9 @@ public class ResolverListAdapter extends BaseAdapter {
} else {
mDisplayResolveInfo.setDisplayIcon(d);
mHolder.bindIcon(mDisplayResolveInfo);
+ // Notify in case view is already bound to resolve the race conditions on
+ // low end devices
+ notifyDataSetChanged();
}
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 10750b695a39..0c56c677e06f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -64,8 +64,7 @@ public class NativeLibraryHelper {
public static final String LIB_DIR_NAME = "lib";
public static final String LIB64_DIR_NAME = "lib64";
- // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
- // that the cpuAbiOverride must be clear.
+ // Special value for indicating that the cpuAbiOverride must be clear.
public static final String CLEAR_ABI_OVERRIDE = "-";
/**
diff --git a/core/java/com/android/internal/content/om/OWNERS b/core/java/com/android/internal/content/om/OWNERS
new file mode 100644
index 000000000000..44fd9fd2a3c2
--- /dev/null
+++ b/core/java/com/android/internal/content/om/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 568631
+include /core/java/android/content/om/OWNERS
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index 3958b9eefefb..aca761de54d4 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -16,15 +16,9 @@
package com.android.internal.inputmethod;
-import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.os.RemoteException;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.view.InputBindResult;
-
-import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@@ -41,34 +35,6 @@ public final class CallbackUtils {
}
/**
- * A utility method using given {@link IInputBindResultResultCallback} to callback the
- * {@link InputBindResult}.
- *
- * @param callback {@link IInputBindResultResultCallback} to be called back.
- * @param resultSupplier the supplier from which {@link InputBindResult} is provided.
- */
- @AnyThread
- public static void onResult(@NonNull IInputBindResultResultCallback callback,
- @NonNull Supplier<InputBindResult> resultSupplier) {
- InputBindResult result = null;
- Throwable exception = null;
-
- try {
- result = resultSupplier.get();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
* A utility method using given {@link IBooleanResultCallback} to callback the result.
*
* @param callback {@link IBooleanResultCallback} to be called back.
@@ -95,87 +61,6 @@ public final class CallbackUtils {
}
/**
- * A utility method using given {@link IInputMethodSubtypeResultCallback} to callback the
- * result.
- *
- * @param callback {@link IInputMethodSubtypeResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IInputMethodSubtypeResultCallback callback,
- @NonNull Supplier<InputMethodSubtype> resultSupplier) {
- InputMethodSubtype result = null;
- Throwable exception = null;
-
- try {
- result = resultSupplier.get();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
- * A utility method using given {@link IInputMethodSubtypeListResultCallback} to callback the
- * result.
- *
- * @param callback {@link IInputMethodSubtypeListResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IInputMethodSubtypeListResultCallback callback,
- @NonNull Supplier<List<InputMethodSubtype>> resultSupplier) {
- List<InputMethodSubtype> result = null;
- Throwable exception = null;
-
- try {
- result = resultSupplier.get();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
- * A utility method using given {@link IInputMethodInfoListResultCallback} to callback the
- * result.
- *
- * @param callback {@link IInputMethodInfoListResultCallback} to be called back.
- * @param resultSupplier the supplier from which the result is provided.
- */
- public static void onResult(@NonNull IInputMethodInfoListResultCallback callback,
- @NonNull Supplier<List<InputMethodInfo>> resultSupplier) {
- List<InputMethodInfo> result = null;
- Throwable exception = null;
-
- try {
- result = resultSupplier.get();
- } catch (Throwable throwable) {
- exception = throwable;
- }
-
- try {
- if (exception != null) {
- callback.onError(ThrowableHolder.of(exception));
- return;
- }
- callback.onResult(result);
- } catch (RemoteException ignored) { }
- }
-
- /**
* A utility method using given {@link IIntResultCallback} to callback the result.
*
* @param callback {@link IIntResultCallback} to be called back.
@@ -227,13 +112,13 @@ public final class CallbackUtils {
}
/**
- * A utility method using given {@link IIInputContentUriTokenResultCallback} to callback the
+ * A utility method using given {@link IInputContentUriTokenResultCallback} to callback the
* result.
*
- * @param callback {@link IIInputContentUriTokenResultCallback} to be called back.
+ * @param callback {@link IInputContentUriTokenResultCallback} to be called back.
* @param resultSupplier the supplier from which the result is provided.
*/
- public static void onResult(@NonNull IIInputContentUriTokenResultCallback callback,
+ public static void onResult(@NonNull IInputContentUriTokenResultCallback callback,
@NonNull Supplier<IInputContentUriToken> resultSupplier) {
IInputContentUriToken result = null;
Throwable exception = null;
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index ba3a34334e86..132272c68ca8 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -23,12 +23,10 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
-import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -416,34 +414,6 @@ public final class Completable {
}
/**
- * @return an instance of {@link Completable.InputBindResult}.
- */
- public static Completable.InputBindResult createInputBindResult() {
- return new Completable.InputBindResult();
- }
-
- /**
- * @return an instance of {@link Completable.InputMethodSubtype}.
- */
- public static Completable.InputMethodSubtype createInputMethodSubtype() {
- return new Completable.InputMethodSubtype();
- }
-
- /**
- * @return an instance of {@link Completable.InputMethodSubtypeList}.
- */
- public static Completable.InputMethodSubtypeList createInputMethodSubtypeList() {
- return new Completable.InputMethodSubtypeList();
- }
-
- /**
- * @return an instance of {@link Completable.InputMethodInfoList}.
- */
- public static Completable.InputMethodInfoList createInputMethodInfoList() {
- return new Completable.InputMethodInfoList();
- }
-
- /**
* @return an instance of {@link Completable.IInputContentUriToken}.
*/
public static Completable.IInputContentUriToken createIInputContentUriToken() {
@@ -480,30 +450,6 @@ public final class Completable {
extends Values<android.view.inputmethod.SurroundingText> { }
/**
- * Completable object of {@link com.android.internal.view.InputBindResult}.
- */
- public static final class InputBindResult
- extends Values<com.android.internal.view.InputBindResult> { }
-
- /**
- * Completable object of {@link android.view.inputmethod.InputMethodSubtype}.
- */
- public static final class InputMethodSubtype
- extends Values<android.view.inputmethod.InputMethodSubtype> { }
-
- /**
- * Completable object of {@link List<android.view.inputmethod.InputMethodSubtype>}.
- */
- public static final class InputMethodSubtypeList
- extends Values<List<android.view.inputmethod.InputMethodSubtype>> { }
-
- /**
- * Completable object of {@link List<android.view.inputmethod.InputMethodInfo>}.
- */
- public static final class InputMethodInfoList
- extends Values<List<android.view.inputmethod.InputMethodInfo>> { }
-
- /**
* Completable object of {@link IInputContentUriToken>}.
*/
public static final class IInputContentUriToken
@@ -544,6 +490,24 @@ public final class Completable {
}
/**
+ * Await the result by the {@link Completable.Boolean}, and log it if there is no result after
+ * given timeout.
+ *
+ * @return the result once {@link ValueBase#onComplete()}
+ */
+ @AnyThread
+ public static boolean getResultOrFalse(@NonNull Completable.Boolean value, String tag,
+ @NonNull String methodName, @Nullable CancellationGroup cancellationGroup,
+ int maxWaitTime) {
+ final boolean timedOut = value.await(maxWaitTime, TimeUnit.MILLISECONDS, cancellationGroup);
+ if (value.hasValue()) {
+ return value.getValue();
+ }
+ logInternal(tag, methodName, timedOut, maxWaitTime, 0);
+ return false;
+ }
+
+ /**
* Await the result by the {@link Completable.Int}, and log it if there is no result after
* given timeout.
*
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 02ffe8c5268e..410e68bf3581 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2007-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
- *
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+package com.android.internal.inputmethod;
import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE;
import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT;
@@ -22,13 +22,11 @@ import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT;
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.text.Editable;
import android.text.Selection;
import android.text.method.KeyListener;
import android.util.Log;
-import android.util.imetracing.InputConnectionHelper;
import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@@ -41,9 +39,9 @@ import android.widget.TextView;
/**
* Base class for an editable InputConnection instance. This is created by {@link TextView} or
- * {@link EditText}.
+ * {@link android.widget.EditText}.
*/
-public class EditableInputConnection extends BaseInputConnection
+public final class EditableInputConnection extends BaseInputConnection
implements DumpableInputConnection {
private static final boolean DEBUG = false;
private static final String TAG = "EditableInputConnection";
@@ -55,7 +53,6 @@ public class EditableInputConnection extends BaseInputConnection
// A negative value means that this connection has been finished by the InputMethodManager.
private int mBatchEditNesting;
- @UnsupportedAppUsage
public EditableInputConnection(TextView textview) {
super(textview, true);
mTextView = textview;
@@ -72,7 +69,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public boolean beginBatchEdit() {
- synchronized(this) {
+ synchronized (this) {
if (mBatchEditNesting >= 0) {
mTextView.beginBatchEdit();
mBatchEditNesting++;
@@ -84,7 +81,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public boolean endBatchEdit() {
- synchronized(this) {
+ synchronized (this) {
if (mBatchEditNesting > 0) {
// When the connection is reset by the InputMethodManager and reportFinish
// is called, some endBatchEdit calls may still be asynchronously received from the
@@ -107,7 +104,7 @@ public class EditableInputConnection extends BaseInputConnection
@Override
public void closeConnection() {
super.closeConnection();
- synchronized(this) {
+ synchronized (this) {
while (mBatchEditNesting > 0) {
endBatchEdit();
}
@@ -159,7 +156,7 @@ public class EditableInputConnection extends BaseInputConnection
mTextView.onEditorAction(actionCode);
return true;
}
-
+
@Override
public boolean performContextMenuAction(int id) {
if (DEBUG) Log.v(TAG, "performContextMenuAction " + id);
@@ -168,13 +165,13 @@ public class EditableInputConnection extends BaseInputConnection
mTextView.endBatchEdit();
return true;
}
-
+
@Override
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
if (mTextView != null) {
ExtractedText et = new ExtractedText();
if (mTextView.extractText(request, et)) {
- if ((flags&GET_EXTRACTED_TEXT_MONITOR) != 0) {
+ if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0) {
mTextView.setExtracting(request);
}
return et;
@@ -213,14 +210,13 @@ public class EditableInputConnection extends BaseInputConnection
// It is possible that any other bit is used as a valid flag in a future release.
// We should reject the entire request in such a case.
- final int KNOWN_FLAGS_MASK = InputConnection.CURSOR_UPDATE_IMMEDIATE |
- InputConnection.CURSOR_UPDATE_MONITOR;
- final int unknownFlags = cursorUpdateMode & ~KNOWN_FLAGS_MASK;
+ final int knownFlagMask = InputConnection.CURSOR_UPDATE_IMMEDIATE
+ | InputConnection.CURSOR_UPDATE_MONITOR;
+ final int unknownFlags = cursorUpdateMode & ~knownFlagMask;
if (unknownFlags != 0) {
if (DEBUG) {
- Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags." +
- " cursorUpdateMode=" + cursorUpdateMode +
- " unknownFlags=" + unknownFlags);
+ Log.d(TAG, "Rejecting requestUpdateCursorAnchorInfo due to unknown flags. "
+ + "cursorUpdateMode=" + cursorUpdateMode + " unknownFlags=" + unknownFlags);
}
return false;
}
@@ -264,7 +260,7 @@ public class EditableInputConnection extends BaseInputConnection
final long token = proto.start(fieldId);
CharSequence editableText = mTextView.getText();
CharSequence selectedText = getSelectedText(0 /* flags */);
- if (InputConnectionHelper.DUMP_TEXT) {
+ if (InputConnectionProtoDumper.DUMP_TEXT) {
if (editableText != null) {
proto.write(EDITABLE_TEXT, editableText.toString());
}
diff --git a/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl
index 2e6d2247b51b..779acb5a57b4 100644
--- a/core/java/com/android/internal/inputmethod/IIInputContentUriTokenResultCallback.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputContentUriTokenResultCallback.aidl
@@ -19,7 +19,7 @@ package com.android.internal.inputmethod;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.ThrowableHolder;
-oneway interface IIInputContentUriTokenResultCallback {
+oneway interface IInputContentUriTokenResultCallback {
void onResult(in IInputContentUriToken result);
void onError(in ThrowableHolder exception);
} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputContextInvoker.java b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
new file mode 100644
index 000000000000..977f9a5a5110
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IInputContextInvoker.java
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputContentInfo;
+
+import com.android.internal.view.IInputContext;
+
+import java.util.Objects;
+
+/**
+ * A stateless wrapper of {@link com.android.internal.view.IInputContext} to encapsulate boilerplate
+ * code around {@link Completable} and {@link RemoteException}.
+ */
+public final class IInputContextInvoker {
+
+ @NonNull
+ private final IInputContext mIInputContext;
+
+ private IInputContextInvoker(@NonNull IInputContext inputContext) {
+ mIInputContext = inputContext;
+ }
+
+ /**
+ * Creates a new instance of {@link IInputContextInvoker} for the given {@link IInputContext}.
+ *
+ * @param inputContext {@link IInputContext} to be wrapped.
+ * @return A new instance of {@link IInputContextInvoker}.
+ */
+ public static IInputContextInvoker create(@NonNull IInputContext inputContext) {
+ Objects.requireNonNull(inputContext);
+ return new IInputContextInvoker(inputContext);
+ }
+
+ /**
+ * Invokes {@link IInputContext#getTextAfterCursor(int, int,
+ * com.android.internal.inputmethod.ICharSequenceResultCallback)}.
+ *
+ * @param length {@code length} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getTextAfterCursor(int length, int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getTextBeforeCursor(int, int, ICharSequenceResultCallback)}.
+ *
+ * @param length {@code length} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getTextBeforeCursor(int length, int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getSelectedText(int, ICharSequenceResultCallback)}.
+ *
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.CharSequence} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.CharSequence getSelectedText(int flags) {
+ final Completable.CharSequence value = Completable.createCharSequence();
+ try {
+ mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes
+ * {@link IInputContext#getSurroundingText(int, int, int, ISurroundingTextResultCallback)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.SurroundingText} that can be used to retrieve the invocation
+ * result. {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.SurroundingText getSurroundingText(int beforeLength, int afterLength,
+ int flags) {
+ final Completable.SurroundingText value = Completable.createSurroundingText();
+ try {
+ mIInputContext.getSurroundingText(beforeLength, afterLength, flags,
+ ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getCursorCapsMode(int, IIntResultCallback)}.
+ *
+ * @param reqModes {@code reqModes} parameter to be passed.
+ * @return {@link Completable.Int} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Int getCursorCapsMode(int reqModes) {
+ final Completable.Int value = Completable.createInt();
+ try {
+ mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#getExtractedText(ExtractedTextRequest, int,
+ * IExtractedTextResultCallback)}.
+ *
+ * @param request {@code request} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @return {@link Completable.ExtractedText} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ final Completable.ExtractedText value = Completable.createExtractedText();
+ try {
+ mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitText(CharSequence, int)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ try {
+ mIInputContext.commitText(text, newCursorPosition);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitCompletion(CompletionInfo)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitCompletion(CompletionInfo text) {
+ try {
+ mIInputContext.commitCompletion(text);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#commitCorrection(CorrectionInfo)}.
+ *
+ * @param correctionInfo {@code correctionInfo} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ try {
+ mIInputContext.commitCorrection(correctionInfo);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setSelection(int, int)}.
+ *
+ * @param start {@code start} parameter to be passed.
+ * @param end {@code start} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setSelection(int start, int end) {
+ try {
+ mIInputContext.setSelection(start, end);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performEditorAction(int)}.
+ *
+ * @param actionCode {@code start} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performEditorAction(int actionCode) {
+ try {
+ mIInputContext.performEditorAction(actionCode);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performContextMenuAction(id)}.
+ *
+ * @param id {@code id} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performContextMenuAction(int id) {
+ try {
+ mIInputContext.performContextMenuAction(id);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setComposingRegion(int, int)}.
+ *
+ * @param start {@code id} parameter to be passed.
+ * @param end {@code id} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingRegion(int start, int end) {
+ try {
+ mIInputContext.setComposingRegion(start, end);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#setComposingText(CharSequence, int)}.
+ *
+ * @param text {@code text} parameter to be passed.
+ * @param newCursorPosition {@code newCursorPosition} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ try {
+ mIInputContext.setComposingText(text, newCursorPosition);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#finishComposingText()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean finishComposingText() {
+ try {
+ mIInputContext.finishComposingText();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#beginBatchEdit()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean beginBatchEdit() {
+ try {
+ mIInputContext.beginBatchEdit();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#endBatchEdit()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean endBatchEdit() {
+ try {
+ mIInputContext.endBatchEdit();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#sendKeyEvent(KeyEvent)}.
+ *
+ * @param event {@code event} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean sendKeyEvent(KeyEvent event) {
+ try {
+ mIInputContext.sendKeyEvent(event);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#clearMetaKeyStates(int)}.
+ *
+ * @param states {@code states} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean clearMetaKeyStates(int states) {
+ try {
+ mIInputContext.clearMetaKeyStates(states);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#deleteSurroundingText(int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ try {
+ mIInputContext.deleteSurroundingText(beforeLength, afterLength);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#deleteSurroundingTextInCodePoints(int, int)}.
+ *
+ * @param beforeLength {@code beforeLength} parameter to be passed.
+ * @param afterLength {@code afterLength} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ try {
+ mIInputContext.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performSpellCheck()}.
+ *
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performSpellCheck() {
+ try {
+ mIInputContext.performSpellCheck();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#performPrivateCommand(String, Bundle)}.
+ *
+ * @param action {@code action} parameter to be passed.
+ * @param data {@code data} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean performPrivateCommand(String action, Bundle data) {
+ try {
+ mIInputContext.performPrivateCommand(action, data);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Invokes {@link IInputContext#requestCursorUpdates(int, IIntResultCallback)}.
+ *
+ * @param cursorUpdateMode {@code cursorUpdateMode} parameter to be passed.
+ * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Boolean requestCursorUpdates(int cursorUpdateMode) {
+ final Completable.Boolean value = Completable.createBoolean();
+ try {
+ mIInputContext.requestCursorUpdates(cursorUpdateMode, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes
+ * {@link IInputContext#commitContent(InputContentInfo, int, Bundle, IIntResultCallback)}.
+ *
+ * @param inputContentInfo {@code inputContentInfo} parameter to be passed.
+ * @param flags {@code flags} parameter to be passed.
+ * @param opts {@code opts} parameter to be passed.
+ * @return {@link Completable.Boolean} that can be used to retrieve the invocation result.
+ * {@link RemoteException} will be treated as an error.
+ */
+ @AnyThread
+ @NonNull
+ public Completable.Boolean commitContent(InputContentInfo inputContentInfo, int flags,
+ Bundle opts) {
+ final Completable.Boolean value = Completable.createBoolean();
+ try {
+ mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
+ } catch (RemoteException e) {
+ value.onError(ThrowableHolder.of(e));
+ }
+ return value;
+ }
+
+ /**
+ * Invokes {@link IInputContext#setImeConsumesInput(boolean)}.
+ *
+ * @param imeConsumesInput {@code imeConsumesInput} parameter to be passed.
+ * @return {@code true} if the invocation is completed without {@link RemoteException}.
+ * {@code false} otherwise.
+ */
+ @AnyThread
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
+ try {
+ mIInputContext.setImeConsumesInput(imeConsumesInput);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl
deleted file mode 100644
index 0dfd5da00abe..000000000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodInfoListResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.inputmethod;
-
-import android.view.inputmethod.InputMethodInfo;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodInfoListResultCallback {
- void onResult(in List<InputMethodInfo> result);
- void onError(in ThrowableHolder exception);
-} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index 11df5a89d8c0..36943e3bd591 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -21,7 +21,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.inputmethod.IBooleanResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
-import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
+import com.android.internal.inputmethod.IInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IVoidResultCallback;
/**
@@ -32,7 +32,7 @@ oneway interface IInputMethodPrivilegedOperations {
void setImeWindowStatusAsync(int vis, int backDisposition);
void reportStartInputAsync(in IBinder startInputToken);
void createInputContentUriToken(in Uri contentUri, in String packageName,
- in IIInputContentUriTokenResultCallback resultCallback);
+ in IInputContentUriTokenResultCallback resultCallback);
void reportFullscreenModeAsync(boolean fullscreen);
void setInputMethod(String id, in IVoidResultCallback resultCallback);
void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype,
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl
deleted file mode 100644
index 619c87e1cd5b..000000000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeListResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.inputmethod;
-
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodSubtypeListResultCallback {
- void onResult(in List<InputMethodSubtype> result);
- void onError(in ThrowableHolder exception);
-} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl
deleted file mode 100644
index 66c09026321f..000000000000
--- a/core/java/com/android/internal/inputmethod/IInputMethodSubtypeResultCallback.aidl
+++ /dev/null
@@ -1,25 +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.inputmethod;
-
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.inputmethod.ThrowableHolder;
-
-oneway interface IInputMethodSubtypeResultCallback {
- void onResult(in InputMethodSubtype result);
- void onError(in ThrowableHolder exception);
-} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
deleted file mode 100644
index b5f2147784c0..000000000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.view.InputChannel;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-
-/**
- * Defines priviledged operations that only the current MSIMS is allowed to call.
- * Actual operations are implemented and handled by MultiClientInputMethodManagerService.
- */
-interface IMultiClientInputMethodPrivilegedOperations {
- IBinder createInputMethodWindowToken(int displayId);
- void deleteInputMethodWindowToken(IBinder token);
- void acceptClient(int clientId, in IInputMethodSession session,
- in IMultiClientInputMethodSession multiClientSession, in InputChannel writeChannel);
- void reportImeWindowTarget(int clientId, int targetWindowHandle, in IBinder imeWindowToken);
- boolean isUidAllowedOnDisplay(int displayId, int uid);
- void setActive(int clientId, boolean active);
-}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
deleted file mode 100644
index b40e82ce7a9f..000000000000
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.os.ResultReceiver;
-import android.view.inputmethod.EditorInfo;
-import com.android.internal.view.IInputContext;
-
-oneway interface IMultiClientInputMethodSession {
- void startInputOrWindowGainedFocus(
- in IInputContext inputContext, int missingMethods, in EditorInfo attribute,
- int controlFlags, int softInputMode, int targetWindowHandle);
- void showSoftInput(int flags, in ResultReceiver resultReceiver);
- void hideSoftInput(int flags, in ResultReceiver resultReceiver);
-}
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/com/android/internal/inputmethod/ImeTracing.java
index 4696ae325e7b..8b21b7ef10ea 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracing.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -36,8 +35,6 @@ import java.io.PrintWriter;
* An abstract class that declares the methods for ime trace related operations - enable trace,
* schedule trace and add new trace to buffer. Both the client and server side classes can use
* it by getting an implementation through {@link ImeTracing#getInstance()}.
- *
- * @hide
*/
public abstract class ImeTracing {
@@ -94,6 +91,28 @@ public abstract class ImeTracing {
}
/**
+ * Calling {@link IInputMethodManager#startImeTrace()}} to capture IME trace.
+ */
+ public final void startImeTrace() {
+ try {
+ mService.startImeTrace();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not start ime trace." + e);
+ }
+ }
+
+ /**
+ * Calling {@link IInputMethodManager#stopImeTrace()} to stop IME trace.
+ */
+ public final void stopImeTrace() {
+ try {
+ mService.stopImeTrace();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not stop ime trace." + e);
+ }
+ }
+
+ /**
* @param proto dump to be added to the buffer
*/
public abstract void addToBuffer(ProtoOutputStream proto, int source);
@@ -106,17 +125,33 @@ public abstract class ImeTracing {
* @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
*/
public abstract void triggerClientDump(String where, InputMethodManager immInstance,
- ProtoOutputStream icProto);
+ @Nullable byte[] icProto);
+
+ /**
+ * A delegate for {@link #triggerServiceDump(String, ServiceDumper, byte[])}.
+ */
+ @FunctionalInterface
+ public interface ServiceDumper {
+ /**
+ * Dumps internal data into {@link ProtoOutputStream}.
+ *
+ * @param proto {@link ProtoOutputStream} to be dumped into.
+ * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto
+ * format.
+ */
+ void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto);
+ }
/**
* Starts a proto dump of the currently connected InputMethodService information.
*
* @param where Place where the trace was triggered.
- * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped.
+ * @param dumper {@link ServiceDumper} to be used to dump
+ * {@link android.inputmethodservice.InputMethodService}.
* @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format.
*/
- public abstract void triggerServiceDump(String where, AbstractInputMethodService service,
- ProtoOutputStream icProto);
+ public abstract void triggerServiceDump(String where, ServiceDumper dumper,
+ @Nullable byte[] icProto);
/**
* Starts a proto dump of the InputMethodManagerService information.
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
index 5a57a6ade98b..31d278b5ca54 100644
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingClientImpl.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import android.annotation.NonNull;
-import android.inputmethodservice.AbstractInputMethodService;
+import android.annotation.Nullable;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -27,7 +27,7 @@ import android.view.inputmethod.InputMethodManager;
import java.io.PrintWriter;
/**
- * @hide
+ * An implementation of {@link ImeTracing} for non system_server processes.
*/
class ImeTracingClientImpl extends ImeTracing {
ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
@@ -40,7 +40,7 @@ class ImeTracingClientImpl extends ImeTracing {
@Override
public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
- ProtoOutputStream icProto) {
+ @Nullable byte[] icProto) {
if (!isEnabled() || !isAvailable()) {
return;
}
@@ -64,8 +64,8 @@ class ImeTracingClientImpl extends ImeTracing {
}
@Override
- public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service,
- ProtoOutputStream icProto) {
+ public void triggerServiceDump(String where, @NonNull ServiceDumper dumper,
+ @Nullable byte[] icProto) {
if (!isEnabled() || !isAvailable()) {
return;
}
@@ -79,7 +79,7 @@ class ImeTracingClientImpl extends ImeTracing {
try {
ProtoOutputStream proto = new ProtoOutputStream();
- service.dumpProtoInternal(proto, icProto);
+ dumper.dumpToProto(proto, icProto);
sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where);
} catch (RemoteException e) {
Log.e(TAG, "Exception while sending ime-related service dump to server", e);
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
index 06e4c5002776..20ff83f5f68e 100644
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingServerImpl.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import static android.os.Build.IS_USER;
import android.annotation.Nullable;
-import android.inputmethodservice.AbstractInputMethodService;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;
@@ -37,13 +36,13 @@ import java.io.IOException;
import java.io.PrintWriter;
/**
- * @hide
+ * An implementation of {@link ImeTracing} for the system_server process.
*/
class ImeTracingServerImpl extends ImeTracing {
private static final String TRACE_DIRNAME = "/data/misc/wmtrace/";
- private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.pb";
- private static final String TRACE_FILENAME_IMS = "ime_trace_service.pb";
- private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.pb";
+ private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.winscope";
+ private static final String TRACE_FILENAME_IMS = "ime_trace_service.winscope";
+ private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.winscope";
private static final int BUFFER_CAPACITY = 4096 * 1024;
// Needed for winscope to auto-detect the dump type. Explained further in
@@ -107,13 +106,12 @@ class ImeTracingServerImpl extends ImeTracing {
@Override
public void triggerClientDump(String where, InputMethodManager immInstance,
- ProtoOutputStream icProto) {
+ @Nullable byte[] icProto) {
// Intentionally left empty, this is implemented in ImeTracingClientImpl
}
@Override
- public void triggerServiceDump(String where, AbstractInputMethodService service,
- ProtoOutputStream icProto) {
+ public void triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto) {
// Intentionally left empty, this is implemented in ImeTracingClientImpl
}
diff --git a/core/java/com/android/internal/view/InputBindResult.aidl b/core/java/com/android/internal/inputmethod/InputBindResult.aidl
index 7ff5c4eac75e..4d4c22bdf4e2 100644
--- a/core/java/com/android/internal/view/InputBindResult.aidl
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.view;
+package com.android.internal.inputmethod;
parcelable InputBindResult;
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index df371ce11954..1357bac30667 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -1,34 +1,34 @@
/*
- * Copyright (C) 2007-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
- *
+ * 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.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-package com.android.internal.view;
+package com.android.internal.inputmethod;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.view.InputChannel;
+import com.android.internal.view.IInputMethodSession;
+
import java.lang.annotation.Retention;
/**
@@ -53,8 +53,7 @@ public final class InputBindResult implements Parcelable {
ResultCode.ERROR_NOT_IME_TARGET_WINDOW,
ResultCode.ERROR_NO_EDITOR,
ResultCode.ERROR_DISPLAY_ID_MISMATCH,
- ResultCode.ERROR_INVALID_DISPLAY_ID,
- ResultCode.ERROR_INVALID_CLIENT,
+ ResultCode.ERROR_INVALID_DISPLAY_ID
})
public @interface ResultCode {
/**
@@ -71,7 +70,7 @@ public final class InputBindResult implements Parcelable {
*
* <p>Some of fields such as {@link #channel} is not yet available.</p>
*
- * @see android.inputmethodservice.InputMethodService##onCreateInputMethodSessionInterface()
+ * @see android.inputmethodservice.InputMethodService#onCreateInputMethodSessionInterface()
**/
int SUCCESS_WAITING_IME_SESSION = 1;
/**
@@ -130,7 +129,8 @@ public final class InputBindResult implements Parcelable {
* Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
* connect to an {@link android.inputmethodservice.InputMethodService} but failed.
*
- * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
+ * @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int,
+ * android.os.UserHandle)
*/
int ERROR_IME_NOT_CONNECTED = 9;
/**
@@ -168,10 +168,6 @@ public final class InputBindResult implements Parcelable {
* display.
*/
int ERROR_INVALID_DISPLAY_ID = 15;
- /**
- * Indicates that the client is not recognized by the system.
- */
- int ERROR_INVALID_CLIENT = 16;
}
@ResultCode
@@ -180,7 +176,6 @@ public final class InputBindResult implements Parcelable {
/**
* The input method service.
*/
- @UnsupportedAppUsage
public final IInputMethodSession method;
/**
@@ -193,26 +188,42 @@ public final class InputBindResult implements Parcelable {
* no input method will be bound.
*/
public final String id;
-
+
/**
* Sequence number of this binding.
*/
public final int sequence;
+ /**
+ * {@code true} if the IME explicitly specifies {@code suppressesSpellChecker="true"}.
+ */
public final boolean isInputMethodSuppressingSpellChecker;
- public InputBindResult(@ResultCode int _result,
- IInputMethodSession _method, InputChannel _channel, String _id, int _sequence,
+ /**
+ * Creates a new instance of {@link InputBindResult}.
+ *
+ * @param result A result code defined in {@link ResultCode}.
+ * @param method {@link IInputMethodSession} to interact with the IME.
+ * @param channel {@link InputChannel} to forward input events to the IME.
+ * @param id The {@link String} representations of the IME, which is the same as
+ * {@link android.view.inputmethod.InputMethodInfo#getId()} and
+ * {@link android.content.ComponentName#flattenToShortString()}.
+ * @param sequence A sequence number of this binding.
+ * @param isInputMethodSuppressingSpellChecker {@code true} if the IME explicitly specifies
+ * {@code suppressesSpellChecker="true"}.
+ */
+ public InputBindResult(@ResultCode int result,
+ IInputMethodSession method, InputChannel channel, String id, int sequence,
boolean isInputMethodSuppressingSpellChecker) {
- result = _result;
- method = _method;
- channel = _channel;
- id = _id;
- sequence = _sequence;
+ this.result = result;
+ this.method = method;
+ this.channel = channel;
+ this.id = id;
+ this.sequence = sequence;
this.isInputMethodSuppressingSpellChecker = isInputMethodSuppressingSpellChecker;
}
- InputBindResult(Parcel source) {
+ private InputBindResult(Parcel source) {
result = source.readInt();
method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
if (source.readInt() != 0) {
@@ -225,19 +236,19 @@ public final class InputBindResult implements Parcelable {
isInputMethodSuppressingSpellChecker = source.readBoolean();
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public String toString() {
- return "InputBindResult{result=" + getResultString() + " method="+ method + " id=" + id
+ return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
+ " sequence=" + sequence
+ " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
/**
- * Used to package this object into a {@link Parcel}.
- *
- * @param dest The {@link Parcel} to be written.
- * @param flags The flags used for parceling.
+ * {@inheritDoc}
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
@@ -257,7 +268,6 @@ public final class InputBindResult implements Parcelable {
/**
* Used to make this class parcelable.
*/
- @UnsupportedAppUsage
public static final Parcelable.Creator<InputBindResult> CREATOR =
new Parcelable.Creator<InputBindResult>() {
@Override
@@ -271,12 +281,15 @@ public final class InputBindResult implements Parcelable {
}
};
+ /**
+ * {@inheritDoc}
+ */
@Override
public int describeContents() {
return channel != null ? channel.describeContents() : 0;
}
- public String getResultString() {
+ private String getResultString() {
switch (result) {
case ResultCode.SUCCESS_WITH_IME_SESSION:
return "SUCCESS_WITH_IME_SESSION";
@@ -310,8 +323,6 @@ public final class InputBindResult implements Parcelable {
return "ERROR_DISPLAY_ID_MISMATCH";
case ResultCode.ERROR_INVALID_DISPLAY_ID:
return "ERROR_INVALID_DISPLAY_ID";
- case ResultCode.ERROR_INVALID_CLIENT:
- return "ERROR_INVALID_CLIENT";
default:
return "Unknown(" + result + ")";
}
@@ -370,11 +381,6 @@ public final class InputBindResult implements Parcelable {
error(ResultCode.ERROR_INVALID_DISPLAY_ID);
/**
- * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
- */
- public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
-
- /**
* Predefined <strong>success</strong> object for
* {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
*/
diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
index 39f1e01eb4a9..7172d0a41909 100644
--- a/core/java/android/util/imetracing/InputConnectionHelper.java
+++ b/core/java/com/android/internal/inputmethod/InputConnectionProtoDumper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.util.imetracing;
+package com.android.internal.inputmethod;
import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE;
import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT;
@@ -41,13 +41,12 @@ import android.view.inputmethod.SurroundingText;
/**
* Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are
* integrated into {@link ImeTracing}.
- * @hide
*/
-public class InputConnectionHelper {
- static final String TAG = "InputConnectionHelper";
+public final class InputConnectionProtoDumper {
+ static final String TAG = "InputConnectionProtoDumper";
public static final boolean DUMP_TEXT = false;
- private InputConnectionHelper() {}
+ private InputConnectionProtoDumper() {}
/**
* Builder for InputConnectionCallProto to hold
@@ -59,10 +58,11 @@ public class InputConnectionHelper {
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
* @param result The text after the cursor position; the length of the
* returned text might be less than <var>length</var>.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length,
- int flags, @Nullable CharSequence result) {
+ @NonNull
+ public static byte[] buildGetTextAfterCursorProto(@IntRange(from = 0) int length, int flags,
+ @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_TEXT_AFTER_CURSOR);
proto.write(GetTextAfterCursor.LENGTH, length);
@@ -73,7 +73,7 @@ public class InputConnectionHelper {
proto.write(GetTextAfterCursor.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -86,9 +86,10 @@ public class InputConnectionHelper {
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
* @param result The text before the cursor position; the length of the
* returned text might be less than <var>length</var>.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
+ @NonNull
+ public static byte[] buildGetTextBeforeCursorProto(@IntRange(from = 0) int length,
int flags, @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_TEXT_BEFORE_CURSOR);
@@ -100,7 +101,7 @@ public class InputConnectionHelper {
proto.write(GetTextBeforeCursor.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -114,10 +115,10 @@ public class InputConnectionHelper {
* no text is selected. In {@link android.os.Build.VERSION_CODES#N} and
* later, returns false when the target application does not implement
* this method.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetSelectedTextProto(int flags,
- @Nullable CharSequence result) {
+ @NonNull
+ public static byte[] buildGetSelectedTextProto(int flags, @Nullable CharSequence result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_SELECTED_TEXT);
proto.write(GetSelectedText.FLAGS, flags);
@@ -127,7 +128,7 @@ public class InputConnectionHelper {
proto.write(GetSelectedText.RESULT, result.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -139,16 +140,16 @@ public class InputConnectionHelper {
* @param flags Supplies additional options controlling how the text is
* returned. May be either {@code 0} or
* {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}.
- * @param result an {@link android.view.inputmethod.SurroundingText} object describing the
+ * @param result an {@link SurroundingText} object describing the
* surrounding text and state of selection, or null if the input connection is no longer valid,
* or the editor can't comply with the request for some reason, or the application does not
* implement this method. The length of the returned text might be less than the sum of
* <var>beforeLength</var> and <var>afterLength</var> .
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0)
- int beforeLength, @IntRange(from = 0) int afterLength, int flags,
- @Nullable SurroundingText result) {
+ @NonNull
+ public static byte[] buildGetSurroundingTextProto(@IntRange(from = 0) int beforeLength,
+ @IntRange(from = 0) int afterLength, int flags, @Nullable SurroundingText result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_SURROUNDING_TEXT);
proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength);
@@ -169,7 +170,7 @@ public class InputConnectionHelper {
proto.end(token_result);
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -180,9 +181,10 @@ public class InputConnectionHelper {
* {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.
* @param result the caps mode flags that are in effect at the current
* cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) {
+ @NonNull
+ public static byte[] buildGetCursorCapsModeProto(int reqModes, int result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_CURSOR_CAPS_MODE);
proto.write(GetCursorCapsMode.REQ_MODES, reqModes);
@@ -190,7 +192,7 @@ public class InputConnectionHelper {
proto.write(GetCursorCapsMode.RESULT, result);
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
/**
@@ -199,17 +201,18 @@ public class InputConnectionHelper {
* data.
*
* @param request Description of how the text should be returned.
- * {@link android.view.inputmethod.ExtractedTextRequest}
+ * {@link ExtractedTextRequest}
* @param flags Additional options to control the client, either {@code 0} or
* {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}.
- * @param result an {@link android.view.inputmethod.ExtractedText}
+ * @param result an {@link ExtractedText}
* object describing the state of the text view and containing the
* extracted text itself, or null if the input connection is no
* longer valid of the editor can't comply with the request for
* some reason.
- * @return ProtoOutputStream holding the InputConnectionCallProto data.
+ * @return Byte-array holding the InputConnectionCallProto data.
*/
- public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest
+ @NonNull
+ public static byte[] buildGetExtractedTextProto(@NonNull ExtractedTextRequest
request, int flags, @Nullable ExtractedText result) {
ProtoOutputStream proto = new ProtoOutputStream();
final long token = proto.start(GET_EXTRACTED_TEXT);
@@ -226,6 +229,6 @@ public class InputConnectionHelper {
proto.write(GetExtractedText.RESULT, result.text.toString());
}
proto.end(token);
- return proto;
+ return proto.getBytes();
}
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index ed1fe1a6229e..9fb0bb52e001 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -142,7 +142,7 @@ public final class InputMethodPrivilegedOperations {
/**
* Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String,
- * IIInputContentUriTokenResultCallback)}.
+ * IInputContentUriTokenResultCallback)}.
*
* @param contentUri Content URI to which a temporary read permission should be granted
* @param packageName Indicates what package needs to have a temporary read permission
diff --git a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
deleted file mode 100644
index 1cf68872e2cf..000000000000
--- a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.inputmethod;
-
-import android.annotation.AnyThread;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.InputChannel;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInputMethodSession;
-
-/**
- * A utility class to take care of boilerplate code around IPCs.
- *
- * <p>Note: All public methods are designed to be thread-safe.</p>
- */
-public class MultiClientInputMethodPrivilegedOperations {
- private static final String TAG = "MultiClientInputMethodPrivilegedOperations";
-
- private static final class OpsHolder {
- @Nullable
- @GuardedBy("this")
- private IMultiClientInputMethodPrivilegedOperations mPrivOps;
-
- /**
- * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
- *
- * <p>This method can be called only once.</p>
- *
- * @param privOps Binder interface to be set.
- */
- @AnyThread
- public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) {
- if (mPrivOps != null) {
- throw new IllegalStateException(
- "IMultiClientInputMethodPrivilegedOperations must be set at most once."
- + " privOps=" + privOps);
- }
- mPrivOps = privOps;
- }
-
- /**
- * A simplified version of {@link android.os.Debug#getCaller()}.
- *
- * @return method name of the caller.
- */
- @AnyThread
- private static String getCallerMethodName() {
- final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
- if (callStack.length <= 4) {
- return "<bottom of call stack>";
- }
- return callStack[4].getMethodName();
- }
-
- @AnyThread
- public synchronized void dispose() {
- mPrivOps = null;
- }
-
- @AnyThread
- @Nullable
- public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() {
- if (mPrivOps == null) {
- Log.e(TAG, getCallerMethodName() + " is ignored."
- + " Call it within attachToken() and InputMethodService.onDestroy()");
- }
- return mPrivOps;
- }
- }
- private final OpsHolder mOps = new OpsHolder();
-
- /**
- * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
- *
- * <p>This method can be called only once.</p>
- *
- * @param privOps Binder interface to be set.
- */
- @AnyThread
- public void set(IMultiClientInputMethodPrivilegedOperations privOps) {
- mOps.set(privOps);
- }
-
- /**
- * Disposes internal Binder proxy so that the real Binder object can be garbage collected.
- */
- @AnyThread
- public void dispose() {
- mOps.dispose();
- }
-
- /**
-
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}.
- *
- * @param displayId display ID on which the IME window will be shown.
- * @return Window token to be specified to the IME window.
- */
- @AnyThread
- public IBinder createInputMethodWindowToken(int displayId) {
- IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return null;
- }
- try {
- return ops.createInputMethodWindowToken(displayId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link
- * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}.
- *
- * @param token Window token that is to be deleted.
- */
- @AnyThread
- public void deleteInputMethodWindowToken(IBinder token) {
- IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.deleteInputMethodWindowToken(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
- * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
- *
- * @param clientId client ID to be accepted.
- * @param session {@link IInputMethodSession} that is also used for traditional IME protocol.
- * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks
- * for multi-client scenarios.
- * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol.
- */
- @AnyThread
- public void acceptClient(int clientId, IInputMethodSession session,
- IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.acceptClient(clientId, session, multiClientSession, writeChannel);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int,
- * IBinder)}.
- *
- * @param clientId client ID about which new IME target window is reported.
- * @param targetWindowHandle integer handle of the target window.
- * @param imeWindowToken {@link IBinder} window token of the IME window.
- */
- @AnyThread
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}.
- *
- * @param displayId display ID to be verified.
- * @param uid UID to be verified.
- * @return {@code true} when {@code uid} is allowed to access to {@code displayId}.
- */
- @AnyThread
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return false;
- }
- try {
- return ops.isUidAllowedOnDisplay(displayId, uid);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Calls {@link IMultiClientInputMethodPrivilegedOperations#setActive(int, boolean)}.
- * @param clientId client ID to be set active/inactive
- * @param active {@code true} set set active.
- */
- @AnyThread
- public void setActive(int clientId, boolean active) {
- final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
- if (ops == null) {
- return;
- }
- try {
- ops.setActive(clientId, active);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
new file mode 100644
index 000000000000..0c2701219ef4
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -0,0 +1,796 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionInspector;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
+import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.SurroundingText;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInputContext;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Takes care of remote method invocations of {@link InputConnection} in the IME client side.
+ *
+ * <p>{@link android.inputmethodservice.RemoteInputConnection} code is executed in the IME process.
+ * It makes IInputContext binder calls under the hood. {@link RemoteInputConnectionImpl} receives
+ * {@link IInputContext} binder calls in the IME client (editor app) process, and forwards them to
+ * {@link InputConnection} that the IME client provided, on the {@link Looper} associated to the
+ * {@link InputConnection}.</p>
+ */
+public final class RemoteInputConnectionImpl extends IInputContext.Stub {
+ private static final String TAG = "RemoteInputConnectionImpl";
+ private static final boolean DEBUG = false;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private InputConnection mInputConnection;
+
+ @NonNull
+ private final Looper mLooper;
+ private final Handler mH;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mFinished = false;
+
+ private final InputMethodManager mParentInputMethodManager;
+ private final WeakReference<View> mServedView;
+
+ public RemoteInputConnectionImpl(@NonNull Looper looper,
+ @NonNull InputConnection inputConnection,
+ @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
+ mInputConnection = inputConnection;
+ mLooper = looper;
+ mH = new Handler(mLooper);
+ mParentInputMethodManager = inputMethodManager;
+ mServedView = new WeakReference<>(servedView);
+ }
+
+ /**
+ * @return {@link InputConnection} to which incoming IPCs will be dispatched.
+ */
+ @Nullable
+ private InputConnection getInputConnection() {
+ synchronized (mLock) {
+ return mInputConnection;
+ }
+ }
+
+ /**
+ * @return {@code true} until the target {@link InputConnection} receives
+ * {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
+ */
+ public boolean isFinished() {
+ synchronized (mLock) {
+ return mFinished;
+ }
+ }
+
+ public boolean isActive() {
+ return mParentInputMethodManager.isActive() && !isFinished();
+ }
+
+ public View getServedView() {
+ return mServedView.get();
+ }
+
+ /**
+ * Called when this object needs to be permanently deactivated.
+ *
+ * <p>Multiple invocations will be simply ignored.</p>
+ */
+ public void deactivate() {
+ if (isFinished()) {
+ // This is a small performance optimization. Still only the 1st call of
+ // reportFinish() will take effect.
+ return;
+ }
+ dispatch(() -> {
+ // Note that we do not need to worry about race condition here, because 1) mFinished is
+ // updated only inside this block, and 2) the code here is running on a Handler hence we
+ // assume multiple closeConnection() tasks will not be handled at the same time.
+ if (isFinished()) {
+ return;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
+ try {
+ InputConnection ic = getInputConnection();
+ // Note we do NOT check isActive() here, because this is safe
+ // for an IME to call at any time, and we need to allow it
+ // through to clean up our state after the IME has switched to
+ // another client.
+ if (ic == null) {
+ return;
+ }
+ @MissingMethodFlags
+ final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
+ if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
+ ic.closeConnection();
+ }
+ } finally {
+ synchronized (mLock) {
+ mInputConnection = null;
+ mFinished = true;
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+
+ // Notify the app that the InputConnection was closed.
+ final View servedView = mServedView.get();
+ if (servedView != null) {
+ final Handler handler = servedView.getHandler();
+ // The handler is null if the view is already detached. When that's the case, for
+ // now, we simply don't dispatch this callback.
+ if (handler != null) {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+ }
+ if (handler.getLooper().isCurrentThread()) {
+ servedView.onInputConnectionClosedInternal();
+ } else {
+ handler.post(servedView::onInputConnectionClosedInternal);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public String toString() {
+ return "RemoteInputConnectionImpl{"
+ + "connection=" + getInputConnection()
+ + " finished=" + isFinished()
+ + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ + " mServedView=" + mServedView.get()
+ + "}";
+ }
+
+ /**
+ * Called by {@link InputMethodManager} to dump the editor state.
+ *
+ * @param proto {@link ProtoOutputStream} to which the editor state should be dumped.
+ * @param fieldId the ID to be passed to
+ * {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}.
+ */
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ synchronized (mLock) {
+ // Check that the call is initiated in the target thread of the current InputConnection
+ // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+ // executed on this thread. Otherwise the messages are dispatched to the correct thread
+ // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
+ // reasons.
+ if ((mInputConnection instanceof DumpableInputConnection)
+ && mLooper.isCurrentThread()) {
+ ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
+ }
+ }
+ }
+
+ /**
+ * Invoke {@link InputConnection#reportFullscreenMode(boolean)} or schedule it on the target
+ * thread associated with {@link InputConnection#getHandler()}.
+ *
+ * @param enabled the parameter to be passed to
+ * {@link InputConnection#reportFullscreenMode(boolean)}.
+ */
+ public void dispatchReportFullscreenMode(boolean enabled) {
+ dispatch(() -> {
+ final InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ return;
+ }
+ ic.reportFullscreenMode(enabled);
+ });
+ }
+
+ @Override
+ public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
+ try {
+ final InputConnection ic = getInputConnection();
+ final CharSequence result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getTextAfterCursor(length, flags);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextAfterCursorProto(
+ length, flags, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
+ try {
+ final InputConnection ic = getInputConnection();
+ final CharSequence result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getTextBeforeCursor(length, flags);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetTextBeforeCursorProto(
+ length, flags, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
+ try {
+ final InputConnection ic = getInputConnection();
+ final CharSequence result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSelectedText on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getSelectedText(flags);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSelectedTextProto(
+ flags, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getSelectedText", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getSelectedText()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void getSurroundingText(int beforeLength, int afterLength, int flags,
+ ISurroundingTextResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
+ try {
+ final InputConnection ic = getInputConnection();
+ final SurroundingText result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getSurroundingText on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getSurroundingText(beforeLength, afterLength, flags);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetSurroundingTextProto(
+ beforeLength, afterLength, flags, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getSurroundingText()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
+ try {
+ final InputConnection ic = getInputConnection();
+ final int result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
+ result = 0;
+ } else {
+ result = ic.getCursorCapsMode(reqModes);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetCursorCapsModeProto(
+ reqModes, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void getExtractedText(ExtractedTextRequest request, int flags,
+ IExtractedTextResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
+ try {
+ final InputConnection ic = getInputConnection();
+ final ExtractedText result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "getExtractedText on inactive InputConnection");
+ result = null;
+ } else {
+ result = ic.getExtractedText(request, flags);
+ }
+ if (ImeTracing.getInstance().isEnabled()) {
+ final byte[] icProto = InputConnectionProtoDumper.buildGetExtractedTextProto(
+ request, flags, result);
+ ImeTracing.getInstance().triggerClientDump(
+ TAG + "#getExtractedText", mParentInputMethodManager, icProto);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to getExtractedText()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void commitText(CharSequence text, int newCursorPosition) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitText on inactive InputConnection");
+ return;
+ }
+ ic.commitText(text, newCursorPosition);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void commitCompletion(CompletionInfo text) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitCompletion on inactive InputConnection");
+ return;
+ }
+ ic.commitCompletion(text);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void commitCorrection(CorrectionInfo info) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitCorrection on inactive InputConnection");
+ return;
+ }
+ ic.commitCorrection(info);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void setSelection(int start, int end) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setSelection on inactive InputConnection");
+ return;
+ }
+ ic.setSelection(start, end);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void performEditorAction(int id) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performEditorAction on inactive InputConnection");
+ return;
+ }
+ ic.performEditorAction(id);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void performContextMenuAction(int id) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performContextMenuAction on inactive InputConnection");
+ return;
+ }
+ ic.performContextMenuAction(id);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void setComposingRegion(int start, int end) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingRegion on inactive InputConnection");
+ return;
+ }
+ ic.setComposingRegion(start, end);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void setComposingText(CharSequence text, int newCursorPosition) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setComposingText on inactive InputConnection");
+ return;
+ }
+ ic.setComposingText(text, newCursorPosition);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void finishComposingText() {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText");
+ try {
+ if (isFinished()) {
+ // In this case, #finishComposingText() is guaranteed to be called already.
+ // There should be no negative impact if we ignore this call silently.
+ if (DEBUG) {
+ Log.w(TAG, "Bug 35301295: Redundant finishComposingText.");
+ }
+ return;
+ }
+ InputConnection ic = getInputConnection();
+ // Note we do NOT check isActive() here, because this is safe
+ // for an IME to call at any time, and we need to allow it
+ // through to clean up our state after the IME has switched to
+ // another client.
+ if (ic == null) {
+ Log.w(TAG, "finishComposingText on inactive InputConnection");
+ return;
+ }
+ ic.finishComposingText();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void sendKeyEvent(KeyEvent event) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "sendKeyEvent on inactive InputConnection");
+ return;
+ }
+ ic.sendKeyEvent(event);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void clearMetaKeyStates(int states) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
+ return;
+ }
+ ic.clearMetaKeyStates(states);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void deleteSurroundingText(int beforeLength, int afterLength) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
+ return;
+ }
+ ic.deleteSurroundingText(beforeLength, afterLength);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT,
+ "InputConnection#deleteSurroundingTextInCodePoints");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
+ return;
+ }
+ ic.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void beginBatchEdit() {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "beginBatchEdit on inactive InputConnection");
+ return;
+ }
+ ic.beginBatchEdit();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void endBatchEdit() {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "endBatchEdit on inactive InputConnection");
+ return;
+ }
+ ic.endBatchEdit();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void performSpellCheck() {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performSpellCheck on inactive InputConnection");
+ return;
+ }
+ ic.performSpellCheck();
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void performPrivateCommand(String action, Bundle data) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "performPrivateCommand on inactive InputConnection");
+ return;
+ }
+ ic.performPrivateCommand(action, data);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
+ try {
+ final InputConnection ic = getInputConnection();
+ final boolean result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+ result = false;
+ } else {
+ result = ic.requestCursorUpdates(cursorUpdateMode);
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
+ IBooleanResultCallback callback) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
+ try {
+ final InputConnection ic = getInputConnection();
+ final boolean result;
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "commitContent on inactive InputConnection");
+ result = false;
+ } else {
+ if (inputContentInfo == null || !inputContentInfo.validate()) {
+ Log.w(TAG, "commitContent with invalid inputContentInfo="
+ + inputContentInfo);
+ result = false;
+ } else {
+ result = ic.commitContent(inputContentInfo, flags, opts);
+ }
+ }
+ try {
+ callback.onResult(result);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to return the result to commitContent()."
+ + " result=" + result, e);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ @Override
+ public void setImeConsumesInput(boolean imeConsumesInput) {
+ dispatch(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setImeConsumesInput");
+ try {
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "setImeConsumesInput on inactive InputConnection");
+ return;
+ }
+ ic.setImeConsumesInput(imeConsumesInput);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+ }
+ });
+ }
+
+ private void dispatch(@NonNull Runnable runnable) {
+ // If we are calling this from the target thread, then we can call right through.
+ // Otherwise, we need to send the message to the target thread.
+ if (mLooper.isCurrentThread()) {
+ runnable.run();
+ return;
+ }
+
+ mH.post(runnable);
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index c56ed2d19927..343a6e697c7c 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -20,13 +20,7 @@ import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-import com.android.internal.view.InputBindResult;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -43,15 +37,9 @@ public final class ResultCallbacks {
@AnyThread
@Nullable
- private static <T> T unwrap(@NonNull AtomicReference<WeakReference<T>> atomicRef) {
- final WeakReference<T> ref = atomicRef.getAndSet(null);
- if (ref == null) {
- // Double-call is guaranteed to be ignored here.
- return null;
- }
- final T value = ref.get();
- ref.clear();
- return value;
+ private static <T> T unwrap(@NonNull AtomicReference<T> atomicRef) {
+ // Only the first caller will receive the non-null original object.
+ return atomicRef.getAndSet(null);
}
/**
@@ -63,8 +51,7 @@ public final class ResultCallbacks {
*/
@AnyThread
public static IIntResultCallback.Stub of(@NonNull Completable.Int value) {
- final AtomicReference<WeakReference<Completable.Int>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.Int> atomicRef = new AtomicReference<>(value);
return new IIntResultCallback.Stub() {
@BinderThread
@@ -100,8 +87,7 @@ public final class ResultCallbacks {
@AnyThread
public static ICharSequenceResultCallback.Stub of(
@NonNull Completable.CharSequence value) {
- final AtomicReference<WeakReference<Completable.CharSequence>> atomicRef =
- new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.CharSequence> atomicRef = new AtomicReference<>(value);
return new ICharSequenceResultCallback.Stub() {
@BinderThread
@@ -127,8 +113,7 @@ public final class ResultCallbacks {
@AnyThread
public static IExtractedTextResultCallback.Stub of(
@NonNull Completable.ExtractedText value) {
- final AtomicReference<WeakReference<Completable.ExtractedText>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.ExtractedText> atomicRef = new AtomicReference<>(value);
return new IExtractedTextResultCallback.Stub() {
@BinderThread
@@ -154,8 +139,7 @@ public final class ResultCallbacks {
@AnyThread
public static ISurroundingTextResultCallback.Stub of(
@NonNull Completable.SurroundingText value) {
- final AtomicReference<WeakReference<Completable.SurroundingText>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.SurroundingText> atomicRef = new AtomicReference<>(value);
return new ISurroundingTextResultCallback.Stub() {
@BinderThread
@@ -171,43 +155,6 @@ public final class ResultCallbacks {
}
/**
- * Creates {@link IInputBindResultResultCallback.Stub} that is to set
- * {@link Completable.InputBindResult} when receiving the result.
- *
- * @param value {@link Completable.InputBindResult} to be set when receiving the result.
- * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC
- * parameter.
- */
- @AnyThread
- public static IInputBindResultResultCallback.Stub of(
- @NonNull Completable.InputBindResult value) {
- final AtomicReference<WeakReference<Completable.InputBindResult>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
- return new IInputBindResultResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(InputBindResult result) {
- final Completable.InputBindResult value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.InputBindResult value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
* Creates {@link IBooleanResultCallback.Stub} that is to set {@link Completable.Boolean} when
* receiving the result.
*
@@ -216,8 +163,7 @@ public final class ResultCallbacks {
*/
@AnyThread
public static IBooleanResultCallback.Stub of(@NonNull Completable.Boolean value) {
- final AtomicReference<WeakReference<Completable.Boolean>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.Boolean> atomicRef = new AtomicReference<>(value);
return new IBooleanResultCallback.Stub() {
@BinderThread
@@ -243,117 +189,6 @@ public final class ResultCallbacks {
}
/**
- * Creates {@link IInputMethodSubtypeResultCallback.Stub} that is to set
- * {@link Completable.InputMethodSubtype} when receiving the result.
- *
- * @param value {@link Completable.InputMethodSubtype} to be set when receiving the result.
- * @return {@link IInputMethodSubtypeResultCallback.Stub} that can be passed as a binder
- * IPC parameter.
- */
- @AnyThread
- public static IInputMethodSubtypeResultCallback.Stub of(
- @NonNull Completable.InputMethodSubtype value) {
- final AtomicReference<WeakReference<Completable.InputMethodSubtype>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
- return new IInputMethodSubtypeResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(InputMethodSubtype result) {
- final Completable.InputMethodSubtype value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.InputMethodSubtype value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
- * Creates {@link IInputMethodSubtypeListResultCallback.Stub} that is to set
- * {@link Completable.InputMethodSubtypeList} when receiving the result.
- *
- * @param value {@link Completable.InputMethodSubtypeList} to be set when receiving the result.
- * @return {@link IInputMethodSubtypeListResultCallback.Stub} that can be passed as a binder
- * IPC parameter.
- */
- @AnyThread
- public static IInputMethodSubtypeListResultCallback.Stub of(
- @NonNull Completable.InputMethodSubtypeList value) {
- final AtomicReference<WeakReference<Completable.InputMethodSubtypeList>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
- return new IInputMethodSubtypeListResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(List<InputMethodSubtype> result) {
- final Completable.InputMethodSubtypeList value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.InputMethodSubtypeList value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
- * Creates {@link IInputMethodInfoListResultCallback.Stub} that is to set
- * {@link Completable.InputMethodInfoList} when receiving the result.
- *
- * @param value {@link Completable.InputMethodInfoList} to be set when receiving the result.
- * @return {@link IInputMethodInfoListResultCallback.Stub} that can be passed as a binder
- * IPC parameter.
- */
- @AnyThread
- public static IInputMethodInfoListResultCallback.Stub of(
- @NonNull Completable.InputMethodInfoList value) {
- final AtomicReference<WeakReference<Completable.InputMethodInfoList>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
-
- return new IInputMethodInfoListResultCallback.Stub() {
- @BinderThread
- @Override
- public void onResult(List<InputMethodInfo> result) {
- final Completable.InputMethodInfoList value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onComplete(result);
- }
-
- @BinderThread
- @Override
- public void onError(ThrowableHolder throwableHolder) {
- final Completable.InputMethodInfoList value = unwrap(atomicRef);
- if (value == null) {
- return;
- }
- value.onError(throwableHolder);
- }
- };
- }
-
- /**
* Creates {@link IVoidResultCallback.Stub} that is to set {@link Completable.Void} when
* receiving the result.
*
@@ -362,8 +197,7 @@ public final class ResultCallbacks {
*/
@AnyThread
public static IVoidResultCallback.Stub of(@NonNull Completable.Void value) {
- final AtomicReference<WeakReference<Completable.Void>> atomicRef =
- new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.Void> atomicRef = new AtomicReference<>(value);
return new IVoidResultCallback.Stub() {
@BinderThread
@@ -389,20 +223,20 @@ public final class ResultCallbacks {
}
/**
- * Creates {@link IIInputContentUriTokenResultCallback.Stub} that is to set
+ * Creates {@link IInputContentUriTokenResultCallback.Stub} that is to set
* {@link Completable.IInputContentUriToken} when receiving the result.
*
* @param value {@link Completable.IInputContentUriToken} to be set when receiving the result.
- * @return {@link IIInputContentUriTokenResultCallback.Stub} that can be passed as a binder IPC
+ * @return {@link IInputContentUriTokenResultCallback.Stub} that can be passed as a binder IPC
* parameter.
*/
@AnyThread
- public static IIInputContentUriTokenResultCallback.Stub of(
+ public static IInputContentUriTokenResultCallback.Stub of(
@NonNull Completable.IInputContentUriToken value) {
- final AtomicReference<WeakReference<Completable.IInputContentUriToken>>
- atomicRef = new AtomicReference<>(new WeakReference<>(value));
+ final AtomicReference<Completable.IInputContentUriToken>
+ atomicRef = new AtomicReference<>(value);
- return new IIInputContentUriTokenResultCallback.Stub() {
+ return new IInputContentUriTokenResultCallback.Stub() {
@BinderThread
@Override
public void onResult(IInputContentUriToken result) {
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 8e7fae7aa061..aa7142eaa39d 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -45,6 +45,7 @@ import android.view.ThreadedRenderer;
import android.view.ViewRootImpl;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
@@ -69,6 +70,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
static final int REASON_CANCEL_NORMAL = 16;
static final int REASON_CANCEL_NOT_BEGUN = 17;
static final int REASON_CANCEL_SAME_VSYNC = 18;
+ static final int REASON_CANCEL_TIMEOUT = 19;
/** @hide */
@IntDef({
@@ -97,6 +99,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
private final Handler mHandler;
private final ChoreographerWrapper mChoreographer;
+ @VisibleForTesting
+ public final boolean mSurfaceOnly;
+
private long mBeginVsyncId = INVALID_ID;
private long mEndVsyncId = INVALID_ID;
private boolean mMetricsFinalized;
@@ -136,71 +141,86 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
public FrameTracker(@NonNull Session session, @NonNull Handler handler,
- @NonNull ThreadedRendererWrapper renderer, @NonNull ViewRootWrapper viewRootWrapper,
+ @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
@NonNull SurfaceControlWrapper surfaceControlWrapper,
@NonNull ChoreographerWrapper choreographer,
- @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
- int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
+ @Nullable FrameMetricsWrapper metrics,
+ int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
+ @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
+ mSurfaceOnly = config.isSurfaceOnly();
mSession = session;
- mRendererWrapper = renderer;
- mMetricsWrapper = metrics;
- mViewRoot = viewRootWrapper;
+ mHandler = handler;
mChoreographer = choreographer;
mSurfaceControlWrapper = surfaceControlWrapper;
- mHandler = handler;
- mObserver = new HardwareRendererObserver(
- this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/);
+
+ // HWUI instrumentation init.
+ mRendererWrapper = mSurfaceOnly ? null : renderer;
+ mMetricsWrapper = mSurfaceOnly ? null : metrics;
+ mViewRoot = mSurfaceOnly ? null : viewRootWrapper;
+ mObserver = mSurfaceOnly
+ ? null
+ : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
+ handler, /* waitForPresentTime= */ false);
+
mTraceThresholdMissedFrames = traceThresholdMissedFrames;
mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
mListener = listener;
- // If the surface isn't valid yet, wait until it's created.
- if (viewRootWrapper.getSurfaceControl().isValid()) {
- mSurfaceControl = viewRootWrapper.getSurfaceControl();
- }
- mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
- @Override
- public void surfaceCreated(SurfaceControl.Transaction t) {
- synchronized (FrameTracker.this) {
- if (mSurfaceControl == null) {
- mSurfaceControl = viewRootWrapper.getSurfaceControl();
- if (mBeginVsyncId != INVALID_ID) {
- mSurfaceControlWrapper.addJankStatsListener(
- FrameTracker.this, mSurfaceControl);
- postTraceStartMarker();
+ if (mSurfaceOnly) {
+ mSurfaceControl = config.getSurfaceControl();
+ mSurfaceChangedCallback = null;
+ } else {
+ // HWUI instrumentation init.
+ // If the surface isn't valid yet, wait until it's created.
+ if (mViewRoot.getSurfaceControl().isValid()) {
+ mSurfaceControl = mViewRoot.getSurfaceControl();
+ mSurfaceChangedCallback = null;
+ } else {
+ mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
+ @Override
+ public void surfaceCreated(SurfaceControl.Transaction t) {
+ synchronized (FrameTracker.this) {
+ if (mSurfaceControl == null) {
+ mSurfaceControl = mViewRoot.getSurfaceControl();
+ if (mBeginVsyncId != INVALID_ID) {
+ mSurfaceControlWrapper.addJankStatsListener(
+ FrameTracker.this, mSurfaceControl);
+ postTraceStartMarker();
+ }
+ }
}
}
- }
- }
- @Override
- public void surfaceReplaced(SurfaceControl.Transaction t) {
- }
+ @Override
+ public void surfaceReplaced(SurfaceControl.Transaction t) {
+ }
- @Override
- public void surfaceDestroyed() {
-
- // Wait a while to give the system a chance for the remaining frames to arrive, then
- // force finish the session.
- mHandler.postDelayed(() -> {
- synchronized (FrameTracker.this) {
- if (DEBUG) {
- Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
- + ", finalized=" + mMetricsFinalized
- + ", info=" + mJankInfos.size()
- + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
- }
- if (!mMetricsFinalized) {
- end(REASON_END_SURFACE_DESTROYED);
- finish(mJankInfos.size() - 1);
- }
+ @Override
+ public void surfaceDestroyed() {
+
+ // Wait a while to give the system a chance for the remaining
+ // frames to arrive, then force finish the session.
+ mHandler.postDelayed(() -> {
+ synchronized (FrameTracker.this) {
+ if (DEBUG) {
+ Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+ + ", finalized=" + mMetricsFinalized
+ + ", info=" + mJankInfos.size()
+ + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
+ }
+ if (!mMetricsFinalized) {
+ end(REASON_END_SURFACE_DESTROYED);
+ finish(mJankInfos.size() - 1);
+ }
+ }
+ }, 50);
}
- }, 50);
+ };
+ // This callback has a reference to FrameTracker,
+ // remember to remove it to avoid leakage.
+ mViewRoot.addSurfaceChangedCallback(mSurfaceChangedCallback);
}
- };
-
- // This callback has a reference to FrameTracker, remember to remove it to avoid leakage.
- viewRootWrapper.addSurfaceChangedCallback(mSurfaceChangedCallback);
+ }
}
/**
@@ -208,16 +228,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/
public synchronized void begin() {
mBeginVsyncId = mChoreographer.getVsyncId() + 1;
- if (mSurfaceControl != null) {
- postTraceStartMarker();
- }
- mRendererWrapper.addObserver(mObserver);
if (DEBUG) {
Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
}
if (mSurfaceControl != null) {
+ postTraceStartMarker();
mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
}
+ if (!mSurfaceOnly) {
+ mRendererWrapper.addObserver(mObserver);
+ }
if (mListener != null) {
mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
}
@@ -273,11 +293,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
* Cancel the trace session of the CUJ.
*/
public synchronized void cancel(@Reasons int reason) {
+ mCancelled = true;
+
// We don't need to end the trace section if it never begun.
if (mTracingStarted) {
Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
}
- mCancelled = true;
// Always remove the observers in cancel call to avoid leakage.
removeObservers();
@@ -377,7 +398,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
for (int i = mJankInfos.size() - 1; i >= 0; i--) {
JankInfo info = mJankInfos.valueAt(i);
if (info.frameVsyncId >= mEndVsyncId) {
- if (info.hwuiCallbackFired && info.surfaceControlCallbackFired) {
+ if (isLastIndexCandidate(info)) {
lastIndex = i;
}
} else {
@@ -395,6 +416,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
finish(indexOnOrAfterEnd);
}
+ private boolean isLastIndexCandidate(JankInfo info) {
+ return mSurfaceOnly
+ ? info.surfaceControlCallbackFired
+ : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
+ }
+
private void finish(int indexOnOrAfterEnd) {
mMetricsFinalized = true;
@@ -410,7 +437,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
for (int i = 0; i <= indexOnOrAfterEnd; i++) {
JankInfo info = mJankInfos.valueAt(i);
- if (info.isFirstFrame) {
+ final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame;
+ if (isFirstDrawn) {
continue;
}
if (info.surfaceControlCallbackFired) {
@@ -435,11 +463,11 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
// TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now.
- if (!info.hwuiCallbackFired) {
+ if (!mSurfaceOnly && !info.hwuiCallbackFired) {
Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
}
}
- if (info.hwuiCallbackFired) {
+ if (!mSurfaceOnly && info.hwuiCallbackFired) {
maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
if (!info.surfaceControlCallbackFired) {
Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
@@ -462,7 +490,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
// Trigger perfetto if necessary.
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
&& missedFramesCount >= mTraceThresholdMissedFrames;
- boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
+ boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
&& maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
if (overMissedFramesThreshold || overFrameTimeThreshold) {
triggerPerfetto();
@@ -473,7 +501,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
mSession.getStatsdInteractionType(),
totalFramesCount,
missedFramesCount,
- maxFrameTimeNanos,
+ maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
missedSfFramesCount,
missedAppFramesCount);
if (mListener != null) {
@@ -496,10 +524,13 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
*/
@VisibleForTesting
public void removeObservers() {
- mRendererWrapper.removeObserver(mObserver);
mSurfaceControlWrapper.removeJankStatsListener(this);
- if (mSurfaceChangedCallback != null) {
- mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
+ if (!mSurfaceOnly) {
+ // HWUI part.
+ mRendererWrapper.removeObserver(mObserver);
+ if (mSurfaceChangedCallback != null) {
+ mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
+ }
}
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index aabcd7f82ac7..aae6f5016891 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,11 +18,11 @@ package com.android.internal.jank;
import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
-import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
-import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
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;
@@ -41,6 +41,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
@@ -58,6 +59,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -72,11 +74,15 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
+import android.view.SurfaceControl;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.FrameTrackerListener;
+import com.android.internal.jank.FrameTracker.Reasons;
+import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import com.android.internal.jank.FrameTracker.ViewRootWrapper;
import com.android.internal.util.PerfettoTrigger;
@@ -103,7 +109,7 @@ public class InteractionJankMonitor {
private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
- private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
+ private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L);
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
@@ -163,6 +169,8 @@ public class InteractionJankMonitor {
public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
+ public static final int CUJ_PIP_TRANSITION = 35;
+ public static final int CUJ_WALLPAPER_TRANSITION = 36;
private static final int NO_STATSD_LOGGING = -1;
@@ -206,6 +214,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
};
private static volatile InteractionJankMonitor sInstance;
@@ -213,10 +223,10 @@ public class InteractionJankMonitor {
private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
this::updateProperties;
- private FrameMetricsWrapper mMetrics;
- private SparseArray<FrameTracker> mRunningTrackers;
- private SparseArray<Runnable> mTimeoutActions;
- private HandlerThread mWorker;
+ private final FrameMetricsWrapper mMetrics;
+ private final SparseArray<FrameTracker> mRunningTrackers;
+ private final SparseArray<Runnable> mTimeoutActions;
+ private final HandlerThread mWorker;
private boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -260,6 +270,8 @@ public class InteractionJankMonitor {
CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ CUJ_PIP_TRANSITION,
+ CUJ_WALLPAPER_TRANSITION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -310,24 +322,31 @@ public class InteractionJankMonitor {
}
/**
- * Create a {@link FrameTracker} instance.
+ * Creates a {@link FrameTracker} instance.
*
+ * @param config the config used in instrumenting
* @param session the session associates with this tracker
* @return instance of the FrameTracker
*/
@VisibleForTesting
- public FrameTracker createFrameTracker(Configuration conf, Session session) {
- final View v = conf.mView;
- final Context c = v.getContext().getApplicationContext();
- final ThreadedRendererWrapper r = new ThreadedRendererWrapper(v.getThreadedRenderer());
- final ViewRootWrapper vr = new ViewRootWrapper(v.getViewRootImpl());
- final SurfaceControlWrapper sc = new SurfaceControlWrapper();
- final ChoreographerWrapper cg = new ChoreographerWrapper(Choreographer.getInstance());
+ public FrameTracker createFrameTracker(Configuration config, Session session) {
+ final View view = config.mView;
+ final ThreadedRendererWrapper threadedRenderer =
+ view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
+ final ViewRootWrapper viewRoot =
+ view == null ? null : new ViewRootWrapper(view.getViewRootImpl());
+
+ final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
+ final ChoreographerWrapper choreographer =
+ new ChoreographerWrapper(Choreographer.getInstance());
synchronized (this) {
- FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(c, act, s);
- return new FrameTracker(session, mWorker.getThreadHandler(), r, vr, sc, cg, mMetrics,
- mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
+ FrameTrackerListener eventsListener =
+ (s, act) -> handleCujEvents(config.getContext(), act, s);
+ return new FrameTracker(session, mWorker.getThreadHandler(),
+ threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics,
+ mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
+ eventsListener, config);
}
}
@@ -376,7 +395,7 @@ public class InteractionJankMonitor {
}
/**
- * Begin a trace session.
+ * Begins a trace session.
*
* @param v an attached view.
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
@@ -385,8 +404,7 @@ public class InteractionJankMonitor {
public boolean begin(View v, @CujType int cujType) {
try {
return beginInternal(
- new Configuration.Builder(cujType)
- .setView(v)
+ Configuration.Builder.withView(cujType, v)
.build());
} catch (IllegalArgumentException ex) {
Log.d(TAG, "Build configuration failed!", ex);
@@ -395,7 +413,7 @@ public class InteractionJankMonitor {
}
/**
- * Begin a trace session.
+ * Begins a trace session.
*
* @param builder the builder of the configurations for instrumenting the CUJ.
* @return boolean true if the tracker is started successfully, false otherwise.
@@ -431,48 +449,60 @@ public class InteractionJankMonitor {
tracker.begin();
// Cancel the trace if we don't get an end() call in specified duration.
- Runnable timeoutAction = () -> cancel(cujType);
- mTimeoutActions.put(cujType, timeoutAction);
- mWorker.getThreadHandler().postDelayed(timeoutAction, conf.mTimeout);
+ scheduleTimeoutAction(
+ cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
return true;
}
}
/**
- * End a trace session.
+ * Schedules a timeout action.
+ * @param cuj cuj type
+ * @param timeout duration to timeout
+ * @param action action once timeout
+ */
+ @VisibleForTesting
+ public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
+ mTimeoutActions.put(cuj, action);
+ mWorker.getThreadHandler().postDelayed(action, timeout);
+ }
+
+ /**
+ * Ends a trace session.
*
* @param cujType the specific {@link InteractionJankMonitor.CujType}.
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
- //TODO (163505250): This should be no-op if not in droid food rom.
synchronized (this) {
-
// remove the timeout action first.
removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
- tracker.end(FrameTracker.REASON_END_NORMAL);
+ tracker.end(REASON_END_NORMAL);
removeTracker(cujType);
return true;
}
}
/**
- * Cancel the trace session.
+ * Cancels the trace session.
*
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
- //TODO (163505250): This should be no-op if not in droid food rom.
+ return cancel(cujType, REASON_CANCEL_NORMAL);
+ }
+
+ boolean cancel(@CujType int cujType, @Reasons int reason) {
synchronized (this) {
// remove the timeout action first.
removeTimeout(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
- tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+ tracker.cancel(reason);
removeTracker(cujType);
return true;
}
@@ -509,7 +539,7 @@ public class InteractionJankMonitor {
}
/**
- * Trigger the perfetto daemon to collect and upload data.
+ * Triggers the perfetto daemon to collect and upload data.
*/
@VisibleForTesting
public void trigger(Session session) {
@@ -608,6 +638,10 @@ public class InteractionJankMonitor {
return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
+ case CUJ_PIP_TRANSITION:
+ return "PIP_TRANSITION";
+ case CUJ_WALLPAPER_TRANSITION:
+ return "WALLPAPER_TRANSITION";
}
return "UNKNOWN";
}
@@ -618,32 +652,64 @@ public class InteractionJankMonitor {
*/
public static class Configuration {
private final View mView;
+ private final Context mContext;
private final long mTimeout;
private final String mTag;
+ private final boolean mSurfaceOnly;
+ private final SurfaceControl mSurfaceControl;
private final @CujType int mCujType;
/**
- * A builder for building Configuration. <br/>
+ * A builder for building Configuration. {@link #setView(View)} is essential
+ * if {@link #setSurfaceOnly(boolean)} is not set, otherwise both
+ * {@link #setSurfaceControl(SurfaceControl)} and {@link #setContext(Context)}
+ * are necessary<br/>
* <b>It may refer to an attached view, don't use static reference for any purpose.</b>
*/
public static class Builder {
private View mAttrView = null;
+ private Context mAttrContext = null;
private long mAttrTimeout = DEFAULT_TIMEOUT_MS;
private String mAttrTag = "";
+ private boolean mAttrSurfaceOnly;
+ private SurfaceControl mAttrSurfaceControl;
private @CujType int mAttrCujType;
/**
+ * Creates a builder which instruments only surface.
* @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+ * @param context context
+ * @param surfaceControl surface control
+ * @return builder
*/
- public Builder(@CujType int cuj) {
+ public static Builder withSurface(@CujType int cuj, @NonNull Context context,
+ @NonNull SurfaceControl surfaceControl) {
+ return new Builder(cuj)
+ .setContext(context)
+ .setSurfaceControl(surfaceControl)
+ .setSurfaceOnly(true);
+ }
+
+ /**
+ * Creates a builder which instruments both surface and view.
+ * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+ * @param view view
+ * @return builder
+ */
+ public static Builder withView(@CujType int cuj, @NonNull View view) {
+ return new Builder(cuj).setView(view);
+ }
+
+ private Builder(@CujType int cuj) {
mAttrCujType = cuj;
}
/**
+ * Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false.
* @param view an attached view
* @return builder
*/
- public Builder setView(@NonNull View view) {
+ private Builder setView(@NonNull View view) {
mAttrView = view;
return this;
}
@@ -669,20 +735,56 @@ public class InteractionJankMonitor {
}
/**
- * Build the {@link Configuration} instance
+ * Indicates if only instrument with surface,
+ * if true, must also setup with {@link #setContext(Context)}
+ * and {@link #setSurfaceControl(SurfaceControl)}.
+ * @param surfaceOnly true if only instrument with surface, false otherwise
+ * @return builder Surface only builder.
+ */
+ private Builder setSurfaceOnly(boolean surfaceOnly) {
+ mAttrSurfaceOnly = surfaceOnly;
+ return this;
+ }
+
+ /**
+ * Specifies a context, must set if {@link #setSurfaceOnly(boolean)} is set.
+ */
+ private Builder setContext(Context context) {
+ mAttrContext = context;
+ return this;
+ }
+
+ /**
+ * Specifies a surface control, must be set if {@link #setSurfaceOnly(boolean)} is set.
+ */
+ private Builder setSurfaceControl(SurfaceControl surfaceControl) {
+ mAttrSurfaceControl = surfaceControl;
+ return this;
+ }
+
+ /**
+ * Builds the {@link Configuration} instance
* @return the instance of {@link Configuration}
* @throws IllegalArgumentException if any invalid attribute is set
*/
public Configuration build() throws IllegalArgumentException {
- return new Configuration(mAttrCujType, mAttrView, mAttrTag, mAttrTimeout);
+ return new Configuration(
+ mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
+ mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl);
}
}
- private Configuration(@CujType int cuj, View view, String tag, long timeout) {
+ private Configuration(@CujType int cuj, View view, String tag, long timeout,
+ boolean surfaceOnly, Context context, SurfaceControl surfaceControl) {
mCujType = cuj;
mTag = tag;
mTimeout = timeout;
mView = view;
+ mSurfaceOnly = surfaceOnly;
+ mContext = context != null
+ ? context
+ : (view != null ? view.getContext().getApplicationContext() : null);
+ mSurfaceControl = surfaceControl;
validate();
}
@@ -698,14 +800,47 @@ public class InteractionJankMonitor {
shouldThrow = true;
msg.append("Invalid timeout value; ");
}
- if (mView == null || !mView.isAttachedToWindow()) {
- shouldThrow = true;
- msg.append("Null view or view is not attached yet; ");
+ if (mSurfaceOnly) {
+ if (mContext == null) {
+ shouldThrow = true;
+ msg.append("Must pass in a context if only instrument surface; ");
+ }
+ if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+ shouldThrow = true;
+ msg.append("Must pass in a valid surface control if only instrument surface; ");
+ }
+ } else {
+ if (mView == null || !mView.isAttachedToWindow()) {
+ shouldThrow = true;
+ msg.append("Null view or unattached view while instrumenting view; ");
+ }
}
if (shouldThrow) {
throw new IllegalArgumentException(msg.toString());
}
}
+
+ /**
+ * @return true if only instrumenting surface, false otherwise
+ */
+ public boolean isSurfaceOnly() {
+ return mSurfaceOnly;
+ }
+
+ /**
+ * @return the surafce control which is instrumenting
+ */
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ View getView() {
+ return mView;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
}
/**
@@ -715,8 +850,8 @@ public class InteractionJankMonitor {
@CujType
private final int mCujType;
private final long mTimeStamp;
- @FrameTracker.Reasons
- private int mReason = FrameTracker.REASON_END_UNKNOWN;
+ @Reasons
+ private int mReason = REASON_END_UNKNOWN;
private final boolean mShouldNotify;
private final String mName;
@@ -756,15 +891,15 @@ public class InteractionJankMonitor {
return mTimeStamp;
}
- public void setReason(@FrameTracker.Reasons int reason) {
+ public void setReason(@Reasons int reason) {
mReason = reason;
}
- public int getReason() {
+ public @Reasons int getReason() {
return mReason;
}
- /** Determine if should notify the receivers of cuj events */
+ /** Determines if should notify the receivers of cuj events */
public boolean shouldNotify() {
return mShouldNotify;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8c63f38494ea..40430605f186 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -196,7 +196,7 @@ public class BatteryStatsImpl extends BatteryStats {
public static final int RESET_REASON_FULL_CHARGE = 3;
public static final int RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE = 4;
- protected Clocks mClocks;
+ protected Clock mClock;
private final AtomicFile mStatsFile;
public final AtomicFile mCheckinFile;
@@ -215,19 +215,15 @@ public class BatteryStatsImpl extends BatteryStats {
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@VisibleForTesting
- protected KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
- new KernelCpuUidUserSysTimeReader(true);
+ protected KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
@VisibleForTesting
protected KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@VisibleForTesting
- protected KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
- new KernelCpuUidFreqTimeReader(true);
+ protected KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader;
@VisibleForTesting
- protected KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
- new KernelCpuUidActiveTimeReader(true);
+ protected KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader;
@VisibleForTesting
- protected KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
- new KernelCpuUidClusterTimeReader(true);
+ protected KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@VisibleForTesting
@@ -300,9 +296,9 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
public final class UidToRemove {
- int startUid;
- int endUid;
- long mTimeAddedInQueueMs;
+ private final int mStartUid;
+ private final int mEndUid;
+ private final long mUidRemovalTimestamp;
/** Remove just one UID */
public UidToRemove(int uid, long timestamp) {
@@ -311,38 +307,18 @@ public class BatteryStatsImpl extends BatteryStats {
/** Remove a range of UIDs, startUid must be smaller than endUid. */
public UidToRemove(int startUid, int endUid, long timestamp) {
- this.startUid = startUid;
- this.endUid = endUid;
- mTimeAddedInQueueMs = timestamp;
- }
-
- void remove() {
- if (startUid == endUid) {
- mCpuUidUserSysTimeReader.removeUid(startUid);
- mCpuUidFreqTimeReader.removeUid(startUid);
- if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mCpuUidActiveTimeReader.removeUid(startUid);
- mCpuUidClusterTimeReader.removeUid(startUid);
- }
- if (mKernelSingleUidTimeReader != null) {
- mKernelSingleUidTimeReader.removeUid(startUid);
- }
- mNumUidsRemoved++;
- } else if (startUid < endUid) {
- mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
- mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
- if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
- mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
- }
- if (mKernelSingleUidTimeReader != null) {
- mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
- }
- // Treat as one. We don't know how many uids there are in between.
- mNumUidsRemoved++;
- } else {
- Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
- }
+ mStartUid = startUid;
+ mEndUid = endUid;
+ mUidRemovalTimestamp = timestamp;
+ }
+
+ public long getUidRemovalTimestamp() {
+ return mUidRemovalTimestamp;
+ }
+
+ @GuardedBy("BatteryStatsImpl.this")
+ void removeLocked() {
+ removeCpuStatsForUidRangeLocked(mStartUid, mEndUid);
}
}
@@ -407,8 +383,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
boolean changed = setChargingLocked(true);
if (changed) {
- final long uptimeMs = mClocks.uptimeMillis();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
}
}
@@ -526,11 +502,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
- public void clearPendingRemovedUids() {
- long cutOffTimeMs = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
+ /**
+ * Removes kernel CPU stats for removed UIDs, in the order they were added to the
+ * mPendingRemovedUids queue.
+ */
+ @GuardedBy("this")
+ public void clearPendingRemovedUidsLocked() {
+ long cutOffTimeMs = mClock.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
while (!mPendingRemovedUids.isEmpty()
- && mPendingRemovedUids.peek().mTimeAddedInQueueMs < cutOffTimeMs) {
- mPendingRemovedUids.poll().remove();
+ && mPendingRemovedUids.peek().getUidRemovalTimestamp() < cutOffTimeMs) {
+ mPendingRemovedUids.poll().removeLocked();
}
}
@@ -628,35 +609,6 @@ public class BatteryStatsImpl extends BatteryStats {
return true;
}
- public interface Clocks {
- /** Elapsed Realtime, see SystemClock.elapsedRealtime() */
- long elapsedRealtime();
-
- /** Uptime, see SystemClock.uptimeMillis() */
- long uptimeMillis();
-
- /** Wall-clock time as per System.currentTimeMillis() */
- long currentTimeMillis();
- }
-
- public static class SystemClocks implements Clocks {
-
- @Override
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
-
- @Override
- public long uptimeMillis() {
- return SystemClock.uptimeMillis();
- }
-
- @Override
- public long currentTimeMillis() {
- return System.currentTimeMillis();
- }
- }
-
public interface ExternalStatsSync {
int UPDATE_CPU = 0x01;
int UPDATE_WIFI = 0x02;
@@ -694,6 +646,8 @@ public class BatteryStatsImpl extends BatteryStats {
Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
void cancelCpuSyncDueToWakelockChange();
Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
+ /** Schedule removal of UIDs corresponding to a removed user */
+ Future<?> scheduleCleanupDueToRemovedUser(int userId);
}
public Handler mHandler;
@@ -1193,12 +1147,12 @@ public class BatteryStatsImpl extends BatteryStats {
}
public BatteryStatsImpl() {
- this(new SystemClocks());
+ this(Clock.SYSTEM_CLOCK);
}
- public BatteryStatsImpl(Clocks clocks) {
- init(clocks);
- mStartClockTimeMs = clocks.currentTimeMillis();
+ public BatteryStatsImpl(Clock clock) {
+ init(clock);
+ mStartClockTimeMs = clock.currentTimeMillis();
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
@@ -1211,8 +1165,12 @@ public class BatteryStatsImpl extends BatteryStats {
clearHistoryLocked();
}
- private void init(Clocks clocks) {
- mClocks = clocks;
+ private void init(Clock clock) {
+ mClock = clock;
+ mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(true, clock);
+ mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, clock);
+ mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, clock);
+ mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, clock);
}
/**
@@ -1768,7 +1726,7 @@ public class BatteryStatsImpl extends BatteryStats {
* State for keeping track of timing information.
*/
public static abstract class Timer extends BatteryStats.Timer implements TimeBaseObs {
- protected final Clocks mClocks;
+ protected final Clock mClock;
protected final int mType;
protected final TimeBase mTimeBase;
@@ -1796,8 +1754,8 @@ public class BatteryStatsImpl extends BatteryStats {
* @param timeBase
* @param in
*/
- public Timer(Clocks clocks, int type, TimeBase timeBase, Parcel in) {
- mClocks = clocks;
+ public Timer(Clock clock, int type, TimeBase timeBase, Parcel in) {
+ mClock = clock;
mType = type;
mTimeBase = timeBase;
@@ -1808,8 +1766,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (DEBUG) Log.i(TAG, "**** READ TIMER #" + mType + ": mTotalTime=" + mTotalTimeUs);
}
- public Timer(Clocks clocks, int type, TimeBase timeBase) {
- mClocks = clocks;
+ public Timer(Clock clock, int type, TimeBase timeBase) {
+ mClock = clock;
mType = type;
mTimeBase = timeBase;
timeBase.add(this);
@@ -1838,7 +1796,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@Override
public boolean reset(boolean detachIfReset) {
- return reset(detachIfReset, mClocks.elapsedRealtime() * 1000);
+ return reset(detachIfReset, mClock.elapsedRealtime() * 1000);
}
@Override
@@ -1984,8 +1942,8 @@ public class BatteryStatsImpl extends BatteryStats {
int mUpdateVersion;
@VisibleForTesting
- public SamplingTimer(Clocks clocks, TimeBase timeBase, Parcel in) {
- super(clocks, 0, timeBase, in);
+ public SamplingTimer(Clock clock, TimeBase timeBase, Parcel in) {
+ super(clock, 0, timeBase, in);
mCurrentReportedCount = in.readInt();
mUnpluggedReportedCount = in.readInt();
mCurrentReportedTotalTimeUs = in.readLong();
@@ -1995,8 +1953,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
@VisibleForTesting
- public SamplingTimer(Clocks clocks, TimeBase timeBase) {
- super(clocks, 0, timeBase);
+ public SamplingTimer(Clock clock, TimeBase timeBase) {
+ super(clock, 0, timeBase);
mTrackingReportedValues = false;
mTimeBaseRunning = timeBase.isRunning();
}
@@ -2006,7 +1964,7 @@ public class BatteryStatsImpl extends BatteryStats {
* be less than the values used for a previous invocation.
*/
public void endSample() {
- endSample(mClocks.elapsedRealtime() * 1000);
+ endSample(mClock.elapsedRealtime() * 1000);
}
/**
@@ -2041,7 +1999,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param count total number of times the event being sampled occurred.
*/
public void updated(long totalTimeUs, int count) {
- update(totalTimeUs, count, mClocks.elapsedRealtime() * 1000);
+ update(totalTimeUs, count, mClock.elapsedRealtime() * 1000);
}
/**
@@ -2071,7 +2029,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param deltaCount additional number of times the event being sampled occurred.
*/
public void add(long deltaTimeUs, int deltaCount) {
- add(deltaTimeUs, deltaCount, mClocks.elapsedRealtime() * 1000);
+ add(deltaTimeUs, deltaCount, mClock.elapsedRealtime() * 1000);
}
/**
@@ -2161,16 +2119,16 @@ public class BatteryStatsImpl extends BatteryStats {
*/
boolean mInDischarge;
- BatchTimer(Clocks clocks, Uid uid, int type, TimeBase timeBase, Parcel in) {
- super(clocks, type, timeBase, in);
+ BatchTimer(Clock clock, Uid uid, int type, TimeBase timeBase, Parcel in) {
+ super(clock, type, timeBase, in);
mUid = uid;
mLastAddedTimeUs = in.readLong();
mLastAddedDurationUs = in.readLong();
mInDischarge = timeBase.isRunning();
}
- BatchTimer(Clocks clocks, Uid uid, int type, TimeBase timeBase) {
- super(clocks, type, timeBase);
+ BatchTimer(Clock clock, Uid uid, int type, TimeBase timeBase) {
+ super(clock, type, timeBase);
mUid = uid;
mInDischarge = timeBase.isRunning();
}
@@ -2233,7 +2191,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void addDuration(BatteryStatsImpl stats, long durationMs) {
- addDuration(stats, durationMs, mClocks.elapsedRealtime());
+ addDuration(stats, durationMs, mClock.elapsedRealtime());
}
public void addDuration(BatteryStatsImpl stats, long durationMs, long elapsedRealtimeMs) {
@@ -2248,7 +2206,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void abortLastDuration(BatteryStatsImpl stats) {
- abortLastDuration(stats, mClocks.elapsedRealtime());
+ abortLastDuration(stats, mClock.elapsedRealtime());
}
public void abortLastDuration(BatteryStatsImpl stats, long elapsedRealtimeMs) {
@@ -2316,17 +2274,17 @@ public class BatteryStatsImpl extends BatteryStats {
*/
long mTotalDurationMs;
- public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public DurationTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, Parcel in) {
- super(clocks, uid, type, timerPool, timeBase, in);
+ super(clock, uid, type, timerPool, timeBase, in);
mMaxDurationMs = in.readLong();
mTotalDurationMs = in.readLong();
mCurrentDurationMs = in.readLong();
}
- public DurationTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public DurationTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase) {
- super(clocks, uid, type, timerPool, timeBase);
+ super(clock, uid, type, timerPool, timeBase);
}
@Override
@@ -2527,17 +2485,17 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
public boolean mInList;
- public StopwatchTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public StopwatchTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, Parcel in) {
- super(clocks, type, timeBase, in);
+ super(clock, type, timeBase, in);
mUid = uid;
mTimerPool = timerPool;
mUpdateTimeUs = in.readLong();
}
- public StopwatchTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public StopwatchTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase) {
- super(clocks, type, timeBase);
+ super(clock, type, timeBase);
mUid = uid;
mTimerPool = timerPool;
}
@@ -2745,10 +2703,10 @@ public class BatteryStatsImpl extends BatteryStats {
* The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
* the main timer is.
*/
- public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public DualTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, TimeBase subTimeBase, Parcel in) {
- super(clocks, uid, type, timerPool, timeBase, in);
- mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase, in);
+ super(clock, uid, type, timerPool, timeBase, in);
+ mSubTimer = new DurationTimer(clock, uid, type, null, subTimeBase, in);
}
/**
@@ -2757,10 +2715,10 @@ public class BatteryStatsImpl extends BatteryStats {
* The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if
* the main timer is.
*/
- public DualTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
+ public DualTimer(Clock clock, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
TimeBase timeBase, TimeBase subTimeBase) {
- super(clocks, uid, type, timerPool, timeBase);
- mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase);
+ super(clock, uid, type, timerPool, timeBase);
+ mSubTimer = new DurationTimer(clock, uid, type, null, subTimeBase);
}
/** Get the secondary timer. */
@@ -3181,7 +3139,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getRpmTimerLocked(String name) {
SamplingTimer rpmt = mRpmStats.get(name);
if (rpmt == null) {
- rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+ rpmt = new SamplingTimer(mClock, mOnBatteryTimeBase);
mRpmStats.put(name, rpmt);
}
return rpmt;
@@ -3191,7 +3149,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getScreenOffRpmTimerLocked(String name) {
SamplingTimer rpmt = mScreenOffRpmStats.get(name);
if (rpmt == null) {
- rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
+ rpmt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mScreenOffRpmStats.put(name, rpmt);
}
return rpmt;
@@ -3204,7 +3162,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getWakeupReasonTimerLocked(String name) {
SamplingTimer timer = mWakeupReasonStats.get(name);
if (timer == null) {
- timer = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+ timer = new SamplingTimer(mClock, mOnBatteryTimeBase);
mWakeupReasonStats.put(name, timer);
}
return timer;
@@ -3217,7 +3175,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getKernelWakelockTimerLocked(String name) {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
- kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
+ kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
return kwlt;
@@ -3226,7 +3184,7 @@ public class BatteryStatsImpl extends BatteryStats {
public SamplingTimer getKernelMemoryTimerLocked(long bucket) {
SamplingTimer kmt = mKernelMemoryStats.get(bucket);
if (kmt == null) {
- kmt = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+ kmt = new SamplingTimer(mClock, mOnBatteryTimeBase);
mKernelMemoryStats.put(bucket, kmt);
}
return kmt;
@@ -3631,8 +3589,8 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void createFakeHistoryEvents(long numEvents) {
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long uptimeMs = mClocks.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
for(long i = 0; i < numEvents; i++) {
noteLongPartialWakelockStart("name1", "historyName1", 1000,
elapsedRealtimeMs, uptimeMs);
@@ -3729,7 +3687,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (dataSize == 0) {
// The history is currently empty; we need it to start with a time stamp.
- cur.currentTime = mClocks.currentTimeMillis();
+ cur.currentTime = mClock.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_RESET, cur);
}
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE, cur);
@@ -3891,7 +3849,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
addIsolatedUidLocked(isolatedUid, appUid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void addIsolatedUidLocked(int isolatedUid, int appUid,
@@ -3936,7 +3894,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteEventLocked(int code, String name, int uid) {
- noteEventLocked(code, name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteEventLocked(code, name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteEventLocked(int code, String name, int uid,
@@ -3949,9 +3907,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteCurrentTimeChangedLocked() {
- final long currentTime = mClocks.currentTimeMillis();
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ final long currentTime = mClock.currentTimeMillis();
+ final long elapsedRealtime = mClock.elapsedRealtime();
+ final long uptime = mClock.uptimeMillis();
noteCurrentTimeChangedLocked(currentTime, elapsedRealtime, uptime);
}
@@ -3961,7 +3919,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteProcessStartLocked(String name, int uid) {
- noteProcessStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteProcessStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteProcessStartLocked(String name, int uid,
@@ -3981,7 +3939,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteProcessCrashLocked(String name, int uid) {
- noteProcessCrashLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteProcessCrashLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteProcessCrashLocked(String name, int uid,
@@ -3994,7 +3952,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteProcessAnrLocked(String name, int uid) {
- noteProcessAnrLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteProcessAnrLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteProcessAnrLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -4006,7 +3964,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteUidProcessStateLocked(int uid, int state) {
- noteUidProcessStateLocked(uid, state, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteUidProcessStateLocked(uid, state, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteUidProcessStateLocked(int uid, int state,
@@ -4027,7 +3985,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteProcessFinishLocked(String name, int uid) {
- noteProcessFinishLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteProcessFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteProcessFinishLocked(String name, int uid,
@@ -4044,7 +4002,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteSyncStartLocked(String name, int uid) {
- noteSyncStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteSyncStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteSyncStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -4058,7 +4016,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteSyncFinishLocked(String name, int uid) {
- noteSyncFinishLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteSyncFinishLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteSyncFinishLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -4073,7 +4031,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteJobStartLocked(String name, int uid) {
- noteJobStartLocked(name, uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteJobStartLocked(name, uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteJobStartLocked(String name, int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -4088,7 +4046,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteJobFinishLocked(String name, int uid, int stopReason) {
noteJobFinishLocked(name, uid, stopReason,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteJobFinishLocked(String name, int uid, int stopReason,
@@ -4104,7 +4062,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast) {
noteJobsDeferredLocked(uid, numDeferred, sinceLast,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteJobsDeferredLocked(int uid, int numDeferred, long sinceLast,
@@ -4116,7 +4074,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid) {
noteAlarmStartLocked(name, workSource, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteAlarmStartLocked(String name, WorkSource workSource, int uid,
@@ -4127,7 +4085,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid) {
noteAlarmFinishLocked(name, workSource, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteAlarmFinishLocked(String name, WorkSource workSource, int uid,
@@ -4139,7 +4097,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
int uid) {
noteAlarmStartOrFinishLocked(historyItem, name, workSource, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
private void noteAlarmStartOrFinishLocked(int historyItem, String name, WorkSource workSource,
@@ -4177,7 +4135,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
String tag) {
noteWakupAlarmLocked(packageName, uid, workSource, tag,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWakupAlarmLocked(String packageName, int uid, WorkSource workSource,
@@ -4236,8 +4194,8 @@ public class BatteryStatsImpl extends BatteryStats {
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
HistoryItem.EVENT_PROC);
if (active != null) {
- long mSecRealtime = mClocks.elapsedRealtime();
- final long mSecUptime = mClocks.uptimeMillis();
+ long mSecRealtime = mClock.elapsedRealtime();
+ final long mSecUptime = mClock.uptimeMillis();
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
@@ -4251,8 +4209,8 @@ public class BatteryStatsImpl extends BatteryStats {
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
HistoryItem.EVENT_PROC);
if (active != null) {
- long mSecRealtime = mClocks.elapsedRealtime();
- final long mSecUptime = mClocks.uptimeMillis();
+ long mSecRealtime = mClock.elapsedRealtime();
+ final long mSecUptime = mClock.uptimeMillis();
for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
@@ -4272,7 +4230,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (mPretendScreenOff != pretendScreenOff) {
mPretendScreenOff = pretendScreenOff;
noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis(), mClock.currentTimeMillis());
}
}
@@ -4282,7 +4240,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type, boolean unimportantForLogging) {
noteStartWakeLocked(uid, pid, wc, name, historyName, type, unimportantForLogging,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStartWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
@@ -4353,7 +4311,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
int type) {
noteStopWakeLocked(uid, pid, wc, name, historyName, type,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStopWakeLocked(int uid, int pid, WorkChain wc, String name, String historyName,
@@ -4436,7 +4394,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type, boolean unimportantForLogging) {
noteStartWakeFromSourceLocked(ws, pid, name, historyName, type, unimportantForLogging,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
@@ -4463,7 +4421,7 @@ public class BatteryStatsImpl extends BatteryStats {
String newHistoryName, int newType, boolean newUnimportantForLogging) {
noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type, newWs, newPid,
newName, newHistoryName, newType, newUnimportantForLogging,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name,
@@ -4515,7 +4473,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
String historyName, int type) {
noteStopWakeFromSourceLocked(ws, pid, name, historyName, type,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name,
@@ -4538,7 +4496,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockStart(String name, String historyName, int uid) {
noteLongPartialWakelockStart(name, historyName, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteLongPartialWakelockStart(String name, String historyName, int uid,
@@ -4550,7 +4508,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockStartFromSource(name, historyName, workSource,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteLongPartialWakelockStartFromSource(String name, String historyName,
@@ -4588,7 +4546,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockFinish(String name, String historyName, int uid) {
noteLongPartialWakelockFinish(name, historyName, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteLongPartialWakelockFinish(String name, String historyName, int uid,
@@ -4600,7 +4558,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
WorkSource workSource) {
noteLongPartialWakelockFinishFromSource(name, historyName, workSource,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteLongPartialWakelockFinishFromSource(String name, String historyName,
@@ -4648,7 +4606,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWakeupReasonLocked(String reason) {
- noteWakeupReasonLocked(reason, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWakeupReasonLocked(reason, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) {
@@ -4718,7 +4676,7 @@ public class BatteryStatsImpl extends BatteryStats {
int mSensorNesting;
public void noteStartSensorLocked(int uid, int sensor) {
- noteStartSensorLocked(uid, sensor, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteStartSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStartSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
@@ -4735,7 +4693,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteStopSensorLocked(int uid, int sensor) {
- noteStopSensorLocked(uid, sensor, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteStopSensorLocked(uid, sensor, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteStopSensorLocked(int uid, int sensor, long elapsedRealtimeMs, long uptimeMs) {
@@ -4754,7 +4712,7 @@ public class BatteryStatsImpl extends BatteryStats {
int mGpsNesting;
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs) {
- noteGpsChangedLocked(oldWs, newWs, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteGpsChangedLocked(oldWs, newWs, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteGpsChangedLocked(WorkSource oldWs, WorkSource newWs,
@@ -4833,7 +4791,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteGpsSignalQualityLocked(int signalLevel) {
- noteGpsSignalQualityLocked(signalLevel, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteGpsSignalQualityLocked(signalLevel, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteGpsSignalQualityLocked(int signalLevel, long elapsedRealtimeMs, long uptimeMs) {
@@ -4861,8 +4819,8 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("this")
public void noteScreenStateLocked(int state) {
- noteScreenStateLocked(state, mClocks.elapsedRealtime(), mClocks.uptimeMillis(),
- mClocks.currentTimeMillis());
+ noteScreenStateLocked(state, mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ mClock.currentTimeMillis());
}
@GuardedBy("this")
@@ -4962,7 +4920,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteScreenBrightnessLocked(int brightness) {
- noteScreenBrightnessLocked(brightness, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteScreenBrightnessLocked(brightness, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteScreenBrightnessLocked(int brightness, long elapsedRealtimeMs, long uptimeMs) {
@@ -4990,7 +4948,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteUserActivityLocked(int uid, int event) {
- noteUserActivityLocked(uid, event, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteUserActivityLocked(uid, event, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteUserActivityLocked(int uid, int event, long elapsedRealtimeMs, long uptimeMs) {
@@ -5001,7 +4959,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWakeUpLocked(String reason, int reasonUid) {
- noteWakeUpLocked(reason, reasonUid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWakeUpLocked(reason, reasonUid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWakeUpLocked(String reason, int reasonUid,
@@ -5011,7 +4969,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteInteractiveLocked(boolean interactive) {
- noteInteractiveLocked(interactive, mClocks.elapsedRealtime());
+ noteInteractiveLocked(interactive, mClock.elapsedRealtime());
}
public void noteInteractiveLocked(boolean interactive, long elapsedRealtimeMs) {
@@ -5028,7 +4986,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteConnectivityChangedLocked(int type, String extra) {
noteConnectivityChangedLocked(type, extra,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteConnectivityChangedLocked(int type, String extra,
@@ -5051,7 +5009,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid) {
return noteMobileRadioPowerStateLocked(powerState, timestampNs, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public boolean noteMobileRadioPowerStateLocked(int powerState, long timestampNs, int uid,
@@ -5099,52 +5057,54 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void notePowerSaveModeLocked(boolean enabled) {
- notePowerSaveModeLocked(enabled, mClocks.elapsedRealtime(), mClocks.uptimeMillis(), false);
+ notePowerSaveModeLocked(enabled, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
- * Handles power save mode state changes.
+ * Toggles the power save mode state.
*/
- public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs,
- boolean forceLog) {
+ public void notePowerSaveModeLockedInit(boolean enabled, long elapsedRealtimeMs,
+ long uptimeMs) {
+ if (mPowerSaveModeEnabled != enabled) {
+ notePowerSaveModeLocked(enabled, elapsedRealtimeMs, uptimeMs);
+ } else {
+ // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
+ // allow the atom to read all future state changes.
+ FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
+ enabled
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ }
+ }
+
+ public void notePowerSaveModeLocked(boolean enabled, long elapsedRealtimeMs, long uptimeMs) {
if (mPowerSaveModeEnabled != enabled) {
int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0;
- mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
- mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
+ mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState;
+ mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState;
mPowerSaveModeEnabled = enabled;
if (enabled) {
mHistoryCur.states2 |= HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode enabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode enabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.startRunningLocked(elapsedRealtimeMs);
} else {
mHistoryCur.states2 &= ~HistoryItem.STATE2_POWER_SAVE_FLAG;
- if (DEBUG_HISTORY) {
- Slog.v(TAG, "Power save mode disabled to: "
- + Integer.toHexString(mHistoryCur.states2));
- }
+ if (DEBUG_HISTORY) Slog.v(TAG, "Power save mode disabled to: "
+ + Integer.toHexString(mHistoryCur.states2));
mPowerSaveModeEnabledTimer.stopRunningLocked(elapsedRealtimeMs);
}
addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs);
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
- } else if (forceLog) {
- // Log an initial value for BATTERY_SAVER_MODE_STATE_CHANGED in order to
- // allow the atom to read all future state changes.
- FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED,
- enabled
- ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
- : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
+ ? FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__ON
+ : FrameworkStatsLog.BATTERY_SAVER_MODE_STATE_CHANGED__STATE__OFF);
}
}
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid) {
noteDeviceIdleModeLocked(mode, activeReason, activeUid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteDeviceIdleModeLocked(final int mode, String activeReason, int activeUid,
@@ -5222,7 +5182,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void notePackageInstalledLocked(String pkgName, long versionCode) {
notePackageInstalledLocked(pkgName, versionCode,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePackageInstalledLocked(String pkgName, long versionCode,
@@ -5238,7 +5198,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void notePackageUninstalledLocked(String pkgName) {
- notePackageUninstalledLocked(pkgName, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ notePackageUninstalledLocked(pkgName, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePackageUninstalledLocked(String pkgName,
@@ -5259,7 +5219,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
void stopAllGpsSignalQualityTimersLocked(int except) {
- stopAllGpsSignalQualityTimersLocked(except, mClocks.elapsedRealtime());
+ stopAllGpsSignalQualityTimersLocked(except, mClock.elapsedRealtime());
}
void stopAllGpsSignalQualityTimersLocked(int except, long elapsedRealtimeMs) {
@@ -5275,7 +5235,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void notePhoneOnLocked() {
- notePhoneOnLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ notePhoneOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePhoneOnLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5291,7 +5251,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void notePhoneOffLocked() {
- notePhoneOffLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ notePhoneOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePhoneOffLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5313,8 +5273,8 @@ public class BatteryStatsImpl extends BatteryStats {
public void onReceive(Context context, Intent intent) {
final boolean state = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
synchronized (BatteryStatsImpl.this) {
- noteUsbConnectionStateLocked(state, mClocks.elapsedRealtime(),
- mClocks.uptimeMillis());
+ noteUsbConnectionStateLocked(state, mClock.elapsedRealtime(),
+ mClock.uptimeMillis());
}
}
}, usbStateFilter);
@@ -5323,8 +5283,8 @@ public class BatteryStatsImpl extends BatteryStats {
final Intent usbState = context.registerReceiver(null, usbStateFilter);
final boolean initState = usbState != null && usbState.getBooleanExtra(
UsbManager.USB_CONNECTED, false);
- noteUsbConnectionStateLocked(initState, mClocks.elapsedRealtime(),
- mClocks.uptimeMillis());
+ noteUsbConnectionStateLocked(initState, mClock.elapsedRealtime(),
+ mClock.uptimeMillis());
}
}
}
@@ -5464,7 +5424,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @param state phone state from ServiceState.getState()
*/
public void notePhoneStateLocked(int state, int simState) {
- notePhoneStateLocked(state, simState, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ notePhoneStateLocked(state, simState, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePhoneStateLocked(int state, int simState,
@@ -5476,7 +5436,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) {
notePhoneSignalStrengthLocked(signalStrength,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePhoneSignalStrengthLocked(SignalStrength signalStrength,
@@ -5490,7 +5450,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) {
notePhoneDataConnectionStateLocked(dataType, hasData, serviceType,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType,
@@ -5533,7 +5493,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiOnLocked() {
- noteWifiOnLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiOnLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5549,7 +5509,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiOffLocked() {
- noteWifiOffLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiOffLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiOffLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5566,7 +5526,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteAudioOnLocked(int uid) {
- noteAudioOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteAudioOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteAudioOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5585,7 +5545,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteAudioOffLocked(int uid) {
- noteAudioOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteAudioOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteAudioOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5606,7 +5566,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteVideoOnLocked(int uid) {
- noteVideoOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteVideoOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteVideoOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5625,7 +5585,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteVideoOffLocked(int uid) {
- noteVideoOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteVideoOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteVideoOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5645,7 +5605,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteResetAudioLocked() {
- noteResetAudioLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteResetAudioLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteResetAudioLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5664,7 +5624,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteResetVideoLocked() {
- noteResetVideoLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteResetVideoLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteResetVideoLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5683,7 +5643,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteActivityResumedLocked(int uid) {
- noteActivityResumedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteActivityResumedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteActivityResumedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5693,7 +5653,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteActivityPausedLocked(int uid) {
- noteActivityPausedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteActivityPausedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteActivityPausedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5704,7 +5664,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteVibratorOnLocked(int uid, long durationMillis) {
noteVibratorOnLocked(uid, durationMillis,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteVibratorOnLocked(int uid, long durationMillis,
@@ -5715,7 +5675,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteVibratorOffLocked(int uid) {
- noteVibratorOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteVibratorOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteVibratorOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5725,7 +5685,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteFlashlightOnLocked(int uid) {
- noteFlashlightOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteFlashlightOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFlashlightOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5742,7 +5702,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteFlashlightOffLocked(int uid) {
- noteFlashlightOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteFlashlightOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFlashlightOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5762,7 +5722,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteCameraOnLocked(int uid) {
- noteCameraOnLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteCameraOnLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteCameraOnLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5779,7 +5739,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteCameraOffLocked(int uid) {
- noteCameraOffLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteCameraOffLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteCameraOffLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -5799,7 +5759,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteResetCameraLocked() {
- noteResetCameraLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteResetCameraLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteResetCameraLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5818,7 +5778,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteResetFlashlightLocked() {
- noteResetFlashlightLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteResetFlashlightLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteResetFlashlightLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5853,7 +5813,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
@@ -5898,7 +5858,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized,
@@ -5919,7 +5879,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteResetBluetoothScanLocked() {
- noteResetBluetoothScanLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteResetBluetoothScanLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteResetBluetoothScanLocked(long elapsedRealtimeMs, long uptimeMs) {
@@ -5939,7 +5899,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults) {
noteBluetoothScanResultsFromSourceLocked(ws, numNewResults,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteBluetoothScanResultsFromSourceLocked(WorkSource ws, int numNewResults,
@@ -5972,7 +5932,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid) {
noteWifiRadioPowerState(powerState, timestampNs, uid,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiRadioPowerState(int powerState, long timestampNs, int uid,
@@ -5999,7 +5959,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiRunningLocked(WorkSource ws) {
- noteWifiRunningLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiRunningLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiRunningLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
@@ -6034,7 +5994,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs) {
noteWifiRunningChangedLocked(oldWs, newWs,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiRunningChangedLocked(WorkSource oldWs, WorkSource newWs,
@@ -6077,7 +6037,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiStoppedLocked(WorkSource ws) {
- noteWifiStoppedLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiStoppedLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiStoppedLocked(WorkSource ws, long elapsedRealtimeMs, long uptimeMs) {
@@ -6111,7 +6071,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiStateLocked(int wifiState, String accessPoint) {
- noteWifiStateLocked(wifiState, accessPoint, mClocks.elapsedRealtime());
+ noteWifiStateLocked(wifiState, accessPoint, mClock.elapsedRealtime());
}
public void noteWifiStateLocked(int wifiState, String accessPoint, long elapsedRealtimeMs) {
@@ -6128,7 +6088,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth) {
noteWifiSupplicantStateChangedLocked(supplState, failedAuth,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth,
@@ -6161,7 +6121,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiRssiChangedLocked(int newRssi) {
- noteWifiRssiChangedLocked(newRssi, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiRssiChangedLocked(newRssi, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiRssiChangedLocked(int newRssi, long elapsedRealtimeMs, long uptimeMs) {
@@ -6193,7 +6153,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteFullWifiLockAcquiredLocked(int uid) {
- noteFullWifiLockAcquiredLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteFullWifiLockAcquiredLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFullWifiLockAcquiredLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6210,7 +6170,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteFullWifiLockReleasedLocked(int uid) {
- noteFullWifiLockReleasedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteFullWifiLockReleasedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFullWifiLockReleasedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6228,7 +6188,7 @@ public class BatteryStatsImpl extends BatteryStats {
int mWifiScanNesting = 0;
public void noteWifiScanStartedLocked(int uid) {
- noteWifiScanStartedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiScanStartedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiScanStartedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6244,7 +6204,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiScanStoppedLocked(int uid) {
- noteWifiScanStoppedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiScanStoppedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6261,7 +6221,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiBatchedScanStartedLocked(int uid, int csph) {
noteWifiBatchedScanStartedLocked(uid, csph,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStartedLocked(int uid, int csph,
@@ -6272,7 +6232,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiBatchedScanStoppedLocked(int uid) {
- noteWifiBatchedScanStoppedLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiBatchedScanStoppedLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStoppedLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6285,7 +6245,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteWifiMulticastEnabledLocked(int uid) {
- noteWifiMulticastEnabledLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiMulticastEnabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiMulticastEnabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6309,7 +6269,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void noteWifiMulticastDisabledLocked(int uid) {
- noteWifiMulticastDisabledLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiMulticastDisabledLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiMulticastDisabledLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -6333,7 +6293,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws) {
noteFullWifiLockAcquiredFromSourceLocked(ws,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFullWifiLockAcquiredFromSourceLocked(WorkSource ws,
@@ -6356,7 +6316,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws) {
noteFullWifiLockReleasedFromSourceLocked(ws,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteFullWifiLockReleasedFromSourceLocked(WorkSource ws,
@@ -6378,7 +6338,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiScanStartedFromSourceLocked(WorkSource ws) {
- noteWifiScanStartedFromSourceLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiScanStartedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiScanStartedFromSourceLocked(WorkSource ws,
@@ -6400,7 +6360,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws) {
- noteWifiScanStoppedFromSourceLocked(ws, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ noteWifiScanStoppedFromSourceLocked(ws, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiScanStoppedFromSourceLocked(WorkSource ws,
@@ -6423,7 +6383,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph) {
noteWifiBatchedScanStartedFromSourceLocked(ws, csph,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStartedFromSourceLocked(WorkSource ws, int csph,
@@ -6444,7 +6404,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws) {
noteWifiBatchedScanStoppedFromSourceLocked(ws,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteWifiBatchedScanStoppedFromSourceLocked(WorkSource ws,
long elapsedRealtimeMs, long uptimeMs) {
@@ -6520,7 +6480,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
Collection<BinderCallsStats.CallStat> callStats) {
noteBinderCallStats(workSourceUid, incrementalCallCount, callStats,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
@@ -7022,16 +6982,16 @@ public class BatteryStatsImpl extends BatteryStats {
}
@Override public long getStartClockTime() {
- final long currentTimeMs = mClocks.currentTimeMillis();
+ final long currentTimeMs = mClock.currentTimeMillis();
if ((currentTimeMs > MILLISECONDS_IN_YEAR
&& mStartClockTimeMs < (currentTimeMs - MILLISECONDS_IN_YEAR))
|| (mStartClockTimeMs > currentTimeMs)) {
// If the start clock time has changed by more than a year, then presumably
// the previous time was completely bogus. So we are going to figure out a
// new time based on how much time has elapsed since we started counting.
- recordCurrentTimeChangeLocked(currentTimeMs, mClocks.elapsedRealtime(),
- mClocks.uptimeMillis());
- return currentTimeMs - (mClocks.elapsedRealtime() - (mRealtimeStartUs / 1000));
+ recordCurrentTimeChangeLocked(currentTimeMs, mClock.elapsedRealtime(),
+ mClock.uptimeMillis());
+ return currentTimeMs - (mClock.elapsedRealtime() - (mRealtimeStartUs / 1000));
}
return mStartClockTimeMs;
}
@@ -7394,7 +7354,7 @@ public class BatteryStatsImpl extends BatteryStats {
private double mProportionalSystemServiceUsage;
public Uid(BatteryStatsImpl bsi, int uid) {
- this(bsi, uid, bsi.mClocks.elapsedRealtime(), bsi.mClocks.uptimeMillis());
+ this(bsi, uid, bsi.mClock.elapsedRealtime(), bsi.mClock.uptimeMillis());
}
public Uid(BatteryStatsImpl bsi, int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -7420,25 +7380,25 @@ public class BatteryStatsImpl extends BatteryStats {
};
mSyncStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
@Override public DualTimer instantiateObject() {
- return new DualTimer(mBsi.mClocks, Uid.this, SYNC, null,
+ return new DualTimer(mBsi.mClock, Uid.this, SYNC, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
};
mJobStats = mBsi.new OverflowArrayMap<DualTimer>(uid) {
@Override public DualTimer instantiateObject() {
- return new DualTimer(mBsi.mClocks, Uid.this, JOB, null,
+ return new DualTimer(mBsi.mClock, Uid.this, JOB, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
};
- mWifiRunningTimer = new StopwatchTimer(mBsi.mClocks, this, WIFI_RUNNING,
+ mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase);
- mFullWifiLockTimer = new StopwatchTimer(mBsi.mClocks, this, FULL_WIFI_LOCK,
+ mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase);
- mWifiScanTimer = new DualTimer(mBsi.mClocks, this, WIFI_SCAN,
+ mWifiScanTimer = new DualTimer(mBsi.mClock, this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
mWifiBatchedScanTimer = new StopwatchTimer[NUM_WIFI_BATCHED_SCAN_BINS];
- mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, this, WIFI_MULTICAST_ENABLED,
+ mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, this, WIFI_MULTICAST_ENABLED,
mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE];
mJobsDeferredEventCount = new Counter(mBsi.mOnBatteryTimeBase);
@@ -7636,7 +7596,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mWifiRunning) {
mWifiRunning = true;
if (mWifiRunningTimer == null) {
- mWifiRunningTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_RUNNING,
+ mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, Uid.this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase);
}
mWifiRunningTimer.startRunningLocked(elapsedRealtimeMs);
@@ -7656,7 +7616,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mFullWifiLockOut) {
mFullWifiLockOut = true;
if (mFullWifiLockTimer == null) {
- mFullWifiLockTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, FULL_WIFI_LOCK,
+ mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, Uid.this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase);
}
mFullWifiLockTimer.startRunningLocked(elapsedRealtimeMs);
@@ -7676,7 +7636,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (!mWifiScanStarted) {
mWifiScanStarted = true;
if (mWifiScanTimer == null) {
- mWifiScanTimer = new DualTimer(mBsi.mClocks, Uid.this, WIFI_SCAN,
+ mWifiScanTimer = new DualTimer(mBsi.mClock, Uid.this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase);
}
@@ -7726,7 +7686,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteWifiMulticastEnabledLocked(long elapsedRealtimeMs) {
if (mWifiMulticastWakelockCount == 0) {
if (mWifiMulticastTimer == null) {
- mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
}
mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
@@ -7915,7 +7875,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
- mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, AUDIO_TURNED_ON,
+ mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, AUDIO_TURNED_ON,
mBsi.mAudioTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mAudioTurnedOnTimer;
@@ -7939,7 +7899,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createVideoTurnedOnTimerLocked() {
if (mVideoTurnedOnTimer == null) {
- mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, VIDEO_TURNED_ON,
+ mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, VIDEO_TURNED_ON,
mBsi.mVideoTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mVideoTurnedOnTimer;
@@ -7963,7 +7923,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createFlashlightTurnedOnTimerLocked() {
if (mFlashlightTurnedOnTimer == null) {
- mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FLASHLIGHT_TURNED_ON, mBsi.mFlashlightTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mFlashlightTurnedOnTimer;
@@ -7987,7 +7947,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createCameraTurnedOnTimerLocked() {
if (mCameraTurnedOnTimer == null) {
- mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, CAMERA_TURNED_ON,
+ mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, CAMERA_TURNED_ON,
mBsi.mCameraTurnedOnTimers, mBsi.mOnBatteryTimeBase);
}
return mCameraTurnedOnTimer;
@@ -8011,7 +7971,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createForegroundActivityTimerLocked() {
if (mForegroundActivityTimer == null) {
- mForegroundActivityTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mForegroundActivityTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_ACTIVITY, null, mBsi.mOnBatteryTimeBase);
}
return mForegroundActivityTimer;
@@ -8019,7 +7979,7 @@ public class BatteryStatsImpl extends BatteryStats {
public StopwatchTimer createForegroundServiceTimerLocked() {
if (mForegroundServiceTimer == null) {
- mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mForegroundServiceTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase);
}
return mForegroundServiceTimer;
@@ -8027,7 +7987,7 @@ public class BatteryStatsImpl extends BatteryStats {
public DualTimer createAggregatedPartialWakelockTimerLocked() {
if (mAggregatedPartialWakelockTimer == null) {
- mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
+ mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClock, this,
AGGREGATED_WAKE_TYPE_PARTIAL, null,
mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase);
}
@@ -8036,7 +7996,7 @@ public class BatteryStatsImpl extends BatteryStats {
public DualTimer createBluetoothScanTimerLocked() {
if (mBluetoothScanTimer == null) {
- mBluetoothScanTimer = new DualTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON,
+ mBluetoothScanTimer = new DualTimer(mBsi.mClock, Uid.this, BLUETOOTH_SCAN_ON,
mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase);
}
@@ -8045,7 +8005,7 @@ public class BatteryStatsImpl extends BatteryStats {
public DualTimer createBluetoothUnoptimizedScanTimerLocked() {
if (mBluetoothUnoptimizedScanTimer == null) {
- mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClocks, Uid.this,
+ mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClock, Uid.this,
BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
}
@@ -8123,7 +8083,7 @@ public class BatteryStatsImpl extends BatteryStats {
public BatchTimer createVibratorOnTimerLocked() {
if (mVibratorOnTimer == null) {
- mVibratorOnTimer = new BatchTimer(mBsi.mClocks, Uid.this, VIBRATOR_ON,
+ mVibratorOnTimer = new BatchTimer(mBsi.mClock, Uid.this, VIBRATOR_ON,
mBsi.mOnBatteryTimeBase);
}
return mVibratorOnTimer;
@@ -8309,10 +8269,10 @@ public class BatteryStatsImpl extends BatteryStats {
detachIfNotNull(mProcessStateTimer[i]);
if (in == null) {
- mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClocks, this, PROCESS_STATE, null,
+ mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClock, this, PROCESS_STATE, null,
mBsi.mOnBatteryTimeBase);
} else {
- mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClocks, this, PROCESS_STATE, null,
+ mProcessStateTimer[i] = new StopwatchTimer(mBsi.mClock, this, PROCESS_STATE, null,
mBsi.mOnBatteryTimeBase, in);
}
}
@@ -8373,10 +8333,10 @@ public class BatteryStatsImpl extends BatteryStats {
}
detachIfNotNull(mWifiBatchedScanTimer[i]);
if (in == null) {
- mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClocks, this, WIFI_BATCHED_SCAN,
+ mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClock, this, WIFI_BATCHED_SCAN,
collected, mBsi.mOnBatteryTimeBase);
} else {
- mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClocks, this, WIFI_BATCHED_SCAN,
+ mWifiBatchedScanTimer[i] = new StopwatchTimer(mBsi.mClock, this, WIFI_BATCHED_SCAN,
collected, mBsi.mOnBatteryTimeBase, in);
}
}
@@ -9170,7 +9130,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int j = 0; j < numSyncs; j++) {
String syncName = in.readString();
if (in.readInt() != 0) {
- mSyncStats.add(syncName, new DualTimer(mBsi.mClocks, Uid.this, SYNC, null,
+ mSyncStats.add(syncName, new DualTimer(mBsi.mClock, Uid.this, SYNC, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
}
}
@@ -9180,7 +9140,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int j = 0; j < numJobs; j++) {
String jobName = in.readString();
if (in.readInt() != 0) {
- mJobStats.add(jobName, new DualTimer(mBsi.mClocks, Uid.this, JOB, null,
+ mJobStats.add(jobName, new DualTimer(mBsi.mClock, Uid.this, JOB, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in));
}
}
@@ -9225,21 +9185,21 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiRunning = false;
if (in.readInt() != 0) {
- mWifiRunningTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_RUNNING,
+ mWifiRunningTimer = new StopwatchTimer(mBsi.mClock, Uid.this, WIFI_RUNNING,
mBsi.mWifiRunningTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mWifiRunningTimer = null;
}
mFullWifiLockOut = false;
if (in.readInt() != 0) {
- mFullWifiLockTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, FULL_WIFI_LOCK,
+ mFullWifiLockTimer = new StopwatchTimer(mBsi.mClock, Uid.this, FULL_WIFI_LOCK,
mBsi.mFullWifiLockTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mFullWifiLockTimer = null;
}
mWifiScanStarted = false;
if (in.readInt() != 0) {
- mWifiScanTimer = new DualTimer(mBsi.mClocks, Uid.this, WIFI_SCAN,
+ mWifiScanTimer = new DualTimer(mBsi.mClock, Uid.this, WIFI_SCAN,
mBsi.mWifiScanTimers, mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase,
in);
} else {
@@ -9255,49 +9215,50 @@ public class BatteryStatsImpl extends BatteryStats {
}
mWifiMulticastWakelockCount = 0;
if (in.readInt() != 0) {
- mWifiMulticastTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, WIFI_MULTICAST_ENABLED,
+ mWifiMulticastTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
+ WIFI_MULTICAST_ENABLED,
mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mWifiMulticastTimer = null;
}
if (in.readInt() != 0) {
- mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, AUDIO_TURNED_ON,
+ mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, AUDIO_TURNED_ON,
mBsi.mAudioTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mAudioTurnedOnTimer = null;
}
if (in.readInt() != 0) {
- mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, VIDEO_TURNED_ON,
+ mVideoTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, VIDEO_TURNED_ON,
mBsi.mVideoTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mVideoTurnedOnTimer = null;
}
if (in.readInt() != 0) {
- mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mFlashlightTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FLASHLIGHT_TURNED_ON, mBsi.mFlashlightTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mFlashlightTurnedOnTimer = null;
}
if (in.readInt() != 0) {
- mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClocks, Uid.this, CAMERA_TURNED_ON,
+ mCameraTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, CAMERA_TURNED_ON,
mBsi.mCameraTurnedOnTimers, mBsi.mOnBatteryTimeBase, in);
} else {
mCameraTurnedOnTimer = null;
}
if (in.readInt() != 0) {
- mForegroundActivityTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mForegroundActivityTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_ACTIVITY, null, mBsi.mOnBatteryTimeBase, in);
} else {
mForegroundActivityTimer = null;
}
if (in.readInt() != 0) {
- mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+ mForegroundServiceTimer = new StopwatchTimer(mBsi.mClock, Uid.this,
FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase, in);
} else {
mForegroundServiceTimer = null;
}
if (in.readInt() != 0) {
- mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
+ mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClock, this,
AGGREGATED_WAKE_TYPE_PARTIAL, null,
mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase,
in);
@@ -9305,14 +9266,14 @@ public class BatteryStatsImpl extends BatteryStats {
mAggregatedPartialWakelockTimer = null;
}
if (in.readInt() != 0) {
- mBluetoothScanTimer = new DualTimer(mBsi.mClocks, Uid.this, BLUETOOTH_SCAN_ON,
+ mBluetoothScanTimer = new DualTimer(mBsi.mClock, Uid.this, BLUETOOTH_SCAN_ON,
mBsi.mBluetoothScanOnTimers, mBsi.mOnBatteryTimeBase,
mOnBatteryBackgroundTimeBase, in);
} else {
mBluetoothScanTimer = null;
}
if (in.readInt() != 0) {
- mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClocks, Uid.this,
+ mBluetoothUnoptimizedScanTimer = new DualTimer(mBsi.mClock, Uid.this,
BLUETOOTH_UNOPTIMIZED_SCAN_ON, null,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase, in);
} else {
@@ -9337,7 +9298,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
if (in.readInt() != 0) {
- mVibratorOnTimer = new BatchTimer(mBsi.mClocks, Uid.this, VIBRATOR_ON,
+ mVibratorOnTimer = new BatchTimer(mBsi.mClock, Uid.this, VIBRATOR_ON,
mBsi.mOnBatteryTimeBase, in);
} else {
mVibratorOnTimer = null;
@@ -9547,7 +9508,7 @@ public class BatteryStatsImpl extends BatteryStats {
return null;
}
- return new StopwatchTimer(mBsi.mClocks, mUid, type, pool, timeBase, in);
+ return new StopwatchTimer(mBsi.mClock, mUid, type, pool, timeBase, in);
}
/**
@@ -9563,7 +9524,7 @@ public class BatteryStatsImpl extends BatteryStats {
return null;
}
- return new DualTimer(mBsi.mClocks, mUid, type, pool, timeBase, bgTimeBase, in);
+ return new DualTimer(mBsi.mClock, mUid, type, pool, timeBase, bgTimeBase, in);
}
boolean reset(long elapsedRealtimeUs) {
@@ -9660,7 +9621,7 @@ public class BatteryStatsImpl extends BatteryStats {
pool = new ArrayList<StopwatchTimer>();
mBsi.mSensorTimers.put(mHandle, pool);
}
- return new DualTimer(mBsi.mClocks, mUid, 0, pool, timeBase, bgTimeBase, in);
+ return new DualTimer(mBsi.mClock, mUid, 0, pool, timeBase, bgTimeBase, in);
}
boolean reset(long elapsedRealtimeUs) {
@@ -10162,7 +10123,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void startLaunchedLocked() {
- startLaunchedLocked(mBsi.mClocks.uptimeMillis());
+ startLaunchedLocked(mBsi.mClock.uptimeMillis());
}
public void startLaunchedLocked(long uptimeMs) {
@@ -10175,7 +10136,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void stopLaunchedLocked() {
- stopLaunchedLocked(mBsi.mClocks.uptimeMillis());
+ stopLaunchedLocked(mBsi.mClock.uptimeMillis());
}
public void stopLaunchedLocked(long uptimeMs) {
@@ -10193,7 +10154,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void startRunningLocked() {
- startRunningLocked(mBsi.mClocks.uptimeMillis());
+ startRunningLocked(mBsi.mClock.uptimeMillis());
}
public void startRunningLocked(long uptimeMs) {
@@ -10206,7 +10167,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public void stopRunningLocked() {
- stopRunningLocked(mBsi.mClocks.uptimeMillis());
+ stopRunningLocked(mBsi.mClock.uptimeMillis());
}
public void stopRunningLocked(long uptimeMs) {
@@ -10265,7 +10226,7 @@ public class BatteryStatsImpl extends BatteryStats {
@GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState) {
updateUidProcessStateLocked(procState,
- mBsi.mClocks.elapsedRealtime(), mBsi.mClocks.uptimeMillis());
+ mBsi.mClock.elapsedRealtime(), mBsi.mClock.uptimeMillis());
}
public void updateUidProcessStateLocked(int procState,
@@ -10430,7 +10391,7 @@ public class BatteryStatsImpl extends BatteryStats {
timers = new ArrayList<StopwatchTimer>();
mBsi.mSensorTimers.put(sensor, timers);
}
- t = new DualTimer(mBsi.mClocks, this, BatteryStats.SENSOR, timers,
+ t = new DualTimer(mBsi.mClock, this, BatteryStats.SENSOR, timers,
mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase);
se.mTimer = t;
return t;
@@ -10481,7 +10442,7 @@ public class BatteryStatsImpl extends BatteryStats {
case WAKE_TYPE_PARTIAL: {
DualTimer t = wl.mTimerPartial;
if (t == null) {
- t = new DualTimer(mBsi.mClocks, this, WAKE_TYPE_PARTIAL,
+ t = new DualTimer(mBsi.mClock, this, WAKE_TYPE_PARTIAL,
mBsi.mPartialTimers, mBsi.mOnBatteryScreenOffTimeBase,
mOnBatteryScreenOffBackgroundTimeBase);
wl.mTimerPartial = t;
@@ -10491,7 +10452,7 @@ public class BatteryStatsImpl extends BatteryStats {
case WAKE_TYPE_FULL: {
StopwatchTimer t = wl.mTimerFull;
if (t == null) {
- t = new StopwatchTimer(mBsi.mClocks, this, WAKE_TYPE_FULL,
+ t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_FULL,
mBsi.mFullTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerFull = t;
}
@@ -10500,7 +10461,7 @@ public class BatteryStatsImpl extends BatteryStats {
case WAKE_TYPE_WINDOW: {
StopwatchTimer t = wl.mTimerWindow;
if (t == null) {
- t = new StopwatchTimer(mBsi.mClocks, this, WAKE_TYPE_WINDOW,
+ t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_WINDOW,
mBsi.mWindowTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerWindow = t;
}
@@ -10509,7 +10470,7 @@ public class BatteryStatsImpl extends BatteryStats {
case WAKE_TYPE_DRAW: {
StopwatchTimer t = wl.mTimerDraw;
if (t == null) {
- t = new StopwatchTimer(mBsi.mClocks, this, WAKE_TYPE_DRAW,
+ t = new StopwatchTimer(mBsi.mClock, this, WAKE_TYPE_DRAW,
mBsi.mDrawTimers, mBsi.mOnBatteryTimeBase);
wl.mTimerDraw = t;
}
@@ -10597,13 +10558,13 @@ public class BatteryStatsImpl extends BatteryStats {
public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
- this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider);
+ this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider);
}
- private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
+ private BatteryStatsImpl(Clock clock, File systemDir, Handler handler,
PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb,
UserInfoProvider userInfoProvider) {
- init(clocks);
+ init(clock);
if (systemDir == null) {
mStatsFile = null;
@@ -10619,8 +10580,8 @@ public class BatteryStatsImpl extends BatteryStats {
mStartCount++;
initTimersAndCounters();
mOnBattery = mOnBatteryInternal = false;
- long uptimeUs = mClocks.uptimeMillis() * 1000;
- long realtimeUs = mClocks.elapsedRealtime() * 1000;
+ long uptimeUs = mClock.uptimeMillis() * 1000;
+ long realtimeUs = mClock.elapsedRealtime() * 1000;
initTimes(uptimeUs, realtimeUs);
mStartPlatformVersion = mEndPlatformVersion = Build.ID;
initDischarge(realtimeUs);
@@ -10637,29 +10598,29 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
protected void initTimersAndCounters() {
- mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
- mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
+ mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
+ mScreenDozeTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
+ mScreenBrightnessTimer[i] = new StopwatchTimer(mClock, null, -100 - i, null,
mOnBatteryTimeBase);
}
- mInteractiveTimer = new StopwatchTimer(mClocks, null, -10, null, mOnBatteryTimeBase);
- mPowerSaveModeEnabledTimer = new StopwatchTimer(mClocks, null, -2, null,
+ mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase);
+ mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null,
mOnBatteryTimeBase);
- mDeviceIdleModeLightTimer = new StopwatchTimer(mClocks, null, -11, null,
+ mDeviceIdleModeLightTimer = new StopwatchTimer(mClock, null, -11, null,
mOnBatteryTimeBase);
- mDeviceIdleModeFullTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
- mDeviceLightIdlingTimer = new StopwatchTimer(mClocks, null, -15, null, mOnBatteryTimeBase);
- mDeviceIdlingTimer = new StopwatchTimer(mClocks, null, -12, null, mOnBatteryTimeBase);
- mPhoneOnTimer = new StopwatchTimer(mClocks, null, -3, null, mOnBatteryTimeBase);
+ mDeviceIdleModeFullTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase);
+ mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null, mOnBatteryTimeBase);
+ mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase);
+ mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
- mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -200-i, null,
+ mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i, null,
mOnBatteryTimeBase);
}
- mPhoneSignalScanningTimer = new StopwatchTimer(mClocks, null, -200+1, null,
+ mPhoneSignalScanningTimer = new StopwatchTimer(mClock, null, -200 + 1, null,
mOnBatteryTimeBase);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClocks, null, -300-i, null,
+ mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClock, null, -300 - i, null,
mOnBatteryTimeBase);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
@@ -10671,38 +10632,38 @@ public class BatteryStatsImpl extends BatteryStats {
NUM_BT_TX_LEVELS);
mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
ModemActivityInfo.getNumTxPowerLevels());
- mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
- mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
+ mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null, mOnBatteryTimeBase);
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
mOnBatteryTimeBase);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
- mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null,
+ mWifiMulticastWakelockTimer = new StopwatchTimer(mClock, null,
WIFI_AGGREGATE_MULTICAST_ENABLED, null, mOnBatteryTimeBase);
- mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase);
- mGlobalWifiRunningTimer = new StopwatchTimer(mClocks, null, -5, null, mOnBatteryTimeBase);
+ mWifiOnTimer = new StopwatchTimer(mClock, null, -4, null, mOnBatteryTimeBase);
+ mGlobalWifiRunningTimer = new StopwatchTimer(mClock, null, -5, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_WIFI_STATES; i++) {
- mWifiStateTimer[i] = new StopwatchTimer(mClocks, null, -600-i, null,
+ mWifiStateTimer[i] = new StopwatchTimer(mClock, null, -600 - i, null,
mOnBatteryTimeBase);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
- mWifiSupplStateTimer[i] = new StopwatchTimer(mClocks, null, -700-i, null,
+ mWifiSupplStateTimer[i] = new StopwatchTimer(mClock, null, -700 - i, null,
mOnBatteryTimeBase);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
- mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
+ mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -800 - i, null,
mOnBatteryTimeBase);
}
- mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null, mOnBatteryTimeBase);
+ mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null, mOnBatteryTimeBase);
for (int i=0; i< mGpsSignalQualityTimer.length; i++) {
- mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null,
- mOnBatteryTimeBase);
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i, null,
+ mOnBatteryTimeBase);
}
- mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
- mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
- mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
- mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
- mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
+ mAudioOnTimer = new StopwatchTimer(mClock, null, -7, null, mOnBatteryTimeBase);
+ mVideoOnTimer = new StopwatchTimer(mClock, null, -8, null, mOnBatteryTimeBase);
+ mFlashlightOnTimer = new StopwatchTimer(mClock, null, -9, null, mOnBatteryTimeBase);
+ mCameraOnTimer = new StopwatchTimer(mClock, null, -13, null, mOnBatteryTimeBase);
+ mBluetoothScanTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
@@ -10717,11 +10678,11 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public BatteryStatsImpl(Parcel p) {
- this(new SystemClocks(), p);
+ this(Clock.SYSTEM_CLOCK, p);
}
- public BatteryStatsImpl(Clocks clocks, Parcel p) {
- init(clocks);
+ public BatteryStatsImpl(Clock clock, Parcel p) {
+ init(clock);
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
@@ -10789,7 +10750,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void updateDailyDeadlineLocked() {
// Get the current time.
- long currentTimeMs = mDailyStartTimeMs = mClocks.currentTimeMillis();
+ long currentTimeMs = mDailyStartTimeMs = mClock.currentTimeMillis();
Calendar calDeadline = Calendar.getInstance();
calDeadline.setTimeInMillis(currentTimeMs);
@@ -10817,7 +10778,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void recordDailyStatsLocked() {
DailyItem item = new DailyItem();
item.mStartTime = mDailyStartTimeMs;
- item.mEndTime = mClocks.currentTimeMillis();
+ item.mEndTime = mClock.currentTimeMillis();
boolean hasData = false;
if (mDailyDischargeStepTracker.mNumStepDurations > 0) {
hasData = true;
@@ -11182,7 +11143,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
void initTimes(long uptimeUs, long realtimeUs) {
- mStartClockTimeMs = mClocks.currentTimeMillis();
+ mStartClockTimeMs = mClock.currentTimeMillis();
mOnBatteryTimeBase.init(uptimeUs, realtimeUs);
mOnBatteryScreenOffTimeBase.init(uptimeUs, realtimeUs);
mRealtimeUs = 0;
@@ -11214,9 +11175,9 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void resetAllStatsCmdLocked() {
- final long mSecUptime = mClocks.uptimeMillis();
+ final long mSecUptime = mClock.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
- long mSecRealtime = mClocks.elapsedRealtime();
+ long mSecRealtime = mClock.elapsedRealtime();
long realtimeUs = mSecRealtime * 1000;
resetAllStatsLocked(mSecUptime, mSecRealtime, RESET_REASON_ADB_COMMAND);
mDischargeStartLevel = mHistoryCur.batteryLevel;
@@ -12750,7 +12711,7 @@ public class BatteryStatsImpl extends BatteryStats {
* Read and distribute kernel wake lock use across apps.
*/
public void updateKernelWakelocksLocked() {
- updateKernelWakelocksLocked(mClocks.elapsedRealtime() * 1000);
+ updateKernelWakelocksLocked(mClock.elapsedRealtime() * 1000);
}
/**
@@ -12771,7 +12732,7 @@ public class BatteryStatsImpl extends BatteryStats {
SamplingTimer kwlt = mKernelWakelockStats.get(name);
if (kwlt == null) {
- kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
+ kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase);
mKernelWakelockStats.put(name, kwlt);
}
@@ -12813,7 +12774,7 @@ public class BatteryStatsImpl extends BatteryStats {
* Reads the newest memory stats from the kernel.
*/
public void updateKernelMemoryBandwidthLocked() {
- updateKernelMemoryBandwidthLocked(mClocks.elapsedRealtime() * 1000);
+ updateKernelMemoryBandwidthLocked(mClock.elapsedRealtime() * 1000);
}
public void updateKernelMemoryBandwidthLocked(long elapsedRealtimeUs) {
@@ -12826,7 +12787,7 @@ public class BatteryStatsImpl extends BatteryStats {
if ((index = mKernelMemoryStats.indexOfKey(bandwidthEntries.keyAt(i))) >= 0) {
timer = mKernelMemoryStats.valueAt(index);
} else {
- timer = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+ timer = new SamplingTimer(mClock, mOnBatteryTimeBase);
mKernelMemoryStats.put(bandwidthEntries.keyAt(i), timer);
}
timer.update(bandwidthEntries.valueAt(i), 1, elapsedRealtimeUs);
@@ -13124,8 +13085,8 @@ public class BatteryStatsImpl extends BatteryStats {
// So, we distribute total time spent by an uid to different cpu freqs based on the
// amount of time cpu was running at that freq.
final int updatedUidsCount = updatedUids.size();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long uptimeMs = mClocks.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
for (int i = 0; i < updatedUidsCount; ++i) {
final Uid u = getUidStatsLocked(updatedUids.keyAt(i), elapsedRealtimeMs, uptimeMs);
final long appCpuTimeUs = updatedUids.valueAt(i);
@@ -13177,8 +13138,8 @@ public class BatteryStatsImpl extends BatteryStats {
@Nullable SparseLongArray updatedUids, boolean onBattery) {
mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
- final long startTimeMs = mClocks.uptimeMillis();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+ final long startTimeMs = mClock.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
mCpuUidUserSysTimeReader.readDelta(false, (uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
@@ -13233,7 +13194,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
});
- final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu stats took " + elapsedTimeMs + "ms");
}
@@ -13294,8 +13255,8 @@ public class BatteryStatsImpl extends BatteryStats {
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final int numClusters = mPowerProfile.getNumCpuClusters();
mWakeLockAllocationsUs = null;
- final long startTimeMs = mClocks.uptimeMillis();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+ final long startTimeMs = mClock.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidFreqTimeReader.readDelta(forceRead, (uid, cpuFreqTimeMs) -> {
@@ -13370,7 +13331,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
});
- final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu freq times took " + elapsedTimeMs + "ms");
}
@@ -13418,8 +13379,8 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@VisibleForTesting
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
- final long startTimeMs = mClocks.uptimeMillis();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+ final long startTimeMs = mClock.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
mCpuUidActiveTimeReader.readDelta(false, (uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
@@ -13434,7 +13395,7 @@ public class BatteryStatsImpl extends BatteryStats {
u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
});
- final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms");
}
@@ -13451,8 +13412,8 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
public void readKernelUidCpuClusterTimesLocked(boolean onBattery,
@Nullable CpuDeltaPowerAccumulator powerAccumulator) {
- final long startTimeMs = mClocks.uptimeMillis();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
+ final long startTimeMs = mClock.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
// If power is being accumulated for attribution, data needs to be read immediately.
final boolean forceRead = powerAccumulator != null;
mCpuUidClusterTimeReader.readDelta(forceRead, (uid, cpuClusterTimesMs) -> {
@@ -13473,7 +13434,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
});
- final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+ final long elapsedTimeMs = mClock.uptimeMillis() - startTimeMs;
if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms");
}
@@ -13652,7 +13613,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
boolean reset) {
mRecordingHistory = true;
- mHistoryCur.currentTime = mClocks.currentTimeMillis();
+ mHistoryCur.currentTime = mClock.currentTimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs,
reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
@@ -13694,7 +13655,7 @@ public class BatteryStatsImpl extends BatteryStats {
final int chargeFullUah, final long chargeTimeToFullSeconds) {
setBatteryStateLocked(status, health, plugType, level, temp, voltageMv, chargeUah,
chargeFullUah, chargeTimeToFullSeconds,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis(), mClocks.currentTimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis(), mClock.currentTimeMillis());
}
public void setBatteryStateLocked(final int status, final int health, final int plugType,
@@ -13931,12 +13892,12 @@ public class BatteryStatsImpl extends BatteryStats {
public long getAwakeTimeBattery() {
// This previously evaluated to mOnBatteryTimeBase.getUptime(getBatteryUptimeLocked());
// for over a decade, but surely that was a mistake.
- return getBatteryUptimeLocked(mClocks.uptimeMillis());
+ return getBatteryUptimeLocked(mClock.uptimeMillis());
}
@UnsupportedAppUsage
public long getAwakeTimePlugged() {
- return (mClocks.uptimeMillis() * 1000) - getAwakeTimeBattery();
+ return (mClock.uptimeMillis() * 1000) - getAwakeTimeBattery();
}
@Override
@@ -14186,7 +14147,7 @@ public class BatteryStatsImpl extends BatteryStats {
* @return battery uptime in microseconds
*/
protected long getBatteryUptimeLocked() {
- return getBatteryUptimeLocked(mClocks.uptimeMillis());
+ return getBatteryUptimeLocked(mClock.uptimeMillis());
}
/**
@@ -14359,7 +14320,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@UnsupportedAppUsage
public Uid getUidStatsLocked(int uid) {
- return getUidStatsLocked(uid, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ return getUidStatsLocked(uid, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public Uid getUidStatsLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
@@ -14388,6 +14349,18 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void onUserRemovedLocked(int userId) {
+ if (mExternalSync != null) {
+ // Clear out the removed user's UIDs after a short delay. The delay is needed
+ // because at the point that this method is called, some activities are still
+ // being wrapped up by those UIDs
+ mExternalSync.scheduleCleanupDueToRemovedUser(userId);
+ }
+ }
+
+ /**
+ * Removes battery stats for UIDs corresponding to a removed user.
+ */
+ public void clearRemovedUserUidsLocked(int userId) {
final int firstUidForUser = UserHandle.getUid(userId, 0);
final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
mUidStats.put(firstUidForUser, null);
@@ -14401,6 +14374,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
}
mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ removeCpuStatsForUidRangeLocked(firstUidForUser, lastUidForUser);
}
/**
@@ -14408,7 +14382,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@UnsupportedAppUsage
public void removeUidStatsLocked(int uid) {
- removeUidStatsLocked(uid, mClocks.elapsedRealtime());
+ removeUidStatsLocked(uid, mClock.elapsedRealtime());
}
/**
@@ -14424,12 +14398,45 @@ public class BatteryStatsImpl extends BatteryStats {
}
/**
+ * Removes the data for the deleted UIDs from the underlying kernel eBPF tables.
+ */
+ @GuardedBy("this")
+ private void removeCpuStatsForUidRangeLocked(int startUid, int endUid) {
+ if (startUid == endUid) {
+ mCpuUidUserSysTimeReader.removeUid(startUid);
+ mCpuUidFreqTimeReader.removeUid(startUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mCpuUidActiveTimeReader.removeUid(startUid);
+ mCpuUidClusterTimeReader.removeUid(startUid);
+ }
+ if (mKernelSingleUidTimeReader != null) {
+ mKernelSingleUidTimeReader.removeUid(startUid);
+ }
+ mNumUidsRemoved++;
+ } else if (startUid < endUid) {
+ mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
+ if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+ mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
+ }
+ if (mKernelSingleUidTimeReader != null) {
+ mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
+ }
+ // Treat as one. We don't know how many uids there are in between.
+ mNumUidsRemoved++;
+ } else {
+ Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+ }
+ }
+
+ /**
* Retrieve the statistics object for a particular process, creating
* if needed.
*/
@UnsupportedAppUsage
public Uid.Proc getProcessStatsLocked(int uid, String name) {
- return getProcessStatsLocked(uid, name, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ return getProcessStatsLocked(uid, name, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
@@ -14448,7 +14455,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@UnsupportedAppUsage
public Uid.Pkg getPackageStatsLocked(int uid, String pkg) {
- return getPackageStatsLocked(uid, pkg, mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ return getPackageStatsLocked(uid, pkg, mClock.elapsedRealtime(), mClock.uptimeMillis());
}
/**
@@ -14468,7 +14475,7 @@ public class BatteryStatsImpl extends BatteryStats {
@UnsupportedAppUsage
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name) {
return getServiceStatsLocked(uid, pkg, name,
- mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+ mClock.elapsedRealtime(), mClock.uptimeMillis());
}
public Uid.Pkg.Serv getServiceStatsLocked(int uid, String pkg, String name,
@@ -14479,7 +14486,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
public void shutdownLocked() {
- recordShutdownLocked(mClocks.currentTimeMillis(), mClocks.elapsedRealtime());
+ recordShutdownLocked(mClock.currentTimeMillis(), mClock.elapsedRealtime());
writeSyncLocked();
mShuttingDown = true;
}
@@ -14701,7 +14708,7 @@ public class BatteryStatsImpl extends BatteryStats {
mNumSingleUidCpuTimeReads = 0;
mNumBatchedSingleUidCpuTimeReads = 0;
- mCpuTimeReadsTrackingStartTimeMs = mClocks.uptimeMillis();
+ mCpuTimeReadsTrackingStartTimeMs = mClock.uptimeMillis();
}
}
@@ -14710,7 +14717,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (oldDelayMillis != newDelayMillis) {
mNumSingleUidCpuTimeReads = 0;
mNumBatchedSingleUidCpuTimeReads = 0;
- mCpuTimeReadsTrackingStartTimeMs = mClocks.uptimeMillis();
+ mCpuTimeReadsTrackingStartTimeMs = mClock.uptimeMillis();
}
}
@@ -14727,7 +14734,7 @@ public class BatteryStatsImpl extends BatteryStats {
private void updateUidRemoveDelay(long newTimeMs) {
UID_REMOVE_DELAY_MS = newTimeMs;
- clearPendingRemovedUids();
+ clearPendingRemovedUidsLocked();
}
public void dumpLocked(PrintWriter pw) {
@@ -14890,7 +14897,7 @@ public class BatteryStatsImpl extends BatteryStats {
Slog.d(TAG, "writeSummaryToParcel duration ms:"
+ (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize());
}
- mLastWriteTimeMs = mClocks.elapsedRealtime();
+ mLastWriteTimeMs = mClock.elapsedRealtime();
writeParcelToFileLocked(p, mStatsFile, sync);
}
@@ -15024,13 +15031,13 @@ public class BatteryStatsImpl extends BatteryStats {
if (mHistoryBuffer.dataPosition() > 0
|| mBatteryStatsHistory.getFilesNumbers().size() > 1) {
mRecordingHistory = true;
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long uptimeMs = mClocks.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
addHistoryBufferLocked(elapsedRealtimeMs, HistoryItem.CMD_START, mHistoryCur);
startRecordingHistory(elapsedRealtimeMs, uptimeMs, false);
}
- recordDailyStatsIfNeededLocked(false, mClocks.currentTimeMillis());
+ recordDailyStatsIfNeededLocked(false, mClock.currentTimeMillis());
}
public int describeContents() {
@@ -15082,7 +15089,7 @@ public class BatteryStatsImpl extends BatteryStats {
// We are just arbitrarily going to insert 1 minute from the sample of
// the last run until samples in this run.
if (mHistoryBaseTimeMs > 0) {
- long oldnow = mClocks.elapsedRealtime();
+ long oldnow = mClock.elapsedRealtime();
mHistoryBaseTimeMs = mHistoryBaseTimeMs - oldnow + 1;
if (DEBUG_HISTORY) {
StringBuilder sb = new StringBuilder(128);
@@ -15333,8 +15340,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (NU > 10000) {
throw new ParcelFormatException("File corrupt: too many uids " + NU);
}
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long uptimeMs = mClocks.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
for (int iu = 0; iu < NU; iu++) {
int uid = in.readInt();
Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
@@ -15642,8 +15649,8 @@ public class BatteryStatsImpl extends BatteryStats {
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
- final long NOW_SYS = mClocks.uptimeMillis() * 1000;
- final long NOWREAL_SYS = mClocks.elapsedRealtime() * 1000;
+ final long nowUptime = mClock.uptimeMillis() * 1000;
+ final long nowRealtime = mClock.elapsedRealtime() * 1000;
out.writeInt(VERSION);
@@ -15662,13 +15669,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
out.writeInt(mStartCount);
- out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
- out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
+ out.writeLong(computeUptime(nowUptime, STATS_SINCE_CHARGED));
+ out.writeLong(computeRealtime(nowRealtime, STATS_SINCE_CHARGED));
out.writeLong(mStartClockTimeMs);
out.writeString(mStartPlatformVersion);
out.writeString(mEndPlatformVersion);
- mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
- mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
+ mOnBatteryTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
+ mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
out.writeInt(mDischargeUnplugLevel);
out.writeInt(mDischargePlugLevel);
out.writeInt(mDischargeCurrentLevel);
@@ -15710,52 +15717,52 @@ public class BatteryStatsImpl extends BatteryStats {
MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false, false);
- mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mScreenOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mScreenDozeTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
- mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mInteractiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, nowRealtime);
out.writeLong(mLongestLightIdleTimeMs);
out.writeLong(mLongestFullIdleTimeMs);
- mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mDeviceIdleModeLightTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mDeviceIdleModeFullTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mDeviceLightIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mPhoneOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
- mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
- mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mPhoneSignalScanningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mPhoneDataConnectionsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
- mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
- mWifiMulticastWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mWifiMulticastWakelockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mWifiOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mGlobalWifiRunningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
for (int i=0; i<NUM_WIFI_STATES; i++) {
- mWifiStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mWifiStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
- mWifiSupplStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mWifiSupplStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
- mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
- mWifiActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mWifiActiveTimer.writeSummaryFromParcelLocked(out, nowRealtime);
mWifiActivity.writeSummaryToParcel(out);
for (int i=0; i< mGpsSignalQualityTimer.length; i++) {
- mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
}
mBluetoothActivity.writeSummaryToParcel(out);
mModemActivity.writeSummaryToParcel(out);
@@ -15764,9 +15771,9 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mHasModemReporting ? 1 : 0);
out.writeInt(mNumConnectivityChange);
- mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
- mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mFlashlightOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mCameraOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
+ mBluetoothScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
out.writeInt(mRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
@@ -15774,7 +15781,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ rpmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15785,7 +15792,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (rpmt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ rpmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15797,7 +15804,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (kwlt != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ kwlt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15809,7 +15816,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (timer != null) {
out.writeInt(1);
out.writeString(ent.getKey());
- timer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ timer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15821,7 +15828,7 @@ public class BatteryStatsImpl extends BatteryStats {
if (kmt != null) {
out.writeInt(1);
out.writeLong(mKernelMemoryStats.keyAt(i));
- kmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ kmt.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15833,92 +15840,93 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(mUidStats.keyAt(iu));
Uid u = mUidStats.valueAt(iu);
- u.mOnBatteryBackgroundTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
- u.mOnBatteryScreenOffBackgroundTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS);
+ u.mOnBatteryBackgroundTimeBase.writeSummaryToParcel(out, nowUptime, nowRealtime);
+ u.mOnBatteryScreenOffBackgroundTimeBase.writeSummaryToParcel(out, nowUptime,
+ nowRealtime);
if (u.mWifiRunningTimer != null) {
out.writeInt(1);
- u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mWifiRunningTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mFullWifiLockTimer != null) {
out.writeInt(1);
- u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mWifiScanTimer != null) {
out.writeInt(1);
- u.mWifiScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mWifiScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
for (int i = 0; i < Uid.NUM_WIFI_BATCHED_SCAN_BINS; i++) {
if (u.mWifiBatchedScanTimer[i] != null) {
out.writeInt(1);
- u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mWifiBatchedScanTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
if (u.mWifiMulticastTimer != null) {
out.writeInt(1);
- u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mAudioTurnedOnTimer != null) {
out.writeInt(1);
- u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mVideoTurnedOnTimer != null) {
out.writeInt(1);
- u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mFlashlightTurnedOnTimer != null) {
out.writeInt(1);
- u.mFlashlightTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mFlashlightTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mCameraTurnedOnTimer != null) {
out.writeInt(1);
- u.mCameraTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mCameraTurnedOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mForegroundActivityTimer != null) {
out.writeInt(1);
- u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mForegroundServiceTimer != null) {
out.writeInt(1);
- u.mForegroundServiceTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mForegroundServiceTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mAggregatedPartialWakelockTimer != null) {
out.writeInt(1);
- u.mAggregatedPartialWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mAggregatedPartialWakelockTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mBluetoothScanTimer != null) {
out.writeInt(1);
- u.mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mBluetoothScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (u.mBluetoothUnoptimizedScanTimer != null) {
out.writeInt(1);
- u.mBluetoothUnoptimizedScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mBluetoothUnoptimizedScanTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -15937,14 +15945,14 @@ public class BatteryStatsImpl extends BatteryStats {
for (int i = 0; i < Uid.NUM_PROCESS_STATE; i++) {
if (u.mProcessStateTimer[i] != null) {
out.writeInt(1);
- u.mProcessStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mProcessStateTimer[i].writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
}
if (u.mVibratorOnTimer != null) {
out.writeInt(1);
- u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ u.mVibratorOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -16043,25 +16051,25 @@ public class BatteryStatsImpl extends BatteryStats {
Uid.Wakelock wl = wakeStats.valueAt(iw);
if (wl.mTimerFull != null) {
out.writeInt(1);
- wl.mTimerFull.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ wl.mTimerFull.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerPartial != null) {
out.writeInt(1);
- wl.mTimerPartial.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ wl.mTimerPartial.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerWindow != null) {
out.writeInt(1);
- wl.mTimerWindow.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ wl.mTimerWindow.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
if (wl.mTimerDraw != null) {
out.writeInt(1);
- wl.mTimerDraw.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ wl.mTimerDraw.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -16072,7 +16080,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(NS);
for (int is=0; is<NS; is++) {
out.writeString(syncStats.keyAt(is));
- syncStats.valueAt(is).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ syncStats.valueAt(is).writeSummaryFromParcelLocked(out, nowRealtime);
}
final ArrayMap<String, DualTimer> jobStats = u.mJobStats.getMap();
@@ -16080,7 +16088,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeInt(NJ);
for (int ij=0; ij<NJ; ij++) {
out.writeString(jobStats.keyAt(ij));
- jobStats.valueAt(ij).writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ jobStats.valueAt(ij).writeSummaryFromParcelLocked(out, nowRealtime);
}
u.writeJobCompletionsToParcelLocked(out);
@@ -16104,7 +16112,7 @@ public class BatteryStatsImpl extends BatteryStats {
Uid.Sensor se = u.mSensorStats.valueAt(ise);
if (se.mTimer != null) {
out.writeInt(1);
- se.mTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ se.mTimer.writeSummaryFromParcelLocked(out, nowRealtime);
} else {
out.writeInt(0);
}
@@ -16143,7 +16151,7 @@ public class BatteryStatsImpl extends BatteryStats {
out.writeString(ps.mServiceStats.keyAt(is));
BatteryStatsImpl.Uid.Pkg.Serv ss = ps.mServiceStats.valueAt(is);
long time = ss.getStartTimeToNowLocked(
- mOnBatteryTimeBase.getUptime(NOW_SYS) / 1000);
+ mOnBatteryTimeBase.getUptime(nowUptime) / 1000);
out.writeLong(time);
out.writeInt(ss.mStarts);
out.writeInt(ss.mLaunches);
@@ -16186,35 +16194,35 @@ public class BatteryStatsImpl extends BatteryStats {
mOnBatteryScreenOffTimeBase.readFromParcel(in);
mScreenState = Display.STATE_UNKNOWN;
- mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
- mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
+ mScreenOnTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase, in);
+ mScreenDozeTimer = new StopwatchTimer(mClock, null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
- mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
+ mScreenBrightnessTimer[i] = new StopwatchTimer(mClock, null, -100 - i, null,
mOnBatteryTimeBase, in);
}
mInteractive = false;
- mInteractiveTimer = new StopwatchTimer(mClocks, null, -10, null, mOnBatteryTimeBase, in);
+ mInteractiveTimer = new StopwatchTimer(mClock, null, -10, null, mOnBatteryTimeBase, in);
mPhoneOn = false;
- mPowerSaveModeEnabledTimer = new StopwatchTimer(mClocks, null, -2, null,
+ mPowerSaveModeEnabledTimer = new StopwatchTimer(mClock, null, -2, null,
mOnBatteryTimeBase, in);
mLongestLightIdleTimeMs = in.readLong();
mLongestFullIdleTimeMs = in.readLong();
- mDeviceIdleModeLightTimer = new StopwatchTimer(mClocks, null, -14, null,
+ mDeviceIdleModeLightTimer = new StopwatchTimer(mClock, null, -14, null,
mOnBatteryTimeBase, in);
- mDeviceIdleModeFullTimer = new StopwatchTimer(mClocks, null, -11, null,
+ mDeviceIdleModeFullTimer = new StopwatchTimer(mClock, null, -11, null,
mOnBatteryTimeBase, in);
- mDeviceLightIdlingTimer = new StopwatchTimer(mClocks, null, -15, null,
+ mDeviceLightIdlingTimer = new StopwatchTimer(mClock, null, -15, null,
mOnBatteryTimeBase, in);
- mDeviceIdlingTimer = new StopwatchTimer(mClocks, null, -12, null, mOnBatteryTimeBase, in);
- mPhoneOnTimer = new StopwatchTimer(mClocks, null, -3, null, mOnBatteryTimeBase, in);
+ mDeviceIdlingTimer = new StopwatchTimer(mClock, null, -12, null, mOnBatteryTimeBase, in);
+ mPhoneOnTimer = new StopwatchTimer(mClock, null, -3, null, mOnBatteryTimeBase, in);
for (int i = 0; i < CellSignalStrength.getNumSignalStrengthLevels(); i++) {
- mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -200-i,
+ mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -200 - i,
null, mOnBatteryTimeBase, in);
}
- mPhoneSignalScanningTimer = new StopwatchTimer(mClocks, null, -200+1, null,
+ mPhoneSignalScanningTimer = new StopwatchTimer(mClock, null, -200 + 1, null,
mOnBatteryTimeBase, in);
for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
- mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClocks, null, -300-i,
+ mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClock, null, -300 - i,
null, mOnBatteryTimeBase, in);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
@@ -16222,39 +16230,39 @@ public class BatteryStatsImpl extends BatteryStats {
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
- mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null,
+ mMobileRadioActiveTimer = new StopwatchTimer(mClock, null, -400, null,
mOnBatteryTimeBase, in);
- mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(mClock, null, -401, null,
mOnBatteryTimeBase, in);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
- mWifiMulticastWakelockTimer = new StopwatchTimer(mClocks, null, -4, null,
+ mWifiMulticastWakelockTimer = new StopwatchTimer(mClock, null, -4, null,
mOnBatteryTimeBase, in);
mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mWifiOn = false;
- mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase, in);
+ mWifiOnTimer = new StopwatchTimer(mClock, null, -4, null, mOnBatteryTimeBase, in);
mGlobalWifiRunning = false;
- mGlobalWifiRunningTimer = new StopwatchTimer(mClocks, null, -5, null,
+ mGlobalWifiRunningTimer = new StopwatchTimer(mClock, null, -5, null,
mOnBatteryTimeBase, in);
for (int i=0; i<NUM_WIFI_STATES; i++) {
- mWifiStateTimer[i] = new StopwatchTimer(mClocks, null, -600-i,
+ mWifiStateTimer[i] = new StopwatchTimer(mClock, null, -600 - i,
null, mOnBatteryTimeBase, in);
}
for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
- mWifiSupplStateTimer[i] = new StopwatchTimer(mClocks, null, -700-i,
+ mWifiSupplStateTimer[i] = new StopwatchTimer(mClock, null, -700 - i,
null, mOnBatteryTimeBase, in);
}
for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
- mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i,
+ mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClock, null, -800 - i,
null, mOnBatteryTimeBase, in);
}
- mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null,
+ mWifiActiveTimer = new StopwatchTimer(mClock, null, -900, null,
mOnBatteryTimeBase, in);
mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
NUM_WIFI_TX_LEVELS, in);
for (int i=0; i<mGpsSignalQualityTimer.length; i++) {
- mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i,
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(mClock, null, -1000 - i,
null, mOnBatteryTimeBase, in);
}
mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -16268,15 +16276,15 @@ public class BatteryStatsImpl extends BatteryStats {
mNumConnectivityChange = in.readInt();
mAudioOnNesting = 0;
// TODO: It's likely a mistake that mAudioOnTimer/mVideoOnTimer don't write/read to parcel!
- mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
+ mAudioOnTimer = new StopwatchTimer(mClock, null, -7, null, mOnBatteryTimeBase);
mVideoOnNesting = 0;
- mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
+ mVideoOnTimer = new StopwatchTimer(mClock, null, -8, null, mOnBatteryTimeBase);
mFlashlightOnNesting = 0;
- mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase, in);
+ mFlashlightOnTimer = new StopwatchTimer(mClock, null, -9, null, mOnBatteryTimeBase, in);
mCameraOnNesting = 0;
- mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
+ mCameraOnTimer = new StopwatchTimer(mClock, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
- mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mBluetoothScanTimer = new StopwatchTimer(mClock, null, -14, null, mOnBatteryTimeBase, in);
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
@@ -16308,7 +16316,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int irpm = 0; irpm < NRPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
- SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
+ SamplingTimer rpmt = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mRpmStats.put(rpmName, rpmt);
}
}
@@ -16317,7 +16325,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int irpm = 0; irpm < NSORPMS; irpm++) {
if (in.readInt() != 0) {
String rpmName = in.readString();
- SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase, in);
+ SamplingTimer rpmt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase, in);
mScreenOffRpmStats.put(rpmName, rpmt);
}
}
@@ -16327,7 +16335,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int ikw = 0; ikw < NKW; ikw++) {
if (in.readInt() != 0) {
String wakelockName = in.readString();
- SamplingTimer kwlt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase, in);
+ SamplingTimer kwlt = new SamplingTimer(mClock, mOnBatteryScreenOffTimeBase, in);
mKernelWakelockStats.put(wakelockName, kwlt);
}
}
@@ -16337,7 +16345,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int iwr = 0; iwr < NWR; iwr++) {
if (in.readInt() != 0) {
String reasonName = in.readString();
- SamplingTimer timer = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
+ SamplingTimer timer = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mWakeupReasonStats.put(reasonName, timer);
}
}
@@ -16347,7 +16355,7 @@ public class BatteryStatsImpl extends BatteryStats {
for (int imt = 0; imt < nmt; imt++) {
if (in.readInt() != 0) {
Long bucket = in.readLong();
- SamplingTimer kmt = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
+ SamplingTimer kmt = new SamplingTimer(mClock, mOnBatteryTimeBase, in);
mKernelMemoryStats.put(bucket, kmt);
}
}
@@ -16367,8 +16375,8 @@ public class BatteryStatsImpl extends BatteryStats {
int numUids = in.readInt();
mUidStats.clear();
- final long elapsedRealtimeMs = mClocks.elapsedRealtime();
- final long uptimeMs = mClocks.uptimeMillis();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
for (int i = 0; i < numUids; i++) {
int uid = in.readInt();
Uid u = new Uid(this, uid, elapsedRealtimeMs, uptimeMs);
@@ -16398,8 +16406,8 @@ public class BatteryStatsImpl extends BatteryStats {
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
- final long uSecUptime = mClocks.uptimeMillis() * 1000;
- final long uSecRealtime = mClocks.elapsedRealtime() * 1000;
+ final long uSecUptime = mClock.uptimeMillis() * 1000;
+ final long uSecRealtime = mClock.elapsedRealtime() * 1000;
final long batteryRealtime = mOnBatteryTimeBase.getRealtime(uSecRealtime);
final long batteryScreenOffRealtime = mOnBatteryScreenOffTimeBase.getRealtime(uSecRealtime);
@@ -16755,7 +16763,7 @@ public class BatteryStatsImpl extends BatteryStats {
pw.print("Batched cpu time reads: ");
pw.println(mNumBatchedSingleUidCpuTimeReads);
pw.print("Batching Duration (min): ");
- pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
+ pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (60 * 1000));
pw.print("All UID cpu time reads since the later of device start or stats reset: ");
pw.println(mNumAllUidCpuTimeReads);
pw.print("UIDs removed since the later of device start or stats reset: ");
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 00385793b62b..e9e489d9ddb7 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -29,6 +29,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -234,8 +235,9 @@ public class BatteryUsageStatsProvider {
final boolean includePowerModels = (query.getFlags()
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
+ final String[] customEnergyConsumerNames = mStats.getCustomEnergyConsumerNames();
final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
- mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ customEnergyConsumerNames, includePowerModels);
if (mBatteryUsageStatsStore == null) {
Log.e(TAG, "BatteryUsageStatsStore is unavailable");
return builder.build();
@@ -247,7 +249,14 @@ public class BatteryUsageStatsProvider {
final BatteryUsageStats snapshot =
mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
if (snapshot != null) {
- builder.add(snapshot);
+ if (Arrays.equals(snapshot.getCustomPowerComponentNames(),
+ customEnergyConsumerNames)) {
+ builder.add(snapshot);
+ } else {
+ Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
+ + "custom power components: "
+ + Arrays.toString(snapshot.getCustomPowerComponentNames()));
+ }
}
}
}
@@ -256,7 +265,7 @@ public class BatteryUsageStatsProvider {
private long elapsedRealtime() {
if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
+ return ((BatteryStatsImpl) mStats).mClock.elapsedRealtime();
} else {
return SystemClock.elapsedRealtime();
}
@@ -264,7 +273,7 @@ public class BatteryUsageStatsProvider {
private long uptimeMillis() {
if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClocks.uptimeMillis();
+ return ((BatteryStatsImpl) mStats).mClock.uptimeMillis();
} else {
return SystemClock.uptimeMillis();
}
@@ -272,7 +281,7 @@ public class BatteryUsageStatsProvider {
private long currentTimeMillis() {
if (mStats instanceof BatteryStatsImpl) {
- return ((BatteryStatsImpl) mStats).mClocks.currentTimeMillis();
+ return ((BatteryStatsImpl) mStats).mClock.currentTimeMillis();
} else {
return System.currentTimeMillis();
}
diff --git a/core/java/com/android/internal/os/Clock.java b/core/java/com/android/internal/os/Clock.java
new file mode 100644
index 000000000000..45007c48777f
--- /dev/null
+++ b/core/java/com/android/internal/os/Clock.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.SystemClock;
+
+/**
+ * A wrapper for SystemClock, intended for mocking in unit tests.
+ */
+public abstract class Clock {
+ /** Elapsed Realtime, see SystemClock.elapsedRealtime() */
+ public long elapsedRealtime() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Uptime, see SystemClock.uptimeMillis() */
+ public long uptimeMillis() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** Wall-clock time as per System.currentTimeMillis() */
+ public long currentTimeMillis() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static final Clock SYSTEM_CLOCK = new Clock() {
+
+ @Override
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+ };
+}
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
index b3aec0c2a561..b04fd47e8d9f 100644
--- a/core/java/com/android/internal/os/KernelCpuProcStringReader.java
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -17,7 +17,6 @@
package com.android.internal.os;
import android.os.StrictMode;
-import android.os.SystemClock;
import android.util.Slog;
import java.io.BufferedReader;
@@ -89,6 +88,7 @@ public class KernelCpuProcStringReader {
private int mErrors = 0;
private final Path mFile;
+ private final Clock mClock;
private char[] mBuf;
private int mSize;
private long mLastReadTime = 0;
@@ -97,7 +97,12 @@ public class KernelCpuProcStringReader {
private final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
public KernelCpuProcStringReader(String file) {
+ this(file, Clock.SYSTEM_CLOCK);
+ }
+
+ public KernelCpuProcStringReader(String file, Clock clock) {
mFile = Paths.get(file);
+ mClock = clock;
}
/**
@@ -168,7 +173,7 @@ public class KernelCpuProcStringReader {
}
}
mSize = total;
- mLastReadTime = SystemClock.elapsedRealtime();
+ mLastReadTime = mClock.elapsedRealtime();
// ReentrantReadWriteLock allows lock downgrading.
mReadLock.lock();
return new ProcFileIterator(total);
@@ -186,7 +191,7 @@ public class KernelCpuProcStringReader {
}
private boolean dataValid() {
- return mSize > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS);
+ return mSize > 0 && (mClock.elapsedRealtime() - mLastReadTime < FRESHNESS);
}
/**
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index 50df166605db..faeb8fc237ec 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -21,7 +21,6 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.StrictMode;
-import android.os.SystemClock;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -60,6 +59,7 @@ public abstract class KernelCpuUidTimeReader<T> {
final boolean mThrottle;
protected boolean mBpfTimesAvailable;
final KernelCpuUidBpfMapReader mBpfReader;
+ private final Clock mClock;
private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
private long mLastReadTimeMs = 0;
@@ -76,15 +76,17 @@ public abstract class KernelCpuUidTimeReader<T> {
void onUidCpuTime(int uid, T time);
}
- KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader,
+ @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock) {
mReader = reader;
mThrottle = throttle;
mBpfReader = bpfReader;
+ mClock = clock;
mBpfTimesAvailable = (mBpfReader != null);
}
- KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- this(reader, null, throttle);
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle, Clock clock) {
+ this(reader, null, throttle, clock);
}
/**
@@ -104,17 +106,17 @@ public abstract class KernelCpuUidTimeReader<T> {
*/
public void readDelta(boolean force, @Nullable Callback<T> cb) {
if (!mThrottle) {
- readDeltaImpl(cb);
+ readDeltaImpl(cb, force);
return;
}
- final long currTimeMs = SystemClock.elapsedRealtime();
+ final long currTimeMs = mClock.elapsedRealtime();
if (!force && currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
if (DEBUG) {
Slog.d(mTag, "Throttle readDelta");
}
return;
}
- readDeltaImpl(cb);
+ readDeltaImpl(cb, force);
mLastReadTimeMs = currTimeMs;
}
@@ -128,7 +130,7 @@ public abstract class KernelCpuUidTimeReader<T> {
readAbsoluteImpl(cb);
return;
}
- final long currTimeMs = SystemClock.elapsedRealtime();
+ final long currTimeMs = mClock.elapsedRealtime();
if (currTimeMs < mLastReadTimeMs + mMinTimeBetweenRead) {
if (DEBUG) {
Slog.d(mTag, "Throttle readAbsolute");
@@ -139,7 +141,7 @@ public abstract class KernelCpuUidTimeReader<T> {
mLastReadTimeMs = currTimeMs;
}
- abstract void readDeltaImpl(@Nullable Callback<T> cb);
+ abstract void readDeltaImpl(@Nullable Callback<T> cb, boolean forceRead);
abstract void readAbsoluteImpl(Callback<T> callback);
@@ -216,17 +218,22 @@ public abstract class KernelCpuUidTimeReader<T> {
private final long[] mUsrSysTime = new long[2];
public KernelCpuUidUserSysTimeReader(boolean throttle) {
- super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle);
+ this(throttle, Clock.SYSTEM_CLOCK);
+ }
+
+ public KernelCpuUidUserSysTimeReader(boolean throttle, Clock clock) {
+ super(KernelCpuProcStringReader.getUserSysTimeReaderInstance(), throttle, clock);
}
@VisibleForTesting
- public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- super(reader, throttle);
+ public KernelCpuUidUserSysTimeReader(KernelCpuProcStringReader reader, boolean throttle,
+ Clock clock) {
+ super(reader, throttle, clock);
}
@Override
- void readDeltaImpl(@Nullable Callback<long[]> cb) {
- try (ProcFileIterator iter = mReader.open(!mThrottle)) {
+ void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
+ try (ProcFileIterator iter = mReader.open(!mThrottle || forceRead)) {
if (iter == null) {
return;
}
@@ -348,14 +355,23 @@ public abstract class KernelCpuUidTimeReader<T> {
private boolean mAllUidTimesAvailable = true;
public KernelCpuUidFreqTimeReader(boolean throttle) {
+ this(throttle, Clock.SYSTEM_CLOCK);
+ }
+
+ public KernelCpuUidFreqTimeReader(boolean throttle, Clock clock) {
this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
- KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
+ KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle, clock);
}
@VisibleForTesting
public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
- super(reader, bpfReader, throttle);
+ this(procFile, reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
+ }
+
+ private KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle, Clock clock) {
+ super(reader, bpfReader, throttle, clock);
mProcFilePath = Paths.get(procFile);
}
@@ -496,7 +512,7 @@ public abstract class KernelCpuUidTimeReader<T> {
}
@Override
- void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
if (mBpfTimesAvailable) {
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
if (checkPrecondition(iter)) {
@@ -628,13 +644,18 @@ public abstract class KernelCpuUidTimeReader<T> {
private long[] mBuffer;
public KernelCpuUidActiveTimeReader(boolean throttle) {
+ this(throttle, Clock.SYSTEM_CLOCK);
+ }
+
+ public KernelCpuUidActiveTimeReader(boolean throttle, Clock clock) {
super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
- KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
+ KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle, clock);
}
@VisibleForTesting
- public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
- super(reader, bpfReader, throttle);
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader,
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
}
private void processUidDelta(@Nullable Callback<Long> cb) {
@@ -655,7 +676,7 @@ public abstract class KernelCpuUidTimeReader<T> {
}
@Override
- void readDeltaImpl(@Nullable Callback<Long> cb) {
+ void readDeltaImpl(@Nullable Callback<Long> cb, boolean forceRead) {
if (mBpfTimesAvailable) {
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
if (checkPrecondition(iter)) {
@@ -800,14 +821,18 @@ public abstract class KernelCpuUidTimeReader<T> {
private long[] mDeltaTime;
public KernelCpuUidClusterTimeReader(boolean throttle) {
+ this(throttle, Clock.SYSTEM_CLOCK);
+ }
+
+ public KernelCpuUidClusterTimeReader(boolean throttle, Clock clock) {
super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
- KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
+ KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle, clock);
}
@VisibleForTesting
public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
- KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
- super(reader, bpfReader, throttle);
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle, Clock.SYSTEM_CLOCK);
}
void processUidDelta(@Nullable Callback<long[]> cb) {
@@ -838,7 +863,7 @@ public abstract class KernelCpuUidTimeReader<T> {
}
@Override
- void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ void readDeltaImpl(@Nullable Callback<long[]> cb, boolean forceRead) {
if (mBpfTimesAvailable) {
try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
if (checkPrecondition(iter)) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e4e28a926ed6..92d5a47a2ed0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -747,7 +747,7 @@ public final class Zygote {
new DataOutputStream(sessionSocket.getOutputStream());
Credentials peerCredentials = sessionSocket.getPeerCredentials();
tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
- args = ZygoteArguments.getInstance(argBuffer);
+ args = ZygoteArguments.getInstance(tmpArgBuffer);
applyUidSecurityPolicy(args, peerCredentials);
// TODO (chriswailes): Should this only be run for debug builds?
validateUsapCommand(args);
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 60a8d802861f..d3224b13e312 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -16,16 +16,20 @@
package com.android.internal.policy;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -34,12 +38,18 @@ import android.content.res.Configuration;
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.SystemProperties;
import android.util.Slog;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
+import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -56,11 +66,17 @@ import java.util.List;
/** @hide */
public class TransitionAnimation {
+ public static final int WALLPAPER_TRANSITION_NONE = 0;
+ public static final int WALLPAPER_TRANSITION_OPEN = 1;
+ public static final int WALLPAPER_TRANSITION_CLOSE = 2;
+ public static final int WALLPAPER_TRANSITION_INTRA_OPEN = 3;
+ public static final int WALLPAPER_TRANSITION_INTRA_CLOSE = 4;
+
// These are the possible states for the enter/exit activities during a thumbnail transition
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
- public static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
- public static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+ private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+ private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
/**
* Maximum duration for the clip reveal animation. This is used when there is a lot of movement
@@ -72,9 +88,15 @@ public class TransitionAnimation {
public static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+ /** Fraction of animation at which the recents thumbnail stays completely transparent */
+ private static final float RECENTS_THUMBNAIL_FADEIN_FRACTION = 0.5f;
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.5f;
+ /** Interpolator to be used for animations that respond directly to a touch */
+ static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
private static final String DEFAULT_PACKAGE = "android";
private final Context mContext;
@@ -86,7 +108,9 @@ public class TransitionAnimation {
new PathInterpolator(0.3f, 0f, 0.1f, 1f);
private final Interpolator mClipHorizontalInterpolator = new PathInterpolator(0, 0, 0.4f, 1f);
private final Interpolator mDecelerateInterpolator;
+ private final Interpolator mFastOutLinearInInterpolator;
private final Interpolator mLinearOutSlowInInterpolator;
+ private final Interpolator mThumbnailFadeInInterpolator;
private final Interpolator mThumbnailFadeOutInterpolator;
private final Rect mTmpFromClipRect = new Rect();
private final Rect mTmpToClipRect = new Rect();
@@ -107,8 +131,19 @@ public class TransitionAnimation {
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.decelerate_cubic);
+ mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.linear_out_slow_in);
+ mThumbnailFadeInInterpolator = input -> {
+ // Linear response for first fraction, then complete after that.
+ if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
+ return 0f;
+ }
+ float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION)
+ / (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
+ return mFastOutLinearInInterpolator.getInterpolation(t);
+ };
mThumbnailFadeOutInterpolator = input -> {
// Linear response for first fraction, then complete after that.
if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
@@ -181,6 +216,13 @@ public class TransitionAnimation {
DEFAULT_PACKAGE, com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
}
+ @Nullable
+ public Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+ final Animation animation = loadCrossProfileAppThumbnailEnterAnimation();
+ return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+ appRect.height(), 0, null);
+ }
+
/** Load animation by resource Id from specific package. */
@Nullable
public Animation loadAnimationRes(String packageName, int resId) {
@@ -347,8 +389,15 @@ public class TransitionAnimation {
}
}
- public Animation createClipRevealAnimationLocked(int transit, boolean enter, Rect appFrame,
- Rect displayFrame, Rect startRect) {
+ public Animation createClipRevealAnimationLocked(@TransitionType int transit,
+ int wallpaperTransit, boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
+ return createClipRevealAnimationLockedCompat(
+ getTransitCompatType(transit, wallpaperTransit), enter, appFrame, displayFrame,
+ startRect);
+ }
+
+ public Animation createClipRevealAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect appFrame, Rect displayFrame, Rect startRect) {
final Animation anim;
if (enter) {
final int appWidth = appFrame.width();
@@ -458,8 +507,14 @@ public class TransitionAnimation {
return anim;
}
- public Animation createScaleUpAnimationLocked(int transit, boolean enter,
- Rect containingFrame, Rect startRect) {
+ public Animation createScaleUpAnimationLocked(@TransitionType int transit, int wallpaperTransit,
+ boolean enter, Rect containingFrame, Rect startRect) {
+ return createScaleUpAnimationLockedCompat(getTransitCompatType(transit, wallpaperTransit),
+ enter, containingFrame, startRect);
+ }
+
+ public Animation createScaleUpAnimationLockedCompat(@TransitionOldType int transit,
+ boolean enter, Rect containingFrame, Rect startRect) {
Animation a;
setupDefaultNextAppTransitionStartRect(startRect, mTmpRect);
final int appWidth = containingFrame.width();
@@ -514,12 +569,19 @@ public class TransitionAnimation {
return a;
}
+ public Animation createThumbnailEnterExitAnimationLocked(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionType int transit, int wallpaperTransit,
+ HardwareBuffer thumbnailHeader, Rect startRect) {
+ return createThumbnailEnterExitAnimationLockedCompat(enter, scaleUp, containingFrame,
+ getTransitCompatType(transit, wallpaperTransit), thumbnailHeader, startRect);
+ }
+
/**
* This animation is created when we are doing a thumbnail transition, for the activity that is
* leaving, and the activity that is entering.
*/
- public Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState,
- Rect containingFrame, int transit, HardwareBuffer thumbnailHeader,
+ public Animation createThumbnailEnterExitAnimationLockedCompat(boolean enter, boolean scaleUp,
+ Rect containingFrame, @TransitionOldType int transit, HardwareBuffer thumbnailHeader,
Rect startRect) {
final int appWidth = containingFrame.width();
final int appHeight = containingFrame.height();
@@ -529,6 +591,7 @@ public class TransitionAnimation {
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = thumbnailHeader != null ? thumbnailHeader.getHeight() : appHeight;
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
@@ -587,8 +650,8 @@ public class TransitionAnimation {
* This alternate animation is created when we are doing a thumbnail transition, for the
* activity that is leaving, and the activity that is entering.
*/
- public Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ public Animation createAspectScaledThumbnailEnterExitAnimationLocked(boolean enter,
+ boolean scaleUp, int orientation, int transit, Rect containingFrame, Rect contentInsets,
@Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean freeform,
Rect startRect, Rect defaultStartRect) {
Animation a;
@@ -601,11 +664,11 @@ public class TransitionAnimation {
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
final int thumbStartY = mTmpRect.top - containingFrame.top;
+ final int thumbTransitState = getThumbnailTransitionState(enter, scaleUp);
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP:
case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
- final boolean scaleUp = thumbTransitState == THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
if (freeform && scaleUp) {
a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
containingFrame, surfaceInsets, startRect, defaultStartRect);
@@ -720,10 +783,151 @@ public class TransitionAnimation {
}
/**
+ * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+ * when a thumbnail is specified with the pending animation override.
+ */
+ public Animation createThumbnailAspectScaleAnimationLocked(Rect appRect,
+ @Nullable Rect contentInsets, HardwareBuffer thumbnailHeader, int orientation,
+ Rect startRect, Rect defaultStartRect, boolean scaleUp) {
+ Animation a;
+ final int thumbWidthI = thumbnailHeader.getWidth();
+ final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+ final int thumbHeightI = thumbnailHeader.getHeight();
+ final int appWidth = appRect.width();
+
+ float scaleW = appWidth / thumbWidth;
+ getNextAppTransitionStartRect(startRect, defaultStartRect, mTmpRect);
+ final float fromX;
+ float fromY;
+ final float toX;
+ float toY;
+ final float pivotX;
+ final float pivotY;
+ if (shouldScaleDownThumbnailTransition(orientation)) {
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+
+ // For the curved translate animation to work, the pivot points needs to be at the
+ // same absolute position as the one from the real surface.
+ toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
+ toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
+ pivotX = mTmpRect.width() / 2;
+ pivotY = appRect.height() / 2 / scaleW;
+ if (mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header is displayed above the thumbnail instead of
+ // overlapping it.
+ fromY -= thumbHeightI;
+ toY -= thumbHeightI * scaleW;
+ }
+ } else {
+ pivotX = 0;
+ pivotY = 0;
+ fromX = mTmpRect.left;
+ fromY = mTmpRect.top;
+ toX = appRect.left;
+ toY = appRect.top;
+ }
+ if (scaleUp) {
+ // Animation up from the thumbnail to the full screen
+ Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(1f, 0f);
+ alpha.setInterpolator(mThumbnailFadeOutInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
+ mTmpToClipRect.set(appRect);
+
+ // Containing frame is in screen space, but we need the clip rect in the
+ // app space.
+ mTmpToClipRect.offsetTo(0, 0);
+ mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
+ mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
+
+ if (contentInsets != null) {
+ mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
+ (int) (-contentInsets.top * scaleW),
+ (int) (-contentInsets.right * scaleW),
+ (int) (-contentInsets.bottom * scaleW));
+ }
+
+ Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+ clipAnim.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ clipAnim.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ set.addAnimation(clipAnim);
+ a = set;
+ } else {
+ // Animation down from the full screen to the thumbnail
+ Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
+ scale.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ scale.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation alpha = new AlphaAnimation(0f, 1f);
+ alpha.setInterpolator(mThumbnailFadeInInterpolator);
+ alpha.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+ Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
+ translate.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+ translate.setDuration(THUMBNAIL_APP_TRANSITION_DURATION);
+
+ // This AnimationSet uses the Interpolators assigned above.
+ AnimationSet set = new AnimationSet(false);
+ set.addAnimation(scale);
+ if (!mGridLayoutRecentsEnabled) {
+ // In the grid layout, the header should be shown for the whole animation.
+ set.addAnimation(alpha);
+ }
+ set.addAnimation(translate);
+ a = set;
+
+ }
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
+ null);
+ }
+
+ /**
+ * Creates an overlay with a background color and a thumbnail for the cross profile apps
+ * animation.
+ */
+ public HardwareBuffer createCrossProfileAppsThumbnail(
+ @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ final int width = frame.width();
+ final int height = frame.height();
+
+ final Picture picture = new Picture();
+ final Canvas canvas = picture.beginRecording(width, height);
+ canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+ final int thumbnailSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+ final Drawable drawable = mContext.getDrawable(thumbnailDrawableRes);
+ drawable.setBounds(
+ (width - thumbnailSize) / 2,
+ (height - thumbnailSize) / 2,
+ (width + thumbnailSize) / 2,
+ (height + thumbnailSize) / 2);
+ drawable.setTint(mContext.getColor(android.R.color.white));
+ drawable.draw(canvas);
+ picture.endRecording();
+
+ return Bitmap.createBitmap(picture).getHardwareBuffer();
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
private Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight,
- int transit) {
+ @TransitionOldType int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
@@ -820,6 +1024,22 @@ public class TransitionAnimation {
return anim;
}
+ private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
+ int wallpaperTransit) {
+ if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_OPEN;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE;
+ } else if (transit == TRANSIT_OPEN) {
+ return TRANSIT_OLD_ACTIVITY_OPEN;
+ } else if (transit == TRANSIT_CLOSE) {
+ return TRANSIT_OLD_ACTIVITY_CLOSE;
+ }
+
+ // We only do some special handle for above type, so use type NONE for default behavior.
+ return TRANSIT_OLD_NONE;
+ }
+
/**
* Calculates the duration for the clip reveal animation. If the clip is "cut off", meaning that
* the start rect is outside of the target rect, and there is a lot of movement going on.
@@ -843,10 +1063,33 @@ public class TransitionAnimation {
}
/**
+ * Return the current thumbnail transition state.
+ */
+ private int getThumbnailTransitionState(boolean enter, boolean scaleUp) {
+ if (enter) {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+ }
+ } else {
+ if (scaleUp) {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+ } else {
+ return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+ }
+ }
+ }
+
+ /**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- private static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
+ public static Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth,
int appHeight, long duration, Interpolator interpolator) {
+ if (a == null) {
+ return null;
+ }
+
if (duration > 0) {
a.setDuration(duration);
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 10224a4b9db6..353c6c083d9d 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -28,7 +28,7 @@ import java.io.File;
*/
public class ProtoLogImpl extends BaseProtoLogImpl {
private static final int BUFFER_CAPACITY = 1024 * 1024;
- private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
+ private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope";
private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
private static ProtoLogImpl sServiceInstance = null;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 10f14b42ae42..84a7f2f5ddf9 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -25,6 +25,7 @@ import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.service.notification.StatusBarNotification;
+import android.view.InsetsVisibilities;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -182,7 +183,7 @@ oneway interface IStatusBar
/**
* Notifies System UI side of system bar attribute change on the specified display.
*
- * @param displayId the ID of the display to notify
+ * @param displayId the ID of the display to notify.
* @param appearance the appearance of the focused window. The light top bar appearance is not
* controlled here, but primaryAppearance and secondaryAppearance.
* @param appearanceRegions a set of appearances which will be only applied in their own bounds.
@@ -191,11 +192,12 @@ oneway interface IStatusBar
* stacks.
* @param navbarColorManagedByIme {@code true} if navigation bar color is managed by IME.
* @param behavior the behavior of the focused window.
- * @param isFullscreen whether any of status or navigation bar is requested invisible.
+ * @param requestedVisibilities the collection of the requested visibilities of system insets.
+ * @param packageName the package name of the focused app.
*/
void onSystemBarAttributesChanged(int displayId, int appearance,
in AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- int behavior, boolean isFullscreen);
+ int behavior, in InsetsVisibilities requestedVisibilities, String packageName);
/**
* Notifies System UI to show transient bars. The transient bars are system bars, e.g., status
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8fb2f9cd8bf9..4dcc82e2e572 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -21,6 +21,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.view.InsetsVisibilities;
import com.android.internal.view.AppearanceRegion;
@@ -39,14 +40,15 @@ public final class RegisterStatusBarResult implements Parcelable {
public final IBinder mImeToken;
public final boolean mNavbarColorManagedByIme;
public final int mBehavior;
- public final boolean mAppFullscreen;
+ public final InsetsVisibilities mRequestedVisibilities;
+ public final String mPackageName;
public final int[] mTransientBarTypes;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
- boolean navbarColorManagedByIme, int behavior, boolean appFullscreen,
- @NonNull int[] transientBarTypes) {
+ boolean navbarColorManagedByIme, int behavior, InsetsVisibilities requestedVisibilities,
+ String packageName, @NonNull int[] transientBarTypes) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
@@ -58,7 +60,8 @@ public final class RegisterStatusBarResult implements Parcelable {
mImeToken = imeToken;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mAppFullscreen = appFullscreen;
+ mRequestedVisibilities = requestedVisibilities;
+ mPackageName = packageName;
mTransientBarTypes = transientBarTypes;
}
@@ -80,7 +83,8 @@ public final class RegisterStatusBarResult implements Parcelable {
dest.writeStrongBinder(mImeToken);
dest.writeBoolean(mNavbarColorManagedByIme);
dest.writeInt(mBehavior);
- dest.writeBoolean(mAppFullscreen);
+ dest.writeTypedObject(mRequestedVisibilities, 0);
+ dest.writeString(mPackageName);
dest.writeIntArray(mTransientBarTypes);
}
@@ -104,12 +108,14 @@ public final class RegisterStatusBarResult implements Parcelable {
final IBinder imeToken = source.readStrongBinder();
final boolean navbarColorManagedByIme = source.readBoolean();
final int behavior = source.readInt();
- final boolean appFullscreen = source.readBoolean();
+ final InsetsVisibilities requestedVisibilities =
+ source.readTypedObject(InsetsVisibilities.CREATOR);
+ final String packageName = source.readString();
final int[] transientBarTypes = source.createIntArray();
return new RegisterStatusBarResult(icons, disabledFlags1, appearance,
appearanceRegions, imeWindowVis, imeBackDisposition, showImeSwitcher,
disabledFlags2, imeToken, navbarColorManagedByIme, behavior,
- appFullscreen, transientBarTypes);
+ requestedVisibilities, packageName, transientBarTypes);
}
@Override
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 8b3c1337c0c8..7a712e50e6a8 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -291,10 +291,10 @@ public class ContrastColorUtil {
* Finds a suitable color such that there's enough contrast.
*
* @param color the color to start searching from.
- * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
- * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+ * @param other the color to ensure contrast against. Assumed to be lighter than {@code color}
+ * @param findFg if true, we assume {@code color} is a foreground, otherwise a background.
* @param minRatio the minimum contrast ratio required.
- * @return a color with the same hue as {@param color}, potentially darkened to meet the
+ * @return a color with the same hue as {@code color}, potentially darkened to meet the
* contrast ratio.
*/
public static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
@@ -331,7 +331,7 @@ public class ContrastColorUtil {
* @param color the color to start searching from.
* @param backgroundColor the color to ensure contrast against.
* @param minRatio the minimum contrast ratio required.
- * @return the same color as {@param color} with potentially modified alpha to meet contrast
+ * @return the same color as {@code color} with potentially modified alpha to meet contrast
*/
public static int findAlphaToMeetContrast(int color, int backgroundColor, double minRatio) {
int fg = color;
@@ -361,10 +361,10 @@ public class ContrastColorUtil {
* Finds a suitable color such that there's enough contrast.
*
* @param color the color to start searching from.
- * @param other the color to ensure contrast against. Assumed to be darker than {@param color}
- * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+ * @param other the color to ensure contrast against. Assumed to be darker than {@code color}
+ * @param findFg if true, we assume {@code color} is a foreground, otherwise a background.
* @param minRatio the minimum contrast ratio required.
- * @return a color with the same hue as {@param color}, potentially darkened to meet the
+ * @return a color with the same hue as {@code color}, potentially lightened to meet the
* contrast ratio.
*/
public static int findContrastColorAgainstDark(int color, int other, boolean findFg,
@@ -393,7 +393,8 @@ public class ContrastColorUtil {
low = l;
}
}
- return findFg ? fg : bg;
+ hsl[2] = high;
+ return ColorUtilsFromCompat.HSLToColor(hsl);
}
public static int ensureTextContrastOnBlack(int color) {
@@ -452,7 +453,7 @@ public class ContrastColorUtil {
}
/**
- * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
+ * Resolves {@code color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
*/
public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) {
if (color == Notification.COLOR_DEFAULT) {
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index a0454510b30b..5b68159888cc 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,5 +1,6 @@
per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
per-file Protocol* = etancohen@google.com, lorenzo@google.com
per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
per-file DataClass* = eugenesusla@google.com \ No newline at end of file
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
deleted file mode 100644
index 783d088b19b9..000000000000
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ /dev/null
@@ -1,930 +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.internal.view;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.util.Log;
-import android.util.imetracing.ImeTracing;
-import android.util.imetracing.InputConnectionHelper;
-import android.util.proto.ProtoOutputStream;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.DumpableInputConnection;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputConnectionInspector;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
-import android.view.inputmethod.InputContentInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.SurroundingText;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.inputmethod.ICharSequenceResultCallback;
-import com.android.internal.inputmethod.IExtractedTextResultCallback;
-import com.android.internal.inputmethod.IIntResultCallback;
-import com.android.internal.inputmethod.ISurroundingTextResultCallback;
-import com.android.internal.os.SomeArgs;
-
-import java.lang.ref.WeakReference;
-
-public final class IInputConnectionWrapper extends IInputContext.Stub {
- private static final String TAG = "IInputConnectionWrapper";
- private static final boolean DEBUG = false;
-
- private static final int DO_GET_TEXT_AFTER_CURSOR = 10;
- private static final int DO_GET_TEXT_BEFORE_CURSOR = 20;
- private static final int DO_GET_SELECTED_TEXT = 25;
- private static final int DO_GET_CURSOR_CAPS_MODE = 30;
- private static final int DO_GET_EXTRACTED_TEXT = 40;
- private static final int DO_COMMIT_TEXT = 50;
- private static final int DO_COMMIT_COMPLETION = 55;
- private static final int DO_COMMIT_CORRECTION = 56;
- private static final int DO_SET_SELECTION = 57;
- private static final int DO_PERFORM_EDITOR_ACTION = 58;
- private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
- private static final int DO_SET_COMPOSING_TEXT = 60;
- private static final int DO_SET_COMPOSING_REGION = 63;
- private static final int DO_FINISH_COMPOSING_TEXT = 65;
- private static final int DO_SEND_KEY_EVENT = 70;
- private static final int DO_DELETE_SURROUNDING_TEXT = 80;
- private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
- private static final int DO_BEGIN_BATCH_EDIT = 90;
- private static final int DO_END_BATCH_EDIT = 95;
- private static final int DO_PERFORM_SPELL_CHECK = 110;
- private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
- private static final int DO_CLEAR_META_KEY_STATES = 130;
- private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
- private static final int DO_CLOSE_CONNECTION = 150;
- private static final int DO_COMMIT_CONTENT = 160;
- private static final int DO_GET_SURROUNDING_TEXT = 41;
- private static final int DO_SET_IME_CONSUMES_INPUT = 170;
-
-
- @GuardedBy("mLock")
- @Nullable
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private InputConnection mInputConnection;
-
- private Looper mMainLooper;
- private Handler mH;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private boolean mFinished = false;
-
- private final InputMethodManager mParentInputMethodManager;
- private final WeakReference<View> mServedView;
-
- class MyHandler extends Handler {
- MyHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- executeMessage(msg);
- }
- }
-
- public IInputConnectionWrapper(@NonNull Looper mainLooper,
- @NonNull InputConnection inputConnection,
- @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
- mInputConnection = inputConnection;
- mMainLooper = mainLooper;
- mH = new MyHandler(mMainLooper);
- mParentInputMethodManager = inputMethodManager;
- mServedView = new WeakReference<>(servedView);
- }
-
- @Nullable
- public InputConnection getInputConnection() {
- synchronized (mLock) {
- return mInputConnection;
- }
- }
-
- private boolean isFinished() {
- synchronized (mLock) {
- return mFinished;
- }
- }
-
- public boolean isActive() {
- return mParentInputMethodManager.isActive() && !isFinished();
- }
-
- public View getServedView() {
- return mServedView.get();
- }
-
- public void deactivate() {
- if (isFinished()) {
- // This is a small performance optimization. Still only the 1st call of
- // reportFinish() will take effect.
- return;
- }
- closeConnection();
-
- // Notify the app that the InputConnection was closed.
- final View servedView = mServedView.get();
- if (servedView != null) {
- final Handler handler = servedView.getHandler();
- // The handler is null if the view is already detached. When that's the case, for
- // now, we simply don't dispatch this callback.
- if (handler != null) {
- if (DEBUG) {
- Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
- }
- if (handler.getLooper().isCurrentThread()) {
- servedView.onInputConnectionClosedInternal();
- } else {
- handler.post(servedView::onInputConnectionClosedInternal);
- }
- }
- }
- }
-
- @Override
- public String toString() {
- return "IInputConnectionWrapper{"
- + "connection=" + getInputConnection()
- + " finished=" + isFinished()
- + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
- + " mServedView=" + mServedView.get()
- + "}";
- }
-
- public void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mLock) {
- // Check that the call is initiated in the main thread of the current InputConnection
- // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
- // executed on this thread. Otherwise the messages are dispatched to the correct thread
- // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
- // reasons.
- if ((mInputConnection instanceof DumpableInputConnection)
- && Looper.myLooper() == mMainLooper) {
- ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
- }
- }
- }
-
- public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
- dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
- }
-
- public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
- dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback));
- }
-
- public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
- dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
- }
-
- /**
- * Dispatches the request for retrieving surrounding text.
- *
- * <p>See {@link InputConnection#getSurroundingText(int, int, int)}.
- */
- public void getSurroundingText(int beforeLength, int afterLength, int flags,
- ISurroundingTextResultCallback callback) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = beforeLength;
- args.arg2 = afterLength;
- args.arg3 = flags;
- args.arg4 = callback;
- dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
- }
-
- public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
- dispatchMessage(
- mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
- }
-
- public void getExtractedText(ExtractedTextRequest request, int flags,
- IExtractedTextResultCallback callback) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = request;
- args.arg2 = callback;
- dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args));
- }
-
- public void commitText(CharSequence text, int newCursorPosition) {
- dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
- }
-
- public void commitCompletion(CompletionInfo text) {
- dispatchMessage(obtainMessageO(DO_COMMIT_COMPLETION, text));
- }
-
- public void commitCorrection(CorrectionInfo info) {
- dispatchMessage(obtainMessageO(DO_COMMIT_CORRECTION, info));
- }
-
- public void setSelection(int start, int end) {
- dispatchMessage(obtainMessageII(DO_SET_SELECTION, start, end));
- }
-
- public void performEditorAction(int id) {
- dispatchMessage(obtainMessageII(DO_PERFORM_EDITOR_ACTION, id, 0));
- }
-
- public void performContextMenuAction(int id) {
- dispatchMessage(obtainMessageII(DO_PERFORM_CONTEXT_MENU_ACTION, id, 0));
- }
-
- public void setComposingRegion(int start, int end) {
- dispatchMessage(obtainMessageII(DO_SET_COMPOSING_REGION, start, end));
- }
-
- public void setComposingText(CharSequence text, int newCursorPosition) {
- dispatchMessage(obtainMessageIO(DO_SET_COMPOSING_TEXT, newCursorPosition, text));
- }
-
- public void finishComposingText() {
- dispatchMessage(obtainMessage(DO_FINISH_COMPOSING_TEXT));
- }
-
- public void sendKeyEvent(KeyEvent event) {
- dispatchMessage(obtainMessageO(DO_SEND_KEY_EVENT, event));
- }
-
- public void clearMetaKeyStates(int states) {
- dispatchMessage(obtainMessageII(DO_CLEAR_META_KEY_STATES, states, 0));
- }
-
- public void deleteSurroundingText(int beforeLength, int afterLength) {
- dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT,
- beforeLength, afterLength));
- }
-
- public void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
- dispatchMessage(obtainMessageII(DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS,
- beforeLength, afterLength));
- }
-
- public void beginBatchEdit() {
- dispatchMessage(obtainMessage(DO_BEGIN_BATCH_EDIT));
- }
-
- public void endBatchEdit() {
- dispatchMessage(obtainMessage(DO_END_BATCH_EDIT));
- }
-
- /**
- * Dispatches the request for performing spell check.
- *
- * @see InputConnection#performSpellCheck()
- */
- public void performSpellCheck() {
- dispatchMessage(obtainMessage(DO_PERFORM_SPELL_CHECK));
- }
-
- public void performPrivateCommand(String action, Bundle data) {
- dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
- }
-
- public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback) {
- dispatchMessage(mH.obtainMessage(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
- 0 /* unused */, callback));
- }
-
- public void closeConnection() {
- dispatchMessage(obtainMessage(DO_CLOSE_CONNECTION));
- }
-
- public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
- IIntResultCallback callback) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = inputContentInfo;
- args.arg2 = opts;
- args.arg3 = callback;
- dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args));
- }
-
- /**
- * Dispatches the request for setting ime consumes input.
- *
- * <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
- */
- public void setImeConsumesInput(boolean imeConsumesInput) {
- dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
- }
-
- void dispatchMessage(Message msg) {
- // If we are calling this from the main thread, then we can call
- // right through. Otherwise, we need to send the message to the
- // main thread.
- if (Looper.myLooper() == mMainLooper) {
- executeMessage(msg);
- msg.recycle();
- return;
- }
-
- mH.sendMessage(msg);
- }
-
- void executeMessage(Message msg) {
- ProtoOutputStream icProto;
- switch (msg.what) {
- case DO_GET_TEXT_AFTER_CURSOR: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
- try {
- final ICharSequenceResultCallback callback =
- (ICharSequenceResultCallback) msg.obj;
- final InputConnection ic = getInputConnection();
- final CharSequence result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
- result = null;
- } else {
- result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
- msg.arg2, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_GET_TEXT_BEFORE_CURSOR: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
- try {
- final ICharSequenceResultCallback callback =
- (ICharSequenceResultCallback) msg.obj;
- final InputConnection ic = getInputConnection();
- final CharSequence result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
- result = null;
- } else {
- result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
- msg.arg2, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_GET_SELECTED_TEXT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
- try {
- final ICharSequenceResultCallback callback =
- (ICharSequenceResultCallback) msg.obj;
- final InputConnection ic = getInputConnection();
- final CharSequence result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getSelectedText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getSelectedText(msg.arg1);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSelectedText", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getSelectedText()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_GET_SURROUNDING_TEXT: {
- final SomeArgs args = (SomeArgs) msg.obj;
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
- try {
- int beforeLength = (int) args.arg1;
- int afterLength = (int) args.arg2;
- int flags = (int) args.arg3;
- final ISurroundingTextResultCallback callback =
- (ISurroundingTextResultCallback) args.arg4;
- final InputConnection ic = getInputConnection();
- final SurroundingText result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getSurroundingText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getSurroundingText(beforeLength, afterLength, flags);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
- afterLength, flags, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getSurroundingText()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- args.recycle();
- }
- return;
- }
- case DO_GET_CURSOR_CAPS_MODE: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
- try {
- final IIntResultCallback callback = (IIntResultCallback) msg.obj;
- final InputConnection ic = getInputConnection();
- final int result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
- result = 0;
- } else {
- result = ic.getCursorCapsMode(msg.arg1);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
- result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_GET_EXTRACTED_TEXT: {
- final SomeArgs args = (SomeArgs) msg.obj;
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
- try {
- final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1;
- final IExtractedTextResultCallback callback =
- (IExtractedTextResultCallback) args.arg2;
- final InputConnection ic = getInputConnection();
- final ExtractedText result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "getExtractedText on inactive InputConnection");
- result = null;
- } else {
- result = ic.getExtractedText(request, msg.arg1);
- }
- if (ImeTracing.getInstance().isEnabled()) {
- icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
- msg.arg1, result);
- ImeTracing.getInstance().triggerClientDump(
- TAG + "#getExtractedText", mParentInputMethodManager, icProto);
- }
- try {
- callback.onResult(result);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to getExtractedText()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- args.recycle();
- }
- return;
- }
- case DO_COMMIT_TEXT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "commitText on inactive InputConnection");
- return;
- }
- ic.commitText((CharSequence) msg.obj, msg.arg1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_SET_SELECTION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "setSelection on inactive InputConnection");
- return;
- }
- ic.setSelection(msg.arg1, msg.arg2);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_PERFORM_EDITOR_ACTION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "performEditorAction on inactive InputConnection");
- return;
- }
- ic.performEditorAction(msg.arg1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_PERFORM_CONTEXT_MENU_ACTION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "performContextMenuAction on inactive InputConnection");
- return;
- }
- ic.performContextMenuAction(msg.arg1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_COMMIT_COMPLETION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "commitCompletion on inactive InputConnection");
- return;
- }
- ic.commitCompletion((CompletionInfo) msg.obj);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_COMMIT_CORRECTION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "commitCorrection on inactive InputConnection");
- return;
- }
- ic.commitCorrection((CorrectionInfo) msg.obj);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_SET_COMPOSING_TEXT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "setComposingText on inactive InputConnection");
- return;
- }
- ic.setComposingText((CharSequence) msg.obj, msg.arg1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_SET_COMPOSING_REGION: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "setComposingRegion on inactive InputConnection");
- return;
- }
- ic.setComposingRegion(msg.arg1, msg.arg2);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_FINISH_COMPOSING_TEXT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText");
- try {
- if (isFinished()) {
- // In this case, #finishComposingText() is guaranteed to be called already.
- // There should be no negative impact if we ignore this call silently.
- if (DEBUG) {
- Log.w(TAG, "Bug 35301295: Redundant finishComposingText.");
- }
- return;
- }
- InputConnection ic = getInputConnection();
- // Note we do NOT check isActive() here, because this is safe
- // for an IME to call at any time, and we need to allow it
- // through to clean up our state after the IME has switched to
- // another client.
- if (ic == null) {
- Log.w(TAG, "finishComposingText on inactive InputConnection");
- return;
- }
- ic.finishComposingText();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_SEND_KEY_EVENT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "sendKeyEvent on inactive InputConnection");
- return;
- }
- ic.sendKeyEvent((KeyEvent) msg.obj);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_CLEAR_META_KEY_STATES: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
- return;
- }
- ic.clearMetaKeyStates(msg.arg1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_DELETE_SURROUNDING_TEXT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
- return;
- }
- ic.deleteSurroundingText(msg.arg1, msg.arg2);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT,
- "InputConnection#deleteSurroundingTextInCodePoints");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
- return;
- }
- ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_BEGIN_BATCH_EDIT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "beginBatchEdit on inactive InputConnection");
- return;
- }
- ic.beginBatchEdit();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_END_BATCH_EDIT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "endBatchEdit on inactive InputConnection");
- return;
- }
- ic.endBatchEdit();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_PERFORM_SPELL_CHECK: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performSpellCheck");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "performSpellCheck on inactive InputConnection");
- return;
- }
- ic.performSpellCheck();
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_PERFORM_PRIVATE_COMMAND: {
- final SomeArgs args = (SomeArgs) msg.obj;
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand");
- try {
- final String action = (String) args.arg1;
- final Bundle data = (Bundle) args.arg2;
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG, "performPrivateCommand on inactive InputConnection");
- return;
- }
- ic.performPrivateCommand(action, data);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- args.recycle();
- }
- return;
- }
- case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
- try {
- final IIntResultCallback callback = (IIntResultCallback) msg.obj;
- final InputConnection ic = getInputConnection();
- final boolean result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
- result = false;
- } else {
- result = ic.requestCursorUpdates(msg.arg1);
- }
- try {
- callback.onResult(result ? 1 : 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_CLOSE_CONNECTION: {
- // Note that we do not need to worry about race condition here, because 1) mFinished
- // is updated only inside this block, and 2) the code here is running on a Handler
- // hence we assume multiple DO_CLOSE_CONNECTION messages will not be handled at the
- // same time.
- if (isFinished()) {
- return;
- }
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
- try {
- InputConnection ic = getInputConnection();
- // Note we do NOT check isActive() here, because this is safe
- // for an IME to call at any time, and we need to allow it
- // through to clean up our state after the IME has switched to
- // another client.
- if (ic == null) {
- return;
- }
- @MissingMethodFlags
- final int missingMethods = InputConnectionInspector.getMissingMethodFlags(ic);
- if ((missingMethods & MissingMethodFlags.CLOSE_CONNECTION) == 0) {
- ic.closeConnection();
- }
- } finally {
- synchronized (mLock) {
- mInputConnection = null;
- mFinished = true;
- }
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- case DO_COMMIT_CONTENT: {
- final int flags = msg.arg1;
- SomeArgs args = (SomeArgs) msg.obj;
- Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
- try {
- final IIntResultCallback callback = (IIntResultCallback) args.arg3;
- final InputConnection ic = getInputConnection();
- final boolean result;
- if (ic == null || !isActive()) {
- Log.w(TAG, "commitContent on inactive InputConnection");
- result = false;
- } else {
- final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
- if (inputContentInfo == null || !inputContentInfo.validate()) {
- Log.w(TAG, "commitContent with invalid inputContentInfo="
- + inputContentInfo);
- result = false;
- } else {
- result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
- }
- }
- try {
- callback.onResult(result ? 1 : 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to return the result to commitContent()."
- + " result=" + result, e);
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- args.recycle();
- }
- return;
- }
- case DO_SET_IME_CONSUMES_INPUT: {
- Trace.traceBegin(Trace.TRACE_TAG_INPUT,
- "InputConnection#setImeConsumesInput");
- try {
- InputConnection ic = getInputConnection();
- if (ic == null || !isActive()) {
- Log.w(TAG,
- "setImeConsumesInput on inactive InputConnection");
- return;
- }
- ic.setImeConsumesInput(msg.arg1 == 1);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_INPUT);
- }
- return;
- }
- }
- Log.w(TAG, "Unhandled message code: " + msg.what);
- }
-
- Message obtainMessage(int what) {
- return mH.obtainMessage(what);
- }
-
- Message obtainMessageII(int what, int arg1, int arg2) {
- return mH.obtainMessage(what, arg1, arg2);
- }
-
- Message obtainMessageO(int what, Object arg1) {
- return mH.obtainMessage(what, 0, 0, arg1);
- }
-
- Message obtainMessageIO(int what, int arg1, Object arg2) {
- return mH.obtainMessage(what, arg1, 0, arg2);
- }
-
- Message obtainMessageOO(int what, Object arg1, Object arg2) {
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = arg1;
- args.arg2 = arg2;
- return mH.obtainMessage(what, 0, 0, args);
- }
-
- Message obtainMessageB(int what, boolean arg1) {
- return mH.obtainMessage(what, arg1 ? 1 : 0, 0);
- }
-}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index b06b4e5351c2..dd42c40edb49 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -23,6 +23,7 @@ import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
+import com.android.internal.inputmethod.IBooleanResultCallback;
import com.android.internal.inputmethod.ICharSequenceResultCallback;
import com.android.internal.inputmethod.IExtractedTextResultCallback;
import com.android.internal.inputmethod.IIntResultCallback;
@@ -78,10 +79,10 @@ import com.android.internal.inputmethod.ISurroundingTextResultCallback;
void getSelectedText(int flags, ICharSequenceResultCallback callback);
- void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback);
+ void requestCursorUpdates(int cursorUpdateMode, IBooleanResultCallback callback);
void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
- IIntResultCallback callback);
+ IBooleanResultCallback callback);
void getSurroundingText(int beforeLength, int afterLength, int flags,
ISurroundingTextResultCallback callback);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 8d82e33dc29f..5354afbd667b 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -35,7 +35,7 @@ import com.android.internal.view.InlineSuggestionsRequestInfo;
* {@hide}
*/
oneway interface IInputMethod {
- void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps,
+ void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps,
int configChanges);
void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index f5c2a2a5aad8..e72afdd78ba4 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -16,7 +16,7 @@
package com.android.internal.view;
-import com.android.internal.view.InputBindResult;
+import com.android.internal.inputmethod.InputBindResult;
/**
* Interface a client of the IInputMethodManager implements, to identify
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index d40c064c62e6..4b72355e2593 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -21,7 +21,7 @@ import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
-import com.android.internal.view.InputBindResult;
+import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 498505cd46ff..dbf45287d9d8 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -846,14 +846,6 @@ public class LockPatternUtils {
return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
}
- /**
- * Retrieves whether the current DPM allows use of the Profile Challenge.
- */
- public boolean isSeparateProfileChallengeAllowed(int userHandle) {
- return isManagedProfile(userHandle)
- && getDevicePolicyManager().isSeparateProfileChallengeAllowed(userHandle);
- }
-
private boolean hasSeparateChallenge(int userHandle) {
try {
return getLockSettings().getSeparateProfileChallengeEnabled(userHandle);
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 0c2d2a9bcf41..3191e23a9883 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -53,6 +53,8 @@ public class NotificationActionListLayout extends LinearLayout {
private int mEmphasizedHeight;
private int mRegularHeight;
@DimenRes private int mCollapsibleIndentDimen = R.dimen.notification_actions_padding_start;
+ int mNumNotGoneChildren;
+ int mNumPriorityChildren;
public NotificationActionListLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -76,15 +78,14 @@ public class NotificationActionListLayout extends LinearLayout {
&& ((EmphasizedNotificationButton) actionView).isPriority();
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int N = getChildCount();
+ private void countAndRebuildMeasureOrder() {
+ final int numChildren = getChildCount();
int textViews = 0;
int otherViews = 0;
- int notGoneChildren = 0;
- int priorityChildren = 0;
+ mNumNotGoneChildren = 0;
+ mNumPriorityChildren = 0;
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < numChildren; i++) {
View c = getChildAt(i);
if (c instanceof TextView) {
textViews++;
@@ -92,9 +93,9 @@ public class NotificationActionListLayout extends LinearLayout {
otherViews++;
}
if (c.getVisibility() != GONE) {
- notGoneChildren++;
+ mNumNotGoneChildren++;
if (isPriority(c)) {
- priorityChildren++;
+ mNumPriorityChildren++;
}
}
}
@@ -119,17 +120,20 @@ public class NotificationActionListLayout extends LinearLayout {
if (needRebuild) {
rebuildMeasureOrder(textViews, otherViews);
}
+ }
+ private int measureAndGetUsedWidth(int widthMeasureSpec, int heightMeasureSpec, int innerWidth,
+ boolean collapsePriorityActions) {
+ final int numChildren = getChildCount();
final boolean constrained =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED;
-
- final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
final int otherSize = mMeasureOrderOther.size();
int usedWidth = 0;
+ int maxPriorityWidth = 0;
int measuredChildren = 0;
int measuredPriorityChildren = 0;
- for (int i = 0; i < N; i++) {
+ for (int i = 0; i < numChildren; i++) {
// Measure shortest children first. To avoid measuring twice, we approximate by looking
// at the text length.
final boolean isPriority;
@@ -154,12 +158,20 @@ public class NotificationActionListLayout extends LinearLayout {
// measure in the order of (approx.) size, a large view can still take more than its
// share if the others are small.
int availableWidth = innerWidth - usedWidth;
- int unmeasuredChildren = notGoneChildren - measuredChildren;
+ int unmeasuredChildren = mNumNotGoneChildren - measuredChildren;
int maxWidthForChild = availableWidth / unmeasuredChildren;
- if (isPriority) {
+ if (isPriority && collapsePriorityActions) {
+ // Collapsing the actions to just the width required to show the icon.
+ if (maxPriorityWidth == 0) {
+ maxPriorityWidth = getResources().getDimensionPixelSize(
+ R.dimen.notification_actions_collapsed_priority_width);
+ }
+ maxWidthForChild = maxPriorityWidth + lp.leftMargin + lp.rightMargin;
+ } else if (isPriority) {
// Priority children get a larger maximum share of the total space:
// maximum priority share = (nPriority + 1) / (MAX + 1)
- int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren;
+ int unmeasuredPriorityChildren = mNumPriorityChildren
+ - measuredPriorityChildren;
int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
/ (Notification.MAX_ACTION_BUTTONS + 1);
@@ -187,6 +199,19 @@ public class NotificationActionListLayout extends LinearLayout {
} else {
mExtraStartPadding = 0;
}
+ return usedWidth;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ countAndRebuildMeasureOrder();
+ final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
+ int usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
+ false /* collapsePriorityButtons */);
+ if (mNumPriorityChildren != 0 && usedWidth >= innerWidth) {
+ usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
+ true /* collapsePriorityButtons */);
+ }
mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index fd6038fd655d..627381c620c1 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -19,6 +19,7 @@ package com.android.internal.widget;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Path;
@@ -65,15 +66,15 @@ public class PointerLocationView extends View implements InputDeviceListener,
private float[] mTraceY = new float[32];
private boolean[] mTraceCurrent = new boolean[32];
private int mTraceCount;
-
+
// True if the pointer is down.
@UnsupportedAppUsage
private boolean mCurDown;
-
+
// Most recent coordinates.
private PointerCoords mCoords = new PointerCoords();
private int mToolType;
-
+
// Most recent velocity.
private float mXVelocity;
private float mYVelocity;
@@ -106,7 +107,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
float[] newTraceX = new float[traceCapacity];
System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount);
mTraceX = newTraceX;
-
+
float[] newTraceY = new float[traceCapacity];
System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount);
mTraceY = newTraceY;
@@ -115,7 +116,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
System.arraycopy(mTraceCurrent, 0, newTraceCurrent, 0, mTraceCount);
mTraceCurrent= newTraceCurrent;
}
-
+
mTraceX[mTraceCount] = x;
mTraceY[mTraceCount] = y;
mTraceCurrent[mTraceCount] = current;
@@ -136,6 +137,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
private final FontMetricsInt mTextMetrics = new FontMetricsInt();
private int mHeaderBottom;
private int mHeaderPaddingTop = 0;
+ private Insets mWaterfallInsets = Insets.NONE;
@UnsupportedAppUsage
private boolean mCurDown;
@UnsupportedAppUsage
@@ -160,7 +162,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
@UnsupportedAppUsage
private boolean mPrintCoords = true;
-
+
public PointerLocationView(Context c) {
super(c);
setFocusableInTouchMode(true);
@@ -209,7 +211,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
PointerState ps = new PointerState();
mPointers.add(ps);
mActivePointerId = 0;
-
+
mVelocity = VelocityTracker.obtain();
String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
@@ -229,8 +231,10 @@ public class PointerLocationView extends View implements InputDeviceListener,
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (insets.getDisplayCutout() != null) {
mHeaderPaddingTop = insets.getDisplayCutout().getSafeInsetTop();
+ mWaterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
} else {
mHeaderPaddingTop = 0;
+ mWaterfallInsets = Insets.NONE;
}
return super.onApplyWindowInsets(insets);
}
@@ -248,7 +252,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
+ " bottom=" + mTextMetrics.bottom);
}
}
-
+
// Draw an oval. When angle is 0 radians, orients the major axis vertically,
// angles less than or greater than 0 radians rotate the major axis left or right.
private RectF mReusableOvalRect = new RectF();
@@ -266,11 +270,6 @@ public class PointerLocationView extends View implements InputDeviceListener,
@Override
protected void onDraw(Canvas canvas) {
- final int w = getWidth();
- final int itemW = w/7;
- final int base = mHeaderPaddingTop-mTextMetrics.ascent+1;
- final int bottom = mHeaderBottom;
-
final int NP = mPointers.size();
if (!mSystemGestureExclusion.isEmpty()) {
@@ -286,71 +285,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
// Labels
- if (mActivePointerId >= 0) {
- final PointerState ps = mPointers.get(mActivePointerId);
-
- canvas.drawRect(0, mHeaderPaddingTop, itemW-1, bottom,mTextBackgroundPaint);
- canvas.drawText(mText.clear()
- .append("P: ").append(mCurNumPointers)
- .append(" / ").append(mMaxNumPointers)
- .toString(), 1, base, mTextPaint);
-
- final int N = ps.mTraceCount;
- if ((mCurDown && ps.mCurDown) || N == 0) {
- canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
- mTextBackgroundPaint);
- canvas.drawText(mText.clear()
- .append("X: ").append(ps.mCoords.x, 1)
- .toString(), 1 + itemW, base, mTextPaint);
- canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
- mTextBackgroundPaint);
- canvas.drawText(mText.clear()
- .append("Y: ").append(ps.mCoords.y, 1)
- .toString(), 1 + itemW * 2, base, mTextPaint);
- } else {
- float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
- float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
- canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
- Math.abs(dx) < mVC.getScaledTouchSlop()
- ? mTextBackgroundPaint : mTextLevelPaint);
- canvas.drawText(mText.clear()
- .append("dX: ").append(dx, 1)
- .toString(), 1 + itemW, base, mTextPaint);
- canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
- Math.abs(dy) < mVC.getScaledTouchSlop()
- ? mTextBackgroundPaint : mTextLevelPaint);
- canvas.drawText(mText.clear()
- .append("dY: ").append(dy, 1)
- .toString(), 1 + itemW * 2, base, mTextPaint);
- }
-
- canvas.drawRect(itemW * 3, mHeaderPaddingTop, (itemW * 4) - 1, bottom,
- mTextBackgroundPaint);
- canvas.drawText(mText.clear()
- .append("Xv: ").append(ps.mXVelocity, 3)
- .toString(), 1 + itemW * 3, base, mTextPaint);
-
- canvas.drawRect(itemW * 4, mHeaderPaddingTop, (itemW * 5) - 1, bottom,
- mTextBackgroundPaint);
- canvas.drawText(mText.clear()
- .append("Yv: ").append(ps.mYVelocity, 3)
- .toString(), 1 + itemW * 4, base, mTextPaint);
-
- canvas.drawRect(itemW * 5, mHeaderPaddingTop, (itemW * 6) - 1, bottom,
- mTextBackgroundPaint);
- canvas.drawRect(itemW * 5, mHeaderPaddingTop,
- (itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint);
- canvas.drawText(mText.clear()
- .append("Prs: ").append(ps.mCoords.pressure, 2)
- .toString(), 1 + itemW * 5, base, mTextPaint);
-
- canvas.drawRect(itemW * 6, mHeaderPaddingTop, w, bottom, mTextBackgroundPaint);
- canvas.drawRect(itemW * 6, mHeaderPaddingTop,
- (itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint);
- canvas.drawText(mText.clear()
- .append("Size: ").append(ps.mCoords.size, 2)
- .toString(), 1 + itemW * 6, base, mTextPaint);
- }
+ drawLabels(canvas);
// Pointer trace.
for (int p = 0; p < NP; p++) {
@@ -463,6 +398,84 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
}
+ private void drawLabels(Canvas canvas) {
+ if (mActivePointerId < 0 || mActivePointerId >= mPointers.size()) {
+ return;
+ }
+
+ final int w = getWidth() - mWaterfallInsets.left - mWaterfallInsets.right;
+ final int itemW = w / 7;
+ final int base = mHeaderPaddingTop - mTextMetrics.ascent + 1;
+ final int bottom = mHeaderBottom;
+
+ canvas.save();
+ canvas.translate(mWaterfallInsets.left, 0);
+ final PointerState ps = mPointers.get(mActivePointerId);
+
+ canvas.drawRect(0, mHeaderPaddingTop, itemW - 1, bottom, mTextBackgroundPaint);
+ canvas.drawText(mText.clear()
+ .append("P: ").append(mCurNumPointers)
+ .append(" / ").append(mMaxNumPointers)
+ .toString(), 1, base, mTextPaint);
+
+ final int count = ps.mTraceCount;
+ if ((mCurDown && ps.mCurDown) || count == 0) {
+ canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
+ mTextBackgroundPaint);
+ canvas.drawText(mText.clear()
+ .append("X: ").append(ps.mCoords.x, 1)
+ .toString(), 1 + itemW, base, mTextPaint);
+ canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
+ mTextBackgroundPaint);
+ canvas.drawText(mText.clear()
+ .append("Y: ").append(ps.mCoords.y, 1)
+ .toString(), 1 + itemW * 2, base, mTextPaint);
+ } else {
+ float dx = ps.mTraceX[count - 1] - ps.mTraceX[0];
+ float dy = ps.mTraceY[count - 1] - ps.mTraceY[0];
+ canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
+ Math.abs(dx) < mVC.getScaledTouchSlop()
+ ? mTextBackgroundPaint : mTextLevelPaint);
+ canvas.drawText(mText.clear()
+ .append("dX: ").append(dx, 1)
+ .toString(), 1 + itemW, base, mTextPaint);
+ canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
+ Math.abs(dy) < mVC.getScaledTouchSlop()
+ ? mTextBackgroundPaint : mTextLevelPaint);
+ canvas.drawText(mText.clear()
+ .append("dY: ").append(dy, 1)
+ .toString(), 1 + itemW * 2, base, mTextPaint);
+ }
+
+ canvas.drawRect(itemW * 3, mHeaderPaddingTop, (itemW * 4) - 1, bottom,
+ mTextBackgroundPaint);
+ canvas.drawText(mText.clear()
+ .append("Xv: ").append(ps.mXVelocity, 3)
+ .toString(), 1 + itemW * 3, base, mTextPaint);
+
+ canvas.drawRect(itemW * 4, mHeaderPaddingTop, (itemW * 5) - 1, bottom,
+ mTextBackgroundPaint);
+ canvas.drawText(mText.clear()
+ .append("Yv: ").append(ps.mYVelocity, 3)
+ .toString(), 1 + itemW * 4, base, mTextPaint);
+
+ canvas.drawRect(itemW * 5, mHeaderPaddingTop, (itemW * 6) - 1, bottom,
+ mTextBackgroundPaint);
+ canvas.drawRect(itemW * 5, mHeaderPaddingTop,
+ (itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint);
+ canvas.drawText(mText.clear()
+ .append("Prs: ").append(ps.mCoords.pressure, 2)
+ .toString(), 1 + itemW * 5, base, mTextPaint);
+
+ canvas.drawRect(itemW * 6, mHeaderPaddingTop, w, bottom, mTextBackgroundPaint);
+ canvas.drawRect(itemW * 6, mHeaderPaddingTop,
+ (itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint);
+ canvas.drawText(mText.clear()
+ .append("Size: ").append(ps.mCoords.size, 2)
+ .toString(), 1 + itemW * 6, base, mTextPaint);
+ canvas.restore();
+ }
+
private void logMotionEvent(String type, MotionEvent event) {
final int action = event.getAction();
final int N = event.getHistorySize();
@@ -601,8 +614,8 @@ public class PointerLocationView extends View implements InputDeviceListener,
NP++;
}
- if (mActivePointerId < 0 ||
- !mPointers.get(mActivePointerId).mCurDown) {
+ if (mActivePointerId < 0 || mActivePointerId >= NP
+ || !mPointers.get(mActivePointerId).mCurDown) {
mActivePointerId = id;
}
@@ -697,7 +710,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
invalidate();
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
onPointerEvent(event);
@@ -849,16 +862,16 @@ public class PointerLocationView extends View implements InputDeviceListener,
private static final class FasterStringBuilder {
private char[] mChars;
private int mLength;
-
+
public FasterStringBuilder() {
mChars = new char[64];
}
-
+
public FasterStringBuilder clear() {
mLength = 0;
return this;
}
-
+
public FasterStringBuilder append(String value) {
final int valueLength = value.length();
final int index = reserve(valueLength);
@@ -866,11 +879,11 @@ public class PointerLocationView extends View implements InputDeviceListener,
mLength += valueLength;
return this;
}
-
+
public FasterStringBuilder append(int value) {
return append(value, 0);
}
-
+
public FasterStringBuilder append(int value, int zeroPadWidth) {
final boolean negative = value < 0;
if (negative) {
@@ -880,16 +893,16 @@ public class PointerLocationView extends View implements InputDeviceListener,
return this;
}
}
-
+
int index = reserve(11);
final char[] chars = mChars;
-
+
if (value == 0) {
chars[index++] = '0';
mLength += 1;
return this;
}
-
+
if (negative) {
chars[index++] = '-';
}
@@ -903,18 +916,18 @@ public class PointerLocationView extends View implements InputDeviceListener,
chars[index++] = '0';
}
}
-
+
do {
int digit = value / divisor;
value -= digit * divisor;
divisor /= 10;
chars[index++] = (char) (digit + '0');
} while (divisor != 0);
-
+
mLength = index;
return this;
}
-
+
public FasterStringBuilder append(float value, int precision) {
int scale = 1;
for (int i = 0; i < precision; i++) {
@@ -934,15 +947,15 @@ public class PointerLocationView extends View implements InputDeviceListener,
value -= Math.floor(value);
append((int) (value * scale), precision);
}
-
+
return this;
}
-
+
@Override
public String toString() {
return new String(mChars, 0, mLength);
}
-
+
private int reserve(int length) {
final int oldLength = mLength;
final int newLength = mLength + length;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6b9d3754c247..f46aadfa3c3f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -224,6 +224,7 @@ cc_library_shared {
"fd_utils.cpp",
"android_hardware_input_InputWindowHandle.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
+ "android_window_WindowInfosListener.cpp",
],
static_libs: [
@@ -231,10 +232,12 @@ cc_library_shared {
"libbinderthreadstateutils",
"libdmabufinfo",
"libgif",
+ "libgui_window_info_static",
"libseccomp_policy",
"libgrallocusage",
"libscrypt_static",
"libstatssocket_lazy",
+ "libskia",
],
shared_libs: [
@@ -311,6 +314,7 @@ cc_library_shared {
header_libs: [
"bionic_libc_platform_headers",
"dnsproxyd_protocol_headers",
+ "libandroid_runtime_vm_headers",
],
},
host: {
@@ -370,6 +374,7 @@ cc_library_shared {
"libinput",
"libbinderthreadstateutils",
"libsqlite",
+ "libgui_window_info_static",
],
shared_libs: [
// libbinder needs to be shared since it has global state
@@ -385,3 +390,24 @@ cc_library_shared {
never: true,
},
}
+
+cc_library_headers {
+ name: "libandroid_runtime_vm_headers",
+ host_supported: true,
+ vendor_available: true,
+ // TODO(b/153609531): remove when libbinder is not native_bridge_supported
+ native_bridge_supported: true,
+ // Allow only modules from the following list to create threads that can be
+ // attached to the JVM. This list should be a subset of the dependencies of
+ // libandroid_runtime.
+ visibility: [
+ "//frameworks/native/libs/binder",
+ ],
+ export_include_dirs: ["include_vm"],
+ header_libs: [
+ "jni_headers",
+ ],
+ export_header_lib_headers: [
+ "jni_headers",
+ ],
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2fd1e543cc5b..aa9995d5abf7 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -22,6 +22,7 @@
#include <android-base/properties.h>
#include <android/graphics/jni_runtime.h>
#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/vm.h>
#include <assert.h>
#include <binder/IBinder.h>
#include <binder/IPCThreadState.h>
@@ -207,6 +208,7 @@ extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
+extern int register_android_window_WindowInfosListener(JNIEnv* env);
// Namespace for Android Runtime flags applied during boot time.
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
@@ -1332,6 +1334,10 @@ void AndroidRuntime::onVmCreated(JNIEnv* env)
return AndroidRuntime::mJavaVM;
}
+extern "C" JavaVM* AndroidRuntimeGetJavaVM() {
+ return AndroidRuntime::getJavaVM();
+}
+
/*
* Get the JNIEnv pointer for this thread.
*
@@ -1649,6 +1655,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),
REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
+
+ REG_JNI(register_android_window_WindowInfosListener),
};
/*
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 701960eb4f11..4bee97631f9f 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -22,6 +22,7 @@ per-file android_view_PointerIcon.* = file:/services/core/java/com/android/serve
# WindowManager
per-file android_graphics_BLASTBufferQueue.cpp = file:/services/core/java/com/android/server/wm/OWNERS
per-file android_view_Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file android_window_WindowInfosListener.cpp = file:/services/core/java/com/android/server/wm/OWNERS
# Resources
per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 995bfa97ab2e..24d35316ef20 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -28,6 +28,8 @@
namespace android {
static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID ptr;
jfieldID name;
jfieldID dispatchingTimeoutMillis;
@@ -101,6 +103,15 @@ std::shared_ptr<InputApplicationHandle> android_view_InputApplicationHandle_getH
return *handle;
}
+jobject android_view_InputApplicationHandle_fromInputApplicationInfo(
+ JNIEnv* env, gui::InputApplicationInfo inputApplicationInfo) {
+ jobject binderObject = javaObjectForIBinder(env, inputApplicationInfo.token);
+ ScopedLocalRef<jstring> name(env, env->NewStringUTF(inputApplicationInfo.name.data()));
+ return env->NewObject(gInputApplicationHandleClassInfo.clazz,
+ gInputApplicationHandleClassInfo.ctor, binderObject, name.get(),
+ inputApplicationInfo.dispatchingTimeoutMillis);
+}
+
// --- JNI ---
static void android_view_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) {
@@ -131,6 +142,10 @@ static const JNINativeMethod gInputApplicationHandleMethods[] = {
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+#define GET_METHOD_ID(var, clazz, methodName, methodSignature) \
+ var = env->GetMethodID(clazz, methodName, methodSignature); \
+ LOG_ALWAYS_FATAL_IF(!(var), "Unable to find method " methodName);
+
int register_android_view_InputApplicationHandle(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/view/InputApplicationHandle",
gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
@@ -139,6 +154,10 @@ int register_android_view_InputApplicationHandle(JNIEnv* env) {
jclass clazz;
FIND_CLASS(clazz, "android/view/InputApplicationHandle");
+ gInputApplicationHandleClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+
+ GET_METHOD_ID(gInputApplicationHandleClassInfo.ctor, clazz, "<init>",
+ "(Landroid/os/IBinder;Ljava/lang/String;J)V");
GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz,
"ptr", "J");
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index ec99d6da5b8e..5d88d8e25160 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -19,7 +19,7 @@
#include <string>
-#include <input/InputApplication.h>
+#include <gui/InputApplication.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -42,6 +42,9 @@ private:
extern std::shared_ptr<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
JNIEnv* env, jobject inputApplicationHandleObj);
+extern jobject android_view_InputApplicationHandle_fromInputApplicationInfo(
+ JNIEnv* env, gui::InputApplicationInfo inputApplicationInfo);
+
} // namespace android
#endif // _ANDROID_VIEW_INPUT_APPLICATION_HANDLE_H
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 463d909821b1..e4ef7d39d77c 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -26,14 +26,19 @@
#include <ui/Region.h>
#include <utils/threads.h>
+#include <android/graphics/matrix.h>
+#include <gui/WindowInfo.h>
+#include "SkRegion.h"
#include "android_hardware_input_InputApplicationHandle.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
-#include "input/InputWindow.h"
#include "jni.h"
namespace android {
+using gui::TouchOcclusionMode;
+using gui::WindowInfo;
+
struct WeakRefHandleField {
jfieldID ctrl;
jmethodID get;
@@ -41,6 +46,8 @@ struct WeakRefHandleField {
};
static struct {
+ jclass clazz;
+ jmethodID ctor;
jfieldID ptr;
jfieldID inputApplicationHandle;
jfieldID token;
@@ -66,11 +73,18 @@ static struct {
jfieldID packageName;
jfieldID inputFeatures;
jfieldID displayId;
- jfieldID portalToDisplayId;
jfieldID replaceTouchableRegionWithCrop;
WeakRefHandleField touchableRegionSurfaceControl;
+ jfieldID transform;
+ jfieldID windowToken;
} gInputWindowHandleClassInfo;
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+ jfieldID nativeRegion;
+} gRegionClassInfo;
+
static Mutex gHandleMutex;
@@ -115,9 +129,9 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>");
- mInfo.flags = Flags<InputWindowInfo::Flag>(
+ mInfo.flags = Flags<WindowInfo::Flag>(
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags));
- mInfo.type = static_cast<InputWindowInfo::Type>(
+ mInfo.type = static_cast<WindowInfo::Type>(
env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType));
mInfo.dispatchingTimeout = std::chrono::milliseconds(
env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis));
@@ -159,12 +173,10 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.ownerUid = env->GetIntField(obj,
gInputWindowHandleClassInfo.ownerUid);
mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
- mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
+ mInfo.inputFeatures = static_cast<WindowInfo::Feature>(
env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
- mInfo.portalToDisplayId = env->GetIntField(obj,
- gInputWindowHandleClassInfo.portalToDisplayId);
jobject inputApplicationHandleObj = env->GetObjectField(obj,
gInputWindowHandleClassInfo.inputApplicationHandle);
@@ -204,6 +216,14 @@ bool NativeInputWindowHandle::updateInfo() {
mInfo.touchableRegionCropHandle.clear();
}
+ jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken);
+ if (windowTokenObj) {
+ mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj);
+ env->DeleteLocalRef(windowTokenObj);
+ } else {
+ mInfo.windowToken.clear();
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -233,6 +253,81 @@ sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
return handle;
}
+jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowInfo windowInfo) {
+ ScopedLocalRef<jobject>
+ applicationHandle(env,
+ android_view_InputApplicationHandle_fromInputApplicationInfo(
+ env, windowInfo.applicationInfo));
+
+ jobject inputWindowHandle =
+ env->NewObject(gInputWindowHandleClassInfo.clazz, gInputWindowHandleClassInfo.ctor,
+ applicationHandle.get(), windowInfo.displayId);
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.token,
+ javaObjectForIBinder(env, windowInfo.token));
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.name,
+ env->NewStringUTF(windowInfo.name.data()));
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsFlags,
+ static_cast<uint32_t>(windowInfo.flags.get()));
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsType,
+ static_cast<int32_t>(windowInfo.type));
+ env->SetLongField(inputWindowHandle, gInputWindowHandleClassInfo.dispatchingTimeoutMillis,
+ std::chrono::duration_cast<std::chrono::milliseconds>(
+ windowInfo.dispatchingTimeout)
+ .count());
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameLeft,
+ windowInfo.frameLeft);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameTop, windowInfo.frameTop);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameRight,
+ windowInfo.frameRight);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameBottom,
+ windowInfo.frameBottom);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset,
+ windowInfo.surfaceInset);
+ env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor,
+ windowInfo.globalScaleFactor);
+
+ SkRegion* region = new SkRegion();
+ for (const auto& r : windowInfo.touchableRegion) {
+ region->op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
+ }
+ ScopedLocalRef<jobject> regionObj(env,
+ env->NewObject(gRegionClassInfo.clazz,
+ gRegionClassInfo.ctor));
+ env->SetLongField(regionObj.get(), gRegionClassInfo.nativeRegion,
+ reinterpret_cast<jlong>(region));
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.touchableRegion,
+ regionObj.get());
+
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.visible,
+ windowInfo.visible);
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.focusable,
+ windowInfo.focusable);
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.hasWallpaper,
+ windowInfo.hasWallpaper);
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.paused, windowInfo.paused);
+ env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.trustedOverlay,
+ windowInfo.trustedOverlay);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
+ static_cast<int32_t>(windowInfo.touchOcclusionMode));
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid, windowInfo.ownerUid);
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName,
+ env->NewStringUTF(windowInfo.packageName.data()));
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputFeatures,
+ static_cast<int32_t>(windowInfo.inputFeatures.get()));
+
+ float transformVals[9];
+ for (int i = 0; i < 9; i++) {
+ transformVals[i] = windowInfo.transform[i % 3][i / 3];
+ }
+ ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformVals));
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.transform, matrixObj.get());
+
+ env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
+ javaObjectForIBinder(env, windowInfo.windowToken));
+
+ return inputWindowHandle;
+}
// --- JNI ---
@@ -275,6 +370,10 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
jclass clazz;
FIND_CLASS(clazz, "android/view/InputWindowHandle");
+ gInputWindowHandleClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+
+ GET_METHOD_ID(gInputWindowHandleClassInfo.ctor, clazz, "<init>",
+ "(Landroid/view/InputApplicationHandle;I)V");
GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
"ptr", "J");
@@ -348,12 +447,15 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
"displayId", "I");
- GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
- "portalToDisplayId", "I");
-
GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
"replaceTouchableRegionWithCrop", "Z");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.transform, clazz, "transform",
+ "Landroid/graphics/Matrix;");
+
+ GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
+ "Landroid/os/IBinder;");
+
jclass weakRefClazz;
FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
@@ -368,6 +470,11 @@ int register_android_view_InputWindowHandle(JNIEnv* env) {
GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
surfaceControlClazz, "mNativeObject", "J");
+ jclass regionClazz;
+ FIND_CLASS(regionClazz, "android/graphics/Region");
+ gRegionClassInfo.clazz = MakeGlobalRefOrDie(env, regionClazz);
+ GET_METHOD_ID(gRegionClassInfo.ctor, gRegionClassInfo.clazz, "<init>", "()V");
+ GET_FIELD_ID(gRegionClassInfo.nativeRegion, gRegionClassInfo.clazz, "mNativeRegion", "J");
return 0;
}
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index de5bd6ef97f4..408e0f1bfa36 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,14 +17,14 @@
#ifndef _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
#define _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
-#include <input/InputWindow.h>
+#include <gui/WindowInfo.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
namespace android {
-class NativeInputWindowHandle : public InputWindowHandle {
+class NativeInputWindowHandle : public gui::WindowInfoHandle {
public:
NativeInputWindowHandle(jweak objWeak);
virtual ~NativeInputWindowHandle();
@@ -37,10 +37,12 @@ private:
jweak mObjWeak;
};
-
extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
JNIEnv* env, jobject inputWindowHandleObj);
+extern jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env,
+ gui::WindowInfo windowInfo);
+
} // namespace android
#endif // _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 4b93363b5b90..8a1f1a09b3c9 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -28,6 +28,7 @@
#include <android/media/AudioVibratorInfo.h>
#include <audiomanager/AudioManager.h>
+#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
#include <media/MicrophoneInfo.h>
@@ -207,6 +208,7 @@ static struct {
jmethodID getId;
jmethodID getResonantFrequency;
jmethodID getQFactor;
+ jmethodID getMaxAmplitude;
} gVibratorMethods;
static Mutex gLock;
@@ -812,7 +814,8 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz)
static jint
android_media_AudioSystem_getDevicesForStream(JNIEnv *env, jobject thiz, jint stream)
{
- return (jint) AudioSystem::getDevicesForStream(static_cast <audio_stream_type_t>(stream));
+ return (jint)deviceTypesToBitMask(
+ AudioSystem::getDevicesForStream(static_cast<audio_stream_type_t>(stream)));
}
static jint
@@ -2704,6 +2707,8 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz
vibratorInfo.resonantFrequency =
env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getResonantFrequency);
vibratorInfo.qFactor = env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getQFactor);
+ vibratorInfo.maxAmplitude =
+ env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude);
vibratorInfos.push_back(vibratorInfo);
}
return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
@@ -3070,6 +3075,8 @@ int register_android_media_AudioSystem(JNIEnv *env)
gVibratorMethods.getResonantFrequency =
GetMethodIDOrDie(env, vibratorClass, "getResonantFrequency", "()F");
gVibratorMethods.getQFactor = GetMethodIDOrDie(env, vibratorClass, "getQFactor", "()F");
+ gVibratorMethods.getMaxAmplitude =
+ GetMethodIDOrDie(env, vibratorClass, "getHapticChannelMaximumAmplitude", "()F");
AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback);
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b40491a49b14..f44e829d49d7 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,8 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
}
void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName,
- jstring devOptIn, jobjectArray featuresObj, jobject rulesFd,
- jlong rulesOffset, jlong rulesLength) {
+ jstring devOptIn, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars appNameChars(env, appName);
ScopedUtfChars devOptInChars(env, devOptIn);
@@ -74,11 +73,8 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa
}
}
- int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
-
android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
- devOptInChars.c_str(), features,
- rulesFd_native, rulesOffset, rulesLength);
+ devOptInChars.c_str(), features);
}
bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
@@ -124,8 +120,7 @@ const JNINativeMethod g_methods[] = {
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
{"setAngleInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/io/"
- "FileDescriptor;JJ)V",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"getShouldUseAngle", "(Ljava/lang/String;)Z",
reinterpret_cast<void*>(shouldUseAngle_native)},
diff --git a/core/jni/android_os_ServiceManager.cpp b/core/jni/android_os_ServiceManager.cpp
index c7479492d404..d642d0ebbfc1 100644
--- a/core/jni/android_os_ServiceManager.cpp
+++ b/core/jni/android_os_ServiceManager.cpp
@@ -29,11 +29,8 @@ namespace android {
// Native because we have a client-side wait in waitForService() and we do not
// want an unnecessary second copy of it.
-static jobject android_os_ServiceManager_waitForService(
- JNIEnv *env,
- jclass /* clazzObj */,
- jstring serviceNameObj) {
-
+static jobject android_os_ServiceManager_waitForServiceNative(JNIEnv* env, jclass /* clazzObj */,
+ jstring serviceNameObj) {
const jchar* serviceName = env->GetStringCritical(serviceNameObj, nullptr);
if (!serviceName) {
jniThrowNullPointerException(env, nullptr);
@@ -55,12 +52,9 @@ static jobject android_os_ServiceManager_waitForService(
// ----------------------------------------------------------------------------
static const JNINativeMethod method_table[] = {
- /* name, signature, funcPtr */
- {
- "waitForService",
- "(Ljava/lang/String;)Landroid/os/IBinder;",
- (void*)android_os_ServiceManager_waitForService
- },
+ /* name, signature, funcPtr */
+ {"waitForServiceNative", "(Ljava/lang/String;)Landroid/os/IBinder;",
+ (void*)android_os_ServiceManager_waitForServiceNative},
};
int register_android_os_ServiceManager(JNIEnv* env) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e93b00d7b148..86d781033e5e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -92,6 +92,7 @@ static struct configuration_offsets_t {
jfieldID mSmallestScreenWidthDpOffset;
jfieldID mScreenWidthDpOffset;
jfieldID mScreenHeightDpOffset;
+ jfieldID mScreenLayoutOffset;
} gConfigurationOffsets;
static struct arraymap_offsets_t {
@@ -1019,6 +1020,7 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config&
config.smallestScreenWidthDp);
env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenLayoutOffset, config.screenLayout);
return result;
}
@@ -1553,6 +1555,8 @@ int register_android_content_AssetManager(JNIEnv* env) {
GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
gConfigurationOffsets.mScreenHeightDpOffset =
GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
+ gConfigurationOffsets.mScreenLayoutOffset =
+ GetFieldIDOrDie(env, configurationClass, "screenLayout", "I");
jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index a699f912806d..7b79b3844a86 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -54,6 +54,7 @@ static struct {
jmethodID onPointerCaptureEvent;
jmethodID onDragEvent;
jmethodID onBatchedInputEventPending;
+ jmethodID onTouchModeChanged;
} gInputEventReceiverClassInfo;
// Add prefix to the beginning of each line in 'str'
@@ -424,6 +425,18 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
finishInputEvent(seq, true /* handled */);
continue;
}
+ case AINPUT_EVENT_TYPE_TOUCH_MODE: {
+ const TouchModeEvent* touchModeEvent = static_cast<TouchModeEvent*>(inputEvent);
+ if (kDebugDispatchCycle) {
+ ALOGD("channel '%s' ~ Received touch mode event: isInTouchMode=%s",
+ getInputChannelName().c_str(), toString(touchModeEvent->isInTouchMode()));
+ }
+ env->CallVoidMethod(receiverObj.get(),
+ gInputEventReceiverClassInfo.onTouchModeChanged,
+ jboolean(touchModeEvent->isInTouchMode()));
+ finishInputEvent(seq, true /* handled */);
+ continue;
+ }
default:
assert(false); // InputConsumer should prevent this from ever happening
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 45e3d1b97e83..16366a4e5bec 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -155,6 +155,7 @@ status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent
event->getYPrecision(),
event->getRawXCursorPosition(),
event->getRawYCursorPosition(),
+ event->getDisplayOrientation(),
event->getDisplaySize().x,
event->getDisplaySize().y, event->getDownTime(),
event->getHistoricalEventTime(i),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index 8177ec6df803..2ef9632f0e31 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -98,9 +98,7 @@ jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
jobject eventObj =
env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
- event->getId(),
- nanoseconds_to_milliseconds(event->getDownTime()),
- nanoseconds_to_milliseconds(event->getEventTime()),
+ event->getId(), event->getDownTime(), event->getEventTime(),
event->getAction(), event->getKeyCode(),
event->getRepeatCount(), event->getMetaState(),
event->getDeviceId(), event->getScanCode(),
@@ -136,8 +134,7 @@ status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
event->initialize(id, deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
- metaState, repeatCount, milliseconds_to_nanoseconds(downTime),
- milliseconds_to_nanoseconds(eventTime));
+ metaState, repeatCount, downTime, eventTime);
return OK;
}
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 6971301cec32..cabf3abe350a 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -22,6 +22,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
#include <attestation/HmacKeyManager.h>
+#include <gui/constants.h>
#include <input/Input.h>
#include <nativehelper/ScopedUtfChars.h>
#include <utils/Log.h>
@@ -56,6 +57,8 @@ static struct {
jfieldID toolMajor;
jfieldID toolMinor;
jfieldID orientation;
+ jfieldID relativeX;
+ jfieldID relativeY;
} gPointerCoordsClassInfo;
static struct {
@@ -212,6 +215,12 @@ static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+ outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+ env->GetFloatField(pointerCoordsObj,
+ gPointerCoordsClassInfo.relativeX));
+ outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+ env->GetFloatField(pointerCoordsObj,
+ gPointerCoordsClassInfo.relativeY));
BitSet64 bits =
BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
@@ -261,6 +270,12 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
float rawY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y);
vec2 transformed = transform.transform(rawX, rawY);
+ float rawRelX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ float rawRelY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ // Apply only rotation and scale, not translation.
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ const vec2 transformedRel = transform.transform(rawRelX, rawRelY) - transformedOrigin;
+
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, transformed.x);
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, transformed.y);
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
@@ -277,6 +292,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR));
env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeX, transformedRel.x);
+ env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeY, transformedRel.y);
uint64_t outBits = 0;
BitSet64 bits = BitSet64(rawPointerCoords->bits);
@@ -289,6 +306,8 @@ static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointer
bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
+ bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_X);
+ bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_Y);
if (!bits.isEmpty()) {
uint32_t packedAxesCount = bits.count();
jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
@@ -378,8 +397,8 @@ static jlong android_view_MotionEvent_nativeInitialize(
flags, edgeFlags, metaState, buttonState,
static_cast<MotionClassification>(classification), transform, xPrecision,
yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE,
- AMOTION_EVENT_INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0,
+ INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, downTimeNanos, eventTimeNanos,
pointerCount, pointerProperties, rawPointerCoords);
return reinterpret_cast<jlong>(event.release());
@@ -872,6 +891,8 @@ int register_android_view_MotionEvent(JNIEnv* env) {
gPointerCoordsClassInfo.toolMajor = GetFieldIDOrDie(env, clazz, "toolMajor", "F");
gPointerCoordsClassInfo.toolMinor = GetFieldIDOrDie(env, clazz, "toolMinor", "F");
gPointerCoordsClassInfo.orientation = GetFieldIDOrDie(env, clazz, "orientation", "F");
+ gPointerCoordsClassInfo.relativeX = GetFieldIDOrDie(env, clazz, "relativeX", "F");
+ gPointerCoordsClassInfo.relativeY = GetFieldIDOrDie(env, clazz, "relativeY", "F");
clazz = FindClassOrDie(env, "android/view/MotionEvent$PointerProperties");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8d12df226ffe..b9233a087c33 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -62,6 +62,8 @@
namespace android {
+using gui::FocusRequest;
+
static void doThrowNPE(JNIEnv* env) {
jniThrowNullPointerException(env, NULL);
}
@@ -812,7 +814,7 @@ static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- transaction->setLayerStack(ctrl, layerStack);
+ transaction->setLayerStack(ctrl, ui::LayerStack::fromValue(layerStack));
}
static void nativeSetShadowRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
@@ -890,8 +892,9 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
}
static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) {
- sp<IBinder> token =
- SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId));
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(physicalDisplayId);
+ if (!id) return nullptr;
+ sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(*id);
return javaObjectForIBinder(env, token);
}
@@ -1010,7 +1013,18 @@ static void nativeSetDisplayLayerStack(JNIEnv* env, jclass clazz,
{
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->setDisplayLayerStack(token, layerStack);
+ transaction->setDisplayLayerStack(token, ui::LayerStack::fromValue(layerStack));
+ }
+}
+
+static void nativeSetDisplayFlags(JNIEnv* env, jclass clazz, jlong transactionObj, jobject tokenObj,
+ jint flags) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == NULL) return;
+
+ {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ transaction->setDisplayFlags(token, flags);
}
}
@@ -1889,6 +1903,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
(void*)nativeSetDisplayLayerStack },
+ {"nativeSetDisplayFlags", "(JLandroid/os/IBinder;I)V",
+ (void*)nativeSetDisplayFlags },
{"nativeSetDisplayProjection", "(JLandroid/os/IBinder;IIIIIIIII)V",
(void*)nativeSetDisplayProjection },
{"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V",
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
new file mode 100644
index 000000000000..ab88b537f96b
--- /dev/null
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "WindowInfosListener"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+#include "android_hardware_input_InputWindowHandle.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using gui::WindowInfo;
+
+namespace {
+
+static struct {
+ jclass clazz;
+ jmethodID onWindowInfosChanged;
+} gListenerClassInfo;
+
+static jclass gInputWindowHandleClass;
+
+struct WindowInfosListener : public gui::WindowInfosListener {
+ WindowInfosListener(JNIEnv* env, jobject listener)
+ : mListener(env->NewWeakGlobalRef(listener)) {}
+
+ void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onWindowInfoChanged.");
+
+ jobject listener = env->NewGlobalRef(mListener);
+ if (listener == nullptr) {
+ // Weak reference went out of scope
+ return;
+ }
+
+ jobjectArray jWindowHandlesArray =
+ env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
+ for (int i = 0; i < windowInfos.size(); i++) {
+ ScopedLocalRef<jobject>
+ jWindowHandle(env,
+ android_view_InputWindowHandle_fromWindowInfo(env,
+ windowInfos[i]));
+ env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
+ }
+
+ env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray);
+ env->DeleteGlobalRef(listener);
+
+ if (env->ExceptionCheck()) {
+ ALOGE("WindowInfosListener.onWindowInfosChanged() failed.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ }
+
+ ~WindowInfosListener() override {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mListener);
+ }
+
+private:
+ jweak mListener;
+};
+
+jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
+ WindowInfosListener* listener = new WindowInfosListener(env, obj);
+ listener->incStrong((void*)nativeCreate);
+ return reinterpret_cast<jlong>(listener);
+}
+
+void destroyNativeService(void* ptr) {
+ WindowInfosListener* listener = reinterpret_cast<WindowInfosListener*>(ptr);
+ listener->decStrong((void*)nativeCreate);
+}
+
+void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
+}
+
+void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+}
+
+static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
+}
+
+const JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ {"nativeCreate", "(Landroid/window/WindowInfosListener;)J", (void*)nativeCreate},
+ {"nativeRegister", "(J)V", (void*)nativeRegister},
+ {"nativeUnregister", "(J)V", (void*)nativeUnregister},
+ {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}};
+
+} // namespace
+
+int register_android_window_WindowInfosListener(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/window/WindowInfosListener", gMethods,
+ NELEM(gMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass clazz = env->FindClass("android/window/WindowInfosListener");
+ gListenerClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+ gListenerClassInfo.onWindowInfosChanged =
+ env->GetMethodID(gListenerClassInfo.clazz, "onWindowInfosChanged",
+ "([Landroid/view/InputWindowHandle;)V");
+
+ clazz = env->FindClass("android/view/InputWindowHandle");
+ gInputWindowHandleClass = MakeGlobalRefOrDie(env, clazz);
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_content_om_OverlayConfig.cpp b/core/jni/com_android_internal_content_om_OverlayConfig.cpp
index 6aa7c10509bc..b37269cc3c26 100644
--- a/core/jni/com_android_internal_content_om_OverlayConfig.cpp
+++ b/core/jni/com_android_internal_content_om_OverlayConfig.cpp
@@ -73,13 +73,13 @@ static jobjectArray createIdmap(JNIEnv* env, jclass /*clazz*/, jstring targetPat
}
if (result->status != 0) {
- LOG(ERROR) << "idmap2: " << result->stderr;
- return nullptr;
+ LOG(ERROR) << "idmap2: " << result->stderr_str;
+ return nullptr;
}
// Return the paths of the idmaps created or updated during the idmap invocation.
std::vector<std::string> idmap_paths;
- std::istringstream input(result->stdout);
+ std::istringstream input(result->stdout_str);
std::string path;
while (std::getline(input, path)) {
idmap_paths.push_back(path);
diff --git a/core/jni/include_vm/android_runtime/vm.h b/core/jni/include_vm/android_runtime/vm.h
new file mode 100644
index 000000000000..a6e7c162d6ed
--- /dev/null
+++ b/core/jni/include_vm/android_runtime/vm.h
@@ -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.
+ */
+
+#pragma once
+
+#include <jni.h>
+
+// Get the Java VM. If the symbol doesn't exist at runtime, it means libandroid_runtime
+// is not loaded in the current process. If the symbol exists but it returns nullptr, it
+// means JavaVM is not yet started.
+extern "C" JavaVM* AndroidRuntimeGetJavaVM();
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c3d159659622..6bc00e2281ef 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -771,6 +771,8 @@ message GlobalSettingsProto {
optional SettingProto power_manager_constants = 93;
reserved 94; // Used to be priv_app_oob_enabled
+ optional SettingProto power_button_long_press_duration_ms = 154 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
message PrepaidSetup {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -1063,5 +1065,5 @@ message GlobalSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 154;
+ // Next tag = 155;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 1bba12ff7fa6..ba4a5b0ce222 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -79,7 +79,7 @@ message SecureSettingsProto {
optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto button_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_magnification_capability = 36 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // Settings for accessibility button mode (navigation bar or floating action menu).
+ // Settings for accessibility button related config
optional SettingProto accessibility_button_mode = 37 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_size = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/OWNERS b/core/proto/android/server/OWNERS
new file mode 100644
index 000000000000..72d39bfb3cd6
--- /dev/null
+++ b/core/proto/android/server/OWNERS
@@ -0,0 +1 @@
+per-file window*.proto = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto
index 1fc4a01936b1..0ce39abfe352 100644
--- a/core/proto/android/server/accessibilitytrace.proto
+++ b/core/proto/android/server/accessibilitytrace.proto
@@ -46,17 +46,18 @@ message AccessibilityTraceProto {
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
optional string calendar_time = 2;
-
- optional string process_name = 3;
- optional string thread_id_name = 4;
+ repeated string logging_type = 3;
+ optional string process_name = 4;
+ optional string thread_id_name = 5;
/* where the trace originated */
- optional string where = 5;
+ optional string where = 6;
- optional string calling_pkg = 6;
- optional string calling_params = 7;
- optional string calling_stacks = 8;
+ optional string calling_pkg = 7;
+ optional string calling_params = 8;
+ optional string calling_stacks = 9;
- optional AccessibilityDumpProto accessibility_service = 9;
- optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10;
+ optional AccessibilityDumpProto accessibility_service = 10;
+ optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 11;
+ optional string cpu_stats = 12;
}
diff --git a/core/proto/android/server/inputmethod/OWNERS b/core/proto/android/server/inputmethod/OWNERS
new file mode 100644
index 000000000000..5deb2ce8f24b
--- /dev/null
+++ b/core/proto/android/server/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index fa1e9d4afcdf..0121bff3e7ef 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -278,7 +278,7 @@ message PinnedTaskControllerProto {
message TaskProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional WindowContainerProto window_container = 1;
+ optional WindowContainerProto window_container = 1 [deprecated=true];
optional int32 id = 2;
reserved 3; // activity
optional bool fills_parent = 4;
@@ -295,12 +295,12 @@ message TaskProto {
optional string real_activity = 13;
optional string orig_activity = 14;
- optional int32 display_id = 15;
+ optional int32 display_id = 15 [deprecated=true];
optional int32 root_task_id = 16;
- optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+ optional int32 activity_type = 17 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType", deprecated=true] ;
optional int32 resize_mode = 18 [(.android.typedef) = "android.appwidget.AppWidgetProviderInfo.ResizeModeFlags"];
- optional int32 min_width = 19;
- optional int32 min_height = 20;
+ optional int32 min_width = 19 [deprecated=true];
+ optional int32 min_height = 20 [deprecated=true];
optional .android.graphics.RectProto adjusted_bounds = 21;
optional .android.graphics.RectProto last_non_fullscreen_bounds = 22;
@@ -312,6 +312,18 @@ message TaskProto {
optional bool created_by_organizer = 28;
optional string affinity = 29;
optional bool has_child_pip_activity = 30;
+ optional TaskFragmentProto task_fragment = 31;
+}
+
+/* represents TaskFragment */
+message TaskFragmentProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional WindowContainerProto window_container = 1;
+ optional int32 display_id = 2;
+ optional int32 activity_type = 3 [(.android.typedef) = "android.app.WindowConfiguration.ActivityType"];
+ optional int32 min_width = 4;
+ optional int32 min_height = 5;
}
/* represents ActivityRecordProto */
@@ -493,6 +505,8 @@ message WindowContainerChildProto {
optional WindowTokenProto window_token = 7;
/* represents a WindowState child */
optional WindowStateProto window = 8;
+ /* represents a WindowState child */
+ optional TaskFragmentProto task_fragment = 9;
}
/* represents ConfigurationContainer */
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 45f8c132fbf0..97097ff91219 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -72,6 +72,7 @@ message UsbHandlerProto {
optional bool adb_enabled = 14;
optional string kernel_state = 15;
optional string kernel_function_list = 16;
+ optional string uevent = 17;
}
message UsbAccessoryProto {
@@ -315,6 +316,7 @@ message UsbProfileGroupSettingsManagerProto {
optional int32 parent_user_id = 1;
repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+ optional string intent = 4;
}
message UsbSettingsDevicePreferenceProto {
diff --git a/core/proto/android/view/OWNERS b/core/proto/android/view/OWNERS
new file mode 100644
index 000000000000..d72a0f00c752
--- /dev/null
+++ b/core/proto/android/view/OWNERS
@@ -0,0 +1,3 @@
+include /services/core/java/com/android/server/wm/OWNERS
+
+per-file ime*.proto = file:/core/java/android/view/inputmethod/OWNERS
diff --git a/core/proto/android/view/inputmethod/OWNERS b/core/proto/android/view/inputmethod/OWNERS
new file mode 100644
index 000000000000..5deb2ce8f24b
--- /dev/null
+++ b/core/proto/android/view/inputmethod/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bad79eb13749..f924229a8fa4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -202,6 +202,8 @@
<protected-broadcast
android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
@@ -2591,7 +2593,7 @@
third-party apps.
-->
<permission android:name="android.permission.MANAGE_DOCUMENTS"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- Allows an application to manage access to crates, usually as part
of a crates picker.
@@ -2608,7 +2610,7 @@
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.CACHE_CONTENT"
- android:protectionLevel="signature|documenter" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi @hide
Allows an application to aggressively allocate disk space.
@@ -2754,7 +2756,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to change to remove/kill tasks -->
<permission android:name="android.permission.REMOVE_TASKS"
- android:protectionLevel="signature|documenter|recents" />
+ android:protectionLevel="signature|recents|role" />
<!-- @deprecated Use MANAGE_ACTIVITY_TASKS instead.
@SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
@@ -3468,6 +3470,13 @@
<permission android:name="android.permission.TRIGGER_SHELL_BUGREPORT"
android:protectionLevel="signature" />
+ <!-- Allows an application to trigger profcollect report upload via shell.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to be the status bar. Currently used only by SystemUI.apk
@hide
@SystemApi -->
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 7a8da36d8a7d..684202bdb29e 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -25,3 +25,6 @@ svetoslavganov@google.com
toddke@google.com
tsuji@google.com
yamasani@google.com
+
+# Multiuser
+per-file res/xml/config_user_types.xml = file:/MULTIUSER_OWNERS
diff --git a/core/res/res/drawable-watch/global_action_item_divider.xml b/core/res/res/drawable-watch/global_action_item_divider.xml
new file mode 100644
index 000000000000..89f3ef415e79
--- /dev/null
+++ b/core/res/res/drawable-watch/global_action_item_divider.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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">
+ <solid android:color="@android:color/transparent" />
+ <size android:height="4dp" />
+</shape> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_grey_background.xml b/core/res/res/drawable-watch/global_actions_item_grey_background.xml
new file mode 100644
index 000000000000..2cff81d63eda
--- /dev/null
+++ b/core/res/res/drawable-watch/global_actions_item_grey_background.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="?attr/colorControlHighlight">
+ <item android:drawable="@drawable/global_actions_item_grey_background_shape"/>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
new file mode 100644
index 000000000000..f2df319c888d
--- /dev/null
+++ b/core/res/res/drawable-watch/global_actions_item_grey_background_shape.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="26dp"/>
+ <solid android:color="@color/wear_material_grey_900"/>
+</shape> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_red_background.xml b/core/res/res/drawable-watch/global_actions_item_red_background.xml
new file mode 100644
index 000000000000..4be8f2df1cf7
--- /dev/null
+++ b/core/res/res/drawable-watch/global_actions_item_red_background.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="?attr/colorControlHighlight">
+ <item android:drawable="@drawable/global_actions_item_red_background_shape"/>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
new file mode 100644
index 000000000000..b556a1bdaa09
--- /dev/null
+++ b/core/res/res/drawable-watch/global_actions_item_red_background_shape.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="26dp"/>
+ <solid android:color="@color/wear_material_red_400"/>
+</shape> \ No newline at end of file
diff --git a/core/res/res/drawable-watch/ic_lock_bugreport.xml b/core/res/res/drawable-watch/ic_lock_bugreport.xml
new file mode 100644
index 000000000000..66dd392d685f
--- /dev/null
+++ b/core/res/res/drawable-watch/ic_lock_bugreport.xml
@@ -0,0 +1,31 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="@android:color/white">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,10V8h-2.81c-0.45,-0.78 -1.07,-1.46 -1.82,-1.96L17,4.41L15.59,3l-2.17,2.17c-0.03,-0.01 -0.05,-0.01 -0.08,-0.01c-0.16,-0.04 -0.32,-0.06 -0.49,-0.09c-0.06,-0.01 -0.11,-0.02 -0.17,-0.03C12.46,5.02 12.23,5 12,5h0c-0.49,0 -0.97,0.07 -1.42,0.18l0.02,-0.01L8.41,3L7,4.41l1.62,1.63l0.01,0C7.88,6.54 7.26,7.22 6.81,8H4v2h2.09C6.03,10.33 6,10.66 6,11v1H4v2h2v1c0,0.34 0.04,0.67 0.09,1H4v2h2.81c1.04,1.79 2.97,3 5.19,3h0c2.22,0 4.15,-1.21 5.19,-3H20v-2h-2.09l0,0c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1l0,0H20zM16,15c0,2.21 -1.79,4 -4,4c-2.21,0 -4,-1.79 -4,-4v-4c0,-2.21 1.79,-4 4,-4h0c2.21,0 4,1.79 4,4V15z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,14h4v2h-4z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M10,10h4v2h-4z"/>
+</vector>
diff --git a/core/res/res/drawable-watch/ic_lock_power_off.xml b/core/res/res/drawable-watch/ic_lock_power_off.xml
new file mode 100644
index 000000000000..34bc88cad19a
--- /dev/null
+++ b/core/res/res/drawable-watch/ic_lock_power_off.xml
@@ -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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/white">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M11,2h2v10h-2zM18.37,5.64l-1.41,1.41c2.73,2.73 2.72,7.16 -0.01,9.89 -2.73,2.73 -7.17,2.73 -9.89,0.01 -2.73,-2.73 -2.74,-7.18 -0.01,-9.91l-1.41,-1.4c-3.51,3.51 -3.51,9.21 0.01,12.73 3.51,3.51 9.21,3.51 12.72,-0.01 3.51,-3.51 3.51,-9.2 0,-12.72z"/>
+</vector>
diff --git a/core/res/res/drawable-watch/ic_restart.xml b/core/res/res/drawable-watch/ic_restart.xml
new file mode 100644
index 000000000000..24d7c347f673
--- /dev/null
+++ b/core/res/res/drawable-watch/ic_restart.xml
@@ -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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@android:color/white">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02c-2.83,-0.48 -5,-2.94 -5,-5.91zM20,13c0,-4.42 -3.58,-8 -8,-8 -0.06,0 -0.12,0.01 -0.18,0.01l1.09,-1.09L11.5,2.5 8,6l3.5,3.5 1.41,-1.41 -1.08,-1.08c0.06,0 0.12,-0.01 0.17,-0.01 3.31,0 6,2.69 6,6 0,2.97 -2.17,5.43 -5,5.91v2.02c3.95,-0.49 7,-3.85 7,-7.93z"/>
+</vector>
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 29c51f2a33c9..7c09fb889c48 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -24,9 +24,9 @@
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/notification_action_button_radius" />
- <padding android:left="12dp"
+ <padding android:left="16dp"
android:top="@dimen/button_padding_vertical_material"
- android:right="12dp"
+ android:right="16dp"
android:bottom="@dimen/button_padding_vertical_material" />
<solid android:color="@color/white" />
</shape>
diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml
index 569e5948e2e0..2e7b62ceb723 100644
--- a/core/res/res/layout-car/car_alert_dialog.xml
+++ b/core/res/res/layout-car/car_alert_dialog.xml
@@ -54,7 +54,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/text_view_start_margin"
android:layout_marginEnd="@dimen/text_view_end_margin"
- style="@style/CarBody2"/>
+ style="@style/CarBody4"/>
<!-- we don't need this spacer, but the id needs to be here for compatibility -->
<Space
diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
index 277b0dcca657..4f815b887088 100644
--- a/core/res/res/layout-car/car_alert_dialog_button_bar.xml
+++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml
@@ -36,6 +36,9 @@
<Button
android:id="@+id/button3"
style="@style/CarAction1"
+ android:minWidth="@dimen/car_touch_target_size"
+ android:paddingStart="@dimen/car_padding_2"
+ android:paddingEnd="@dimen/car_padding_2"
android:background="@drawable/car_dialog_button_background"
android:layout_marginRight="@dimen/button_end_margin"
android:layout_width="wrap_content"
@@ -44,6 +47,9 @@
<Button
android:id="@+id/button2"
style="@style/CarAction1"
+ android:minWidth="@dimen/car_touch_target_size"
+ android:paddingStart="@dimen/car_padding_2"
+ android:paddingEnd="@dimen/car_padding_2"
android:background="@drawable/car_dialog_button_background"
android:layout_marginRight="@dimen/button_end_margin"
android:layout_width="wrap_content"
@@ -52,6 +58,9 @@
<Button
android:id="@+id/button1"
style="@style/CarAction1"
+ android:minWidth="@dimen/car_touch_target_size"
+ android:paddingStart="@dimen/car_padding_2"
+ android:paddingEnd="@dimen/car_padding_2"
android:background="@drawable/car_dialog_button_background"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_layout_height" />
diff --git a/core/res/res/layout-watch/global_actions.xml b/core/res/res/layout-watch/global_actions.xml
index c50d3f771d1a..b7479d848465 100644
--- a/core/res/res/layout-watch/global_actions.xml
+++ b/core/res/res/layout-watch/global_actions.xml
@@ -16,7 +16,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:paddingTop="?attr/dialogPreferredPadding"
- android:paddingBottom="?attr/dialogPreferredPadding"
+ android:showDividers="middle"
+ android:divider="@drawable/global_action_item_divider"
android:orientation="vertical"/>
diff --git a/core/res/res/layout-watch/global_actions_item.xml b/core/res/res/layout-watch/global_actions_item.xml
index ae87e63c4821..3d3f34136801 100644
--- a/core/res/res/layout-watch/global_actions_item.xml
+++ b/core/res/res/layout-watch/global_actions_item.xml
@@ -13,52 +13,35 @@
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:gravity="center"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <LinearLayout
- android:duplicateParentState="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxHeight="?attr/listPreferredItemHeightSmall"
- android:gravity="center_vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
- android:background="?attr/selectableItemBackground"
- android:clipToPadding="false">
-
- <ImageView android:id="@+id/icon"
- android:duplicateParentState="true"
- android:background="@drawable/global_action_icon_background"
- android:scaleType="centerInside"
- android:padding="8dp"
- android:gravity="center"
- android:layout_marginEnd="8dp"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- android:layout_width="40dp"
- android:layout_height="40dp"/>
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:minHeight="52dp"
+ android:minWidth="172dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="12dp"
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp"
+ android:background="@drawable/global_actions_item_grey_background">
- <FrameLayout android:id="@+id/widget_frame"
- android:duplicateParentState="true"
- android:gravity="center"
- android:visibility="gone"
- android:orientation="horizontal"
- android:layout_marginEnd="8dp"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- android:layout_width="40dp"
- android:layout_height="40dp"/>
+ <ImageView android:id="@+id/icon"
+ android:duplicateParentState="true"
+ android:scaleType="centerInside"
+ android:gravity="center"
+ android:layout_marginEnd="8dp"
+ android:layout_width="24dp"
+ android:layout_height="24dp"/>
- <TextView android:id="@+id/message"
- android:duplicateParentState="true"
- android:textAppearance="?attr/textAppearanceListItem"
- android:ellipsize="end"
- android:layout_weight="1"
- android:layout_marginTop="4dp"
- android:layout_marginBottom="4dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
-</FrameLayout>
+ <TextView android:id="@+id/message"
+ android:duplicateParentState="true"
+ android:ellipsize="end"
+ android:textSize="15sp"
+ android:letterSpacing="0.013"
+ android:fadingEdgeLength="12dp"
+ android:textColor="@android:color/white"
+ android:layout_weight="1"
+ android:fontFamily="google-sans-text-medium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 614779a79253..c6983ae5e045 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -93,7 +93,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 2991b1706a64..95ddc2e4ea79 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -93,7 +93,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 3564f9755a5d..bef1d0b319b4 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -119,7 +119,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_header_separating_margin"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 4920c7953d65..7b834a4eca11 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -21,7 +21,7 @@
android:layout_height="@dimen/notification_text_height"
android:layout_gravity="top"
android:layout_marginTop="@dimen/notification_text_margin_top"
- android:ellipsize="marquee"
+ android:ellipsize="end"
android:fadingEdge="horizontal"
android:gravity="top"
android:singleLine="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 6946b3c07652..56d9a84f93f1 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie gemerk nie"</string>
<string name="selected" msgid="6614607926197755875">"gekies"</string>
<string name="not_selected" msgid="410652016565864475">"nie gekies nie"</string>
+ <string name="in_progress" msgid="2149208189184319441">"aan die gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Voltooi handeling met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Voltooi handeling met gebruik van %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Voltooi handeling"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9ab2f1939c84..39a3d6e02b99 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ምልክት አልተደረገበትም"</string>
<string name="selected" msgid="6614607926197755875">"ተመርጧል"</string>
<string name="not_selected" msgid="410652016565864475">"አልተመረጠም"</string>
+ <string name="in_progress" msgid="2149208189184319441">"በሂደት ላይ"</string>
<string name="whichApplication" msgid="5432266899591255759">"... በመጠቀም ድርጊቱን አጠናቅ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sን ተጠቅመው እርምጃ ያጠናቅቁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"እርምጃውን አጠናቅቅ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0310b181398d..e854bd12438e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -341,7 +341,7 @@
<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_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عال ويمكن استكشاف الشاشة باستخدام الإيماءات."</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>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"التحكم في تكبير الشاشة"</string>
@@ -1262,6 +1262,7 @@
<string name="not_checked" msgid="7972320087569023342">"لم يتم وضع علامة"</string>
<string name="selected" msgid="6614607926197755875">"محدّد"</string>
<string name="not_selected" msgid="410652016565864475">"غير محدّد"</string>
+ <string name="in_progress" msgid="2149208189184319441">"قيد التقدّم"</string>
<string name="whichApplication" msgid="5432266899591255759">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏إكمال الإجراء باستخدام %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"إكمال الإجراء"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 65e4664589f3..a398c350b626 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিক চিহ্ন দিয়া হোৱা নাই"</string>
<string name="selected" msgid="6614607926197755875">"বাছনি কৰা"</string>
<string name="not_selected" msgid="410652016565864475">"বাছনি কৰা হোৱা নাই"</string>
+ <string name="in_progress" msgid="2149208189184319441">"চলি আছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এয়া ব্যৱহাৰ কৰি কার্য সম্পূর্ণ কৰক"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যৱহাৰ কৰি কাৰ্যটো সম্পূৰ্ণ কৰক"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"কাৰ্য সম্পূৰ্ণ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index bab05ad489b3..00145ba10009 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"yoxlanılmayıb"</string>
<string name="selected" msgid="6614607926197755875">"seçilib"</string>
<string name="not_selected" msgid="410652016565864475">"seçilməyib"</string>
+ <string name="in_progress" msgid="2149208189184319441">"davam edir"</string>
<string name="whichApplication" msgid="5432266899591255759">"Əməliyyatı tamamlayın:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s istifadə edərək əməliyyatı tamamlayın"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Əməliyyatı tamamlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4179ab03dac3..65bfec1e9bf2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"izabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije izabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dovrši radnju preko"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Završi radnju"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2d94ffe2b0eb..a4726cf174cd 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не пазначана"</string>
<string name="selected" msgid="6614607926197755875">"выбраны"</string>
<string name="not_selected" msgid="410652016565864475">"не выбраны"</string>
+ <string name="in_progress" msgid="2149208189184319441">"выконваецца"</string>
<string name="whichApplication" msgid="5432266899591255759">"Завяршыць дзеянне з дапамогай"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завяршыць дзеянне з дапамогай %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завяршыць дзеянне"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 40807e5650fa..c74f4889cbc0 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"без отметка"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в ход"</string>
<string name="whichApplication" msgid="5432266899591255759">"Изпълняване на действието чрез"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завършване на действието посредством %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Изпълняване на действието"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2f68cdab4a03..584fb613e735 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"টিকচিহ্ন দেওয়া নেই"</string>
<string name="selected" msgid="6614607926197755875">"বেছে নেওয়া হয়েছে"</string>
<string name="not_selected" msgid="410652016565864475">"বেছে নেওয়া হয়নি"</string>
+ <string name="in_progress" msgid="2149208189184319441">"কাজ চলছে"</string>
<string name="whichApplication" msgid="5432266899591255759">"এটি ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ব্যবহার করে ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ক্রিয়াকলাপ সম্পূর্ণ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3ebb7ccc8471..b6497abb33be 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije označeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Završite radnju pomoću aplikacije"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Završite radnju pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvršiti akciju"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 880ad1804498..507fecd8460a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionat"</string>
<string name="selected" msgid="6614607926197755875">"seleccionat"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'acció mitjançant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completa l\'acció amb %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa l\'acció"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a8ae213b6180..fa1a66539534 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nevybráno"</string>
<string name="selected" msgid="6614607926197755875">"vybráno"</string>
<string name="not_selected" msgid="410652016565864475">"nevybráno"</string>
+ <string name="in_progress" msgid="2149208189184319441">"probíhá"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončit akci pomocí aplikace"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončit akci pomocí aplikace %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončit akci"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 45f02e363fe9..0c26400158f4 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"slået fra"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"i gang"</string>
<string name="whichApplication" msgid="5432266899591255759">"Brug"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Gennemfør handling ved hjælp af %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Afslut handling"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 865f02663299..4b1c544d8442 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"deaktiviert"</string>
<string name="selected" msgid="6614607926197755875">"ausgewählt"</string>
<string name="not_selected" msgid="410652016565864475">"nicht ausgewählt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"Noch nicht abgeschlossen"</string>
<string name="whichApplication" msgid="5432266899591255759">"Aktion durchführen mit"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Aktion mit %1$s abschließen"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Abschließen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 67bd43325cf7..6fe7d63537c2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"μη επιλεγμένο"</string>
<string name="selected" msgid="6614607926197755875">"επιλεγμένο"</string>
<string name="not_selected" msgid="410652016565864475">"μη επιλεγμένο"</string>
+ <string name="in_progress" msgid="2149208189184319441">"σε εξέλιξη"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ολοκλήρωση ενέργειας με τη χρήση"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ολοκληρωμένη ενέργεια με χρήση %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ολοκλήρωση ενέργειας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 8cf968d503a7..13725ded27f4 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index b772335a6438..920576344d36 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not checked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e1e81870cfa3..45709f0c10e5 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 892041c2332d..8749d05a9ff0 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"not ticked"</string>
<string name="selected" msgid="6614607926197755875">"selected"</string>
<string name="not_selected" msgid="410652016565864475">"not selected"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In progress"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete action using"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Complete action using %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Complete action"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index a7ad2b6dc16a..996a33244a8d 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎not checked‎‏‎‎‏‎"</string>
<string name="selected" msgid="6614607926197755875">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎selected‎‏‎‎‏‎"</string>
<string name="not_selected" msgid="410652016565864475">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎not selected‎‏‎‎‏‎"</string>
+ <string name="in_progress" msgid="2149208189184319441">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎in progress‎‏‎‎‏‎"</string>
<string name="whichApplication" msgid="5432266899591255759">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎Complete action using‎‏‎‎‏‎"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎Complete action using %1$s‎‏‎‎‏‎"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎Complete action‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 5206c484b893..569cfcffabf6 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"desactivado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar la acción mediante"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9a29addc10b0..716b2c85ce2b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"no seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"no seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar acción utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar acción con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 4dd7d9469f4c..6153a8328174 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"märkimata"</string>
<string name="selected" msgid="6614607926197755875">"valitud"</string>
<string name="not_selected" msgid="410652016565864475">"pole valitud"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pooleli"</string>
<string name="whichApplication" msgid="5432266899591255759">"Lõpetage toiming rakendusega"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Toimingu lõpetamine, kasutades rakendust %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Vii toiming lõpule"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d4705c100a8f..a56d19aaa701 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"markatu gabe"</string>
<string name="selected" msgid="6614607926197755875">"hautatuta"</string>
<string name="not_selected" msgid="410652016565864475">"hautatu gabe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"abian"</string>
<string name="whichApplication" msgid="5432266899591255759">"Gauzatu ekintza hau erabilita:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Osatu ekintza %1$s erabiliz"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Osatu ekintza"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fcca1b39330a..b0bc2c29eeab 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"بدون علامت"</string>
<string name="selected" msgid="6614607926197755875">"انتخاب شده"</string>
<string name="not_selected" msgid="410652016565864475">"انتخاب نشده"</string>
+ <string name="in_progress" msgid="2149208189184319441">"درحال انجام"</string>
<string name="whichApplication" msgid="5432266899591255759">"تکمیل کنش بااستفاده از"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏تکمیل کنش بااستفاده از %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"تکمیل عملکرد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5998269aae48..5c7b4a736133 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ei valittu"</string>
<string name="selected" msgid="6614607926197755875">"valittu"</string>
<string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
+ <string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
<string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 860e10bcff4d..83a9ac21912d 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"non coché"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Continuer avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 55fa1453caf8..4fec45beb470 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"désactivé"</string>
<string name="selected" msgid="6614607926197755875">"sélectionné"</string>
<string name="not_selected" msgid="410652016565864475">"non sélectionné"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en cours"</string>
<string name="whichApplication" msgid="5432266899591255759">"Continuer avec"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Terminer l\'action avec %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Terminer l\'action"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 76f54ff26b09..cdbe604d0ce3 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"non seleccionado"</string>
<string name="selected" msgid="6614607926197755875">"elemento seleccionado"</string>
<string name="not_selected" msgid="410652016565864475">"elemento non seleccionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"en curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completar a acción usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completar a acción usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completar acción"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index e473ab670e75..710e94324693 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ચેક કર્યું નથી"</string>
<string name="selected" msgid="6614607926197755875">"પસંદ કરેલી"</string>
<string name="not_selected" msgid="410652016565864475">"પસંદ નહીં કરેલી"</string>
+ <string name="in_progress" msgid="2149208189184319441">"પ્રક્રિયા ચાલુ છે"</string>
<string name="whichApplication" msgid="5432266899591255759">"આના ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ઉપયોગથી ક્રિયા પૂર્ણ કરો"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ક્રિયા પૂર્ણ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index dcf357b2307a..b5261e03b62f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"बंद है"</string>
<string name="selected" msgid="6614607926197755875">"चुना गया"</string>
<string name="not_selected" msgid="410652016565864475">"नहीं चुना गया"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी है"</string>
<string name="whichApplication" msgid="5432266899591255759">"इसका इस्तेमाल करके कार्रवाई को पूरा करें"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s का उपयोग करके कार्रवाई पूरी करें"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"कार्रवाई पूरी करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 9aa3350d6ecd..281cf8504280 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nije potvrđeno"</string>
<string name="selected" msgid="6614607926197755875">"odabrano"</string>
<string name="not_selected" msgid="410652016565864475">"nije odabrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"u tijeku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Radnju dovrši pomoću stavke"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dovršavanje radnje pomoću aplikacije %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dovrši radnju"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 908b77956b44..9e0232da0100 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nincs kiválasztva"</string>
<string name="selected" msgid="6614607926197755875">"kiválasztva"</string>
<string name="not_selected" msgid="410652016565864475">"nincs kiválasztva"</string>
+ <string name="in_progress" msgid="2149208189184319441">"folyamatban"</string>
<string name="whichApplication" msgid="5432266899591255759">"Művelet végrehajtása a következővel:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Művelet elvégzése a(z) %1$s segítségével"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Művelet végrehajtása"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a7992f968ea0..6fdf577d609a 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"նշված չէ"</string>
<string name="selected" msgid="6614607926197755875">"ընտրված է"</string>
<string name="not_selected" msgid="410652016565864475">"ընտրված չէ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ընթացքում է"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ավարտել գործողությունը` օգտագործելով"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Եզրափակել գործողությունը՝ օգտագործելով %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ավարտել գործողությունը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 17382c453f7f..1988b4084fb3 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -326,12 +326,12 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"melakukan dan mengelola panggilan telepon"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensor tubuh"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"mengakses data sensor tentang tanda-tanda vital"</string>
- <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Mengambil konten jendela"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten jendela tempat Anda berinteraksi."</string>
+ <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Membaca konten di jendela"</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten di jendela yang sedang Anda buka."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Mengaktifkan Jelajahi dengan Sentuhan"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketuk akan diucapkan dengan jelas dan layar dapat dijelajahi menggunakan gestur."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Mengamati teks yang Anda ketik"</string>
- <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Termasuk data pribadi, seperti nomor kartu kredit dan sandi."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Mengontrol perbesaran layar"</string>
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Mengontrol tingkat zoom dan pemosisian layar."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Melakukan isyarat"</string>
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak dicentang"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 93e25a6b014e..abd7735b7e67 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ekki valið"</string>
<string name="selected" msgid="6614607926197755875">"valið"</string>
<string name="not_selected" msgid="410652016565864475">"ekki valið"</string>
+ <string name="in_progress" msgid="2149208189184319441">"í gangi"</string>
<string name="whichApplication" msgid="5432266899591255759">"Ljúka aðgerð með"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Ljúka aðgerð með %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Ljúka aðgerð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6919a3dc6c7c..d44dcdf96e97 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -327,13 +327,13 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensori del corpo"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperare contenuti della finestra"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esaminare i contenuti di una finestra con cui interagisci."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esamina i contenuti di una finestra con cui interagisci."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Attivare Esplora al tocco"</string>
- <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo utilizzando i gesti."</string>
+ <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo con i gesti."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Osservare il testo digitato"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
<string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controllare l\'ingrandimento del display"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla il livello di zoom e la posizione del display."</string>
+ <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla la posizione e il livello di zoom del display."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Eseguire gesti"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string>
<string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gesti con sensore di impronte"</string>
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"deselezionato"</string>
<string name="selected" msgid="6614607926197755875">"selezionato"</string>
<string name="not_selected" msgid="410652016565864475">"non selezionato"</string>
+ <string name="in_progress" msgid="2149208189184319441">"In corso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Completa l\'azione con"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Completamento azione con %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Completa azione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 716e97c70768..2daf5b162c3f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"לא מסומן"</string>
<string name="selected" msgid="6614607926197755875">"נבחר"</string>
<string name="not_selected" msgid="410652016565864475">"לא נבחר"</string>
+ <string name="in_progress" msgid="2149208189184319441">"בתהליך"</string>
<string name="whichApplication" msgid="5432266899591255759">"השלמת הפעולה באמצעות"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏השלמת הפעולה באמצעות %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"להשלמת הפעולה"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 30a62e3d8df0..1f9f4ab828b8 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"OFF"</string>
<string name="selected" msgid="6614607926197755875">"選択済み"</string>
<string name="not_selected" msgid="410652016565864475">"未選択"</string>
+ <string name="in_progress" msgid="2149208189184319441">"進行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"アプリケーションを選択"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sを使用してアクションを完了"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"アクションを実行"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 789e2794e083..fb17c515cbfd 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"არ არის მონიშნული"</string>
<string name="selected" msgid="6614607926197755875">"არჩეულია"</string>
<string name="not_selected" msgid="410652016565864475">"არ არის არჩეული"</string>
+ <string name="in_progress" msgid="2149208189184319441">"მიმდინარეობს"</string>
<string name="whichApplication" msgid="5432266899591255759">"რა გამოვიყენოთ?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"მოქმედების %1$s-ის გამოყენებით დასრულება"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"მოქმედების დასრულება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6e8281326606..d9fde8fba397 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгіленбеген"</string>
<string name="selected" msgid="6614607926197755875">"таңдалған"</string>
<string name="not_selected" msgid="410652016565864475">"таңдалмаған"</string>
+ <string name="in_progress" msgid="2149208189184319441">"орындалуда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Әрекетті аяқтау"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Әрекетті %1$s қолданбасын пайдаланып аяқтау"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Әрекетті аяқтау"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index d56a55ded4a9..716e810c62e1 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"មិន​បាន​ធីក​"</string>
<string name="selected" msgid="6614607926197755875">"បាន​ជ្រើសរើស"</string>
<string name="not_selected" msgid="410652016565864475">"មិនបានជ្រើសរើសទេ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"កំពុងដំណើរការ"</string>
<string name="whichApplication" msgid="5432266899591255759">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"បញ្ចប់​សកម្មភាព​ដោយ​ប្រើ​ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"បញ្ចប់សកម្មភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ed2f964d29e7..4d328d353abc 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ"</string>
<string name="selected" msgid="6614607926197755875">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="not_selected" msgid="410652016565864475">"ಆಯ್ಕೆಮಾಡಲಾಗಿಲ್ಲ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ಬಳಸಿಕೊಂಡು ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ಕ್ರಿಯೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ea8fe228896a..a0054c077e0b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"선택 안함"</string>
<string name="selected" msgid="6614607926197755875">"선택됨"</string>
<string name="not_selected" msgid="410652016565864475">"선택되지 않음"</string>
+ <string name="in_progress" msgid="2149208189184319441">"진행 중"</string>
<string name="whichApplication" msgid="5432266899591255759">"작업을 수행할 때 사용하는 애플리케이션"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s을(를) 사용하여 작업 완료"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"작업 완료"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 421608687033..45624a4918ab 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"белгилене элек"</string>
<string name="selected" msgid="6614607926197755875">"тандалган"</string>
<string name="not_selected" msgid="410652016565864475">"тандалган жок"</string>
+ <string name="in_progress" msgid="2149208189184319441">"аткарылууда"</string>
<string name="whichApplication" msgid="5432266899591255759">"Кайсынысын колдоносуз?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s аркылуу аракетти аягына чейин чыгаруу"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Аракетти аягына чыгаруу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index b1c0d591c4fb..7544c393fe8d 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ບໍ່ໄດ້ໝາຍຖືກ"</string>
<string name="selected" msgid="6614607926197755875">"ເລືອກແລ້ວ"</string>
<string name="not_selected" msgid="410652016565864475">"ບໍ່ໄດ້ເລືອກແລ້ວ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ກຳລັງດຳເນີນການ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ດຳເນີນການໂດຍໃຊ້"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ສຳ​ເລັດ​​​ການ​ດຳ​ເນີນ​ການ​ໂດຍ​ໃຊ້ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ສຳເລັດຄຳສັ່ງ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 1879dcc85513..18ac1b1e37e0 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nepažymėta"</string>
<string name="selected" msgid="6614607926197755875">"pasirinkta"</string>
<string name="not_selected" msgid="410652016565864475">"nepasirinkta"</string>
+ <string name="in_progress" msgid="2149208189184319441">"vykdoma"</string>
<string name="whichApplication" msgid="5432266899591255759">"Užbaigti veiksmą naudojant"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Užbaigti veiksmą naudojant %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Užbaigti veiksmą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 15eda966c48c..1799e388de33 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nav atzīmēts"</string>
<string name="selected" msgid="6614607926197755875">"atlasīts"</string>
<string name="not_selected" msgid="410652016565864475">"nav atlasīts"</string>
+ <string name="in_progress" msgid="2149208189184319441">"notiek apstrāde"</string>
<string name="whichApplication" msgid="5432266899591255759">"Izvēlieties lietotni"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Pabeigt darbību, izmantojot %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Pabeigt darbību"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b29d49ef8dbe..39c3b8ae90bc 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"не е штиклирано"</string>
<string name="selected" msgid="6614607926197755875">"избрано"</string>
<string name="not_selected" msgid="410652016565864475">"не е избрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"во тек"</string>
<string name="whichApplication" msgid="5432266899591255759">"Активирај со"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Остварете го дејството со %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши го дејството"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 44172ea0438e..d6491b728a90 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"പരിശോധിക്കാത്തത്"</string>
<string name="selected" msgid="6614607926197755875">"തിരഞ്ഞെടുത്തു"</string>
<string name="not_selected" msgid="410652016565864475">"തിരഞ്ഞെടുത്തിട്ടില്ല"</string>
+ <string name="in_progress" msgid="2149208189184319441">"പുരോഗതിയിലാണ്"</string>
<string name="whichApplication" msgid="5432266899591255759">"പൂർണ്ണമായ പ്രവർത്തനം ഉപയോഗിക്കുന്നു"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ഉപയോഗിച്ച് പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"പ്രവർത്തനം പൂർത്തിയാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 424401584b83..e17b884979b2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"тэмдэглээгүй"</string>
<string name="selected" msgid="6614607926197755875">"сонгосон"</string>
<string name="not_selected" msgid="410652016565864475">"сонгоогүй"</string>
+ <string name="in_progress" msgid="2149208189184319441">"үргэлжилж байна"</string>
<string name="whichApplication" msgid="5432266899591255759">"Үйлдлийг дуусгах"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ашиглан үйлдлийг гүйцээх"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Үйлдлийг дуусгах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1747bdb68515..7c120f42cb2d 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"तपासले नाही"</string>
<string name="selected" msgid="6614607926197755875">"निवडला"</string>
<string name="not_selected" msgid="410652016565864475">"निवडला नाही"</string>
+ <string name="in_progress" msgid="2149208189184319441">"प्रगतीपथावर आहे"</string>
<string name="whichApplication" msgid="5432266899591255759">"याचा वापर करून क्रिया पूर्ण करा"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s वापरून क्रिया पूर्ण करा"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"क्रिया पूर्ण झाली"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 0bf24d7bcc10..76d52a9c5251 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"tidak ditandai"</string>
<string name="selected" msgid="6614607926197755875">"dipilih"</string>
<string name="not_selected" msgid="410652016565864475">"tidak dipilih"</string>
+ <string name="in_progress" msgid="2149208189184319441">"dalam proses"</string>
<string name="whichApplication" msgid="5432266899591255759">"Selesaikan tindakan menggunakan"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Selesaikan tindakan menggunakan %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Selesaikan tindakan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 14409d895099..74ecc1d5d2b5 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ခြစ် မထား"</string>
<string name="selected" msgid="6614607926197755875">"ရွေးချယ်ထားသည်"</string>
<string name="not_selected" msgid="410652016565864475">"ရွေးချယ်မထားပါ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ဆောင်ရွက်နေသည်"</string>
<string name="whichApplication" msgid="5432266899591255759">"အောက်ပါတို့ကို အသုံးပြုမှု အပြီးသတ်ခြင်း"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ကို သုံးပြီး လုပ်ဆောင်ချက် ပြီးဆုံးပါစေ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"လုပ်ဆောင်ချက်ကို အပြီးသတ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d046447c82c8..4394e4958bf4 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ikke avmerket"</string>
<string name="selected" msgid="6614607926197755875">"valgt"</string>
<string name="not_selected" msgid="410652016565864475">"ikke valgt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Fullfør med"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Fullfør handlingen med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Fullfør handlingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6da40726d123..2d8b565e3cff 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"जाँच गरिएको छैन"</string>
<string name="selected" msgid="6614607926197755875">"चयन गरियो"</string>
<string name="not_selected" msgid="410652016565864475">"चयन गरिएन"</string>
+ <string name="in_progress" msgid="2149208189184319441">"जारी छ"</string>
<string name="whichApplication" msgid="5432266899591255759">"प्रयोग गरेर कारबाही पुरा गर्नुहोस्"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"निम्न एपको प्रयोग गरी कारबाही पुरा गर्नुहोस्: %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"पूर्ण कारबाही"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 75857ff45ccb..0c6105472182 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"niet aangevinkt"</string>
<string name="selected" msgid="6614607926197755875">"geselecteerd"</string>
<string name="not_selected" msgid="410652016565864475">"niet geselecteerd"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bezig"</string>
<string name="whichApplication" msgid="5432266899591255759">"Actie voltooien met"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Actie voltooien via %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Actie voltooien"</string>
diff --git a/core/res/res/values-notround-watch/dimens.xml b/core/res/res/values-notround-watch/dimens.xml
index f0204d0f2d34..b63f9c7b2b15 100644
--- a/core/res/res/values-notround-watch/dimens.xml
+++ b/core/res/res/values-notround-watch/dimens.xml
@@ -25,4 +25,7 @@
<item name="input_extract_action_button_width" type="dimen">24dp</item>
<item name="input_extract_action_button_height" type="dimen">24dp</item>
<item name="input_extract_action_icon_padding" type="dimen">3dp</item>
+
+ <item name="global_actions_vertical_padding_percentage" type="fraction">16.7%</item>
+ <item name="global_actions_horizontal_padding_percentage" type="fraction">2.8%</item>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 11d6e7f4cfa1..7d27d4e5fc69 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ଯାଞ୍ଚ ହୋଇନାହିଁ"</string>
<string name="selected" msgid="6614607926197755875">"ଚୟନ କରାଯାଇଛି"</string>
<string name="not_selected" msgid="410652016565864475">"ଚୟନ କରାଯାଇନାହିଁ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ଚାଲୁଅଛି"</string>
<string name="whichApplication" msgid="5432266899591255759">"ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ବ୍ୟବହାର କରି କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"କାର୍ଯ୍ୟ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 264829636af7..12bf1bbe831d 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ਨਿਸ਼ਾਨਬੱਧ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
<string name="selected" msgid="6614607926197755875">"ਚੁਣਿਆ ਗਿਆ"</string>
<string name="not_selected" msgid="410652016565864475">"ਨਹੀਂ ਚੁਣਿਆ ਗਿਆ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ਜਾਰੀ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ਇਸਨੂੰ ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ਵਰਤਦੇ ਹੋਏ ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ਕਾਰਵਾਈ ਪੂਰੀ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c45ac41462ae..31172f673e41 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -333,7 +333,7 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Czujniki na ciele"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"dostęp do danych czujnika podstawowych funkcji życiowych"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pobieranie zawartości okna"</string>
- <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
+ <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Włączenie czytania dotykiem"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Klikane elementy będą wymawiane na głos, a ekran można przeglądać, używając gestów."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Obserwowanie wpisywanego tekstu"</string>
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nie wybrano"</string>
<string name="selected" msgid="6614607926197755875">"wybrano"</string>
<string name="not_selected" msgid="410652016565864475">"nie wybrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"w toku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Wykonaj czynność przez..."</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Wykonaj czynność w aplikacji %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Wykonaj działanie"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 8198ae939e3b..ab5f17621ce4 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5a336ae237e5..6455af0542f6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não selecionado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em curso"</string>
<string name="whichApplication" msgid="5432266899591255759">"Concluir ação utilizando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir ação utilizando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 8198ae939e3b..ab5f17621ce4 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"não marcado"</string>
<string name="selected" msgid="6614607926197755875">"selecionado"</string>
<string name="not_selected" msgid="410652016565864475">"não selecionado"</string>
+ <string name="in_progress" msgid="2149208189184319441">"em andamento"</string>
<string name="whichApplication" msgid="5432266899591255759">"Complete a ação usando"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Concluir a ação usando %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Concluir ação"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ae0a62901a94..c20a590c5238 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"nebifat"</string>
<string name="selected" msgid="6614607926197755875">"selectat"</string>
<string name="not_selected" msgid="410652016565864475">"neselectat"</string>
+ <string name="in_progress" msgid="2149208189184319441">"în curs"</string>
<string name="whichApplication" msgid="5432266899591255759">"Finalizare acțiune utilizând"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Finalizați acțiunea utilizând %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Finalizați acțiunea"</string>
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
index 1d8c669ac55b..f288b41fb556 100644
--- a/core/res/res/values-round-watch/dimens.xml
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -25,4 +25,7 @@
<item name="input_extract_action_button_width" type="dimen">32dp</item>
<item name="input_extract_action_button_height" type="dimen">32dp</item>
<item name="input_extract_action_icon_padding" type="dimen">5dp</item>
+
+ <item name="global_actions_vertical_padding_percentage" type="fraction">20.8%</item>
+ <item name="global_actions_horizontal_padding_percentage" type="fraction">5.2%</item>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8d3edf1d79e3..9c961ba9c904 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не отмечено"</string>
<string name="selected" msgid="6614607926197755875">"выбрано"</string>
<string name="not_selected" msgid="410652016565864475">"не выбрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"в процессе"</string>
<string name="whichApplication" msgid="5432266899591255759">"Что использовать?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Выполнить с помощью приложения \"%1$s\""</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Выполнить действие"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 132ee6f261c7..2b20e66be7c6 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"පරීක්ෂා කර නැත"</string>
<string name="selected" msgid="6614607926197755875">"තෝරන ලදි"</string>
<string name="not_selected" msgid="410652016565864475">"තෝරා නොමැත"</string>
+ <string name="in_progress" msgid="2149208189184319441">"සිදු වෙමින් පවතී"</string>
<string name="whichApplication" msgid="5432266899591255759">"පහත භාවිතයෙන් ක්‍රියාව සම්පූර්ණ කරන්න"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s භාවිතා කරමින් ක්‍රියාව සම්පුර්ණ කරන්න"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ක්‍රියාව සම්පූර්ණ කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 67b0ce0d50b1..cb18e2c57095 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"nezačiarknuté"</string>
<string name="selected" msgid="6614607926197755875">"vybrané"</string>
<string name="not_selected" msgid="410652016565864475">"nevybrané"</string>
+ <string name="in_progress" msgid="2149208189184319441">"prebieha"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončiť akciu pomocou aplikácie"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončiť akciu pomocou aplikácie %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Dokončiť akciu"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d632e0c53b6f..414d0bdee091 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string>
<string name="selected" msgid="6614607926197755875">"izbrano"</string>
<string name="not_selected" msgid="410652016565864475">"ni izbrano"</string>
+ <string name="in_progress" msgid="2149208189184319441">"v teku"</string>
<string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 239c9f8709da..16edb814b959 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"nuk u përzgjodh"</string>
<string name="selected" msgid="6614607926197755875">"i zgjedhur"</string>
<string name="not_selected" msgid="410652016565864475">"i pazgjedhur"</string>
+ <string name="in_progress" msgid="2149208189184319441">"në vazhdim"</string>
<string name="whichApplication" msgid="5432266899591255759">"Përfundo veprimin duke përdorur"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Përfundo veprimin duke përdorur %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Përfundo veprimin"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index cd03394c58d4..5261bd3f43df 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1202,6 +1202,7 @@
<string name="not_checked" msgid="7972320087569023342">"није означено"</string>
<string name="selected" msgid="6614607926197755875">"изабрано"</string>
<string name="not_selected" msgid="410652016565864475">"није изабрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"у току"</string>
<string name="whichApplication" msgid="5432266899591255759">"Доврши радњу преко"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завршите радњу помоћу апликације %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Заврши радњу"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 940ee568228d..e494ef0ec87e 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"inte markerad"</string>
<string name="selected" msgid="6614607926197755875">"valt"</string>
<string name="not_selected" msgid="410652016565864475">"inte valt"</string>
+ <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
<string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index fbee2c3d8ff8..41664782148c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"haijateuliwa"</string>
<string name="selected" msgid="6614607926197755875">"imechaguliwa"</string>
<string name="not_selected" msgid="410652016565864475">"haijachaguliwa"</string>
+ <string name="in_progress" msgid="2149208189184319441">"inaendelea"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kamilisha kitendo ukitumia"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Kamilisha kitendo ukitumia %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Kamilisha kitendo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 4a5be62120f6..ffd5078bdaf2 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"முடக்கப்பட்டுள்ளது"</string>
<string name="selected" msgid="6614607926197755875">"தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="not_selected" msgid="410652016565864475">"தேர்ந்தெடுக்கப்படவில்லை"</string>
+ <string name="in_progress" msgid="2149208189184319441">"செயலிலுள்ளது"</string>
<string name="whichApplication" msgid="5432266899591255759">"இதைப் பயன்படுத்தி செயலை நிறைவுசெய்"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$s ஐப் பயன்படுத்தி செயலை முடிக்கவும்"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"செயலை முடி"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index e6cca4e988ec..f3d2faaf4ebe 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1046,8 +1046,8 @@
<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_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">
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ఎంచుకోలేదు"</string>
<string name="selected" msgid="6614607926197755875">"ఎంచుకోబడింది"</string>
<string name="not_selected" msgid="410652016565864475">"ఎంచుకోబడలేదు"</string>
+ <string name="in_progress" msgid="2149208189184319441">"ప్రోగ్రెస్‌లో ఉంది"</string>
<string name="whichApplication" msgid="5432266899591255759">"దీన్ని ఉపయోగించి చర్యను పూర్తి చేయండి"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"%1$sను ఉపయోగించి చర్యను పూర్తి చేయి"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"చర్యను పూర్తి చేయి"</string>
@@ -1474,8 +1475,8 @@
<string name="ime_action_previous" msgid="6548799326860401611">"మునుపటి"</string>
<string name="ime_action_default" msgid="8265027027659800121">"అమలు చేయి"</string>
<string name="dial_number_using" msgid="6060769078933953531">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nనంబర్ డయల్ చేయండి"</string>
- <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nపరిచయాన్ని క్రియేట్ చేయండి"</string>
- <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"క్రింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్‌లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string>
+ <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nకాంటాక్ట్‌ను క్రియేట్ చేయండి"</string>
+ <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"కింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్‌లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string>
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"మీరు ఈ రిక్వెస్ట్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"యాక్సెస్ రిక్వెస్ట్‌"</string>
<string name="allow" msgid="6195617008611933762">"అనుమతించండి"</string>
diff --git a/core/res/res/values-television/strings.xml b/core/res/res/values-television/strings.xml
new file mode 100644
index 000000000000..37b10494a884
--- /dev/null
+++ b/core/res/res/values-television/strings.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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!--- Title of notification triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+ <string name="sensor_privacy_start_use_mic_notification_content_title">Microphone is blocked</string>
+ <!--- Title of notification triggered if the camera is disabled but an app tried to access it. [CHAR LIMIT=NONE] -->
+ <string name="sensor_privacy_start_use_camera_notification_content_title">Camera is blocked</string>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 379830a8a189..44516d7a7ae6 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"ยังไม่เลือก"</string>
<string name="selected" msgid="6614607926197755875">"เลือกไว้"</string>
<string name="not_selected" msgid="410652016565864475">"ไม่ได้เลือกไว้"</string>
+ <string name="in_progress" msgid="2149208189184319441">"กำลังดำเนินการ"</string>
<string name="whichApplication" msgid="5432266899591255759">"ทำงานให้เสร็จโดยใช้"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"ดำเนินการให้เสร็จสมบูรณ์โดยใช้ %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"ทำงานให้เสร็จสิ้น"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 73848a33abb8..a21c4f05a686 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"hindi nilagyan ng check"</string>
<string name="selected" msgid="6614607926197755875">"pinili"</string>
<string name="not_selected" msgid="410652016565864475">"hindi pinili"</string>
+ <string name="in_progress" msgid="2149208189184319441">"isinasagawa"</string>
<string name="whichApplication" msgid="5432266899591255759">"Kumpletuhin ang pagkilos gamit ang"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Tapusin ang pagkilos gamit ang %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Gawin ang pagkilos"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 3325e7e96d5e..deda3174510a 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"işaretli değil"</string>
<string name="selected" msgid="6614607926197755875">"seçili"</string>
<string name="not_selected" msgid="410652016565864475">"seçili değil"</string>
+ <string name="in_progress" msgid="2149208189184319441">"devam ediyor"</string>
<string name="whichApplication" msgid="5432266899591255759">"İşlemi şunu kullanarak tamamla"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"İşlemi %1$s kullanarak tamamla"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"İşlemi tamamla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 4912be887d9e..fe64d9dd89c7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1222,6 +1222,7 @@
<string name="not_checked" msgid="7972320087569023342">"не вибрано"</string>
<string name="selected" msgid="6614607926197755875">"вибрано"</string>
<string name="not_selected" msgid="410652016565864475">"не вибрано"</string>
+ <string name="in_progress" msgid="2149208189184319441">"триває"</string>
<string name="whichApplication" msgid="5432266899591255759">"Що використовувати?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Завершити дію за допомогою %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Завершити дію"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 2936a080723b..de5a3014610a 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"چیک نہیں کیا گیا"</string>
<string name="selected" msgid="6614607926197755875">"منتخب کردہ"</string>
<string name="not_selected" msgid="410652016565864475">"غیر منتخب کردہ"</string>
+ <string name="in_progress" msgid="2149208189184319441">"جاری ہے"</string>
<string name="whichApplication" msgid="5432266899591255759">"اس کا استعمال کرکے کارروائی مکمل کریں"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"‏%1$s کا استعمال کر کے کارروائی مکمل کریں"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"کارروائی مکمل کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index df7d4670dc29..ecc5f524f90d 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"belgilanmadi"</string>
<string name="selected" msgid="6614607926197755875">"tanlangan"</string>
<string name="not_selected" msgid="410652016565864475">"tanlanmagan"</string>
+ <string name="in_progress" msgid="2149208189184319441">"bajarilmoqda"</string>
<string name="whichApplication" msgid="5432266899591255759">"Nima ishlatilsin?"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"“%1$s” bilan ochish"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Amalni bajarish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 259696dc587c..38a424bc22d9 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"chưa chọn"</string>
<string name="selected" msgid="6614607926197755875">"đã chọn"</string>
<string name="not_selected" msgid="410652016565864475">"chưa được chọn"</string>
+ <string name="in_progress" msgid="2149208189184319441">"đang thực hiện"</string>
<string name="whichApplication" msgid="5432266899591255759">"Hoàn tất thao tác bằng"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Hoàn tất thao tác bằng %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Hoàn thành tác vụ"</string>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/core/res/res/values-watch/colors.xml
index cf2017f1eb6f..854fbfd9168b 100644
--- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
+++ b/core/res/res/values-watch/colors.xml
@@ -1,7 +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.
@@ -13,11 +12,11 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
- ~ limitations under the License
+ ~ limitations under the License.
-->
-<resources>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">3</integer>
-</resources>
+<resources>
+ <!-- Wear Material standard colors -->
+ <color name="wear_material_red_400">#EE675C</color>
+ <color name="wear_material_grey_900">#202124</color>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values-watch/strings.xml b/core/res/res/values-watch/strings.xml
index dde8b2e3ec50..57c136e98132 100644
--- a/core/res/res/values-watch/strings.xml
+++ b/core/res/res/values-watch/strings.xml
@@ -25,5 +25,8 @@
<xliff:g id="number" example="123">%2$d</xliff:g>.</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. Override from base which says "Body Sensors". [CHAR_LIMIT=25] -->
- <string name="permgrouplab_sensors">Sensors</string>
-</resources>
+<string name="permgrouplab_sensors">Sensors</string>
+
+
+ <!-- label for item that opens emergency features in the power menu on Wear [CHAR LIMIT=24] -->
+ <string name="global_action_emergency">Emergency SOS</string></resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 68a5ce7ee349..4b6dda56359c 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -327,17 +327,17 @@
<string name="permgrouplab_sensors" msgid="9134046949784064495">"身体传感器"</string>
<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_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您与之互动的窗口的内容。"</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>
- <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"控制显示内容放大功能"</string>
- <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"控制显示内容的缩放级别和位置。"</string>
+ <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"包括信用卡号和密码等个人数据。"</string>
+ <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_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"指纹手势"</string>
- <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕获在设备指纹传感器上执行的手势。"</string>
+ <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕捉在设备指纹传感器上执行的手势。"</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"截取屏幕截图"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"可截取显示画面的屏幕截图。"</string>
<string name="permlab_statusBar" msgid="8798267849526214017">"停用或修改状态栏"</string>
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾选"</string>
<string name="selected" msgid="6614607926197755875">"已选择"</string>
<string name="not_selected" msgid="410652016565864475">"未选择"</string>
+ <string name="in_progress" msgid="2149208189184319441">"进行中"</string>
<string name="whichApplication" msgid="5432266899591255759">"选择要使用的应用:"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"使用%1$s完成操作"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fa2fee5f4083..ccfb02628ed4 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"揀咗"</string>
<string name="not_selected" msgid="410652016565864475">"未揀"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"完成操作需使用"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1c02c6bbb285..107f1150b6db 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"未勾選"</string>
<string name="selected" msgid="6614607926197755875">"已選取"</string>
<string name="not_selected" msgid="410652016565864475">"未選取"</string>
+ <string name="in_progress" msgid="2149208189184319441">"處理中"</string>
<string name="whichApplication" msgid="5432266899591255759">"選擇要使用的應用程式"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"完成操作需使用 %1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"完成操作"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index f93b84463874..9c9d68a68141 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1182,6 +1182,7 @@
<string name="not_checked" msgid="7972320087569023342">"akuhloliwe"</string>
<string name="selected" msgid="6614607926197755875">"okukhethiwe"</string>
<string name="not_selected" msgid="410652016565864475">"akukhethiwe"</string>
+ <string name="in_progress" msgid="2149208189184319441">"kuyaqhubeka"</string>
<string name="whichApplication" msgid="5432266899591255759">"Qedela isenzo usebenzisa"</string>
<string name="whichApplicationNamed" msgid="6969946041713975681">"Qedela isenzo usebenzisa i-%1$s"</string>
<string name="whichApplicationLabel" msgid="7852182961472531728">"Qedela isenzo"</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d052d70442c4..cbf35f4a080d 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -285,9 +285,6 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system default text classifier -->
<flag name="textClassifier" value="0x10000" />
- <!-- Additional flag from base permission type: this permission can be automatically
- granted to the document manager -->
- <flag name="documenter" value="0x40000" />
<!-- Additional flag from base permission type: this permission automatically
granted to device configurator -->
<flag name="configurator" value="0x80000" />
@@ -398,6 +395,12 @@
this attribute unnecessary. -->
<attr name="sharedUserLabel" format="reference" />
+ <!-- The maximum device SDK version for which the application will remain in the user ID
+ defined in sharedUserId. Used when the application wants to migrate out of using shared
+ user ID, but has to maintain backwards compatibility with the API level specified
+ and before. -->
+ <attr name="sharedUserMaxSdkVersion" format="integer" />
+
<!-- Internal version code. This is the number used to determine whether
one version is more recent than another: it has no other meaning than
that higher numbers are more recent. You could use this number to
@@ -1193,7 +1196,7 @@
<code>dalvik.system.DelegateLastClassLoader</code>. If unspecified,
the default value of this attribute is <code>dalvik.system.PathClassLoader</code>.
- If an unknown classloader is provided, a PackageParserException with cause
+ If an unknown classloader is provided, a PackageManagerException with cause
<code>PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED</code> will be
thrown and the app will not be installed.
-->
@@ -1653,6 +1656,7 @@
<attr name="revisionCode" />
<attr name="sharedUserId" />
<attr name="sharedUserLabel" />
+ <attr name="sharedUserMaxSdkVersion" />
<attr name="installLocation" />
<attr name="isolatedSplits" />
<attr name="isFeatureSplit" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index db43b5b31e7e..17bb2d29e79b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -657,12 +657,26 @@
<!-- Indicate the display area rect for foldable devices in folded state. -->
<string name="config_foldedArea"></string>
+ <!-- Indicates whether to enable an animation when unfolding a device or not -->
+ <bool name="config_unfoldTransitionEnabled">false</bool>
+
<!-- Indicates that the device supports having more than one internal display on at the same
time. Only applicable to devices with more than one internal display. If this option is
set to false, DisplayManager will make additional effort to ensure no more than 1 internal
display is powered on at the same time. -->
<bool name="config_supportsConcurrentInternalDisplays">true</bool>
+ <!-- Map of DeviceState to rotation lock setting. Each entry must be in the format
+ "key:value", for example: "0:1".
+ The keys are device states, and the values are one of
+ Settings.Secure.DeviceStateRotationLockSetting.
+ Any device state that doesn't have a default set here will be treated as
+ DEVICE_STATE_ROTATION_LOCK_IGNORED meaning it will not have its own rotation lock setting.
+ If this map is missing, the feature is disabled and only one global rotation lock setting
+ will apply, regardless of device state. -->
+ <string-array name="config_perDeviceStateRotationLockDefaults" />
+
+
<!-- Desk dock behavior -->
<!-- The number of degrees to rotate the display when the device is in a desk dock.
@@ -957,6 +971,20 @@
-->
<integer name="config_longPressOnPowerBehavior">5</integer>
+ <!-- The time in milliseconds after which a press on power button is considered "long". -->
+ <integer name="config_longPressOnPowerDurationMs">500</integer>
+
+ <!-- The possible UI options to be surfaced for configuring long press power on duration
+ action. Value set in config_longPressOnPowerDurationMs should be one of the available
+ options to allow users to restore default. -->
+ <integer-array name="config_longPressOnPowerDurationSettings">
+ <item>250</item>
+ <item>350</item>
+ <item>500</item>
+ <item>650</item>
+ <item>750</item>
+ </integer-array>
+
<!-- Whether the setting to change long press on power behaviour from default to assistant (5)
is available in Settings.
-->
@@ -1425,6 +1453,14 @@
<integer-array name="config_autoBrightnessLcdBacklightValues">
</integer-array>
+ <!-- Array of output values for LCD backlight in doze mode corresponding to the lux values
+ in the config_autoBrightnessLevels array. This array should have size one greater
+ than the size of the config_autoBrightnessLevels array.
+ The brightness values must be between 0 and 255 and be non-decreasing.
+ This must be overridden in platform specific overlays -->
+ <integer-array name="config_autoBrightnessLcdBacklightValues_doze">
+ </integer-array>
+
<!-- Array of desired screen brightness in nits corresponding to the lux values
in the config_autoBrightnessLevels array. As with config_screenBrightnessMinimumNits and
config_screenBrightnessMaximumNits, the display brightness is defined as the measured
@@ -3607,8 +3643,8 @@
-->
<integer name="config_largeScreenSmallestScreenWidthDp">600</integer>
- <!-- True if the device is using leagacy split. -->
- <bool name="config_useLegacySplit">true</bool>
+ <!-- True if the device is using legacy split. -->
+ <bool name="config_useLegacySplit">false</bool>
<!-- True if the device supports running activities on secondary displays. -->
<bool name="config_supportsMultiDisplay">true</bool>
@@ -3740,6 +3776,7 @@
<string-array translatable="false" name="config_nonBlockableNotificationPackages">
<item>com.android.dialer</item>
<item>com.android.messaging</item>
+ <item>com.android.cellbroadcastreceiver.module</item>
</string-array>
<!-- An array of packages that can make sound on the ringer stream in priority-only DND
@@ -4739,9 +4776,15 @@
MediaSessionService. -->
<string name="config_customMediaSessionPolicyProvider"></string>
+ <!-- The min scale for the wallpaper when it's zoomed out -->
+ <item name="config_wallpaperMinScale" format="float" type="dimen">1</item>
+
<!-- The max scale for the wallpaper when it's zoomed in -->
<item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item>
+ <!-- If true, the wallpaper will scale regardless of the value of shouldZoomOutWallpaper() -->
+ <bool name="config_alwaysScaleWallpaper">false</bool>
+
<!-- Package name that will receive an explicit manifest broadcast for
android.os.action.POWER_SAVE_MODE_CHANGED. -->
<string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
@@ -4760,6 +4803,16 @@
<!-- pdp data reject retry delay in ms -->
<integer name="config_pdp_reject_retry_delay_ms">-1</integer>
+ <!-- Duration in milliseconds for device to vibrate on mash press on power
+ button. -->
+ <integer name="config_mashPressVibrateTimeOnPowerButton">0</integer>
+
+ <!-- Control the behavior when the user presses the power button 5 times.
+ 0 - Nothing
+ 1 - Launch panic button gesture
+ -->
+ <integer name="config_mashPressOnPowerBehavior">0</integer>
+
<!-- Whether or not to enable the binder heavy hitter watcher by default -->
<bool name="config_defaultBinderHeavyHitterWatcherEnabled">false</bool>
@@ -4902,13 +4955,21 @@
<bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
<bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+ <bool name="config_cecHdmiCecVersion14b_default">false</bool>
<bool name="config_cecHdmiCecVersion20_allowed">true</bool>
- <bool name="config_cecHdmiCecVersion20_default">false</bool>
+ <bool name="config_cecHdmiCecVersion20_default">true</bool>
+
+ <bool name="config_cecRoutingControl_userConfigurable">true</bool>
+ <bool name="config_cecRoutingControlEnabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlEnabled_default">false</bool>
+ <bool name="config_cecRoutingControlDisabled_allowed">true</bool>
+ <bool name="config_cecRoutingControlDisabled_default">true</bool>
<bool name="config_cecPowerControlMode_userConfigurable">true</bool>
<bool name="config_cecPowerControlModeTv_allowed">true</bool>
- <bool name="config_cecPowerControlModeTv_default">true</bool>
+ <bool name="config_cecPowerControlModeTv_default">false</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_allowed">true</bool>
+ <bool name="config_cecPowerControlModeTvAndAudioSystem_default">true</bool>
<bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
<bool name="config_cecPowerControlModeBroadcast_default">false</bool>
<bool name="config_cecPowerControlModeNone_allowed">true</bool>
@@ -4920,6 +4981,12 @@
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
<bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+ <bool name="config_cecSystemAudioControl_userConfigurable">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlEnabled_default">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_allowed">true</bool>
+ <bool name="config_cecSystemAudioControlDisabled_default">false</bool>
+
<bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
<bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
@@ -5044,4 +5111,95 @@
<!-- the number of the max cached processes in the system. -->
<integer name="config_customizedMaxCachedProcesses">32</integer>
+
+ <!-- Whether this device should support taking app snapshots on closure -->
+ <bool name="config_disableTaskSnapshots">false</bool>
+
+ <!-- The display cutout configs for secondary built-in display. -->
+ <string name="config_secondaryBuiltInDisplayCutout" translatable="false"></string>
+ <string name="config_secondaryBuiltInDisplayCutoutRectApproximation" translatable="false">
+ @string/config_secondaryBuiltInDisplayCutout
+ </string>
+ <bool name="config_fillSecondaryBuiltInDisplayCutout">false</bool>
+ <bool name="config_maskSecondaryBuiltInDisplayCutout">false</bool>
+
+ <!-- An array contains unique ids of all built-in displays and the unique id of a display can be
+ obtained from {@link Display#getUniqueId}. This array should be set for multi-display
+ devices if there are different display related configs(e.g. display cutout, rounded corner)
+ between each built-in display.
+ It is used as an index for multi-display related configs:
+ First look up the index of the unique id of the given built-in display unique id in this
+ array and use this index to get the info in corresponding config arrays such as:
+ - config_displayCutoutPathArray
+ - config_displayCutoutApproximationRectArray
+ - config_fillBuiltInDisplayCutoutArray
+ - config_maskBuiltInDisplayCutoutArray
+ - config_waterfallCutoutArray
+
+ Leave this array empty for single display device and the system will load the default main
+ built-in related configs.
+ -->
+ <string-array name="config_displayUniqueIdArray" translatable="false">
+ <!-- Example:
+ <item>"local:1234567891"</item> // main built-in display
+ <item>"local:1234567892"</item> // secondary built-in display
+ -->
+ </string-array>
+
+ <!-- The display cutout path config for each display in a multi-display device. -->
+ <string-array name="config_displayCutoutPathArray" translatable="false">
+ <item>@string/config_mainBuiltInDisplayCutout</item>
+ <item>@string/config_secondaryBuiltInDisplayCutout</item>
+ </string-array>
+
+ <!-- The display cutout approximation rect config for each display in a multi-display device.
+ -->
+ <string-array name="config_displayCutoutApproximationRectArray" translatable="false">
+ <item>@string/config_mainBuiltInDisplayCutoutRectApproximation</item>
+ <item>@string/config_secondaryBuiltInDisplayCutoutRectApproximation</item>
+ </string-array>
+
+ <!-- The maskBuiltInDisplayCutout config for each display in a multi-display device. -->
+ <array name="config_maskBuiltInDisplayCutoutArray" translatable="false">
+ <item>@bool/config_maskMainBuiltInDisplayCutout</item>
+ <item>@bool/config_maskSecondaryBuiltInDisplayCutout</item>
+ </array>
+
+ <!-- The fillBuiltInDisplayCutout config for each display in a multi-display device. -->
+ <array name="config_fillBuiltInDisplayCutoutArray" translatable="false">
+ <item>@bool/config_fillMainBuiltInDisplayCutout</item>
+ <item>@bool/config_fillSecondaryBuiltInDisplayCutout</item>
+ </array>
+
+ <array name="config_mainBuiltInDisplayWaterfallCutout" translatable="false">
+ <item>@dimen/waterfall_display_left_edge_size</item>
+ <item>@dimen/waterfall_display_top_edge_size</item>
+ <item>@dimen/waterfall_display_right_edge_size</item>
+ <item>@dimen/waterfall_display_bottom_edge_size</item>
+ </array>
+
+ <array name="config_secondaryBuiltInDisplayWaterfallCutout" translatable="false">
+ <item>@dimen/secondary_waterfall_display_left_edge_size</item>
+ <item>@dimen/secondary_waterfall_display_top_edge_size</item>
+ <item>@dimen/secondary_waterfall_display_right_edge_size</item>
+ <item>@dimen/secondary_waterfall_display_bottom_edge_size</item>
+ </array>
+
+ <!-- The waterfall cutout config for each display in a multi-display device. -->
+ <array name="config_waterfallCutoutArray" translatable="false">
+ <item>@array/config_mainBuiltInDisplayWaterfallCutout</item>
+ <item>@array/config_secondaryBuiltInDisplayWaterfallCutout</item>
+ </array>
+
+ <!-- Whether the airplane mode should be reset when device boots in non-safemode after exiting
+ from safemode.
+ This flag should be enabled only when the product does not have any UI to toggle airplane
+ mode like automotive devices.-->
+ <bool name="config_autoResetAirplaneMode">false</bool>
+
+ <!-- Wear OS: the name of the package containing the device's sysui. -->
+ <string name="config_wearSysUiPackage" translatable="false"/>
+
+ <!-- Wear OS: the name of the main activity of the device's sysui. -->
+ <string name="config_wearSysUiMainActivity" translatable="false"/>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index de7a1175b4a3..e8bb6067932e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -237,6 +237,9 @@
value is calculated in ConversationLayout#updateActionListPadding() -->
<dimen name="notification_actions_padding_start">36dp</dimen>
+ <!-- The max width of a priority action button when it is collapsed to just the icon. -->
+ <dimen name="notification_actions_collapsed_priority_width">60dp</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) -->
@@ -920,7 +923,7 @@
<dimen name="chooser_action_button_icon_size">18dp</dimen>
- <!-- For Waterfall Display -->
+ <!-- For main built-in Waterfall Display -->
<dimen name="waterfall_display_left_edge_size">0px</dimen>
<dimen name="waterfall_display_top_edge_size">0px</dimen>
<dimen name="waterfall_display_right_edge_size">0px</dimen>
@@ -943,4 +946,10 @@
<dimen name="starting_surface_icon_size">160dp</dimen>
<!-- The default width/height of the icon on the spec of adaptive icon drawable. -->
<dimen name="starting_surface_default_icon_size">108dp</dimen>
+
+ <!-- For secondary built-in Waterfall Display -->
+ <dimen name="secondary_waterfall_display_left_edge_size">0px</dimen>
+ <dimen name="secondary_waterfall_display_top_edge_size">0px</dimen>
+ <dimen name="secondary_waterfall_display_right_edge_size">0px</dimen>
+ <dimen name="secondary_waterfall_display_bottom_edge_size">0px</dimen>
</resources>
diff --git a/core/res/res/values/dimens_car.xml b/core/res/res/values/dimens_car.xml
index 0ef60c4c9531..36f1edb4d417 100644
--- a/core/res/res/values/dimens_car.xml
+++ b/core/res/res/values/dimens_car.xml
@@ -110,10 +110,10 @@
<dimen name="car_textview_fading_edge_length">40dp</dimen>
<!-- Dialog start padding for button bar layout -->
- <dimen name="button_bar_layout_start_padding">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="button_bar_layout_start_padding">@dimen/car_padding_2</dimen>
<!-- Dialog end padding for button bar layout -->
- <dimen name="button_bar_layout_end_padding">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="button_bar_layout_end_padding">@dimen/car_padding_2</dimen>
<!-- Dialog top padding for button bar layout -->
<dimen name="button_bar_layout_top_padding">@*android:dimen/car_padding_2</dimen>
@@ -122,7 +122,7 @@
<dimen name="button_layout_height">@*android:dimen/car_card_action_bar_height</dimen>
<!-- Dialog button end margin -->
- <dimen name="button_end_margin">@*android:dimen/car_padding_4</dimen>
+ <dimen name="button_end_margin">@*android:dimen/car_padding_2</dimen>
<!-- Dialog top padding when there is no title -->
<dimen name="dialog_no_title_padding_top">@*android:dimen/car_padding_4</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2403a605972e..fd03d7295415 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3201,10 +3201,159 @@
<public type="string" name="config_defaultRingtoneVibrationSound" id="0x0104003b" />
<!-- ===============================================================
+ Resources added in version S-V2 of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01fe0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x01fd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x01fc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01fb0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01fa0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01f90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01f80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01f70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01f60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01f50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01f40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01f30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01f20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01f10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01f00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01ef0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ee0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
+ Resources added in version T of the platform
+
+ NOTE: add <public> elements within a <staging-public-group> like so:
+
+ <staging-public-group type="attr" first-id="0x01ff0000">
+ <public name="exampleAttr1" />
+ <public name="exampleAttr2" />
+ </staging-public-group>
+
+ To add a new <staging-public-group> block, find the id value for the
+ last <staging-public-group> block defined for thie API level, and
+ subtract 0x00010000 from it to get to the id of the new block.
+
+ For example, if the block closest to the end of this file has an id
+ of 0x01ee0000, the id of the new block should be 0x01ed0000
+ (0x01ee0000 - 0x00010000 = 0x01ed0000).
+ =============================================================== -->
+ <eat-comment />
+
+ <staging-public-group type="attr" first-id="0x01df0000">
+ <public name="sharedUserMaxSdkVersion" />
+ </staging-public-group>
+
+ <staging-public-group type="id" first-id="0x01de0000">
+ </staging-public-group>
+
+ <staging-public-group type="style" first-id="0x0dfd0000">
+ </staging-public-group>
+
+ <staging-public-group type="string" first-id="0x0dfc0000">
+ </staging-public-group>
+
+ <staging-public-group type="dimen" first-id="0x01db0000">
+ </staging-public-group>
+
+ <staging-public-group type="color" first-id="0x01da0000">
+ </staging-public-group>
+
+ <staging-public-group type="array" first-id="0x01d90000">
+ </staging-public-group>
+
+ <staging-public-group type="drawable" first-id="0x01d80000">
+ </staging-public-group>
+
+ <staging-public-group type="layout" first-id="0x01d70000">
+ </staging-public-group>
+
+ <staging-public-group type="anim" first-id="0x01d60000">
+ </staging-public-group>
+
+ <staging-public-group type="animator" first-id="0x01d50000">
+ </staging-public-group>
+
+ <staging-public-group type="interpolator" first-id="0x01d40000">
+ </staging-public-group>
+
+ <staging-public-group type="mipmap" first-id="0x01d30000">
+ </staging-public-group>
+
+ <staging-public-group type="integer" first-id="0x01d20000">
+ </staging-public-group>
+
+ <staging-public-group type="transition" first-id="0x01d10000">
+ </staging-public-group>
+
+ <staging-public-group type="raw" first-id="0x01d00000">
+ </staging-public-group>
+
+ <staging-public-group type="bool" first-id="0x01cf0000">
+ </staging-public-group>
+
+ <staging-public-group type="fraction" first-id="0x01ce0000">
+ </staging-public-group>
+
+ <!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
Any new items (attrs, styles, ids, etc.) *must* be added in a
- public-group block, as the preceding comment explains.
+ staging-public-group block, as the preceding comment explains.
Items added outside of a group may have their value recalculated
every time something new is added to this file.
=============================================================== -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a99a22009e3b..d2c6f7880cb9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3195,6 +3195,9 @@
<!-- Default not selected text used by accessibility for an element that can be selected or unselected. [CHAR LIMIT=NONE] -->
<string name="not_selected">not selected</string>
+ <!-- Default state description for indeterminate progressbar. [CHAR LIMIT=NONE] -->
+ <string name="in_progress">in progress</string>
+
<!-- Title of intent resolver dialog when selecting an application to run. -->
<string name="whichApplication">Complete action using</string>
<!-- Title of intent resolver dialog when selecting an application to run
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index adb046e76c88..527b92895aa5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -439,6 +439,8 @@
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
<java-symbol type="integer" name="config_immersive_mode_confirmation_panic" />
<java-symbol type="integer" name="config_longPressOnPowerBehavior" />
+ <java-symbol type="integer" name="config_longPressOnPowerDurationMs" />
+ <java-symbol type="array" name="config_longPressOnPowerDurationSettings" />
<java-symbol type="bool" name="config_longPressOnPowerForAssistantSettingAvailable" />
<java-symbol type="integer" name="config_veryLongPressOnPowerBehavior" />
<java-symbol type="integer" name="config_veryLongPressTimeout" />
@@ -798,6 +800,7 @@
<java-symbol type="string" name="ime_action_previous" />
<java-symbol type="string" name="ime_action_search" />
<java-symbol type="string" name="ime_action_send" />
+ <java-symbol type="string" name="in_progress" />
<java-symbol type="string" name="invalidPin" />
<java-symbol type="string" name="js_dialog_before_unload_positive_button" />
<java-symbol type="string" name="js_dialog_before_unload_negative_button" />
@@ -1882,6 +1885,7 @@
<java-symbol type="array" name="config_autoBrightnessButtonBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
+ <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues_doze" />
<java-symbol type="array" name="config_autoBrightnessLevels" />
<java-symbol type="array" name="config_ambientThresholdLevels" />
<java-symbol type="array" name="config_ambientBrighteningThresholds" />
@@ -2217,6 +2221,7 @@
<java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
+ <java-symbol type="bool" name="config_autoResetAirplaneMode" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -3183,6 +3188,7 @@
<java-symbol type="id" name="notification_action_list_margin_target" />
<java-symbol type="dimen" name="notification_actions_padding_start"/>
+ <java-symbol type="dimen" name="notification_actions_collapsed_priority_width"/>
<java-symbol type="dimen" name="notification_action_disabled_alpha" />
<java-symbol type="id" name="tag_margin_end_when_icon_visible" />
<java-symbol type="id" name="tag_margin_end_when_icon_gone" />
@@ -3837,6 +3843,9 @@
<java-symbol type="array" name="config_foldedDeviceStates" />
<java-symbol type="string" name="config_foldedArea" />
<java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
+ <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
+ <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
+
<java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
<java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
@@ -4312,9 +4321,17 @@
<java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
<java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+ <java-symbol type="bool" name="config_cecRoutingControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecRoutingControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecPowerControlMode_userConfigurable" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_allowed" />
+ <java-symbol type="bool" name="config_cecPowerControlModeTvAndAudioSystem_default" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
<java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
<java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
@@ -4326,6 +4343,12 @@
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
<java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControl_userConfigurable" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlEnabled_default" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecSystemAudioControlDisabled_default" />
+
<java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
<java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
@@ -4425,4 +4448,31 @@
<java-symbol type="bool" name="config_volumeShowRemoteSessions" />
<java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
+
+ <java-symbol type="bool" name="config_disableTaskSnapshots" />
+
+ <java-symbol type="string" name="config_secondaryBuiltInDisplayCutout" />
+ <java-symbol type="string" name="config_secondaryBuiltInDisplayCutoutRectApproximation" />
+ <java-symbol type="bool" name="config_fillSecondaryBuiltInDisplayCutout" />
+ <java-symbol type="bool" name="config_maskSecondaryBuiltInDisplayCutout" />
+ <java-symbol type="array" name="config_displayUniqueIdArray" />
+ <java-symbol type="array" name="config_displayCutoutPathArray" />
+ <java-symbol type="array" name="config_displayCutoutApproximationRectArray" />
+ <java-symbol type="array" name="config_fillBuiltInDisplayCutoutArray" />
+ <java-symbol type="array" name="config_maskBuiltInDisplayCutoutArray" />
+ <java-symbol type="dimen" name="secondary_waterfall_display_left_edge_size" />
+ <java-symbol type="dimen" name="secondary_waterfall_display_top_edge_size" />
+ <java-symbol type="dimen" name="secondary_waterfall_display_right_edge_size" />
+ <java-symbol type="dimen" name="secondary_waterfall_display_bottom_edge_size" />
+ <java-symbol type="array" name="config_mainBuiltInDisplayWaterfallCutout" />
+ <java-symbol type="array" name="config_secondaryBuiltInDisplayWaterfallCutout" />
+ <java-symbol type="array" name="config_waterfallCutoutArray" />
+
+ <java-symbol type="fraction" name="global_actions_vertical_padding_percentage" />
+ <java-symbol type="fraction" name="global_actions_horizontal_padding_percentage" />
+ <java-symbol type="drawable" name="global_actions_item_red_background" />
+ <java-symbol type="color" name="wear_material_grey_900" />
+
+ <java-symbol type="string" name="config_wearSysUiPackage"/>
+ <java-symbol type="string" name="config_wearSysUiMainActivity"/>
</resources>
diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml
index 71dfc551ccc1..766315029f3d 100644
--- a/core/res/res/xml/config_user_types.xml
+++ b/core/res/res/xml/config_user_types.xml
@@ -62,6 +62,10 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF
<default-restrictions no_sms="true" no_outgoing_calls="true" />
</profile-type>
+ <full-type
+ name="android.os.usertype.full.RESTRICTED"
+ enabled='0' />
+
<profile-type
name="com.example.profilename"
max-allowed-per-parent="2" />
@@ -78,6 +82,7 @@ Mandatory attributes:
Supported optional properties (to be used as shown in the example above) are as follows.
For profile and full users:
default-restrictions (with values defined in UserRestrictionUtils.USER_RESTRICTIONS)
+ enabled
For profile users only:
max-allowed-per-parent
icon-badge
@@ -98,6 +103,9 @@ If this file is updated, the properties of any pre-existing user types will be u
Note, however, that default-restrictions refers to the restrictions applied at the time of user
creation; therefore, the active restrictions of any pre-existing users will not be updated.
+If a user type is disabled, by setting enabled='0', then no further users of that type may be
+created; however, any pre-existing users of that type will remain.
+
The 'change-user-type' tag should be used in conjunction with the 'version' property of
'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to'
type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'.
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 873b9ebe4f1a..5ad37484bcb8 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,18 +18,18 @@
-->
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
-<zen version="9">
- <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
- reminders="false" events="false" repeatCallers="true" convos="false"
- convosFrom="3"/>
+<zen version="10">
+ <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="true"
+ messagesFrom="2" reminders="false" events="false" repeatCallers="true" convos="true"
+ convosFrom="2"/>
<automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
component="android/com.android.server.notification.EventConditionProvider"
conditionId="condition://android/event?userId=-10000&amp;calendar=&amp;reply=1"/>
<automatic ruleId="EVERY_NIGHT_DEFAULT_RULE" enabled="false" snoozing="false" name="Sleeping"
zen="1" component="android/com.android.server.notification.ScheduleConditionProvider"
conditionId="condition://android/schedule?days=1.2.3.4.5.6.7&amp;start=22.0&amp;end=7.0&amp;exitAtAlarm=true"/>
- <!-- all visual effects that exist as of P -->
- <disallow visualEffects="511" />
+ <!-- everything when screen off (for old target sdks); fullscreen; lights; peek; ambient -->
+ <disallow visualEffects="157" />
<!-- whether there are notification channels that can bypass dnd -->
<state areChannelsBypassingDnd="false" />
</zen>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2650d9f69dd7..37d059a575f0 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -88,9 +88,12 @@
<!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
<shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
- <!-- Czechia: 7-8 digits, starting with 9, plus EU:
- http://www.o2.cz/osobni/en/services-by-alphabet/91670-premium_sms.html -->
- <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
+ <!-- Czechia: Premium numbers start with 90, and are either 5 or 7 digits (5 digits is a
+ subscription request, you will be charged for the messages received, but it's necessary
+ to warn on the _request_ as that's the last chance to stop), plus EU:
+ https://www.t-mobile.cz/platebni-a-premium-sms
+ https://www.vodafone.cz/pece/vyuctovani-platby-kredit/platby-mobilem/cena-premium-sms/ -->
+ <shortcode country="cz" premium="90\\d{5}|90\\d{3}" free="116\\d{3}" />
<!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
<shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240|72438" />
@@ -245,6 +248,9 @@
<!-- Slovakia: 4 digits (premium), plus EU: http://www.cmtelecom.com/premium-sms/slovakia -->
<shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
+ <!-- Taiwan -->
+ <shortcode country="tw" pattern="\\d{4}" free="1922" />
+
<!-- Thailand: 4186001 used by AIS_TH_DCB -->
<shortcode country="th" pattern="\\d{1,5}" premium="4\\d{6}" free="4186001" />
diff --git a/core/tests/BroadcastRadioTests/OWNERS b/core/tests/BroadcastRadioTests/OWNERS
index ea4421eae96a..3e360e7e992c 100644
--- a/core/tests/BroadcastRadioTests/OWNERS
+++ b/core/tests/BroadcastRadioTests/OWNERS
@@ -1,2 +1,3 @@
+keunyoung@google.com
+oscarazu@google.com
twasilczyk@google.com
-randolphs@google.com
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 7c6271cbdf61..c194989b2752 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -65,6 +65,7 @@ public class StartProgramListUpdatesFanoutTest {
@Mock ITunerSession mHalTunerSessionMock;
private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
+ private final Object mLock = new Object();
// RadioModule under test
private RadioModule mRadioModule;
@@ -96,7 +97,7 @@ public class StartProgramListUpdatesFanoutTest {
mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "",
0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {},
- null, null));
+ null, null), mLock);
doAnswer((Answer) invocation -> {
mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
index c65ef9a56cd8..53ba140e6aad 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityServiceTest.java
@@ -16,18 +16,40 @@
package android.accessibilityservice;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowTokenClient;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,6 +64,8 @@ import org.mockito.MockitoAnnotations;
public class AccessibilityServiceTest {
private static final String TAG = "AccessibilityServiceTest";
private static final int CONNECTION_ID = 1;
+ private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
+ TYPE_ACCESSIBILITY_OVERLAY);
private static class AccessibilityServiceTestClass extends AccessibilityService {
private IAccessibilityServiceClient mCallback;
@@ -49,7 +73,11 @@ public class AccessibilityServiceTest {
AccessibilityServiceTestClass() {
super();
- attachBaseContext(InstrumentationRegistry.getContext());
+ Context context = ApplicationProvider.getApplicationContext();
+ final Display display = context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY);
+
+ attachBaseContext(context.createTokenContext(new WindowTokenClient(), display));
mLooper = InstrumentationRegistry.getContext().getMainLooper();
}
@@ -78,14 +106,33 @@ public class AccessibilityServiceTest {
private @Mock IBinder mMockIBinder;
private IAccessibilityServiceClient mServiceInterface;
private AccessibilityServiceTestClass mService;
+ private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mService = new AccessibilityServiceTestClass();
+ mService.onCreate();
mService.setupCallback(mMockClientForCallback);
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
+ doAnswer(invocation -> {
+ Object[] args = invocation.getArguments();
+ final int displayId = (int) args[0];
+ final IBinder token = new Binder();
+ WindowManagerGlobal.getWindowManagerService().addWindowToken(token,
+ TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */);
+ mWindowTokens.put(displayId, token);
+ return token;
+ }).when(mMockConnection).getOverlayWindowToken(anyInt());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (int i = mWindowTokens.size() - 1; i >= 0; --i) {
+ WindowManagerGlobal.getWindowManagerService().removeWindowToken(
+ mWindowTokens.valueAt(i), mWindowTokens.keyAt(i));
+ }
}
@Test
@@ -101,4 +148,79 @@ public class AccessibilityServiceTest {
verify(mMockConnection).getSystemActions();
}
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay());
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createDisplayContext(session.getDisplay())
+ .createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+ @Test(expected = WindowManager.BadTokenException.class)
+ public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType()
+ throws Exception {
+ try (VirtualDisplaySession session = new VirtualDisplaySession()) {
+ final Context context = mService.createWindowContext(session.getDisplay(),
+ TYPE_APPLICATION_OVERLAY, null /* options */);
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> context.getSystemService(WindowManager.class)
+ .addView(new View(context), mParams)
+ );
+ }
+ }
+
+
+ private static class VirtualDisplaySession implements AutoCloseable {
+ private final VirtualDisplay mVirtualDisplay;
+
+ VirtualDisplaySession() {
+ final DisplayManager displayManager = ApplicationProvider.getApplicationContext()
+ .getSystemService(DisplayManager.class);
+ final int width = 800;
+ final int height = 480;
+ final int density = 160;
+ ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 2 /* maxImages */);
+ mVirtualDisplay = displayManager.createVirtualDisplay(
+ TAG, width, height, density, reader.getSurface(),
+ VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ }
+
+ private Display getDisplay() {
+ return mVirtualDisplay.getDisplay();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mVirtualDisplay.release();
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 4b0ed65e5fde..2c3c1ed0c40e 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -26,8 +26,11 @@ import android.content.pm.PackageInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import androidx.annotation.NonNull;
import androidx.test.filters.LargeTest;
+import com.android.internal.annotations.VisibleForTesting;
+
import junit.framework.TestCase;
import org.mockito.Mockito;
@@ -110,6 +113,13 @@ public class ApplicationPackageManagerTest extends TestCase {
public boolean isAllow3rdPartyOnInternal(Context context) {
return mAllow3rdPartyOnInternal;
}
+
+ @Override
+ @VisibleForTesting
+ protected @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app,
+ StorageManager storageManager, IPackageManager pm) {
+ return super.getPackageCandidateVolumes(app, storageManager, pm);
+ }
}
private StorageManager getMockedStorageManager() {
@@ -223,7 +233,7 @@ public class ApplicationPackageManagerTest extends TestCase {
appInfo.flags = 0;
appInfo.volumeUuid = sInternalVolUuid;
- Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
+ Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(false);
appPkgMgr.setAllow3rdPartyOnInternal(true);
List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
appInfo, storageManager, pm);
@@ -231,7 +241,7 @@ public class ApplicationPackageManagerTest extends TestCase {
appInfo.volumeUuid = sInternalVolUuid;
appPkgMgr.setAllow3rdPartyOnInternal(true);
- Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true);
+ Mockito.when(pm.isPackageDeviceAdminOnAnyUser(appInfo.packageName)).thenReturn(true);
candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
verifyReturnedVolumes(candidates, sInternalVol);
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index cd07d464ee65..685671b083c4 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -399,6 +399,8 @@ public class NotificationTest {
assertEquals(cDay.getSecondaryTextColor(), cNight.getSecondaryTextColor());
assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor());
assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor());
+ assertEquals(cDay.getTertiaryAccentColor(), cNight.getTertiaryAccentColor());
+ assertEquals(cDay.getOnAccentTextColor(), cNight.getOnAccentTextColor());
assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor());
assertEquals(cDay.getContrastColor(), cNight.getContrastColor());
assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha());
@@ -413,20 +415,26 @@ public class NotificationTest {
assertThat(c.getSecondaryTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
+ assertThat(c.getTertiaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
+ assertThat(c.getOnAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID);
assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID);
assertThat(c.getRippleAlpha()).isAtLeast(0x00);
assertThat(c.getRippleAlpha()).isAtMost(0xff);
- // Assert that various colors have sufficient contrast
+ // Assert that various colors have sufficient contrast with the background
assertContrastIsAtLeast(c.getPrimaryTextColor(), c.getBackgroundColor(), 4.5);
assertContrastIsAtLeast(c.getSecondaryTextColor(), c.getBackgroundColor(), 4.5);
assertContrastIsAtLeast(c.getPrimaryAccentColor(), c.getBackgroundColor(), 4.5);
assertContrastIsAtLeast(c.getErrorColor(), c.getBackgroundColor(), 4.5);
assertContrastIsAtLeast(c.getContrastColor(), c.getBackgroundColor(), 4.5);
- // This accent color is only used for emphasized buttons
+ // These colors are only used for emphasized buttons; they do not need contrast
assertContrastIsAtLeast(c.getSecondaryAccentColor(), c.getBackgroundColor(), 1);
+ assertContrastIsAtLeast(c.getTertiaryAccentColor(), c.getBackgroundColor(), 1);
+
+ // The text that is used within the accent color DOES need to have contrast
+ assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5);
}
private void assertContrastIsAtLeast(int foreground, int background, double minContrast) {
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index 8c05978ad03c..7a2c63f70dd1 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
+import org.junit.After;
import org.junit.Test;
/**
@@ -57,8 +58,70 @@ public class PropertyInvalidatedCacheTests {
}
}
+ // Clear the test mode after every test, in case this process is used for other tests.
+ @After
+ public void tearDown() throws Exception {
+ PropertyInvalidatedCache.setTestMode(false);
+ }
+
+ // This test is disabled pending an sepolicy change that allows any app to set the
+ // test property.
+ @Test
+ public void testBasicCache() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Create a cache that uses simple arithmetic to computer its values.
+ PropertyInvalidatedCache<Integer, Boolean> testCache =
+ new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ @Override
+ protected boolean bypass(Integer x) {
+ return x % 13 == 0;
+ }
+ };
+
+ PropertyInvalidatedCache.setTestMode(true);
+ PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY);
+
+ tester.verify(0);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(1);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(2);
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(3);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(5), testCache.query(5));
+ tester.verify(4);
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(4);
+
+ // Invalidate the cache, and verify that the next read on 3 goes to the server.
+ testCache.invalidateCache();
+ assertEquals(tester.value(3), testCache.query(3));
+ tester.verify(5);
+
+ // Test bypass. The query for 13 always bypasses the cache.
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(8);
+ assertEquals(tester.value(12), testCache.query(12));
+ assertEquals(tester.value(13), testCache.query(13));
+ assertEquals(tester.value(14), testCache.query(14));
+ tester.verify(9);
+ }
+
@Test
- public void testDisableCache1() {
+ public void testDisableCache() {
// A stand-in for the binder. The test verifies that calls are passed through to
// this class properly.
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index fb820cb2f5e5..6f17ea994699 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -286,7 +286,6 @@ public class ActivityThreadTest {
}
@Test
- @FlakyTest(bugId = 194242735)
public void testHandleActivityConfigurationChanged_EnsureUpdatesProcessedInOrder()
throws Exception {
final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index 4609c23bbbd1..0e5f2e1ae37b 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -26,6 +26,7 @@ import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.ViewNodeBuilder;
import android.app.assist.AssistStructure.ViewNodeParcelable;
import android.content.Context;
+import android.os.LocaleList;
import android.os.Parcel;
import android.os.SystemClock;
import android.text.InputFilter;
@@ -244,6 +245,34 @@ public class AssistStructureTest {
assertBigView(clone.getViewNode());
}
+ @Test
+ public void testViewNodeParcelableControlFlags() {
+ View view = newBigView();
+ mActivity.addView(view);
+ waitUntilViewsAreLaidOff();
+
+ assertThat(view.getViewRootImpl()).isNotNull();
+ ViewNodeBuilder viewStructure = new ViewNodeBuilder();
+ viewStructure.setAutofillId(view.getAutofillId());
+ view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
+
+ // Set highest and lowest control flags
+ viewStructure.setReceiveContentMimeTypes(new String[] {});
+ viewStructure.setLocaleList(new LocaleList());
+
+ ViewNodeParcelable viewNodeParcelable = new ViewNodeParcelable(viewStructure.getViewNode());
+
+ // Check properties on "original" view node.
+ assertBigView(viewNodeParcelable.getViewNode());
+ assertControlFlags(viewNodeParcelable.getViewNode());
+
+ // Check properties on "cloned" view node.
+ ViewNodeParcelable clone = cloneThroughParcel(viewNodeParcelable);
+ assertBigView(clone.getViewNode());
+ assertControlFlags(clone.getViewNode());
+
+ }
+
private EditText newSmallView() {
EditText view = new EditText(mContext);
view.setText("I AM GROOT");
@@ -297,6 +326,20 @@ public class AssistStructureTest {
assertThat(hint.charAt(BIG_VIEW_SIZE - 1)).isEqualTo(BIG_VIEW_CHAR);
}
+ /**
+ * Assert the lowest and highest bit control flags.
+ *
+ * The lowest and highest flags are {@link ViewNode#FLAGS_HAS_LOCALE_LIST} and
+ * {@link ViewNode#FLAGS_HAS_MIME_TYPES} respectively, so we check these two during parceling to
+ * make sure the entire range of control flags are copied.
+ *
+ * TODO: Need to change this test if the flag bits are added/changed in the future.
+ */
+ private void assertControlFlags(ViewNode view) {
+ assertThat(view.getReceiveContentMimeTypes()).isNotNull();
+ assertThat(view.getLocaleList()).isNotNull();
+ }
+
private ViewNodeParcelable cloneThroughParcel(ViewNodeParcelable viewNodeParcelable) {
Parcel parcel = Parcel.obtain();
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 9915e3852b8d..50639be57f22 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -156,7 +156,8 @@ public class ObjectPoolTests {
.setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
.setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
.setIsForward(true).setAssistToken(assistToken)
- .setShareableActivityToken(shareableActivityToken).build();
+ .setShareableActivityToken(shareableActivityToken)
+ .setTaskFragmentToken(new Binder()).build();
LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
LaunchActivityItem item = itemSupplier.get();
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 75da0bfba581..1173c9210ed5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -112,6 +112,7 @@ class TestUtils {
private IBinder mShareableActivityToken;
private FixedRotationAdjustments mFixedRotationAdjustments;
private boolean mLaunchedFromBubble;
+ private IBinder mTaskFragmentToken;
LaunchActivityItemBuilder setIntent(Intent intent) {
mIntent = intent;
@@ -213,13 +214,18 @@ class TestUtils {
return this;
}
+ LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) {
+ mTaskFragmentToken = taskFragmentToken;
+ return this;
+ }
+
LaunchActivityItem build() {
return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
null /* activityClientController */, mFixedRotationAdjustments,
- mShareableActivityToken, mLaunchedFromBubble);
+ mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index df0c64c810c6..98c9afd2eb6b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -209,6 +209,7 @@ public class TransactionParcelTests {
.setPendingNewIntents(referrerIntentList()).setIsForward(true)
.setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
.setShareableActivityToken(new Binder())
+ .setTaskFragmentToken(new Binder())
.build();
writeAndPrepareForReading(item);
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index d1776fb7e5c1..3d7d807ca53d 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -32,7 +32,6 @@ import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
-import android.inputmethodservice.InputMethodService;
import android.media.ImageReader;
import android.os.UserHandle;
import android.view.Display;
@@ -140,13 +139,6 @@ public class ContextTest {
}
@Test
- public void testIsUiContext_InputMethodService_returnsTrue() {
- final InputMethodService ims = new InputMethodService();
-
- assertTrue(ims.isUiContext());
- }
-
- @Test
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 5d75d9b3f70b..7f53776d9bdf 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -34,7 +34,6 @@ import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
@@ -574,7 +573,7 @@ public class PackageManagerTests extends AndroidTestCase {
ParsingPackage pkg;
- InstallParams(String outFileName, int rawResId) throws PackageParserException {
+ InstallParams(String outFileName, int rawResId) {
this.pkg = getParsedPackage(outFileName, rawResId);
this.packageURI = Uri.fromFile(new File(pkg.getPath()));
}
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 49b720cfba07..cf7e5c663ba7 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -15,19 +15,18 @@
*/
package android.content.pm;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.AUTH;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.PERMISSION;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.ROLLBACK;
-import static android.content.pm.PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
+import static android.content.pm.SigningDetails.CertCapabilities.AUTH;
+import static android.content.pm.SigningDetails.CertCapabilities.INSTALLED_DATA;
+import static android.content.pm.SigningDetails.CertCapabilities.PERMISSION;
+import static android.content.pm.SigningDetails.CertCapabilities.ROLLBACK;
+import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.content.pm.PackageParser.SigningDetails;
import android.util.ArraySet;
import android.util.PackageUtils;
@@ -990,11 +989,11 @@ public class SigningDetailsTest {
private void assertSigningDetailsContainsLineage(SigningDetails details,
String... pastSigners) {
// This method should only be invoked for results that contain a single signer.
- assertEquals(1, details.signatures.length);
- assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+ assertEquals(1, details.getSignatures().length);
+ assertTrue(details.getSignatures()[0].toCharsString().equalsIgnoreCase(
pastSigners[pastSigners.length - 1]));
Set<String> signatures = new ArraySet<>(pastSigners);
- for (Signature pastSignature : details.pastSigningCertificates) {
+ for (Signature pastSignature : details.getPastSigningCertificates()) {
assertTrue(signatures.remove(pastSignature.toCharsString()));
}
assertEquals(0, signatures.size());
diff --git a/core/tests/coretests/src/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
index 22f6ec0b24b1..701e6194d4ee 100644
--- a/core/tests/coretests/src/android/graphics/FontListParserTest.java
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.fail;
+import android.graphics.fonts.FontCustomizationParser;
import android.graphics.fonts.FontStyle;
import android.os.LocaleList;
import android.text.FontConfig;
@@ -46,6 +47,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -318,6 +320,52 @@ public final class FontListParserTest {
}
}
+ @Test
+ public void alias() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + " </family>"
+ + " <family name='custom-family'>"
+ + " <font>missing.ttf</font>"
+ + " </family>"
+ + " <alias name='custom-alias' to='sans-serif'/>"
+ + "</familyset>";
+ FontConfig config = readFamilies(xml, true /* include non-existing font files */);
+ List<FontConfig.Alias> aliases = config.getAliases();
+ assertThat(aliases.size()).isEqualTo(1);
+ assertThat(aliases.get(0).getName()).isEqualTo("custom-alias");
+ assertThat(aliases.get(0).getOriginal()).isEqualTo("sans-serif");
+ }
+
+ @Test
+ public void dropped_FamilyAlias() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<familyset>"
+ + " <family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + " </family>"
+ + " <family name='custom-family'>"
+ + " <font>missing.ttf</font>"
+ + " </family>"
+ + " <alias name='custom-alias' to='custom-family'/>"
+ + "</familyset>";
+ FontConfig config = readFamilies(xml, false /* exclude not existing file */);
+ assertThat(config.getAliases()).isEmpty();
+ }
+
+ private FontConfig readFamilies(String xml, boolean allowNonExisting)
+ throws IOException, XmlPullParserException {
+ ByteArrayInputStream buffer = new ByteArrayInputStream(
+ xml.getBytes(StandardCharsets.UTF_8));
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(buffer, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readFamilies(parser, "", new FontCustomizationParser.Result(), null,
+ 0L /* last modified date */, 0 /* config version */, allowNonExisting);
+ }
+
private FontConfig.FontFamily readFamily(String xml)
throws IOException, XmlPullParserException {
ByteArrayInputStream buffer = new ByteArrayInputStream(
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cdee4c32..fd7753b66a46 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -773,46 +773,51 @@ public class DeviceConfigTest {
try {
// Ensure the device starts in a known state.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
// Assert starting state.
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties1)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
// Test disabled (persistent). Persistence is not actually tested, that would require
// a host test.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
- assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_PERSISTENT);
assertThat(DeviceConfig.setProperties(properties2)).isFalse();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
// Return to not disabled.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties2)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE2);
// Test disabled (persistent). Absence of persistence is not actually tested, that would
// require a host test.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
- assertThat(DeviceConfig.isSyncDisabled()).isTrue();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT);
assertThat(DeviceConfig.setProperties(properties1)).isFalse();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE2);
// Return to not disabled.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
- assertThat(DeviceConfig.isSyncDisabled()).isFalse();
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ assertThat(DeviceConfig.getSyncDisabledMode())
+ .isEqualTo(Settings.Config.SYNC_DISABLED_MODE_NONE);
assertThat(DeviceConfig.setProperties(properties1)).isTrue();
assertThat(DeviceConfig.getProperties(NAMESPACE, KEY).getString(KEY, DEFAULT_VALUE))
.isEqualTo(VALUE);
} finally {
// Try to return to the default sync disabled state in case of failure.
- DeviceConfig.setSyncDisabled(Settings.Config.SYNC_DISABLED_MODE_NONE);
+ DeviceConfig.setSyncDisabledMode(Settings.Config.SYNC_DISABLED_MODE_NONE);
// NAMESPACE will be cleared by cleanUp()
}
@@ -827,4 +832,80 @@ public class DeviceConfigTest {
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
}
+ @Test
+ public void deleteProperty_nullNamespace() {
+ try {
+ DeviceConfig.deleteProperty(null, KEY);
+ Assert.fail("Null namespace should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deleteProperty_nullName() {
+ try {
+ DeviceConfig.deleteProperty(NAMESPACE, null);
+ Assert.fail("Null name should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deletePropertyString() {
+ final String value = "new_value";
+ final String default_value = "default";
+ DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyBoolean() {
+ final boolean value = true;
+ final boolean default_value = false;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyInt() {
+ final int value = 123;
+ final int default_value = 999;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyLong() {
+ final long value = 456789;
+ final long default_value = 123456;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyFloat() {
+ final float value = 456.789f;
+ final float default_value = 123.456f;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deleteProperty_empty() {
+ assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+ assertThat(result).isNull();
+ }
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 6301f32169f7..507638e45abe 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -698,15 +698,15 @@ public class InsetsControllerTest {
@Test
public void testRequestedState() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- final InsetsState state = mTestHost.getRequestedState();
+ final InsetsVisibilities request = mTestHost.getRequestedVisibilities();
mController.hide(statusBars() | navigationBars());
- assertFalse(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
- assertFalse(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
+ assertFalse(request.getVisibility(ITYPE_STATUS_BAR));
+ assertFalse(request.getVisibility(ITYPE_NAVIGATION_BAR));
mController.show(statusBars() | navigationBars());
- assertTrue(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR));
- assertTrue(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
+ assertTrue(request.getVisibility(ITYPE_STATUS_BAR));
+ assertTrue(request.getVisibility(ITYPE_NAVIGATION_BAR));
});
}
@@ -837,20 +837,20 @@ public class InsetsControllerTest {
public static class TestHost extends ViewRootInsetsControllerHost {
- private final InsetsState mRequestedState = new InsetsState();
+ private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
TestHost(ViewRootImpl viewRoot) {
super(viewRoot);
}
@Override
- public void onInsetsModified(InsetsState insetsState) {
- mRequestedState.set(insetsState, true);
- super.onInsetsModified(insetsState);
+ public void updateRequestedVisibilities(InsetsVisibilities visibilities) {
+ mRequestedVisibilities.set(visibilities);
+ super.updateRequestedVisibilities(visibilities);
}
- public InsetsState getRequestedState() {
- return mRequestedState;
+ public InsetsVisibilities getRequestedVisibilities() {
+ return mRequestedVisibilities;
}
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsVisibilitiesTest.java b/core/tests/coretests/src/android/view/InsetsVisibilitiesTest.java
new file mode 100644
index 000000000000..5664e0b0aa0f
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsVisibilitiesTest.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 android.view;
+
+import static android.view.InsetsState.FIRST_TYPE;
+import static android.view.InsetsState.LAST_TYPE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsState.InternalInsetsType;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link InsetsVisibilities}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:InsetsVisibilities
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class InsetsVisibilitiesTest {
+
+ @Test
+ public void testEquals() {
+ final InsetsVisibilities v1 = new InsetsVisibilities();
+ final InsetsVisibilities v2 = new InsetsVisibilities();
+ final InsetsVisibilities v3 = new InsetsVisibilities();
+ assertEquals(v1, v2);
+ assertEquals(v1, v3);
+
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ v1.setVisibility(type, false);
+ v2.setVisibility(type, false);
+ }
+ assertEquals(v1, v2);
+ assertNotEquals(v1, v3);
+
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ v1.setVisibility(type, true);
+ v2.setVisibility(type, true);
+ }
+ assertEquals(v1, v2);
+ assertNotEquals(v1, v3);
+ }
+
+ @Test
+ public void testSet() {
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ final InsetsVisibilities v1 = new InsetsVisibilities();
+ final InsetsVisibilities v2 = new InsetsVisibilities();
+
+ v1.setVisibility(type, true);
+ assertNotEquals(v1, v2);
+
+ v2.set(v1);
+ assertEquals(v1, v2);
+
+ v2.setVisibility(type, false);
+ assertNotEquals(v1, v2);
+
+ v1.set(v2);
+ assertEquals(v1, v2);
+ }
+ }
+
+ @Test
+ public void testCopyConstructor() {
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ final InsetsVisibilities v1 = new InsetsVisibilities();
+ v1.setVisibility(type, true);
+ final InsetsVisibilities v2 = new InsetsVisibilities(v1);
+ assertEquals(v1, v2);
+
+ v2.setVisibility(type, false);
+ assertNotEquals(v1, v2);
+ }
+ }
+
+ @Test
+ public void testGetterAndSetter() {
+ final InsetsVisibilities v1 = new InsetsVisibilities();
+ final InsetsVisibilities v2 = new InsetsVisibilities();
+
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ assertEquals(InsetsState.getDefaultVisibility(type), v1.getVisibility(type));
+ }
+
+ for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ v1.setVisibility(type, true);
+ assertTrue(v1.getVisibility(type));
+
+ v2.setVisibility(type, false);
+ assertFalse(v2.getVisibility(type));
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 623008eafe10..98ab592e344f 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -36,7 +36,6 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
public class KeyEventTest {
- private static final int ID = 0xabcdef;
private static final int DOWN_TIME = 50;
private static final long EVENT_TIME = 100;
private static final int ACTION = KeyEvent.ACTION_DOWN;
@@ -47,7 +46,6 @@ public class KeyEventTest {
private static final int SCAN_CODE = 0;
private static final int FLAGS = 0;
private static final int SOURCE = InputDevice.SOURCE_KEYBOARD;
- private static final byte[] HMAC = null;
private static final String CHARACTERS = null;
private static final int ID_SOURCE_MASK = 0x3 << 30;
@@ -164,8 +162,8 @@ public class KeyEventTest {
}
private static KeyEvent createKey() {
- return KeyEvent.obtain(ID, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
- DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
+ return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
+ DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, CHARACTERS);
}
private static void compareKeys(KeyEvent key1, KeyEvent key2) {
diff --git a/core/tests/coretests/src/android/view/ViewInputConnectionTest.java b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
index d667af39f585..d60c0c688e1a 100644
--- a/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
@@ -19,13 +19,25 @@ package android.view;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.annotation.DurationMillisLong;
import android.app.Instrumentation;
import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.text.InputType;
import android.text.format.DateUtils;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
@@ -45,12 +57,23 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
/**
* Tests for internal APIs/behaviors of {@link View} and {@link InputConnection}.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ViewInputConnectionTest {
+ @DurationMillisLong
+ private static final long TIMEOUT = 5000;
+ @DurationMillisLong
+ private static final long EXPECTED_TIMEOUT = 500;
+
@Rule
public ActivityTestRule<ViewInputConnectionTestActivity> mActivityRule =
new ActivityTestRule<>(ViewInputConnectionTestActivity.class);
@@ -289,4 +312,282 @@ public class ViewInputConnectionTest {
super.onInputConnectionClosedInternal();
}
}
+
+
+ @Test
+ public void testInputConnectionCallbacks_nonUiThread() throws Throwable {
+ try (InputConnectionHandlingThread thread = new InputConnectionHandlingThread()) {
+ final ViewGroup viewGroup = getOnMainSync(() -> mActivity.findViewById(R.id.root));
+ final TestOffThreadEditor editor = getOnMainSync(() -> {
+ final TestOffThreadEditor myEditor =
+ new TestOffThreadEditor(viewGroup.getContext(), thread.getHandler());
+ viewGroup.addView(myEditor);
+ myEditor.requestFocus();
+ return myEditor;
+ });
+
+ mInstrumentation.waitForIdleSync();
+
+ assertThat(editor.mOnCreateInputConnectionCalled.await(TIMEOUT, TimeUnit.MILLISECONDS))
+ .isTrue();
+
+ // Invalidate the currently used InputConnection by moving the focus to a new EditText.
+ mActivityRule.runOnUiThread(() -> {
+ final EditText editText = new EditText(viewGroup.getContext());
+ viewGroup.addView(editText);
+ editText.requestFocus();
+ });
+
+ // Make sure that InputConnection#closeConnection() gets called on the handler thread.
+ assertThat(editor.mInputConnectionClosedCalled.await(TIMEOUT, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(editor.mInputConnectionClosedCallingThreadId.get())
+ .isEqualTo(thread.getThreadId());
+
+ // Make sure that View#onInputConnectionClosed() is not yet dispatched, because
+ // InputConnection#closeConnection() is still blocked.
+ assertThat(editor.mOnInputConnectionClosedCalled.await(
+ EXPECTED_TIMEOUT, TimeUnit.MILLISECONDS)).isFalse();
+
+ // Unblock InputConnection#closeConnection()
+ editor.mInputConnectionClosedBlocker.countDown();
+
+ // Make sure that View#onInputConnectionClosed() is dispatched on the main thread.
+ assertThat(editor.mOnInputConnectionClosedCalled.await(TIMEOUT, TimeUnit.MILLISECONDS))
+ .isTrue();
+ assertThat(editor.mInputConnectionClosedBlockerTimedOut.get()).isFalse();
+ assertThat(editor.mOnInputConnectionClosedCallingThreadId.get())
+ .isEqualTo(getOnMainSync(Process::myTid));
+ }
+ }
+
+ private <T> T getOnMainSync(@NonNull Supplier<T> supplier) throws Throwable {
+ final AtomicReference<T> result = new AtomicReference<>();
+ mActivityRule.runOnUiThread(() -> result.set(supplier.get()));
+ return result.get();
+ }
+
+ private static class TestOffThreadEditor extends View {
+ private static final int TEST_VIEW_HEIGHT = 10;
+
+ public CountDownLatch mOnCreateInputConnectionCalled = new CountDownLatch(1);
+ public CountDownLatch mInputConnectionClosedCalled = new CountDownLatch(1);
+ public CountDownLatch mInputConnectionClosedBlocker = new CountDownLatch(1);
+ public AtomicBoolean mInputConnectionClosedBlockerTimedOut = new AtomicBoolean();
+ public AtomicReference<Integer> mInputConnectionClosedCallingThreadId =
+ new AtomicReference<>();
+
+ public CountDownLatch mOnInputConnectionClosedCalled = new CountDownLatch(1);
+ public AtomicReference<Integer> mOnInputConnectionClosedCallingThreadId =
+ new AtomicReference<>();
+
+ private final Handler mInputConnectionHandler;
+
+ TestOffThreadEditor(Context context, @NonNull Handler inputConnectionHandler) {
+ super(context);
+ setBackgroundColor(Color.YELLOW);
+ setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, TEST_VIEW_HEIGHT));
+ setFocusableInTouchMode(true);
+ setFocusable(true);
+ mInputConnectionHandler = inputConnectionHandler;
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ mOnCreateInputConnectionCalled.countDown();
+ outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
+ return new NoOpInputConnection() {
+ @Override
+ public Handler getHandler() {
+ return mInputConnectionHandler;
+ }
+
+ @Override
+ public void closeConnection() {
+ mInputConnectionClosedCallingThreadId.compareAndSet(null, Process.myTid());
+ mInputConnectionClosedCalled.countDown();
+ try {
+ if (mInputConnectionClosedBlocker.await(TIMEOUT, TimeUnit.MILLISECONDS)) {
+ return;
+ }
+ } catch (InterruptedException e) {
+ }
+ mInputConnectionClosedBlockerTimedOut.set(true);
+ }
+ };
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor() {
+ return true;
+ }
+
+ @Override
+ public void onInputConnectionClosedInternal() {
+ mOnInputConnectionClosedCallingThreadId.compareAndSet(null, Process.myTid());
+ mOnInputConnectionClosedCalled.countDown();
+ super.onInputConnectionClosedInternal();
+ }
+ }
+
+ static class NoOpInputConnection implements InputConnection {
+
+ @Override
+ public CharSequence getTextBeforeCursor(int n, int flags) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int n, int flags) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getSelectedText(int flags) {
+ return null;
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes) {
+ return 0;
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+ return null;
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ return false;
+ }
+
+ @Override
+ public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
+ return false;
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ return false;
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ return false;
+ }
+
+ @Override
+ public boolean finishComposingText() {
+ return false;
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+ return false;
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text) {
+ return false;
+ }
+
+ @Override
+ public boolean commitCorrection(CorrectionInfo correctionInfo) {
+ return false;
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ return false;
+ }
+
+ @Override
+ public boolean performEditorAction(int editorAction) {
+ return false;
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id) {
+ return false;
+ }
+
+ @Override
+ public boolean beginBatchEdit() {
+ return false;
+ }
+
+ @Override
+ public boolean endBatchEdit() {
+ return false;
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean clearMetaKeyStates(int states) {
+ return false;
+ }
+
+ @Override
+ public boolean reportFullscreenMode(boolean enabled) {
+ return false;
+ }
+
+ @Override
+ public boolean performPrivateCommand(String action, Bundle data) {
+ return false;
+ }
+
+ @Override
+ public boolean requestCursorUpdates(int cursorUpdateMode) {
+ return false;
+ }
+
+ @Override
+ public Handler getHandler() {
+ return null;
+ }
+
+ @Override
+ public void closeConnection() {
+
+ }
+
+ @Override
+ public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ return false;
+ }
+ }
+
+ private static final class InputConnectionHandlingThread extends HandlerThread
+ implements AutoCloseable {
+
+ private final Handler mHandler;
+
+ InputConnectionHandlingThread() {
+ super("IC-callback");
+ start();
+ mHandler = Handler.createAsync(getLooper());
+ }
+
+ @NonNull
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @Override
+ public void close() {
+ quitSafely();
+ try {
+ join(TIMEOUT);
+ } catch (InterruptedException e) {
+ fail("Failed to stop the thread: " + e);
+ }
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java
index 05e8bd8b6cab..0a99b08f58ff 100644
--- a/core/tests/coretests/src/android/view/WindowInfoTest.java
+++ b/core/tests/coretests/src/android/view/WindowInfoTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import android.app.ActivityTaskManager;
import android.os.IBinder;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -83,6 +84,7 @@ public class WindowInfoTest {
assertEquals(0, w.layer);
assertEquals(AccessibilityNodeInfo.UNDEFINED_NODE_ID, w.accessibilityIdOfAnchor);
assertEquals(Display.INVALID_DISPLAY, w.displayId);
+ assertEquals(ActivityTaskManager.INVALID_TASK_ID, w.taskId);
assertNull(w.title);
assertNull(w.token);
assertNull(w.childTokens);
@@ -123,6 +125,7 @@ public class WindowInfoTest {
windowInfo.displayId = 2;
windowInfo.layer = 3;
windowInfo.accessibilityIdOfAnchor = 4L;
+ windowInfo.taskId = 5;
windowInfo.title = "title";
windowInfo.token = mock(IBinder.class);
windowInfo.childTokens = new ArrayList<>();
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 46c96c94dc11..2c8c38532d10 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -21,6 +21,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.os.Parcel;
+import android.view.Display;
import androidx.test.runner.AndroidJUnit4;
@@ -41,14 +42,14 @@ public class AccessibilityEventTest {
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 34;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
// and assertEqualAccessibilityRecord
/** The number of properties of the {@link AccessibilityRecord} class. */
- private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 24;
+ private static final int A11Y_RECORD_NON_STATIC_FIELD_COUNT = 25;
@Test
public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
@@ -69,6 +70,14 @@ public class AccessibilityEventTest {
}
@Test
+ public void testSourceDisplayId_getSetWorkAcrossParceling() {
+ final int sourceDisplayId = Display.DEFAULT_DISPLAY;
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setDisplayId(sourceDisplayId);
+ assertEquals(sourceDisplayId, copyEventViaParcel(event).getDisplayId());
+ }
+
+ @Test
public void testWindowChanges_getSetWorkAcrossParceling() {
final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
| AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8643a37bba8d..3045d7d575d9 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -34,6 +34,8 @@ import java.util.List;
public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceConnection.Stub {
public void setServiceInfo(AccessibilityServiceInfo info) {}
+ public void setAttributionTag(String attributionTag) {}
+
public String[] findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, long threadId,
@@ -166,4 +168,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
public void logTrace(long timestamp, String where, String callingParams, int processId,
long threadId, int callingUid, Bundle callingStack) {}
+
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
}
diff --git a/core/tests/coretests/src/android/widget/ProgressBarTest.java b/core/tests/coretests/src/android/widget/ProgressBarTest.java
new file mode 100644
index 000000000000..5b92471101c4
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/ProgressBarTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ProgressBarTest {
+ private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private ProgressBar mBar;
+ private AccessibilityNodeInfo mInfo;
+
+ @Before
+ public void setUp() throws Exception {
+ // enable accessibility
+ mInstrumentation.getUiAutomation();
+ // create ProgressBar on main thread and call setProgress on main thread
+ mInstrumentation.runOnMainSync(() ->
+ mBar = new ProgressBar(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ null,
+ com.android.internal.R.attr.progressBarStyleHorizontal
+ )
+ );
+ mInfo = AccessibilityNodeInfo.obtain();
+ }
+
+ @After
+ public void tearDown() {
+ mInfo.recycle();
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_default() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("50%", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_determinateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(false);
+ assertFalse(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ mInstrumentation.runOnMainSync(() -> mBar.setProgress(50));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_default() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_viewApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ // A workaround for the not-attached ProgressBar.
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ info.setStateDescription(host.getStateDescription());
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ }
+ });
+
+ mBar.setStateDescription("custom state");
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ assertEquals("custom state", mBar.getStateDescription().toString());
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+
+ mBar.setStateDescription(null);
+
+ assertNull(mBar.getStateDescription());
+ mInfo.recycle();
+ mInfo = AccessibilityNodeInfo.obtain();
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("in progress", mInfo.getStateDescription().toString());
+ }
+
+ @Test
+ public void testStateDescription_indeterminateProgressBar_custom_accessibilityNodeInfoApi() {
+ mBar.setIndeterminate(true);
+ assertTrue(mBar.isIndeterminate());
+ mBar.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.setStateDescription("custom state");
+ }
+ });
+
+ // call setMax to invoke call to ProgressBar#onProgressRefresh()
+ mInstrumentation.runOnMainSync(() -> mBar.setMax(200));
+
+ mBar.onInitializeAccessibilityNodeInfo(mInfo);
+ assertEquals("custom state", mInfo.getStateDescription().toString());
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 020f4a06b892..a6e351d9cee7 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -22,12 +22,15 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+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.content.res.Configuration;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.view.IWindowManager;
@@ -38,6 +41,8 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for {@link WindowContextController}
@@ -53,15 +58,18 @@ import org.junit.runner.RunWith;
@Presubmit
public class WindowContextControllerTest {
private WindowContextController mController;
+ @Mock
private IWindowManager mMockWms;
+ @Mock
+ private WindowTokenClient mMockToken;
@Before
public void setUp() throws Exception {
- mMockWms = mock(IWindowManager.class);
- mController = new WindowContextController(new Binder(), mMockWms);
-
- doReturn(true).when(mMockWms).attachWindowContextToDisplayArea(any(), anyInt(),
- anyInt(), any());
+ MockitoAnnotations.initMocks(this);
+ mController = new WindowContextController(mMockToken, mMockWms);
+ doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
+ doReturn(new Configuration()).when(mMockWms).attachWindowContextToDisplayArea(any(),
+ anyInt(), anyInt(), any());
}
@Test(expected = IllegalStateException.class)
@@ -85,6 +93,8 @@ public class WindowContextControllerTest {
null /* options */);
assertThat(mController.mAttachedToDisplayArea).isTrue();
+ verify(mMockToken).onConfigurationChanged(any(), eq(DEFAULT_DISPLAY),
+ eq(false) /* shouldReportConfigChange */);
mController.detachIfNeeded();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
new file mode 100644
index 000000000000..045b3a27456f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.text.ParcelableSpan;
+import android.text.SpannableString;
+import android.text.style.LocaleSpan;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Unit tests for AccessibilityUtils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityUtilsTest {
+ @Test
+ public void textOrSpanChanged_stringChange_returnTextChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "b";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.TEXT, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_stringNotChange_returnNoneChange() {
+ final CharSequence beforeText = "a";
+
+ final CharSequence afterText = "a";
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToNonParcelableSpan_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonSpanToParcelableSpan_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final CharSequence beforeText = new SpannableString("a");
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanToParcelableSpan_returnParcelableSpanChange() {
+ final Object nonParcelableSpan = new Object();
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 0, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_nonParcelableSpanChange_returnNoneChange() {
+ final Object nonParcelableSpan = new Object();
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(nonParcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(nonParcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.NONE, type);
+ }
+
+ @Test
+ public void textOrSpanChanged_parcelableSpanChange_returnParcelableSpanChange() {
+ final ParcelableSpan parcelableSpan = new LocaleSpan(Locale.ENGLISH);
+ final SpannableString beforeText = new SpannableString("a");
+ beforeText.setSpan(parcelableSpan, 0, 1, 0);
+
+ final SpannableString afterText = new SpannableString("a");
+ afterText.setSpan(parcelableSpan, 1, 1, 0);
+
+ @AccessibilityUtils.A11yTextChangeType int type = AccessibilityUtils.textOrSpanChanged(
+ beforeText, afterText);
+ assertEquals(AccessibilityUtils.PARCELABLE_SPAN, type);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 96b4316ffafc..7cd8197ce1e4 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -23,6 +23,7 @@ import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION;
import static com.google.common.truth.Truth.assertThat;
@@ -50,6 +51,7 @@ import androidx.test.rule.ActivityTestRule;
import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before;
@@ -69,7 +71,6 @@ public class FrameTrackerTest {
public ActivityTestRule<ViewAttachTestActivity> mRule =
new ActivityTestRule<>(ViewAttachTestActivity.class);
- private FrameTracker mTracker;
private ThreadedRendererWrapper mRenderer;
private FrameMetricsWrapper mWrapper;
private SurfaceControlWrapper mSurfaceControlWrapper;
@@ -85,7 +86,6 @@ public class FrameTrackerTest {
View view = mActivity.getWindow().getDecorView();
assertThat(view.isAttachedToWindow()).isTrue();
- Handler handler = mRule.getActivity().getMainThreadHandler();
mWrapper = Mockito.spy(new FrameMetricsWrapper());
mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer()));
doNothing().when(mRenderer).addObserver(any());
@@ -103,229 +103,355 @@ public class FrameTrackerTest {
mListenerCapture.capture());
mChoreographer = mock(ChoreographerWrapper.class);
+ }
- Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
- mTracker = Mockito.spy(
+ private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
+ Handler handler = mRule.getActivity().getMainThreadHandler();
+ Session session = new Session(cuj, postfix);
+ Configuration config = mock(Configuration.class);
+ when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
+ when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
+ FrameTracker frameTracker = Mockito.spy(
new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
mSurfaceControlWrapper, mChoreographer, mWrapper,
- /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
- null));
- doNothing().when(mTracker).triggerPerfetto();
- doNothing().when(mTracker).postTraceStartMarker();
+ /* traceThresholdMissedFrames= */ 1,
+ /* traceThresholdFrameTimeMillis= */ -1,
+ /* FrameTrackerListener= */ null, config));
+ doNothing().when(frameTracker).triggerPerfetto();
+ doNothing().when(frameTracker).postTraceStartMarker();
+ return frameTracker;
}
@Test
public void testOnlyFirstWindowFrameOverThreshold() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
// Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
.then(unusedInvocation -> System.nanoTime());
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account
- sendFirstWindowFrame(100, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
// send another frame with a short duration - should not be considered janky
- sendFirstWindowFrame(5, JANK_NONE, 101L);
+ sendFirstWindowFrame(tracker, 5, JANK_NONE, 101L);
// end the trace session, the last janky frame is after the end() so is discarded.
when(mChoreographer.getVsyncId()).thenReturn(102L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(5, JANK_NONE, 102L);
- sendFrame(500, JANK_APP_DEADLINE_MISSED, 103L);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ sendFrame(tracker, 5, JANK_NONE, 102L);
+ sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
- verify(mTracker).removeObservers();
- verify(mTracker, never()).triggerPerfetto();
+ verify(tracker).removeObservers();
+ verify(tracker, never()).triggerPerfetto();
}
@Test
public void testSfJank() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
- sendFrame(4, JANK_NONE, 100L);
+ sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(4, JANK_NONE, 102L);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ sendFrame(tracker, 4, JANK_NONE, 102L);
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(mTracker).triggerPerfetto();
+ verify(tracker).triggerPerfetto();
}
@Test
public void testFirstFrameJankyNoTrigger() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// send first frame - janky
- sendFrame(40, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
// send another frame - not jank
- sendFrame(4, JANK_NONE, 101L);
+ sendFrame(tracker, 4, JANK_NONE, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(4, JANK_NONE, 102L);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ sendFrame(tracker, 4, JANK_NONE, 102L);
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(mTracker, never()).triggerPerfetto();
+ verify(tracker, never()).triggerPerfetto();
}
@Test
public void testOtherFrameOverThreshold() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
- sendFrame(4, JANK_NONE, 100L);
+ sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(40, JANK_APP_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(4, JANK_NONE, 102L);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ sendFrame(tracker, 4, JANK_NONE, 102L);
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(mTracker).triggerPerfetto();
+ verify(tracker).triggerPerfetto();
}
@Test
public void testLastFrameOverThresholdBeforeEnd() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// send first frame - not janky
- sendFrame(4, JANK_NONE, 100L);
+ sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - not janky
- sendFrame(4, JANK_NONE, 101L);
+ sendFrame(tracker, 4, JANK_NONE, 101L);
// end the trace session, simulate one more valid callback came after the end call.
when(mChoreographer.getVsyncId()).thenReturn(102L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
// One more callback with VSYNC after the end() vsync id.
- sendFrame(4, JANK_NONE, 103L);
+ sendFrame(tracker, 4, JANK_NONE, 103L);
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// We detected a janky frame - trigger Perfetto
- verify(mTracker).triggerPerfetto();
+ verify(tracker).triggerPerfetto();
}
@Test
public void testBeginCancel() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer).addObserver(any());
// First frame - not janky
- sendFrame(4, JANK_NONE, 100L);
+ sendFrame(tracker, 4, JANK_NONE, 100L);
// normal frame - not janky
- sendFrame(4, JANK_NONE, 101L);
+ sendFrame(tracker, 4, JANK_NONE, 101L);
// a janky frame
- sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
- mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
- verify(mTracker).removeObservers();
+ tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+ verify(tracker).removeObservers();
// Since the tracker has been cancelled, shouldn't trigger perfetto.
- verify(mTracker, never()).triggerPerfetto();
+ verify(tracker, never()).triggerPerfetto();
}
@Test
public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(101L);
- mTracker.end(FrameTracker.REASON_END_NORMAL);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
// Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel.
- verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
+ verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
// Observers should be removed in this case, or FrameTracker object will be leaked.
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel.
- verify(mTracker, never()).triggerPerfetto();
+ verify(tracker, never()).triggerPerfetto();
}
@Test
public void testCancelIfEndVsyncIdLessThanBeginVsyncId() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
when(mChoreographer.getVsyncId()).thenReturn(100L);
- mTracker.begin();
+ tracker.begin();
verify(mRenderer, only()).addObserver(any());
// end the trace session at the same vsync id, end vsync id will less than the begin one.
// Because the begin vsync id is supposed to the next frame,
- mTracker.end(FrameTracker.REASON_END_NORMAL);
+ tracker.end(FrameTracker.REASON_END_NORMAL);
// The begin vsync id (101) is larger than the end one (100), will be treat as cancel.
- verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
+ verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
// Observers should be removed in this case, or FrameTracker object will be leaked.
- verify(mTracker).removeObservers();
+ verify(tracker).removeObservers();
// Should never trigger Perfetto since it is a cancel.
- verify(mTracker, never()).triggerPerfetto();
+ verify(tracker, never()).triggerPerfetto();
}
@Test
public void testCancelWhenSessionNeverBegun() {
- mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
- verify(mTracker).removeObservers();
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
+ tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+ verify(tracker).removeObservers();
}
@Test
public void testEndWhenSessionNeverBegun() {
- mTracker.end(FrameTracker.REASON_END_NORMAL);
- verify(mTracker).removeObservers();
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
+ tracker.end(FrameTracker.REASON_END_NORMAL);
+ verify(tracker).removeObservers();
}
- private void sendFirstWindowFrame(long durationMillis,
+ @Test
+ public void testSurfaceOnlyOtherFrameJanky() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+ when(mChoreographer.getVsyncId()).thenReturn(100L);
+ tracker.begin();
+ verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+ // First frame - not janky
+ sendFrame(tracker, JANK_NONE, 100L);
+ // normal frame - not janky
+ sendFrame(tracker, JANK_NONE, 101L);
+ // a janky frame
+ sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
+ tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+ // an extra frame to trigger finish
+ sendFrame(tracker, JANK_NONE, 103L);
+
+ verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+ verify(tracker).triggerPerfetto();
+ }
+
+ @Test
+ public void testSurfaceOnlyFirstFrameJanky() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+ when(mChoreographer.getVsyncId()).thenReturn(100L);
+ tracker.begin();
+ verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+ // First frame - janky
+ sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+ // normal frame - not janky
+ sendFrame(tracker, JANK_NONE, 101L);
+ // normal frame - not janky
+ sendFrame(tracker, JANK_NONE, 102L);
+
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
+ tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+ // an extra frame to trigger finish
+ sendFrame(tracker, JANK_NONE, 103L);
+
+ verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+ verify(tracker, never()).triggerPerfetto();
+ }
+
+ @Test
+ public void testSurfaceOnlyLastFrameJanky() {
+ FrameTracker tracker = spyFrameTracker(
+ CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+ when(mChoreographer.getVsyncId()).thenReturn(100L);
+ tracker.begin();
+ verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+ // First frame - not janky
+ sendFrame(tracker, JANK_NONE, 100L);
+ // normal frame - not janky
+ sendFrame(tracker, JANK_NONE, 101L);
+ // normal frame - not janky
+ sendFrame(tracker, JANK_NONE, 102L);
+
+ when(mChoreographer.getVsyncId()).thenReturn(102L);
+ tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+ // janky frame, should be ignored, trigger finish
+ sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+
+ verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+ verify(tracker, never()).triggerPerfetto();
+ }
+
+ private void sendFirstWindowFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId) {
- sendFrame(durationMillis, jankType, vsyncId, true /* firstWindowFrame */);
+ sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ true);
}
- private void sendFrame(long durationMillis,
+ private void sendFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId) {
- sendFrame(durationMillis, jankType, vsyncId, false /* firstWindowFrame */);
+ sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ false);
+ }
+
+ /**
+ * Used for surface only test.
+ */
+ private void sendFrame(FrameTracker tracker, @JankType int jankType, long vsyncId) {
+ sendFrame(tracker, /* durationMillis= */ -1,
+ jankType, vsyncId, /* firstWindowFrame= */ false);
}
- private void sendFrame(long durationMillis,
+ private void sendFrame(FrameTracker tracker, long durationMillis,
@JankType int jankType, long vsyncId, boolean firstWindowFrame) {
- when(mWrapper.getTiming()).thenReturn(new long[] { 0, vsyncId });
- doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper)
- .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
- doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
- .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
- mTracker.onFrameMetricsAvailable(0);
+ if (!tracker.mSurfaceOnly) {
+ when(mWrapper.getTiming()).thenReturn(new long[]{0, vsyncId});
+ doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper)
+ .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
+ doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
+ .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
+ tracker.onFrameMetricsAvailable(0);
+ }
mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
new JankData(vsyncId, jankType)
});
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index c153b38d3f02..d7a5e2613175 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,8 +16,6 @@
package com.android.internal.jank;
-import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
-import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
@@ -25,17 +23,17 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
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.os.Handler;
import android.os.HandlerThread;
-import android.os.Message;
import android.provider.DeviceConfig;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -43,8 +41,12 @@ import android.view.ViewAttachTestActivity;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
+import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.jank.InteractionJankMonitor.Session;
import org.junit.Before;
@@ -92,12 +94,15 @@ public class InteractionJankMonitorTest {
verify(mWorker).start();
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
+ Configuration config = mock(Configuration.class);
+ when(config.isSurfaceOnly()).thenReturn(false);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
- mock(FrameTracker.ChoreographerWrapper.class),
- new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
- /*traceThresholdFrameTimeMillis=*/ -1, null));
+ new ViewRootWrapper(mView.getViewRootImpl()),
+ new SurfaceControlWrapper(), mock(ChoreographerWrapper.class),
+ new FrameMetricsWrapper(),
+ /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
+ /* FrameTrackerListener */ null, config));
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).triggerPerfetto();
doNothing().when(tracker).postTraceStartMarker();
@@ -138,28 +143,30 @@ public class InteractionJankMonitorTest {
public void testBeginCancel() {
InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
- ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
+ Configuration config = mock(Configuration.class);
+ when(config.isSurfaceOnly()).thenReturn(false);
FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
new ThreadedRendererWrapper(mView.getThreadedRenderer()),
- new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
- mock(FrameTracker.ChoreographerWrapper.class),
- new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
- /*traceThresholdFrameTimeMillis=*/ -1, null));
+ new ViewRootWrapper(mView.getViewRootImpl()),
+ new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class),
+ new FrameMetricsWrapper(),
+ /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
+ /* FrameTrackerListener */ null, config));
doReturn(tracker).when(monitor).createFrameTracker(any(), any());
doNothing().when(tracker).triggerPerfetto();
doNothing().when(tracker).postTraceStartMarker();
assertThat(monitor.begin(mView, session.getCuj())).isTrue();
verify(tracker).begin();
- verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(),
- anyLong());
- Runnable runnable = captor.getValue().getCallback();
+ verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
+ Runnable runnable = captor.getValue();
assertThat(runnable).isNotNull();
mWorker.getThreadHandler().removeCallbacks(runnable);
runnable.run();
- verify(tracker).cancel(FrameTracker.REASON_CANCEL_NORMAL);
+ verify(tracker).cancel(FrameTracker.REASON_CANCEL_TIMEOUT);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index b6878013b254..25bc1ee149ad 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -38,7 +38,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
/** Test that BatteryStatsImpl.Uid.mOnBatteryBackgroundTimeBase works correctly. */
@SmallTest
public void testBgTimeBase() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long cur = 0; // realtime in us
@@ -106,7 +106,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
/** Test that BatteryStatsImpl.Uid.mOnBatteryScreenOffBackgroundTimeBase works correctly. */
@SmallTest
public void testScreenOffBgTimeBase() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long cur = 0; // realtime in us
@@ -154,7 +154,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
@SmallTest
public void testWifiScan() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -207,7 +207,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
}
private void doTestAppBluetoothScanInternal(WorkSource ws) throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -276,7 +276,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
@SmallTest
public void testJob() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
final String jobName = "job_name";
long curr = 0; // realtime in us
@@ -337,7 +337,7 @@ public class BatteryStatsBackgroundStatsTest extends TestCase {
@SmallTest
public void testSyncs() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
final String syncName = "sync_name";
long curr = 0; // realtime in us
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 85f9c97629e3..8ca0da022559 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -46,7 +46,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
*/
@Test
public void testNoteBinderCallStats() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
int callingUid = Process.FIRST_APPLICATION_UID + 1;
@@ -89,7 +89,7 @@ public class BatteryStatsBinderCallStatsTest extends TestCase {
@Test
public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
int callingUid = Process.FIRST_APPLICATION_UID + 1;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
index ade3a991b576..6ff2b6411282 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCounterTest.java
@@ -30,7 +30,7 @@ public class BatteryStatsCounterTest extends TestCase {
@SmallTest
public void testCounter() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
@@ -70,7 +70,7 @@ public class BatteryStatsCounterTest extends TestCase {
@SmallTest
public void testParceling() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 54d8701c5a6e..1ac89eac241e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -96,7 +96,7 @@ public class BatteryStatsCpuTimesTest {
@Mock
PowerProfile mPowerProfile;
- private MockClocks mClocks;
+ private MockClock mClocks;
private MockBatteryStatsImpl mBatteryStatsImpl;
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -104,7 +104,7 @@ public class BatteryStatsCpuTimesTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mClocks = new MockClocks();
+ mClocks = new MockClock();
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
.setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
.setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
@@ -1263,21 +1263,21 @@ public class BatteryStatsCpuTimesTest {
mBatteryStatsImpl.new UidToRemove(1, mClocks.elapsedRealtime()));
mBatteryStatsImpl.getPendingRemovedUids().add(
mBatteryStatsImpl.new UidToRemove(5, 10, mClocks.elapsedRealtime()));
- mBatteryStatsImpl.clearPendingRemovedUids();
+ mBatteryStatsImpl.clearPendingRemovedUidsLocked();
assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
mClocks.realtime = mClocks.uptime = 100_000;
- mBatteryStatsImpl.clearPendingRemovedUids();
+ mBatteryStatsImpl.clearPendingRemovedUidsLocked();
assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
mClocks.realtime = mClocks.uptime = 200_000;
mBatteryStatsImpl.getPendingRemovedUids().add(
mBatteryStatsImpl.new UidToRemove(100, mClocks.elapsedRealtime()));
- mBatteryStatsImpl.clearPendingRemovedUids();
+ mBatteryStatsImpl.clearPendingRemovedUidsLocked();
assertEquals(3, mBatteryStatsImpl.getPendingRemovedUids().size());
mClocks.realtime = mClocks.uptime = 400_000;
- mBatteryStatsImpl.clearPendingRemovedUids();
+ mBatteryStatsImpl.clearPendingRemovedUidsLocked();
assertEquals(1, mBatteryStatsImpl.getPendingRemovedUids().size());
verify(mCpuUidActiveTimeReader).removeUid(1);
verify(mCpuUidActiveTimeReader).removeUidsInRange(5, 10);
@@ -1289,7 +1289,7 @@ public class BatteryStatsCpuTimesTest {
verify(mCpuUidUserSysTimeReader).removeUidsInRange(5, 10);
mClocks.realtime = mClocks.uptime = 800_000;
- mBatteryStatsImpl.clearPendingRemovedUids();
+ mBatteryStatsImpl.clearPendingRemovedUidsLocked();
assertEquals(0, mBatteryStatsImpl.getPendingRemovedUids().size());
verify(mCpuUidActiveTimeReader).removeUid(100);
verify(mCpuUidClusterTimeReader).removeUid(100);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
index efb871027830..678d3eeaef62 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDualTimerTest.java
@@ -29,7 +29,7 @@ public class BatteryStatsDualTimerTest extends TestCase {
@SmallTest
public void testResetDetach() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
clocks.realtime = clocks.uptime = 100;
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
index 78fa3fb734d6..b7edb174a296 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsDurationTimerTest.java
@@ -33,7 +33,7 @@ public class BatteryStatsDurationTimerTest extends TestCase {
@SmallTest
public void testStartStop() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
@@ -105,7 +105,7 @@ public class BatteryStatsDurationTimerTest extends TestCase {
*/
@SmallTest
public void testReset() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
@@ -141,7 +141,7 @@ public class BatteryStatsDurationTimerTest extends TestCase {
*/
@SmallTest
public void testResetAndDetach() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
@@ -168,7 +168,7 @@ public class BatteryStatsDurationTimerTest extends TestCase {
@SmallTest
public void testParceling() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
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 464412f17722..a43d32d00bb6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -60,7 +60,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testNoteBluetoothScanResultLocked() throws Exception {
- MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClock());
bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
@@ -89,7 +89,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testNoteStartWakeLocked() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
int pid = 10;
@@ -120,7 +120,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testNoteUidProcessStateLocked() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
// map of ActivityManager process states and how long to simulate run time in each state
@@ -205,7 +205,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testUpdateTimeBasesLocked() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.updateTimeBasesLocked(false, Display.STATE_OFF, 0, 0);
@@ -229,7 +229,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testNoteScreenStateLocked() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
@@ -258,7 +258,7 @@ public class BatteryStatsNoteTest extends TestCase {
*/
@SmallTest
public void testNoteScreenStateTimersLocked() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
clocks.realtime = clocks.uptime = 100;
@@ -295,9 +295,8 @@ public class BatteryStatsNoteTest extends TestCase {
}
@SmallTest
- @SkipPresubmit("b/180015146")
public void testAlarmStartAndFinishLocked() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -333,9 +332,8 @@ public class BatteryStatsNoteTest extends TestCase {
}
@SmallTest
- @SkipPresubmit("b/180015146")
public void testAlarmStartAndFinishLocked_workSource() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -379,7 +377,7 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteWakupAlarmLocked() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -397,7 +395,7 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteWakupAlarmLocked_workSource_uid() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -430,7 +428,7 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteWakupAlarmLocked_workSource_workChain() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -456,7 +454,7 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteGpsChanged() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -480,7 +478,7 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testNoteGpsChanged_workSource() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.setRecordAllHistoryLocked(true);
bi.forceRecordAllHistory();
@@ -504,9 +502,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testUpdateDisplayMeasuredEnergyStatsLocked() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
clocks.realtime = 0;
int screen = Display.STATE_OFF;
@@ -589,9 +587,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testUpdateCustomMeasuredEnergyStatsLocked_neverCalled() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
bi.setOnBatteryInternal(true);
final int uid1 = 11500;
@@ -603,9 +601,9 @@ public class BatteryStatsNoteTest extends TestCase {
@SmallTest
public void testUpdateCustomMeasuredEnergyStatsLocked() {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
- bi.initMeasuredEnergyStats();
+ bi.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
final int bucketA = 0; // Custom bucket 0
final int bucketB = 1; // Custom bucket 1
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index dd814e651ede..0d2249f33e98 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -30,7 +30,7 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
@SmallTest
public void testSettingStalePreservesData() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks,
Mockito.mock(BatteryStatsImpl.TimeBase.class));
@@ -56,9 +56,8 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
}
@SmallTest
- @SkipPresubmit("b/180015146")
public void testEndSampleAndContinueWhenTimeOrCountDecreases() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
final BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks,
timeBase);
@@ -72,7 +71,10 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
assertEquals(0, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
// This is less than we currently have, so we will end the sample. Time isn't running, so
- // nothing should happen.
+ // nothing should happen, except that tracking will stop.
+ timer.update(0, 0, SystemClock.elapsedRealtime() * 1000);
+
+ // Start tracking again
timer.update(0, 0, SystemClock.elapsedRealtime() * 1000);
assertEquals(0, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -86,9 +88,13 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
assertEquals(100, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(10, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
- // This is less than we currently have, so we should end our sample and continue with the
- // entire amount updated here.
- timer.update(50, 5, SystemClock.elapsedRealtime() * 1000);
+ // This is less than we currently have, so we should end our sample.
+ timer.update(30, 3, SystemClock.elapsedRealtime() * 1000);
+
+ // Restart tracking
+ timer.update(30, 3, SystemClock.elapsedRealtime() * 1000);
+
+ timer.add(50, 5);
assertEquals(150, timer.getTotalTimeLocked(200, BatteryStats.STATS_SINCE_CHARGED));
assertEquals(15, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
@@ -101,7 +107,7 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
@SmallTest
public void testFirstUpdateIsAbsorbed() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
BatteryStatsImpl.SamplingTimer timer = new BatteryStatsImpl.SamplingTimer(clocks, timeBase);
@@ -140,7 +146,7 @@ public class BatteryStatsSamplingTimerTest extends TestCase {
@SmallTest
public void testSampleTimerSummaryParceling() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
clocks.realtime = 0;
clocks.uptime = 0;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index b851f0ad3414..f833981da1b5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -34,7 +34,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testSensorStartStop() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.mForceOnBattery = true;
clocks.realtime = 100;
@@ -71,7 +71,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testCountingWhileOffBattery() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -107,7 +107,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testCountingWhileOnBattery() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -142,7 +142,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testBatteryStatusOnToOff() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -188,7 +188,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testBatteryStatusOffToOn() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
long curr = 0; // realtime in us
@@ -240,7 +240,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testPooledBackgroundUsage() throws Exception {
final int UID_2 = 20000; // second uid for testing pool usage
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.mForceOnBattery = true;
long curr = 0; // realtime in us
@@ -377,7 +377,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testSensorReset() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
bi.mForceOnBattery = true;
clocks.realtime = 100;
@@ -421,7 +421,7 @@ public class BatteryStatsSensorTest extends TestCase {
@SmallTest
public void testSensorResetTimes() throws Exception {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
final int which = BatteryStats.STATS_SINCE_CHARGED;
bi.mForceOnBattery = true;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
index f76f31619f79..94092f148c60 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsStopwatchTimerTest.java
@@ -31,7 +31,7 @@ public class BatteryStatsStopwatchTimerTest extends TestCase {
// negative values of count.
@SmallTest
public void testCount() throws Exception {
- final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
timeBase.init(clocks.uptimeMillis(), clocks.elapsedRealtime());
final BatteryStatsImpl.StopwatchTimer timer = new BatteryStatsImpl.StopwatchTimer(clocks,
@@ -142,7 +142,7 @@ public class BatteryStatsStopwatchTimerTest extends TestCase {
assertEquals(expectedCount, timer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
}
- private static long updateTime(MockClocks clocks, long time) {
+ private static long updateTime(MockClock clocks, long time) {
return clocks.realtime = clocks.uptime = time;
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
index 11a01b3a42dc..be3fc8a2f59c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTimerTest.java
@@ -22,7 +22,6 @@ import android.util.StringBuilderPrinter;
import androidx.test.filters.SmallTest;
-import com.android.internal.os.BatteryStatsImpl.Clocks;
import com.android.internal.os.BatteryStatsImpl.TimeBase;
import com.android.internal.os.BatteryStatsImpl.Timer;
@@ -41,12 +40,12 @@ public class BatteryStatsTimerTest extends TestCase {
int nextComputeCurrentCount;
- TestTimer(Clocks clocks, int type, TimeBase timeBase, Parcel in) {
- super(clocks, type, timeBase, in);
+ TestTimer(Clock clock, int type, TimeBase timeBase, Parcel in) {
+ super(clock, type, timeBase, in);
}
- TestTimer(Clocks clocks, int type, TimeBase timeBase) {
- super(clocks, type, timeBase);
+ TestTimer(Clock clock, int type, TimeBase timeBase) {
+ super(clock, type, timeBase);
}
@Override
@@ -91,7 +90,7 @@ public class BatteryStatsTimerTest extends TestCase {
@SmallTest
public void testRunning() throws Exception {
TimeBase timeBase = new TimeBase();
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.nextComputeCurrentCount = 3000;
@@ -112,7 +111,7 @@ public class BatteryStatsTimerTest extends TestCase {
@SmallTest
public void testParceling() throws Exception {
TimeBase timeBase = new TimeBase();
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
// Test write then read
TestTimer timer1 = new TestTimer(clocks, 0, timeBase);
@@ -157,7 +156,7 @@ public class BatteryStatsTimerTest extends TestCase {
@SmallTest
public void testResetNoDetach() throws Exception {
TimeBase timeBase = new TimeBase();
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setCount(1);
@@ -180,7 +179,7 @@ public class BatteryStatsTimerTest extends TestCase {
@SmallTest
public void testResetDetach() throws Exception {
TimeBase timeBase = new TimeBase();
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setCount(1);
@@ -208,7 +207,7 @@ public class BatteryStatsTimerTest extends TestCase {
Assert.assertEquals(40, timeBase.getRealtime(200));
// the past uptime is 35 and the past runtime is 40
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer1 = new TestTimer(clocks, 0, timeBase);
timer1.setCount(1);
@@ -250,7 +249,7 @@ public class BatteryStatsTimerTest extends TestCase {
timeBase.setRunning(false, 45, 60);
Assert.assertEquals(40, timeBase.getRealtime(200));
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setCount(1);
@@ -275,7 +274,7 @@ public class BatteryStatsTimerTest extends TestCase {
timeBase.setRunning(false, 45, 60);
Assert.assertEquals(40, timeBase.getRealtime(200));
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setCount(1);
@@ -296,7 +295,7 @@ public class BatteryStatsTimerTest extends TestCase {
timeBase.setRunning(false, 45, 60);
Assert.assertEquals(40, timeBase.getRealtime(200));
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setCount(1);
@@ -313,7 +312,7 @@ public class BatteryStatsTimerTest extends TestCase {
@SmallTest
public void testLogState() throws Exception {
TimeBase timeBase = new TimeBase();
- MockClocks clocks = new MockClocks();
+ MockClock clocks = new MockClock();
TestTimer timer = new TestTimer(clocks, 0, timeBase);
timer.setTotalTime(100);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
index 4df3190cc2d7..9270346c0388 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUidTest.java
@@ -27,7 +27,7 @@ public class BatteryStatsUidTest extends TestCase {
private static final String TAG = "BatteryStatsTimeBaseTest";
static class TestBsi extends BatteryStatsImpl {
- TestBsi(MockClocks clocks) {
+ TestBsi(MockClock clocks) {
super(clocks);
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
index e90bcb76e457..de50dcbf6c00 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
@@ -51,6 +51,7 @@ public class BatteryStatsUserLifecycleTests {
private static final long POLL_INTERVAL_MS = 500;
private static final long USER_REMOVE_TIMEOUT_MS = 5_000;
private static final long STOP_USER_TIMEOUT_MS = 10_000;
+ private static final long USER_UIDS_REMOVE_TIMEOUT_MS = 15_000;
private static final long BATTERYSTATS_POLLING_TIMEOUT_MS = 5_000;
private static final String CPU_DATA_TAG = "cpu";
@@ -78,7 +79,6 @@ public class BatteryStatsUserLifecycleTests {
}
@Test
- @SkipPresubmit("b/180015146")
public void testNoCpuDataForRemovedUser() throws Exception {
mIam.startUserInBackground(mTestUserId);
waitUntilTrue("No uids for started user " + mTestUserId,
@@ -108,7 +108,7 @@ public class BatteryStatsUserLifecycleTests {
return true;
}, USER_REMOVE_TIMEOUT_MS);
waitUntilTrue("Uids still found for removed user " + mTestUserId,
- () -> getNumberOfUidsInBatteryStats() == 0, BATTERYSTATS_POLLING_TIMEOUT_MS);
+ () -> getNumberOfUidsInBatteryStats() == 0, USER_UIDS_REMOVE_TIMEOUT_MS);
}
@After
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index 0147cdb186f3..74b6dbe76a16 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -18,6 +18,9 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.app.ActivityManager;
import android.content.Context;
import android.os.BatteryConsumer;
@@ -263,6 +266,39 @@ public class BatteryUsageStatsProviderTest {
.of(180.0);
}
+ @Test
+ public void testAggregateBatteryStats_incompatibleSnapshot() {
+ Context context = InstrumentationRegistry.getContext();
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
+
+ BatteryUsageStatsStore batteryUsageStatsStore = mock(BatteryUsageStatsStore.class);
+
+ when(batteryUsageStatsStore.listBatteryUsageStatsTimestamps())
+ .thenReturn(new long[]{1000, 2000});
+
+ when(batteryUsageStatsStore.loadBatteryUsageStats(1000)).thenReturn(
+ new BatteryUsageStats.Builder(batteryStats.getCustomEnergyConsumerNames())
+ .setStatsDuration(1234).build());
+
+ // Add a snapshot, with a different set of custom power components. It should
+ // be skipped by the aggregation.
+ when(batteryUsageStatsStore.loadBatteryUsageStats(2000)).thenReturn(
+ new BatteryUsageStats.Builder(new String[]{"different"})
+ .setStatsDuration(4321).build());
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+ batteryStats, batteryUsageStatsStore);
+
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(0, 3000)
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+ assertThat(stats.getCustomPowerComponentNames())
+ .isEqualTo(batteryStats.getCustomEnergyConsumerNames());
+ assertThat(stats.getStatsDuration()).isEqualTo(1234);
+ }
+
private static class TestHandler extends Handler {
TestHandler() {
super(Looper.getMainLooper());
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 083090c54619..e7fa6566f16f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -50,7 +50,7 @@ public class BatteryUsageStatsRule implements TestRule {
.build();
private final PowerProfile mPowerProfile;
- private final MockClocks mMockClocks = new MockClocks();
+ private final MockClock mMockClock = new MockClock();
private final MockBatteryStatsImpl mBatteryStats;
private BatteryUsageStats mBatteryUsageStats;
@@ -63,8 +63,8 @@ public class BatteryUsageStatsRule implements TestRule {
public BatteryUsageStatsRule(long currentTime) {
Context context = InstrumentationRegistry.getContext();
mPowerProfile = spy(new PowerProfile(context, true /* forTest */));
- mMockClocks.currentTime = currentTime;
- mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
+ mMockClock.currentTime = currentTime;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock);
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.onSystemReady();
}
@@ -166,12 +166,12 @@ public class BatteryUsageStatsRule implements TestRule {
}
public void setTime(long realtimeMs, long uptimeMs) {
- mMockClocks.realtime = realtimeMs;
- mMockClocks.uptime = uptimeMs;
+ mMockClock.realtime = realtimeMs;
+ mMockClock.uptime = uptimeMs;
}
public void setCurrentTime(long currentTimeMs) {
- mMockClocks.currentTime = currentTimeMs;
+ mMockClock.currentTime = currentTimeMs;
}
BatteryUsageStats apply(PowerCalculator... calculators) {
@@ -191,7 +191,7 @@ public class BatteryUsageStatsRule implements TestRule {
}
for (PowerCalculator calculator : calculators) {
- calculator.calculate(builder, mBatteryStats, mMockClocks.realtime, mMockClocks.uptime,
+ calculator.calculate(builder, mBatteryStats, mMockClock.realtime, mMockClock.uptime,
query);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
index e478cd776558..51f20f380475 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -47,7 +47,7 @@ import java.util.Arrays;
public class BatteryUsageStatsStoreTest {
private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
- private final MockClocks mMockClocks = new MockClocks();
+ private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStats;
private BatteryUsageStatsStore mBatteryUsageStatsStore;
private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
@@ -55,8 +55,8 @@ public class BatteryUsageStatsStoreTest {
@Before
public void setup() {
- mMockClocks.currentTime = 123;
- mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
+ mMockClock.currentTime = 123;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock);
mBatteryStats.setNoAutoReset(true);
mBatteryStats.setPowerProfile(mock(PowerProfile.class));
mBatteryStats.onSystemReady();
@@ -75,7 +75,7 @@ public class BatteryUsageStatsStoreTest {
@Test
public void testStoreSnapshot() {
- mMockClocks.currentTime = 1_600_000;
+ mMockClock.currentTime = 1_600_000;
prepareBatteryStats();
mBatteryStats.resetAllStatsCmdLocked();
@@ -99,9 +99,9 @@ public class BatteryUsageStatsStoreTest {
public void testGarbageCollectOldSnapshots() throws Exception {
prepareBatteryStats();
- mMockClocks.realtime = 10_000_000;
- mMockClocks.uptime = 10_000_000;
- mMockClocks.currentTime = 10_000_000;
+ mMockClock.realtime = 10_000_000;
+ mMockClock.uptime = 10_000_000;
+ mMockClock.currentTime = 10_000_000;
final int snapshotFileSize = getSnapshotFileSize();
final int numberOfSnapshots =
@@ -109,9 +109,9 @@ public class BatteryUsageStatsStoreTest {
for (int i = 0; i < numberOfSnapshots + 2; i++) {
mBatteryStats.resetAllStatsCmdLocked();
- mMockClocks.realtime += 10_000_000;
- mMockClocks.uptime += 10_000_000;
- mMockClocks.currentTime += 10_000_000;
+ mMockClock.realtime += 10_000_000;
+ mMockClock.uptime += 10_000_000;
+ mMockClock.currentTime += 10_000_000;
prepareBatteryStats();
}
@@ -137,11 +137,11 @@ public class BatteryUsageStatsStoreTest {
private void prepareBatteryStats() {
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
/* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
- mMockClocks.realtime, mMockClocks.uptime, mMockClocks.currentTime);
+ mMockClock.realtime, mMockClock.uptime, mMockClock.currentTime);
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
/* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
- mMockClocks.realtime + 500_000, mMockClocks.uptime + 500_000,
- mMockClocks.currentTime + 500_000);
+ mMockClock.realtime + 500_000, mMockClock.uptime + 500_000,
+ mMockClock.currentTime + 500_000);
}
private void clearDirectory(File dir) {
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 fedbf7a9868e..c58df4e64163 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -228,7 +228,7 @@ public class BatteryUsageStatsTest {
}
private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryUsageStats.Builder builder =
@@ -267,7 +267,7 @@ public class BatteryUsageStatsTest {
}
private BatteryUsageStats.Builder buildBatteryUsageStats2(String[] customPowerComponentNames) {
- final MockClocks clocks = new MockClocks();
+ final MockClock clocks = new MockClock();
final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
final BatteryUsageStats.Builder builder =
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index b59b84bb15ba..63e13fd071c6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -186,7 +186,6 @@ public class BstatsCpuTimesValidationTest {
sPerProcStateTimesAvailable = fgCpuTimes != null;
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testCpuFreqTimes() throws Exception {
if (!sCpuFreqTimesAvailable) {
@@ -215,7 +214,6 @@ public class BstatsCpuTimesValidationTest {
batteryOffScreenOn();
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testCpuFreqTimes_screenOff() throws Exception {
if (!sCpuFreqTimesAvailable) {
@@ -278,7 +276,6 @@ public class BstatsCpuTimesValidationTest {
batteryOffScreenOn();
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testCpuFreqTimes_stateTop() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
@@ -312,7 +309,6 @@ public class BstatsCpuTimesValidationTest {
batteryOffScreenOn();
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testIsolatedCpuFreqTimes_stateService() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
@@ -354,7 +350,6 @@ public class BstatsCpuTimesValidationTest {
batteryOffScreenOn();
}
- @SkipPresubmit("b/185960974 flaky")
@Test
public void testCpuFreqTimes_stateTopSleeping() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
@@ -389,7 +384,6 @@ public class BstatsCpuTimesValidationTest {
}
@Test
- @SkipPresubmit("b/183225190 flaky")
public void testCpuFreqTimes_stateFgService() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
Log.w(TAG, "Skipping " + testName.getMethodName()
@@ -455,7 +449,6 @@ public class BstatsCpuTimesValidationTest {
batteryOff();
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testCpuFreqTimes_stateBg() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
@@ -522,7 +515,6 @@ public class BstatsCpuTimesValidationTest {
batteryOffScreenOn();
}
- @SkipPresubmit("b/184201598 flaky")
@Test
public void testCpuFreqTimes_trackingDisabled() throws Exception {
if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
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 177f34875894..1da1a9095d87 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.os.FileUtils;
-import android.os.SystemClock;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -56,7 +55,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mReader;
private VerifiableCallback mCallback;
- private Random mRand = new Random(12345);
+ private final Random mRand = new Random(12345);
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
private final long[][] mInitialTimes = new long[][]{
{15334000, 310964000},
@@ -67,6 +66,8 @@ public class KernelCpuUidUserSysTimeReaderTest {
{47000, 17000}
};
+ private final MockClock mMockClock = new MockClock();
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
@@ -76,7 +77,8 @@ public class KernelCpuUidUserSysTimeReaderTest {
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
mReader = new KernelCpuUidUserSysTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath(), mMockClock),
+ false, mMockClock);
mCallback = new VerifiableCallback();
}
@@ -87,10 +89,13 @@ public class KernelCpuUidUserSysTimeReaderTest {
}
@Test
- @SkipPresubmit("b/180015146")
public void testThrottler() throws Exception {
+ mMockClock.realtime = 1000;
+
mReader = new KernelCpuUidUserSysTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath(), mMockClock),
+ true, mMockClock);
+
mReader.setThrottle(500);
writeToFile(uidLines(mUids, mInitialTimes));
@@ -103,8 +108,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
mReader.readDelta(false, mCallback);
assertEquals(0, mCallback.mData.size());
- // TODO(b/180473895): Replace sleeps with injected simulated time.
- SystemClock.sleep(600);
+ mMockClock.realtime += 600;
long[][] times2 = increaseTime(times1);
writeToFile(uidLines(mUids, times2));
@@ -123,7 +127,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
mReader.readDelta(true, mCallback);
assertEquals(6, mCallback.mData.size());
- SystemClock.sleep(600);
+ mMockClock.realtime += 600;
long[][] times4 = increaseTime(times3);
writeToFile(uidLines(mUids, times4));
@@ -138,7 +142,7 @@ public class KernelCpuUidUserSysTimeReaderTest {
mReader.readDelta(false, mCallback);
assertEquals(0, mCallback.mData.size());
- SystemClock.sleep(600);
+ mMockClock.realtime += 600;
mCallback.clear();
mReader.readDelta(false, mCallback);
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 99d576d259ec..d57eb65a037f 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -36,13 +36,13 @@ import java.util.concurrent.Future;
* Mocks a BatteryStatsImpl object.
*/
public class MockBatteryStatsImpl extends BatteryStatsImpl {
- public BatteryStatsImpl.Clocks clocks;
+ public Clock mClock;
public boolean mForceOnBattery;
private NetworkStats mNetworkStats;
- MockBatteryStatsImpl(Clocks clocks) {
- super(clocks);
- this.clocks = mClocks;
+ MockBatteryStatsImpl(Clock clock) {
+ super(clock);
+ this.mClock = mClock;
initTimersAndCounters();
setExternalStatsSyncLocked(new DummyExternalStatsSync());
@@ -54,14 +54,13 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
}
MockBatteryStatsImpl() {
- this(new MockClocks());
+ this(new MockClock());
}
- public void initMeasuredEnergyStats() {
+ public void initMeasuredEnergyStats(String[] customBucketNames) {
final boolean[] supportedStandardBuckets =
new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
Arrays.fill(supportedStandardBuckets, true);
- final String[] customBucketNames = {"FOO", "BAR"};
mGlobalMeasuredEnergyStats =
new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames);
}
@@ -190,6 +189,11 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
}
@Override
+ public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
+ return null;
+ }
+
+ @Override
public Future<?> scheduleCpuSyncDueToRemovedUid(int uid) {
return null;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockClocks.java b/core/tests/coretests/src/com/android/internal/os/MockClock.java
index c26505e57346..ac69c33e50a2 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockClocks.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockClock.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-public class MockClocks implements BatteryStatsImpl.Clocks {
+public class MockClock extends Clock {
/** ElapsedRealtime in ms */
public long realtime;
/** Uptime in ms */
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 272f2287dd6e..0f05be06bff6 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -24,6 +24,7 @@ import android.os.Binder;
import android.os.Parcel;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.view.InsetsVisibilities;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -59,7 +60,8 @@ public class RegisterStatusBarResultTest {
new Binder() /* imeToken */,
true /* navbarColorManagedByIme */,
BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
- true /* appFullscreen */,
+ new InsetsVisibilities() /* requestedVisibilities */,
+ "test" /* packageName */,
new int[0] /* transientBarTypes */);
final RegisterStatusBarResult copy = clone(original);
@@ -79,7 +81,8 @@ public class RegisterStatusBarResultTest {
assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
assertThat(copy.mBehavior).isEqualTo(original.mBehavior);
- assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
+ assertThat(copy.mRequestedVisibilities).isEqualTo(original.mRequestedVisibilities);
+ assertThat(copy.mPackageName).isEqualTo(original.mPackageName);
assertThat(copy.mTransientBarTypes).isEqualTo(original.mTransientBarTypes);
}
diff --git a/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
new file mode 100644
index 000000000000..fdba811f3eaa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/BitUtilsTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static com.android.internal.util.BitUtils.bytesToBEInt;
+import static com.android.internal.util.BitUtils.bytesToLEInt;
+import static com.android.internal.util.BitUtils.getUint16;
+import static com.android.internal.util.BitUtils.getUint32;
+import static com.android.internal.util.BitUtils.getUint8;
+import static com.android.internal.util.BitUtils.packBits;
+import static com.android.internal.util.BitUtils.uint16;
+import static com.android.internal.util.BitUtils.uint32;
+import static com.android.internal.util.BitUtils.uint8;
+import static com.android.internal.util.BitUtils.unpackBits;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitUtilsTest {
+
+ @Test
+ public void testUnsignedByteWideningConversions() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte bm1 = -1;
+ assertEquals(0, uint8(b0));
+ assertEquals(1, uint8(b1));
+ assertEquals(127, uint8(Byte.MAX_VALUE));
+ assertEquals(128, uint8(Byte.MIN_VALUE));
+ assertEquals(255, uint8(bm1));
+ assertEquals(255, uint8((byte)255));
+ }
+
+ @Test
+ public void testUnsignedShortWideningConversions() {
+ short s0 = 0;
+ short s1 = 1;
+ short sm1 = -1;
+ assertEquals(0, uint16(s0));
+ assertEquals(1, uint16(s1));
+ assertEquals(32767, uint16(Short.MAX_VALUE));
+ assertEquals(32768, uint16(Short.MIN_VALUE));
+ assertEquals(65535, uint16(sm1));
+ assertEquals(65535, uint16((short)65535));
+ }
+
+ @Test
+ public void testUnsignedShortComposition() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte b2 = 2;
+ byte b10 = 10;
+ byte b16 = 16;
+ byte b128 = -128;
+ byte b224 = -32;
+ byte b255 = -1;
+ assertEquals(0x0000, uint16(b0, b0));
+ assertEquals(0xffff, uint16(b255, b255));
+ assertEquals(0x0a01, uint16(b10, b1));
+ assertEquals(0x8002, uint16(b128, b2));
+ assertEquals(0x01ff, uint16(b1, b255));
+ assertEquals(0x80ff, uint16(b128, b255));
+ assertEquals(0xe010, uint16(b224, b16));
+ }
+
+ @Test
+ public void testUnsignedIntWideningConversions() {
+ assertEquals(0, uint32(0));
+ assertEquals(1, uint32(1));
+ assertEquals(2147483647L, uint32(Integer.MAX_VALUE));
+ assertEquals(2147483648L, uint32(Integer.MIN_VALUE));
+ assertEquals(4294967295L, uint32(-1));
+ assertEquals(4294967295L, uint32((int)4294967295L));
+ }
+
+ @Test
+ public void testBytesToInt() {
+ assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0)));
+ assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255)));
+ assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1)));
+ assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2)));
+ assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255)));
+ assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1)));
+
+ assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0)));
+ assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1)));
+ assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255)));
+ }
+
+ @Test
+ public void testUnsignedGetters() {
+ ByteBuffer b = ByteBuffer.allocate(4);
+ b.putInt(0xffff);
+
+ assertEquals(0x0, getUint8(b, 0));
+ assertEquals(0x0, getUint8(b, 1));
+ assertEquals(0xff, getUint8(b, 2));
+ assertEquals(0xff, getUint8(b, 3));
+
+ assertEquals(0x0, getUint16(b, 0));
+ assertEquals(0xffff, getUint16(b, 2));
+
+ b.rewind();
+ b.putInt(0xffffffff);
+ assertEquals(0xffffffffL, getUint32(b, 0));
+ }
+
+ @Test
+ public void testBitsPacking() {
+ BitPackingTestCase[] testCases = {
+ new BitPackingTestCase(0, ints()),
+ new BitPackingTestCase(1, ints(0)),
+ new BitPackingTestCase(2, ints(1)),
+ new BitPackingTestCase(3, ints(0, 1)),
+ new BitPackingTestCase(4, ints(2)),
+ new BitPackingTestCase(6, ints(1, 2)),
+ new BitPackingTestCase(9, ints(0, 3)),
+ new BitPackingTestCase(~Long.MAX_VALUE, ints(63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)),
+ new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)),
+ };
+ for (BitPackingTestCase tc : testCases) {
+ int[] got = unpackBits(tc.packedBits);
+ assertTrue(
+ "unpackBits("
+ + tc.packedBits
+ + "): expected "
+ + Arrays.toString(tc.bits)
+ + " but got "
+ + Arrays.toString(got),
+ Arrays.equals(tc.bits, got));
+ }
+ for (BitPackingTestCase tc : testCases) {
+ long got = packBits(tc.bits);
+ assertEquals(
+ "packBits("
+ + Arrays.toString(tc.bits)
+ + "): expected "
+ + tc.packedBits
+ + " but got "
+ + got,
+ tc.packedBits,
+ got);
+ }
+
+ long[] moreTestCases = {
+ 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(),
+ };
+ for (long l : moreTestCases) {
+ assertEquals(l, packBits(unpackBits(l)));
+ }
+ }
+
+ static byte[] bytes(int b1, int b2, int b3, int b4) {
+ return new byte[] {b(b1), b(b2), b(b3), b(b4)};
+ }
+
+ static byte b(int i) {
+ return (byte) i;
+ }
+
+ static int[] ints(int... array) {
+ return array;
+ }
+
+ static class BitPackingTestCase {
+ final int[] bits;
+ final long packedBits;
+
+ BitPackingTestCase(long packedBits, int[] bits) {
+ this.bits = bits;
+ this.packedBits = packedBits;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
new file mode 100644
index 000000000000..9da720cbfa87
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.internal.util;
+
+import static androidx.core.graphics.ColorUtils.calculateContrast;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Color;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+public class ContrastColorUtilTest extends TestCase {
+
+ @SmallTest
+ public void testEnsureTextContrastAgainstDark() {
+ int darkBg = 0xFF35302A;
+
+ int blueContrastColor = ContrastColorUtil.ensureTextContrast(Color.BLUE, darkBg, true);
+ assertContrastIsWithinRange(blueContrastColor, darkBg, 4.5, 4.75);
+
+ int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, darkBg, true);
+ assertContrastIsWithinRange(redContrastColor, darkBg, 4.5, 4.75);
+
+ final int darkGreen = 0xff008800;
+ int greenContrastColor = ContrastColorUtil.ensureTextContrast(darkGreen, darkBg, true);
+ assertContrastIsWithinRange(greenContrastColor, darkBg, 4.5, 4.75);
+
+ int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.DKGRAY, darkBg, true);
+ assertContrastIsWithinRange(grayContrastColor, darkBg, 4.5, 4.75);
+
+ int selfContrastColor = ContrastColorUtil.ensureTextContrast(darkBg, darkBg, true);
+ assertContrastIsWithinRange(selfContrastColor, darkBg, 4.5, 4.75);
+ }
+
+ @SmallTest
+ public void testEnsureTextContrastAgainstLight() {
+ int lightBg = 0xFFFFF8F2;
+
+ final int lightBlue = 0xff8888ff;
+ int blueContrastColor = ContrastColorUtil.ensureTextContrast(lightBlue, lightBg, false);
+ assertContrastIsWithinRange(blueContrastColor, lightBg, 4.5, 4.75);
+
+ int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, lightBg, false);
+ assertContrastIsWithinRange(redContrastColor, lightBg, 4.5, 4.75);
+
+ int greenContrastColor = ContrastColorUtil.ensureTextContrast(Color.GREEN, lightBg, false);
+ assertContrastIsWithinRange(greenContrastColor, lightBg, 4.5, 4.75);
+
+ int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.LTGRAY, lightBg, false);
+ assertContrastIsWithinRange(grayContrastColor, lightBg, 4.5, 4.75);
+
+ int selfContrastColor = ContrastColorUtil.ensureTextContrast(lightBg, lightBg, false);
+ assertContrastIsWithinRange(selfContrastColor, lightBg, 4.5, 4.75);
+ }
+
+ private void assertContrastIsWithinRange(int foreground, int background,
+ double minContrast, double maxContrast) {
+ assertContrastIsAtLeast(foreground, background, minContrast);
+ assertContrastIsAtMost(foreground, background, maxContrast);
+ }
+
+ private void assertContrastIsAtLeast(int foreground, int background, double minContrast) {
+ try {
+ assertThat(calculateContrast(foreground, background)).isAtLeast(minContrast);
+ } catch (AssertionError e) {
+ throw new AssertionError(
+ String.format("Insufficient contrast: foreground=#%08x background=#%08x",
+ foreground, background), e);
+ }
+ }
+
+ private void assertContrastIsAtMost(int foreground, int background, double maxContrast) {
+ try {
+ assertThat(calculateContrast(foreground, background)).isAtMost(maxContrast);
+ } catch (AssertionError e) {
+ throw new AssertionError(
+ String.format("Excessive contrast: foreground=#%08x background=#%08x",
+ foreground, background), e);
+ }
+ }
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
new file mode 100644
index 000000000000..4497770ef40d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/RingBufferTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RingBufferTest {
+
+ @Test
+ public void testEmptyRingBuffer() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ assertArrayEquals(new String[0], buffer.toArray());
+ }
+
+ @Test
+ public void testIncorrectConstructorArguments() {
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, -10);
+ fail("Should not be able to create a negative capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 0);
+ fail("Should not be able to create a 0 capacity RingBuffer");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testRingBufferWithNoWrapping() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected = {"a", "b", "c", "d", "e"};
+ assertArrayEquals(expected, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithCapacity1() {
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
+
+ buffer.append("a");
+ assertArrayEquals(new String[]{"a"}, buffer.toArray());
+
+ buffer.append("b");
+ assertArrayEquals(new String[]{"b"}, buffer.toArray());
+
+ buffer.append("c");
+ assertArrayEquals(new String[]{"c"}, buffer.toArray());
+
+ buffer.append("d");
+ assertArrayEquals(new String[]{"d"}, buffer.toArray());
+
+ buffer.append("e");
+ assertArrayEquals(new String[]{"e"}, buffer.toArray());
+ }
+
+ @Test
+ public void testRingBufferWithWrapping() {
+ int capacity = 100;
+ RingBuffer<String> buffer = new RingBuffer<>(String.class, capacity);
+
+ buffer.append("a");
+ buffer.append("b");
+ buffer.append("c");
+ buffer.append("d");
+ buffer.append("e");
+
+ String[] expected1 = {"a", "b", "c", "d", "e"};
+ assertArrayEquals(expected1, buffer.toArray());
+
+ String[] expected2 = new String[capacity];
+ int firstIndex = 0;
+ int lastIndex = capacity - 1;
+
+ expected2[firstIndex] = "e";
+ for (int i = 1; i < capacity; i++) {
+ buffer.append("x");
+ expected2[i] = "x";
+ }
+ assertArrayEquals(expected2, buffer.toArray());
+
+ buffer.append("x");
+ expected2[firstIndex] = "x";
+ assertArrayEquals(expected2, buffer.toArray());
+
+ for (int i = 0; i < 10; i++) {
+ for (String s : expected2) {
+ buffer.append(s);
+ }
+ }
+ assertArrayEquals(expected2, buffer.toArray());
+
+ buffer.append("a");
+ expected2[lastIndex] = "a";
+ assertArrayEquals(expected2, buffer.toArray());
+ }
+
+ @Test
+ public void testGetNextSlot() {
+ int capacity = 100;
+ RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
+
+ final DummyClass1[] actual = new DummyClass1[capacity];
+ final DummyClass1[] expected = new DummyClass1[capacity];
+ for (int i = 0; i < capacity; ++i) {
+ final DummyClass1 obj = buffer.getNextSlot();
+ obj.x = capacity * i;
+ actual[i] = obj;
+ expected[i] = new DummyClass1();
+ expected[i].x = capacity * i;
+ }
+ assertArrayEquals(expected, buffer.toArray());
+
+ for (int i = 0; i < capacity; ++i) {
+ if (actual[i] != buffer.getNextSlot()) {
+ fail("getNextSlot() should re-use objects if available");
+ }
+ }
+
+ RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(No nullary constructor)", buffer2.getNextSlot());
+
+ RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
+ assertNull("getNextSlot() should return null if the object can't be initiated "
+ + "(Inaccessible class)", buffer3.getNextSlot());
+ }
+
+ public static final class DummyClass1 {
+ int x;
+
+ public boolean equals(Object o) {
+ if (o instanceof DummyClass1) {
+ final DummyClass1 other = (DummyClass1) o;
+ return other.x == this.x;
+ }
+ return false;
+ }
+ }
+
+ public static final class DummyClass2 {
+ public DummyClass2(int x) {}
+ }
+
+ private static final class DummyClass3 {}
+}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 269d8424a78f..516a5d288c0f 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -299,7 +299,7 @@ public class ActivityThreadClientTest {
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
mThread /* client */, null /* asssitToken */,
null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
- false /* launchedFromBubble */);
+ false /* launchedFromBubble */, null /* taskfragmentToken */);
}
@Override
diff --git a/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
new file mode 100644
index 000000000000..996d7b435e5a
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/window/ConfigurationHelperTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+
+import android.app.ResourcesManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link ConfigurationHelper}
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksMockingCoreTests:ConfigurationHelperTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ConfigurationHelperTest {
+ MockitoSession mMockitoSession;
+ ResourcesManager mResourcesManager;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(ResourcesManager.class)
+ .startMocking();
+ doReturn(mock(ResourcesManager.class)).when(ResourcesManager::getInstance);
+ mResourcesManager = ResourcesManager.getInstance();
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testShouldUpdateResources_NullConfig_ReturnsTrue() {
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), null /* config */,
+ new Configuration(), new Configuration(), false /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DisplayChanged_ReturnsTrue() {
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+ new Configuration(), new Configuration(), true /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentResources_ReturnsTrue() {
+ doReturn(false).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), new Configuration(),
+ new Configuration(), new Configuration(), false /* displayChanged */,
+ null /* configChanged */)).isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentBounds_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ config.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+ config.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+ final Configuration newConfig = new Configuration();
+ newConfig.windowConfiguration.setBounds(new Rect(0, 0, 20, 20));
+ newConfig.windowConfiguration.setMaxBounds(new Rect(0, 0, 20, 20));
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_SameConfig_ReturnsFalse() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isFalse();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentConfig_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ newConfig.setToDefaults();
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_DifferentNonPublicConfig_ReturnsTrue() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ newConfig.windowConfiguration.setAppBounds(new Rect(0, 0, 10, 10));
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, null /* configChanged */))
+ .isTrue();
+ }
+
+ @Test
+ public void testShouldUpdateResources_OverrideConfigChanged_ReturnsFalse() {
+ doReturn(true).when(mResourcesManager).isSameResourcesOverrideConfig(any(), any());
+
+ final Configuration config = new Configuration();
+ final Configuration newConfig = new Configuration();
+ final boolean configChanged = true;
+
+ assertThat(ConfigurationHelper.shouldUpdateResources(new Binder(), config, newConfig,
+ new Configuration(), false /* displayChanged */, configChanged))
+ .isEqualTo(configChanged);
+ }
+}
diff --git a/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
new file mode 100644
index 000000000000..fa4aa803c75e
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+import static android.content.res.Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_RTL;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+import static android.content.res.Configuration.SCREENLAYOUT_LONG_NO;
+import static android.content.res.Configuration.SCREENLAYOUT_LONG_YES;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_NO;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_UNDEFINED;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_YES;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_SMALL;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link SizeConfigurationBuckets}
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingCoreTests:SizeConfigurationBucketsTest
+ */
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SizeConfigurationBucketsTest {
+
+ /**
+ * Tests that a change in any of the non-size-related screen layout fields results in
+ * {@link SizeConfigurationBuckets#areNonSizeLayoutFieldsUnchanged} returning false.
+ */
+ @Test
+ public void testNonSizeRelatedScreenLayoutFields() {
+ // Test layout direction
+ assertEquals(true, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_UNDEFINED));
+ assertEquals(false, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_LTR));
+ assertEquals(false, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_RTL));
+
+ // Test layout roundness
+ assertEquals(true, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_UNDEFINED));
+ assertEquals(false, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_NO));
+ assertEquals(false, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_YES));
+
+ // Test layout compat needed
+ assertEquals(false, SizeConfigurationBuckets
+ .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_COMPAT_NEEDED));
+ }
+
+ /**
+ * Tests that null size configuration buckets unflips the correct configuration flags.
+ */
+ @Test
+ public void testNullSizeConfigurationBuckets() {
+ // Check that all 3 size configurations are filtered out of the diff if the buckets are null
+ // and non-size attributes of screen layout are unchanged. Add a non-size related config
+ // change (i.e. CONFIG_LOCALE) to test that the diff is not set to zero.
+ final int diff = CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_SCREEN_LAYOUT
+ | CONFIG_LOCALE;
+ final int filteredDiffNonSizeLayoutUnchanged = SizeConfigurationBuckets.filterDiff(diff,
+ Configuration.EMPTY, Configuration.EMPTY, null);
+ assertEquals(CONFIG_LOCALE, filteredDiffNonSizeLayoutUnchanged);
+
+ // Check that only screen size and smallest screen size are filtered out of the diff if the
+ // buckets are null and non-size attributes of screen layout are changed.
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+ final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+ Configuration.EMPTY, newConfig, null);
+ assertEquals(CONFIG_SCREEN_LAYOUT | CONFIG_LOCALE, filteredDiffNonSizeLayoutChanged);
+ }
+
+ /**
+ * Tests that {@link SizeConfigurationBuckets.crossesSizeThreshold()} correctly checks whether
+ * the bucket thresholds have or have not been crossed. This test includes boundary checks
+ * to ensure that arithmetic is inclusive and exclusive in the right places.
+ */
+ @Test
+ public void testCrossesSizeThreshold() {
+ final int[] thresholds = new int[] { 360, 600 };
+ final int nThresholds = thresholds.length;
+ for (int i = -1; i < nThresholds; i++) {
+ final int minValueInBucket = i < 0 ? 0 : thresholds[i];
+ final int maxValueInBucket = i < nThresholds - 1
+ ? thresholds[i + 1] - 1 : Integer.MAX_VALUE;
+ final int bucketRange = maxValueInBucket - minValueInBucket;
+ // Set old value to 1/4 in between the two thresholds.
+ final int oldValue = (int) (minValueInBucket + bucketRange * 0.25);
+ // Test 3 values of new value spread across bucket range: minValueInBucket, bucket
+ // midpoint, and max value in bucket. In all 3 cases, the bucket has not changed so
+ // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return false.
+ checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket, false);
+ checkCrossesSizeThreshold(thresholds, oldValue,
+ (int) (minValueInBucket + bucketRange * 0.5), false);
+ checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket, false);
+ // Test 4 values of size spread outside of bucket range: more than 1 less than min
+ // value, 1 less than min value, 1 more than max value, and more than 1 more than max
+ // value. In all 4 cases, the bucket has changed so
+ // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return true.
+ // Only test less than min value if min value > 0.
+ if (minValueInBucket > 0) {
+ checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 20, true);
+ checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 1, true);
+ }
+ // Only test greater than max value if not in highest bucket.
+ if (i < nThresholds - 1) {
+ checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 1, true);
+ checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 20, true);
+ }
+ }
+ }
+
+ /**
+ * Tests that if screen layout size changed but did not cross a threshold, the filtered diff
+ * does not include screen layout.
+ */
+ @Test
+ public void testScreenLayoutFilteredIfSizeDidNotCrossThreshold() {
+ // Set only small and large sizes
+ final Configuration[] sizeConfigs = new Configuration[2];
+ sizeConfigs[0] = new Configuration();
+ sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+ sizeConfigs[1] = new Configuration();
+ sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_LARGE;
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+ // Change screen layout size from small to normal and check that screen layout flag is
+ // not part of the diff because a threshold was not crossed.
+ final int diff = CONFIG_SCREEN_LAYOUT;
+ final Configuration oldConfig = new Configuration();
+ oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+ final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+ sizeBuckets);
+ assertEquals(0, filteredDiff);
+
+ // If a non-size attribute of screen layout changed, then screen layout should not be
+ // filtered from the diff.
+ newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+ final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+ oldConfig, newConfig, sizeBuckets);
+ assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged);
+ }
+
+ /**
+ * Tests that if screen layout size changed and did cross a threshold, the filtered diff
+ * includes screen layout.
+ */
+ @Test
+ public void testScreenLayoutNotFilteredIfSizeCrossedThreshold() {
+ // Set only small and normal sizes
+ final Configuration[] sizeConfigs = new Configuration[2];
+ sizeConfigs[0] = new Configuration();
+ sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+ sizeConfigs[1] = new Configuration();
+ sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+ // Change screen layout size from small to normal and check that screen layout flag is
+ // still part of the diff because a threshold was crossed.
+ final int diff = CONFIG_SCREEN_LAYOUT;
+ final Configuration oldConfig = new Configuration();
+ oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+ final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+ sizeBuckets);
+ assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff);
+ }
+
+ /**
+ * Tests that anytime screen layout size is decreased, the filtered diff still includes screen
+ * layout.
+ */
+ @Test
+ public void testScreenLayoutNotFilteredIfSizeDecreased() {
+ // The size thresholds can be anything, but can't be null
+ final int[] horizontalThresholds = new int[] { 360, 600 };
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+ horizontalThresholds, null /* vertical */, null /* smallest */,
+ null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+ final int[] sizeValuesInOrder = new int[] {
+ SCREENLAYOUT_SIZE_SMALL, SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_LARGE,
+ SCREENLAYOUT_SIZE_XLARGE
+ };
+ final int nSizes = sizeValuesInOrder.length;
+ for (int larger = nSizes - 1; larger > 0; larger--) {
+ for (int smaller = larger - 1; smaller >= 0; smaller--) {
+ final Configuration oldConfig = new Configuration();
+ oldConfig.screenLayout |= sizeValuesInOrder[larger];
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= sizeValuesInOrder[smaller];
+ assertTrue(String.format("oldSize=%d, newSize=%d", oldConfig.screenLayout,
+ newConfig.screenLayout),
+ sizeBuckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig));
+ }
+ }
+ }
+
+ /**
+ * Tests that if screen layout long changed but did not cross a threshold, the filtered diff
+ * does not include screen layout.
+ */
+ @Test
+ public void testScreenLayoutFilteredIfLongDidNotCrossThreshold() {
+ // Do not set any long threshold
+ final Configuration[] sizeConfigs = new Configuration[1];
+ sizeConfigs[0] = Configuration.EMPTY;
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+ // Change screen layout long from not long to long and check that screen layout flag is
+ // not part of the diff because a threshold was not crossed.
+ final int diff = CONFIG_SCREEN_LAYOUT;
+ final Configuration oldConfig = new Configuration();
+ oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO;
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= SCREENLAYOUT_LONG_YES;
+ final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+ sizeBuckets);
+ assertEquals(0, filteredDiff);
+
+ // If a non-size attribute of screen layout changed, then screen layout should not be
+ // filtered from the diff.
+ newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+ final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+ oldConfig, newConfig, sizeBuckets);
+ assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged);
+ }
+
+ /**
+ * Tests that if screen layout long changed and did cross a threshold, the filtered diff
+ * includes screen layout.
+ */
+ @Test
+ public void testScreenLayoutNotFilteredIfLongCrossedThreshold() {
+ // Set only small and normal sizes
+ final Configuration[] sizeConfigs = new Configuration[1];
+ sizeConfigs[0] = new Configuration();
+ sizeConfigs[0].screenLayout |= SCREENLAYOUT_LONG_NO;
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+ // Change screen layout long from not long to long and check that screen layout flag is
+ // still part of the diff because a threshold was crossed.
+ final int diff = CONFIG_SCREEN_LAYOUT;
+ final Configuration oldConfig = new Configuration();
+ oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO;
+ final Configuration newConfig = new Configuration();
+ newConfig.screenLayout |= SCREENLAYOUT_LONG_YES;
+ final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+ sizeBuckets);
+ assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff);
+ }
+
+ /**
+ * Tests that horizontal buckets are correctly checked in
+ * {@link SizeConfigurationBuckets#filterDiff()}.
+ */
+ @Test
+ public void testHorizontalSizeThresholds() {
+ final int[] horizontalThresholds = new int[] { 360, 600 };
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+ horizontalThresholds, null /* vertical */, null /* smallest */,
+ null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+ final Configuration oldConfig = new Configuration();
+ final Configuration newConfig = new Configuration();
+
+ oldConfig.screenWidthDp = 480;
+ // Test that value within bucket filters out screen size config
+ newConfig.screenWidthDp = 520;
+ assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig,
+ newConfig, sizeBuckets));
+ // Test that value outside bucket does not filter out screen size config
+ newConfig.screenWidthDp = 640;
+ assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE,
+ oldConfig, newConfig, sizeBuckets));
+ }
+
+ /**
+ * Tests that vertical buckets are correctly checked in
+ * {@link SizeConfigurationBuckets#filterDiff()}.
+ */
+ @Test
+ public void testVerticalSizeThresholds() {
+ final int[] verticalThresholds = new int[] { 360, 600 };
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+ null, verticalThresholds /* vertical */, null /* smallest */,
+ null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+ final Configuration oldConfig = new Configuration();
+ final Configuration newConfig = new Configuration();
+
+ oldConfig.screenHeightDp = 480;
+ // Test that value within bucket filters out screen size config
+ newConfig.screenHeightDp = 520;
+ assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig,
+ newConfig, sizeBuckets));
+ // Test that value outside bucket does not filter out screen size config
+ newConfig.screenHeightDp = 640;
+ assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE,
+ oldConfig, newConfig, sizeBuckets));
+ }
+
+ /**
+ * Tests that smallest width buckets are correctly checked in
+ * {@link SizeConfigurationBuckets#filterDiff()}.
+ */
+ @Test
+ public void testSmallestWidthSizeThresholds() {
+ final int[] smallestWidthThresholds = new int[] { 360, 600 };
+ final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+ null, null /* vertical */, smallestWidthThresholds /* smallest */,
+ null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+ final Configuration oldConfig = new Configuration();
+ final Configuration newConfig = new Configuration();
+
+ oldConfig.smallestScreenWidthDp = 480;
+ // Test that value within bucket filters out smallest screen size config
+ newConfig.smallestScreenWidthDp = 520;
+ assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SMALLEST_SCREEN_SIZE, oldConfig,
+ newConfig, sizeBuckets));
+ // Test that value outside bucket does not filter out smallest screen size config
+ newConfig.smallestScreenWidthDp = 640;
+ assertEquals(CONFIG_SMALLEST_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(
+ CONFIG_SMALLEST_SCREEN_SIZE, oldConfig, newConfig, sizeBuckets));
+ }
+
+ private void checkCrossesSizeThreshold(int[] thresholds, int oldValue, int newValue,
+ boolean expected) {
+ final String errorString = String.format(
+ "thresholds=%s, oldValue=%d, newValue=%d, expected=%b", Arrays.toString(thresholds),
+ oldValue, newValue, expected);
+ final boolean actual = SizeConfigurationBuckets.crossesSizeThreshold(thresholds, oldValue,
+ newValue);
+ assertEquals(errorString, expected, actual);
+ }
+}
diff --git a/core/tests/uwbtests/OWNERS b/core/tests/uwbtests/OWNERS
deleted file mode 100644
index c7b09a21e1d0..000000000000
--- a/core/tests/uwbtests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /core/java/android/uwb/OWNERS
diff --git a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java b/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
deleted file mode 100644
index 4cad535a3a51..000000000000
--- a/core/tests/uwbtests/src/android/uwb/AdapterStateListenerTest.java
+++ /dev/null
@@ -1,288 +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.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.times;
-import static org.mockito.Mockito.verify;
-
-
-import android.os.RemoteException;
-import android.uwb.UwbManager.AdapterStateCallback;
-
-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.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Test of {@link AdapterStateListener}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class AdapterStateListenerTest {
-
- IUwbAdapter mUwbAdapter = mock(IUwbAdapter.class);
-
- Answer mRegisterSuccessAnswer = new Answer() {
- public Object answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
- try {
- cb.onAdapterStateChanged(AdapterState.STATE_DISABLED, StateChangeReason.UNKNOWN);
- } catch (RemoteException e) {
- // Nothing to do
- }
- return new Object();
- }
- };
-
- Throwable mThrowRemoteException = new RemoteException("RemoteException");
-
- private static Executor getExecutor() {
- return new Executor() {
- @Override
- public void execute(Runnable command) {
- command.run();
- }
- };
- }
-
- private static void verifyCallbackStateChangedInvoked(
- AdapterStateCallback callback, int numTimes) {
- verify(callback, times(numTimes)).onStateChanged(anyInt(), anyInt());
- }
-
- @Test
- public void testRegister_RegisterUnregister() throws RemoteException {
- doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
-
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
- AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
-
- // Verify that the adapter state listener registered with the UWB Adapter
- adapterStateListener.register(getExecutor(), callback1);
- verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
- verifyCallbackStateChangedInvoked(callback1, 1);
- verifyCallbackStateChangedInvoked(callback2, 0);
-
- // Register a second client and no new call to UWB Adapter
- adapterStateListener.register(getExecutor(), callback2);
- verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
- verifyCallbackStateChangedInvoked(callback1, 1);
- verifyCallbackStateChangedInvoked(callback2, 1);
-
- // Unregister first callback
- adapterStateListener.unregister(callback1);
- verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
- verify(mUwbAdapter, times(0)).unregisterAdapterStateCallbacks(any());
- verifyCallbackStateChangedInvoked(callback1, 1);
- verifyCallbackStateChangedInvoked(callback2, 1);
-
- // Unregister second callback
- adapterStateListener.unregister(callback2);
- verify(mUwbAdapter, times(1)).registerAdapterStateCallbacks(any());
- verify(mUwbAdapter, times(1)).unregisterAdapterStateCallbacks(any());
- verifyCallbackStateChangedInvoked(callback1, 1);
- verifyCallbackStateChangedInvoked(callback2, 1);
- }
-
- @Test
- public void testRegister_RegisterSameCallbackTwice() throws RemoteException {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
- doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
-
- adapterStateListener.register(getExecutor(), callback);
- verifyCallbackStateChangedInvoked(callback, 1);
-
- adapterStateListener.register(getExecutor(), callback);
- verifyCallbackStateChangedInvoked(callback, 1);
-
- // Invoke a state change and ensure the callback is only called once
- adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
- StateChangeReason.UNKNOWN);
- verifyCallbackStateChangedInvoked(callback, 2);
- }
-
- @Test
- public void testCallback_RunViaExecutor_Success() throws RemoteException {
- // Verify that the callbacks are invoked on the executor when successful
- doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
- runViaExecutor();
- }
-
- private void runViaExecutor() {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
-
- Executor executor = mock(Executor.class);
-
- // Do not run commands received and ensure that the callback is not invoked
- doAnswer(new ExecutorAnswer(false)).when(executor).execute(any());
- adapterStateListener.register(executor, callback);
- verify(executor, times(1)).execute(any());
- verifyCallbackStateChangedInvoked(callback, 0);
-
- // Manually invoke the callback and ensure callback is not invoked
- adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
- StateChangeReason.UNKNOWN);
- verify(executor, times(2)).execute(any());
- verifyCallbackStateChangedInvoked(callback, 0);
-
- // Run the command that the executor receives
- doAnswer(new ExecutorAnswer(true)).when(executor).execute(any());
- adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
- StateChangeReason.UNKNOWN);
- verify(executor, times(3)).execute(any());
- verifyCallbackStateChangedInvoked(callback, 1);
- }
-
- class ExecutorAnswer implements Answer {
-
- final boolean mShouldRun;
- ExecutorAnswer(boolean shouldRun) {
- mShouldRun = shouldRun;
- }
-
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- if (mShouldRun) {
- ((Runnable) invocation.getArgument(0)).run();
- }
- return null;
- }
- }
-
- @Test
- public void testNotify_AllCallbacksNotified() throws RemoteException {
- doAnswer(mRegisterSuccessAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
-
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- List<AdapterStateCallback> callbacks = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
- adapterStateListener.register(getExecutor(), callback);
- callbacks.add(callback);
- }
-
- // Ensure every callback got the initial state
- for (AdapterStateCallback callback : callbacks) {
- verifyCallbackStateChangedInvoked(callback, 1);
- }
-
- // Invoke a state change and ensure all callbacks are invoked
- adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
- StateChangeReason.ALL_SESSIONS_CLOSED);
- for (AdapterStateCallback callback : callbacks) {
- verifyCallbackStateChangedInvoked(callback, 2);
- }
- }
-
- @Test
- public void testStateChange_CorrectValue() {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
-
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
-
- adapterStateListener.register(getExecutor(), callback);
-
- runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED,
- AdapterState.STATE_ENABLED_INACTIVE,
- AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
- AdapterStateCallback.STATE_ENABLED_INACTIVE);
-
- runStateChangeValue(StateChangeReason.SESSION_STARTED, AdapterState.STATE_ENABLED_ACTIVE,
- AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED,
- AdapterStateCallback.STATE_ENABLED_ACTIVE);
-
- runStateChangeValue(StateChangeReason.SYSTEM_BOOT, AdapterState.STATE_DISABLED,
- AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT,
- AdapterStateCallback.STATE_DISABLED);
-
- runStateChangeValue(StateChangeReason.SYSTEM_POLICY, AdapterState.STATE_DISABLED,
- AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY,
- AdapterStateCallback.STATE_DISABLED);
-
- runStateChangeValue(StateChangeReason.UNKNOWN, AdapterState.STATE_DISABLED,
- AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN,
- AdapterStateCallback.STATE_DISABLED);
- }
-
- private void runStateChangeValue(@StateChangeReason int reasonIn, @AdapterState int stateIn,
- @AdapterStateCallback.StateChangedReason int reasonOut,
- @AdapterStateCallback.State int stateOut) {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
- adapterStateListener.register(getExecutor(), callback);
-
- adapterStateListener.onAdapterStateChanged(stateIn, reasonIn);
- verify(callback, times(1)).onStateChanged(stateOut, reasonOut);
- }
-
- @Test
- public void testStateChange_FirstRegisterGetsCorrectState() throws RemoteException {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback = mock(AdapterStateCallback.class);
-
- Answer registerAnswer = new Answer() {
- public Object answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
- try {
- cb.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
- StateChangeReason.SESSION_STARTED);
- } catch (RemoteException e) {
- // Nothing to do
- }
- return new Object();
- }
- };
-
- doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
-
- adapterStateListener.register(getExecutor(), callback);
- verify(callback).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
- AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
- }
-
- @Test
- public void testStateChange_SecondRegisterGetsCorrectState() {
- AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
- AdapterStateCallback callback1 = mock(AdapterStateCallback.class);
- AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
-
- adapterStateListener.register(getExecutor(), callback1);
- adapterStateListener.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
- StateChangeReason.SYSTEM_BOOT);
-
- adapterStateListener.register(getExecutor(), callback2);
- verify(callback2).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
- AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
deleted file mode 100644
index 24267e46e9e2..000000000000
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ /dev/null
@@ -1,267 +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.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.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.AttributionSource;
-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.ArgumentCaptor;
-
-import java.util.concurrent.Executor;
-
-/**
- * Test of {@link AdapterStateListener}.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RangingManagerTest {
-
- private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
- private static final PersistableBundle PARAMS = new PersistableBundle();
- private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
- private static final int UID = 343453;
- private static final String PACKAGE_NAME = "com.uwb.test";
- private static final AttributionSource ATTRIBUTION_SOURCE =
- new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
-
- @Test
- public void testOpenSession_OpenRangingInvoked() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingManager rangingManager = new RangingManager(adapter);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), any(), any(), any());
- }
-
- @Test
- public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingManager rangingManager = new RangingManager(adapter);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
-
- rangingManager.onRangingOpened(new SessionHandle(2));
- verify(callback, times(0)).onOpened(any());
- }
-
- @Test
- public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
- RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
- ArgumentCaptor<SessionHandle> sessionHandleCaptor =
- ArgumentCaptor.forClass(SessionHandle.class);
-
- RangingManager rangingManager = new RangingManager(adapter);
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
-
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
- verify(adapter, times(2)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
-
- rangingManager.onRangingOpened(sessionHandle1);
- verify(callback1, times(1)).onOpened(any());
- verify(callback2, times(0)).onOpened(any());
-
- rangingManager.onRangingOpened(sessionHandle2);
- verify(callback1, times(1)).onOpened(any());
- verify(callback2, times(1)).onOpened(any());
- }
-
- @Test
- public void testCorrectCallbackInvoked() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingManager rangingManager = new RangingManager(adapter);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
-
- ArgumentCaptor<SessionHandle> sessionHandleCaptor =
- ArgumentCaptor.forClass(SessionHandle.class);
-
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle handle = sessionHandleCaptor.getValue();
-
- rangingManager.onRangingOpened(handle);
- verify(callback, times(1)).onOpened(any());
-
- rangingManager.onRangingStarted(handle, PARAMS);
- verify(callback, times(1)).onStarted(eq(PARAMS));
-
- rangingManager.onRangingStartFailed(handle, REASON, PARAMS);
- verify(callback, times(1)).onStartFailed(eq(REASON), eq(PARAMS));
-
- RangingReport report = UwbTestUtils.getRangingReports(1);
- rangingManager.onRangingResult(handle, report);
- verify(callback, times(1)).onReportReceived(eq(report));
-
- rangingManager.onRangingReconfigured(handle, PARAMS);
- verify(callback, times(1)).onReconfigured(eq(PARAMS));
-
- rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
- verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
-
- rangingManager.onRangingStopped(handle, REASON, PARAMS);
- verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
-
- rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
- verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
-
- rangingManager.onRangingClosed(handle, REASON, PARAMS);
- verify(callback, times(1)).onClosed(eq(REASON), eq(PARAMS));
- }
-
- @Test
- public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- // Verify that if multiple sessions are registered, only the session that is
- // requested to close receives the associated callbacks
- RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
- RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
-
- RangingManager rangingManager = new RangingManager(adapter);
- ArgumentCaptor<SessionHandle> sessionHandleCaptor =
- ArgumentCaptor.forClass(SessionHandle.class);
-
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
-
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
- verify(adapter, times(2)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
-
- rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
- verify(callback1, times(1)).onClosed(anyInt(), any());
- verify(callback2, times(0)).onClosed(anyInt(), any());
-
- rangingManager.onRangingClosed(sessionHandle2, REASON, PARAMS);
- verify(callback1, times(1)).onClosed(anyInt(), any());
- verify(callback2, times(1)).onClosed(anyInt(), any());
- }
-
- @Test
- public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
- RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
-
- ArgumentCaptor<SessionHandle> sessionHandleCaptor =
- ArgumentCaptor.forClass(SessionHandle.class);
-
- RangingManager rangingManager = new RangingManager(adapter);
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
-
- rangingManager.onRangingStarted(sessionHandle1, PARAMS);
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
- verify(adapter, times(2)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
- rangingManager.onRangingStarted(sessionHandle2, PARAMS);
-
- rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
- verify(callback1, times(1)).onReportReceived(any());
- verify(callback2, times(0)).onReportReceived(any());
-
- rangingManager.onRangingResult(sessionHandle2, UwbTestUtils.getRangingReports(1));
- verify(callback1, times(1)).onReportReceived(any());
- verify(callback2, times(1)).onReportReceived(any());
- }
-
- @Test
- public void testReasons() throws RemoteException {
- runReason(RangingChangeReason.LOCAL_API,
- RangingSession.Callback.REASON_LOCAL_REQUEST);
-
- runReason(RangingChangeReason.MAX_SESSIONS_REACHED,
- RangingSession.Callback.REASON_MAX_SESSIONS_REACHED);
-
- runReason(RangingChangeReason.PROTOCOL_SPECIFIC,
- RangingSession.Callback.REASON_PROTOCOL_SPECIFIC_ERROR);
-
- runReason(RangingChangeReason.REMOTE_REQUEST,
- RangingSession.Callback.REASON_REMOTE_REQUEST);
-
- runReason(RangingChangeReason.SYSTEM_POLICY,
- RangingSession.Callback.REASON_SYSTEM_POLICY);
-
- runReason(RangingChangeReason.BAD_PARAMETERS,
- RangingSession.Callback.REASON_BAD_PARAMETERS);
-
- runReason(RangingChangeReason.UNKNOWN,
- RangingSession.Callback.REASON_UNKNOWN);
- }
-
- private void runReason(@RangingChangeReason int reasonIn,
- @RangingSession.Callback.Reason int reasonOut) throws RemoteException {
- IUwbAdapter adapter = mock(IUwbAdapter.class);
- RangingManager rangingManager = new RangingManager(adapter);
- RangingSession.Callback callback = mock(RangingSession.Callback.class);
-
- ArgumentCaptor<SessionHandle> sessionHandleCaptor =
- ArgumentCaptor.forClass(SessionHandle.class);
-
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
- verify(adapter, times(1)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- SessionHandle handle = sessionHandleCaptor.getValue();
-
- rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
-
- // Open a new session
- rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
- verify(adapter, times(2)).openRanging(
- eq(ATTRIBUTION_SOURCE), sessionHandleCaptor.capture(), any(), any());
- handle = sessionHandleCaptor.getValue();
- rangingManager.onRangingOpened(handle);
-
- rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onStartFailed(eq(reasonOut), eq(PARAMS));
-
- rangingManager.onRangingReconfigureFailed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onReconfigureFailed(eq(reasonOut), eq(PARAMS));
-
- rangingManager.onRangingStopFailed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onStopFailed(eq(reasonOut), eq(PARAMS));
-
- rangingManager.onRangingClosed(handle, reasonIn, PARAMS);
- verify(callback, times(1)).onClosed(eq(reasonOut), eq(PARAMS));
- }
-}
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
deleted file mode 100644
index 75c6924a1939..000000000000
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.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 android.os.SystemClock;
-
-import java.util.concurrent.Executor;
-
-public class UwbTestUtils {
- private UwbTestUtils() {}
-
- public static AngleMeasurement getAngleMeasurement() {
- return new AngleMeasurement(
- getDoubleInRange(-Math.PI, Math.PI),
- getDoubleInRange(0, Math.PI),
- getDoubleInRange(0, 1));
- }
-
- public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
- return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
- .setAltitude(getAngleMeasurement())
- .build();
- }
-
- public static DistanceMeasurement getDistanceMeasurement() {
- return new DistanceMeasurement.Builder()
- .setMeters(getDoubleInRange(0, 100))
- .setErrorMeters(getDoubleInRange(0, 10))
- .setConfidenceLevel(getDoubleInRange(0, 1))
- .build();
- }
-
- public static RangingMeasurement getRangingMeasurement() {
- return getRangingMeasurement(getUwbAddress(false));
- }
-
- public static RangingMeasurement getRangingMeasurement(UwbAddress address) {
- return new RangingMeasurement.Builder()
- .setDistanceMeasurement(getDistanceMeasurement())
- .setAngleOfArrivalMeasurement(getAngleOfArrivalMeasurement())
- .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos())
- .setRemoteDeviceAddress(address != null ? address : getUwbAddress(false))
- .setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
- .build();
- }
-
- public static RangingReport getRangingReports(int numMeasurements) {
- RangingReport.Builder builder = new RangingReport.Builder();
- for (int i = 0; i < numMeasurements; i++) {
- builder.addMeasurement(getRangingMeasurement());
- }
- return builder.build();
- }
-
- private static double getDoubleInRange(double min, double max) {
- return min + (max - min) * Math.random();
- }
-
- public static UwbAddress getUwbAddress(boolean isShortAddress) {
- byte[] addressBytes = new byte[isShortAddress ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH :
- UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH];
- for (int i = 0; i < addressBytes.length; i++) {
- addressBytes[i] = (byte) getDoubleInRange(1, 255);
- }
- return UwbAddress.fromBytes(addressBytes);
- }
-
- public static Executor getExecutor() {
- return new Executor() {
- @Override
- public void execute(Runnable command) {
- command.run();
- }
- };
- }
-}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
index 084e1dbced4a..d1e4322ab612 100644
--- a/data/etc/car/Android.bp
+++ b/data/etc/car/Android.bp
@@ -130,6 +130,13 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "allowed_privapp_com.google.android.car.adaslocation",
+ sub_dir: "permissions",
+ src: "com.google.android.car.adaslocation.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "allowed_privapp_com.google.android.car.kitchensink",
sub_dir: "permissions",
src: "com.google.android.car.kitchensink.xml",
diff --git a/data/etc/car/com.android.car.activityresolver.xml b/data/etc/car/com.android.car.activityresolver.xml
index d48bc15b1678..927c7387b175 100644
--- a/data/etc/car/com.android.car.activityresolver.xml
+++ b/data/etc/car/com.android.car.activityresolver.xml
@@ -19,3 +19,4 @@
<permission name="android.permission.MANAGE_USERS"/>
</privapp-permissions>
</permissions>
+
diff --git a/data/etc/car/com.google.android.car.adaslocation.xml b/data/etc/car/com.google.android.car.adaslocation.xml
new file mode 100644
index 000000000000..cc1ef3c3e160
--- /dev/null
+++ b/data/etc/car/com.google.android.car.adaslocation.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<permissions>
+ <privapp-permissions package="com.google.android.car.adaslocation">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 813b7995fe89..07389b26bc64 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -187,7 +187,6 @@ applications that come with the platform
<permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
<permission name="android.permission.REGISTER_STATS_PULL_ATOM"/>
<permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
- <permission name="android.permission.SET_TIME_ZONE"/>
<permission name="android.permission.SHUTDOWN"/>
<permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
<permission name="android.permission.STATUS_BAR"/>
@@ -202,6 +201,8 @@ applications that come with the platform
<permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ <permission name="android.permission.LOG_COMPAT_CHANGE"/>
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
</privapp-permissions>
<privapp-permissions package="com.android.providers.calendar">
@@ -342,6 +343,8 @@ applications that come with the platform
<!-- Needed for test only -->
<permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
<permission name="android.permission.POWER_SAVER" />
+ <!-- Needed for CTS tests -->
+ <permission name="android.permission.READ_ACTIVE_EMERGENCY_SESSION"/>
<permission name="android.permission.READ_CARRIER_APP_INFO"/>
<permission name="android.permission.READ_FRAME_BUFFER"/>
<permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -533,6 +536,11 @@ applications that come with the platform
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.wallpaper.livepicker">
+ <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
+ <permission name="android.permission.BIND_WALLPAPER"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b67988ee9646..b49b289d44c0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -79,12 +79,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-2029985709": {
- "message": "setFocusedTask: taskId=%d",
- "level": "DEBUG",
- "group": "WM_DEBUG_FOCUS",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"-2024464438": {
"message": "app-onAnimationFinished(): mOuter=%s",
"level": "DEBUG",
@@ -103,6 +97,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "-2010331310": {
+ "message": "resumeTopActivity: Top activity resumed (dontWaitForPause) %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-2006946193": {
"message": "setClientVisible: %s clientVisible=%b Callers=%s",
"level": "VERBOSE",
@@ -181,6 +181,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1924376693": {
+ "message": " Setting Ready-group to %b. group=%s from %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-1918702467": {
"message": "onSyncFinishedDrawing %s",
"level": "VERBOSE",
@@ -211,6 +217,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "-1886145147": {
+ "message": "resumeTopActivity: Going to sleep and all paused",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1884933373": {
"message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
"level": "INFO",
@@ -247,12 +259,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransition.java"
},
- "-1861864501": {
- "message": "resumeTopActivityLocked: Going to sleep and all paused",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1844540996": {
"message": " Initial targets: %s",
"level": "VERBOSE",
@@ -325,12 +331,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimationController.java"
},
- "-1768090656": {
- "message": "Re-launching after pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1750384749": {
"message": "Launch on display check: allow launch on public display",
"level": "DEBUG",
@@ -415,12 +415,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1655805455": {
- "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1647332198": {
"message": "remove RecentTask %s when finishing user %d",
"level": "INFO",
@@ -433,6 +427,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
},
+ "-1633115609": {
+ "message": "Key dispatch not paused for screen off",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1632122349": {
"message": "Changing surface while display frozen: %s",
"level": "VERBOSE",
@@ -487,6 +487,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "-1564228464": {
+ "message": "App died while pausing: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1559645910": {
"message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s",
"level": "DEBUG",
@@ -565,12 +571,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
- "-1492696222": {
- "message": "App died during pause, not stopping: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1480772131": {
"message": "No app or window is requesting an orientation, return %d for display id=%d",
"level": "VERBOSE",
@@ -613,6 +613,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1442613680": {
+ "message": " Creating Ready-group for Transition %d with root=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-1438175584": {
"message": "Input focus has changed to %s display=%d",
"level": "VERBOSE",
@@ -637,12 +643,24 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1421296808": {
+ "message": "Moving to RESUMED: %s (in existing)",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1419762046": {
"message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
"level": "DEBUG",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
},
+ "-1419461256": {
+ "message": "resumeTopActivity: Resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1413901262": {
"message": "startRecentsActivity(): intent=%s",
"level": "DEBUG",
@@ -709,6 +727,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-1311436264": {
+ "message": "Unregister task fragment organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-1305966693": {
"message": "Sending position change to %s, onTop: %b",
"level": "VERBOSE",
@@ -805,6 +829,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-1187377055": {
+ "message": "Enqueue pending stop if needed: %s wasStopping=%b visibleRequested=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-1176488860": {
"message": "SURFACE isSecure=%b: %s",
"level": "INFO",
@@ -919,12 +949,6 @@
"group": "WM_DEBUG_RECENTS_ANIMATIONS",
"at": "com\/android\/server\/wm\/RecentsAnimation.java"
},
- "-1066383762": {
- "message": "Sleep still waiting to pause %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1060365734": {
"message": "Attempted to add QS dialog window with bad token %s. Aborting.",
"level": "WARN",
@@ -985,6 +1009,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "-957060823": {
+ "message": "Moving to PAUSING: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-951939129": {
"message": "Unregister task organizer=%s uid=%d",
"level": "VERBOSE",
@@ -1201,6 +1231,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-706481945": {
+ "message": "TaskFragment parent info changed name=%s parentTaskId=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"-705939410": {
"message": "Waiting for pause to complete...",
"level": "VERBOSE",
@@ -1237,12 +1273,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-672228342": {
- "message": "resumeTopActivityLocked: Top activity resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
@@ -1267,11 +1297,11 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-650261962": {
- "message": "Sleep needs to pause %s",
+ "-648891906": {
+ "message": "Activity not running or entered PiP, resuming next.",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-641258376": {
"message": "realStartActivityLocked: Skipping start of r=%s some activities pausing...",
@@ -1309,12 +1339,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-606328116": {
- "message": "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-597091183": {
"message": "Delete TaskDisplayArea uid=%d",
"level": "VERBOSE",
@@ -1375,11 +1399,11 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowAnimator.java"
},
- "-533690126": {
- "message": "resumeTopActivityLocked: Resumed %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "-542756093": {
+ "message": "TaskFragment vanished name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"-532081937": {
"message": " Commit activity becoming invisible: %s",
@@ -1387,11 +1411,11 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "-527683022": {
- "message": "resumeTopActivityLocked: Skip resume: some activity pausing.",
+ "-521613870": {
+ "message": "App died during pause, not stopping: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-519504830": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s isEntrance=%b Callers=%s",
@@ -1477,18 +1501,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotation.java"
},
- "-427457280": {
- "message": "App died while pausing: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
- "-417514857": {
- "message": "Key dispatch not paused for screen off",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-415865166": {
"message": "findFocusedWindow: Found new focus @ %s",
"level": "VERBOSE",
@@ -1597,11 +1609,17 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
- "-303497363": {
- "message": "reparent: moving activity=%s to task=%d at %d",
+ "-312353598": {
+ "message": "Executing finish of activity: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "-310337305": {
+ "message": "Activity config changed during resume: %s, new next: %s",
"level": "INFO",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-302468788": {
"message": "Expected target rootTask=%s to be top most but found rootTask=%s",
@@ -1621,12 +1639,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-279436615": {
- "message": "Moving to PAUSING: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -1639,6 +1651,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "-248761393": {
+ "message": "startPausing: taskFrag =%s mResumedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"-240296576": {
"message": "handleAppTransitionReady: displayId=%d appTransition={%s} openingApps=[%s] closingApps=[%s] transit=%s",
"level": "VERBOSE",
@@ -1651,12 +1669,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-234244777": {
- "message": "Activity config changed during resume: %s, new next: %s",
- "level": "INFO",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-230587670": {
"message": "SyncGroup %d: Unfinished container: %s",
"level": "VERBOSE",
@@ -1723,12 +1735,6 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "-118786523": {
- "message": "Resume failed; resetting state to %s: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-116086365": {
"message": "******************** ENABLING SCREEN!",
"level": "INFO",
@@ -1777,6 +1783,18 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/Session.java"
},
+ "-80004683": {
+ "message": "Resume failed; resetting state to %s: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "-55185509": {
+ "message": "setFocusedTask: taskId=%d touchedActivity=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"-50336993": {
"message": "moveFocusableActivityToTop: activity=%s",
"level": "DEBUG",
@@ -1909,12 +1927,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowContextListenerController.java"
},
- "94402792": {
- "message": "Moving to RESUMED: %s (in existing)",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"95216706": {
"message": "hideIme target: %s ",
"level": "DEBUG",
@@ -1933,6 +1945,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "102618780": {
+ "message": "resumeTopActivity: Pausing %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"108170907": {
"message": "Add starting %s: startingData=%s",
"level": "VERBOSE",
@@ -2173,6 +2191,18 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "327461496": {
+ "message": "Complete pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "341055768": {
+ "message": "resumeTopActivity: Skip resume: need to start pausing",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2191,6 +2221,12 @@
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "352982444": {
+ "message": " allReady query: used=%b override=%b states=[%s]",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"355720268": {
"message": "stopFreezingDisplayLocked: Unfreezing now",
"level": "DEBUG",
@@ -2227,11 +2263,11 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "391189028": {
- "message": "pauseBackTasks: task=%s mResumedActivity=%s",
- "level": "DEBUG",
+ "378825104": {
+ "message": "Enqueueing pending pause: %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"397105698": {
"message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
@@ -2365,6 +2401,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
+ "573582981": {
+ "message": "reparent: moving activity=%s to new task fragment in task=%d at %d",
+ "level": "INFO",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/ActivityRecord.java"
+ },
"579298675": {
"message": "Moving to DESTROYED: %s (removed from history)",
"level": "VERBOSE",
@@ -2467,6 +2509,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "660908897": {
+ "message": "Auto-PIP allowed, entering PIP mode directly: %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"662572728": {
"message": "Attempted to add a toast window with bad token %s. Aborting.",
"level": "WARN",
@@ -2485,12 +2533,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "669361121": {
+ "message": "Sleep still need to stop %d activities",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"674932310": {
"message": "Setting Intent of %s to target %s",
"level": "VERBOSE",
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/Task.java"
},
+ "675705156": {
+ "message": "resumeTopActivity: Top activity resumed %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"685047360": {
"message": "Resizing window %s",
"level": "VERBOSE",
@@ -2521,12 +2581,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "709500946": {
- "message": "resumeTopActivityLocked: Skip resume: need to start pausing",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"715749922": {
"message": "Allowlisting %d:%s",
"level": "WARN",
@@ -2545,6 +2599,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "743418423": {
+ "message": "Sending TaskFragment error exception=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"744171317": {
"message": " SKIP: %s",
"level": "VERBOSE",
@@ -2629,12 +2689,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "897964776": {
- "message": "Complete pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"898863925": {
"message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
"level": "WARN",
@@ -2659,6 +2713,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "935418348": {
+ "message": "resumeTopActivity: Skip resume: some activity pausing.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"950074526": {
"message": "setLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -2707,11 +2767,11 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
},
- "988389910": {
- "message": "resumeTopActivityLocked: Pausing %s",
- "level": "DEBUG",
+ "987903142": {
+ "message": "Sleep needs to pause %s",
+ "level": "VERBOSE",
"group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
},
"996960396": {
"message": "Starting Transition %d",
@@ -2719,18 +2779,24 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
- "1001509841": {
- "message": "Auto-PIP allowed, entering PIP mode directly: %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1011462000": {
+ "message": "Re-launching after pause: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
+ "1022095595": {
+ "message": "TaskFragment info changed name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1023413388": {
"message": "Finish waiting for pause of: %s",
"level": "VERBOSE",
@@ -2917,6 +2983,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1284122013": {
+ "message": "TaskFragment appeared name=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1288731814": {
"message": "WindowState.hideLw: setting mFocusMayChange true",
"level": "INFO",
@@ -3073,6 +3145,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
+ "1494644409": {
+ "message": " Rejecting as detached: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"1495525537": {
"message": "createWallpaperAnimations()",
"level": "DEBUG",
@@ -3163,12 +3241,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/WindowContainer.java"
},
- "1585450696": {
- "message": "resumeTopActivityLocked: Restarting %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1589610525": {
"message": "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: anim=%s transit=%s isEntrance=true Callers=%s",
"level": "VERBOSE",
@@ -3217,6 +3289,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "1653025361": {
+ "message": "Register task fragment organizer=%s uid=%d pid=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_ORGANIZER",
+ "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+ },
"1653210583": {
"message": "Removing app %s delayed=%b animation=%s animating=%b",
"level": "VERBOSE",
@@ -3229,6 +3307,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/InsetsStateController.java"
},
+ "1670933628": {
+ "message": " Setting allReady override",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"1671994402": {
"message": "Nulling last startingData",
"level": "VERBOSE",
@@ -3385,18 +3469,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
- "1837992242": {
- "message": "Executing finish of activity: %s",
- "level": "VERBOSE",
- "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",
@@ -3409,6 +3481,12 @@
"group": "WM_DEBUG_FOCUS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1856783490": {
+ "message": "resumeTopActivity: Restarting %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1865125884": {
"message": "finishScreenTurningOn: mAwake=%b, mScreenOnEarly=%b, mScreenOnFully=%b, mKeyguardDrawComplete=%b, mWindowManagerDrawComplete=%b",
"level": "DEBUG",
@@ -3421,30 +3499,24 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1884961873": {
- "message": "Sleep still need to stop %d activities",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1891501279": {
"message": "cancelAnimation(): reason=%s",
"level": "DEBUG",
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "1894239744": {
- "message": "Enqueueing pending pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"1903353011": {
"message": "notifyAppStopped: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_ADD_REMOVE",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1912291550": {
+ "message": "Sleep still waiting to pause %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/TaskFragment.java"
+ },
"1918448345": {
"message": "Task appeared taskId=%d",
"level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl
new file mode 100644
index 000000000000..05a25f0210c8
--- /dev/null
+++ b/data/keyboards/Vendor_0171_Product_0419.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Amazon Luna Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Action button (circle icon, left of the Home button)
+key 158 BUTTON_SELECT
+
+# Home button (branded button in the center of the controller)
+key 172 BUTTON_MODE
+
+# Menu button (hamburger icon, right of the Home button)
+key 315 BUTTON_START
+
+# Alexa Push-To-Talk button (microphone icon, below the Home button)
+key 217 MEDIA_RECORD
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
index 68477edf97d1..00cd18ca596a 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.contains;
import static com.google.errorprone.matchers.Matchers.methodInvocation;
@@ -52,6 +53,7 @@ import javax.lang.model.element.Modifier;
@BugPattern(
name = "AndroidFrameworkBinderIdentity",
summary = "Verifies that Binder.clearCallingIdentity() is always restored",
+ linkType = NONE,
severity = WARNING)
public final class BinderIdentityChecker extends BugChecker implements MethodInvocationTreeMatcher {
private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod()
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java
index 9d1cf87a5f9d..4bee99e0ffca 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BluetoothPermissionChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.bugpatterns.android.RequiresPermissionChecker.simpleNameMatches;
import static com.google.errorprone.matchers.Matchers.allOf;
@@ -60,6 +61,7 @@ import java.util.regex.Pattern;
@BugPattern(
name = "AndroidFrameworkBluetoothPermission",
summary = "Verifies that all Bluetooth APIs have consistent permissions",
+ linkType = NONE,
severity = WARNING)
public final class BluetoothPermissionChecker extends BugChecker implements MethodTreeMatcher {
private static final Matcher<MethodTree> BLUETOOTH_API = allOf(
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ClientSidePermissionCheckChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ClientSidePermissionCheckChecker.java
index 8651a1a7fbeb..d27e7a146866 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/ClientSidePermissionCheckChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ClientSidePermissionCheckChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.enclosingClass;
@@ -42,6 +43,7 @@ import com.sun.source.tree.Tree;
@BugPattern(
name = "AndroidFrameworkClientSidePermissionCheck",
summary = "Verifies that permission checks aren't done in the app's process",
+ linkType = NONE,
severity = WARNING)
public final class ClientSidePermissionCheckChecker
extends BugChecker implements MethodInvocationTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
index e759663b5d16..43abc8bc3e54 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.bugpatterns.android.TargetSdkChecker.binaryTreeExact;
import static com.google.errorprone.matchers.Matchers.allOf;
@@ -51,6 +52,7 @@ import com.sun.source.tree.Tree.Kind;
@BugPattern(
name = "AndroidFrameworkCompatChange",
summary = "Verifies that behavior changes use the modern compatibility framework",
+ linkType = NONE,
severity = WARNING)
public final class CompatChangeChecker extends BugChecker implements BinaryTreeMatcher {
private static final Matcher<ExpressionTree> VERSION_CODE =
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
index 3a1bc1eeb9ae..c1a2048fb59d 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.bugpatterns.android.UidChecker.getFlavor;
import static com.google.errorprone.matchers.Matchers.anyOf;
@@ -54,6 +55,7 @@ import java.util.List;
@BugPattern(
name = "AndroidFrameworkContextUserId",
summary = "Verifies that system_server calls use Context.getUserId()",
+ linkType = NONE,
severity = WARNING)
public final class ContextUserIdChecker extends BugChecker implements MethodInvocationTreeMatcher {
private static final Matcher<Tree> INSIDE_MANAGER =
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
index c4c1ab6482ee..209dafaaa8fe 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
@@ -43,6 +44,7 @@ import java.util.List;
@BugPattern(
name = "AndroidFrameworkEfficientCollections",
summary = "Verifies efficient collections best-practices",
+ linkType = NONE,
severity = WARNING)
public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher {
private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List");
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
index c29a095ecc1b..cae5d8e6846d 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.enclosingClass;
@@ -45,6 +46,7 @@ import com.sun.source.tree.Tree;
@BugPattern(
name = "AndroidFrameworkEfficientParcelable",
summary = "Verifies Parcelable performance best-practices",
+ linkType = NONE,
severity = WARNING)
public final class EfficientParcelableChecker extends BugChecker
implements MethodInvocationTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
index 3a0fbd33933f..5c60d779188c 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
@@ -63,6 +64,7 @@ import javax.lang.model.element.Modifier;
@BugPattern(
name = "AndroidFrameworkEfficientStrings",
summary = "Verifies efficient Strings best-practices",
+ linkType = NONE,
severity = WARNING)
public final class EfficientStringsChecker extends BugChecker
implements MethodInvocationTreeMatcher, NewClassTreeMatcher, CompoundAssignmentTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java
index b5f26e7dc9dd..8706a68226ef 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.instanceMethod;
@@ -61,6 +62,7 @@ import javax.lang.model.element.Name;
@BugPattern(
name = "AndroidFrameworkEfficientXml",
summary = "Verifies efficient XML best-practices",
+ linkType = NONE,
severity = WARNING)
public final class EfficientXmlChecker extends BugChecker
implements MethodInvocationTreeMatcher, NewClassTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java
index e323a895ee94..c1e08217a163 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/PendingIntentMutabilityChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.anyOf;
import static com.google.errorprone.matchers.Matchers.contains;
@@ -45,6 +46,7 @@ import java.util.regex.Pattern;
@BugPattern(
name = "AndroidFrameworkPendingIntentMutability",
summary = "Verifies that FLAG_MUTABLE or FLAG_IMMUTABLE is always set",
+ linkType = NONE,
severity = WARNING)
public final class PendingIntentMutabilityChecker extends BugChecker
implements MethodInvocationTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
index d1e4309c365e..9a41cb46f194 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
@@ -79,6 +80,7 @@ import javax.lang.model.element.Name;
@BugPattern(
name = "AndroidFrameworkRequiresPermission",
summary = "Verifies that @RequiresPermission annotations are consistent across AIDL",
+ linkType = NONE,
severity = WARNING)
public final class RequiresPermissionChecker extends BugChecker
implements MethodTreeMatcher, MethodInvocationTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
index 130b256e6622..f8b401b213b8 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.contains;
@@ -60,6 +61,7 @@ import java.util.List;
@BugPattern(
name = "AndroidFrameworkRethrowFromSystem",
summary = "Verifies that system_server calls use rethrowFromSystemServer()",
+ linkType = NONE,
severity = WARNING)
public final class RethrowFromSystemChecker extends BugChecker implements TryTreeMatcher {
private static final Matcher<Tree> INSIDE_MANAGER =
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
index 032ae00536b9..5581d9987866 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.Matchers.allOf;
import static com.google.errorprone.matchers.Matchers.anyOf;
@@ -59,6 +60,7 @@ import com.sun.source.tree.Tree.Kind;
@BugPattern(
name = "AndroidFrameworkTargetSdk",
summary = "Verifies that all target SDK comparisons are sane",
+ linkType = NONE,
severity = WARNING)
public final class TargetSdkChecker extends BugChecker implements BinaryTreeMatcher {
private static final Matcher<ExpressionTree> VERSION_CODE = FieldMatchers
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
index a2ee065cd1cc..a4ad069fdfac 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
@@ -16,6 +16,7 @@
package com.google.errorprone.bugpatterns.android;
+import static com.google.errorprone.BugPattern.LinkType.NONE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import com.google.auto.service.AutoService;
@@ -46,6 +47,7 @@ import java.util.regex.Pattern;
@BugPattern(
name = "AndroidFrameworkUid",
summary = "Verifies that PID, UID and user ID arguments aren't crossed",
+ linkType = NONE,
severity = WARNING)
public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher,
NewClassTreeMatcher {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
new file mode 100644
index 000000000000..3d7b94f880ac
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallChecker.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.instanceMethod;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+ name = "AndroidFrameworkUnattributedNoteOpCall",
+ summary = "Verifies that a noteOp() call is attributed",
+ severity = WARNING)
+public final class UnattributedNoteOpCallChecker extends BugChecker
+ implements MethodInvocationTreeMatcher {
+
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOP_CALL_4 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOp(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_STARTOPNOTHROW_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("startOpNoThrow(int,int,java.lang.String,boolean)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOp(int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("noteProxyOpNoThrow(java.lang.String,java.lang.String,int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_1 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_2 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(java.lang.String,int,java.lang.String)"));
+ private static final Matcher<ExpressionTree> UNATTRIBUTED_FINISHOP_CALL_3 = methodInvocation(
+ instanceMethod().onExactClass("android.app.AppOpsManager")
+ .withSignature("finishOp(int,int,java.lang.String)"));
+
+ @Override
+ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+ if (UNATTRIBUTED_NOTEOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_NOTEOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOp call! Please use noteOp(int, String, String, String) or noteOp(int, CallerIdentity)")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteOpNoThrow call! Please use noteOpNoThrow(String, int, String, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_3.matches(tree, state)
+ || UNATTRIBUTED_STARTOP_CALL_4.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOp call! Please use startOp(int, int, String, boolean, String, String)")
+ .build();
+ }
+ if (UNATTRIBUTED_STARTOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_STARTOPNOTHROW_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed startOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOP_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOp call!")
+ .build();
+ }
+ if (UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_NOTEPROXYOPNOTHROW_CALL_2.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed noteProxyOpNoThrow call!")
+ .build();
+ }
+ if (UNATTRIBUTED_FINISHOP_CALL_1.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_2.matches(tree, state)
+ || UNATTRIBUTED_FINISHOP_CALL_3.matches(tree, state)) {
+ return buildDescription(tree)
+ .setMessage("Unattributed finishOp call!")
+ .build();
+ }
+
+
+
+ return Description.NO_MATCH;
+ }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
new file mode 100644
index 000000000000..9a98c7c2d4c9
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UnattributedNoteOpCallCheckerTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UnattributedNoteOpCallCheckerTest {
+ private CompilationTestHelper mCompilationHelper;
+
+ @Before
+ public void setUp() {
+ mCompilationHelper = CompilationTestHelper.newInstance(
+ UnattributedNoteOpCallChecker.class, getClass());
+ }
+
+ @Test
+ public void testNoteOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " mAppOps.noteOp(\"foo\", 0, \"bar\", \"baz\", \"qux\");",
+ " mAppOps.noteOp(0, 0, \"bar\", \"baz\", \"qux\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(0, 1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testStartOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(0, 0, \"bar\", true);",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.startOpNoThrow(\"foo\", 1, \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(1, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOp(\"foo\", \"bar\");",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testNoteProxyOpNoThrow() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.noteProxyOpNoThrow(\"foo\", \"bar\", 1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+ @Test
+ public void testFinishOp() {
+ mCompilationHelper
+ .addSourceFile("/android/app/AppOpsManager.java")
+ .addSourceLines("Example.java",
+ "import android.app.AppOpsManager;",
+ "public class Example {",
+ " void example() {",
+ " AppOpsManager mAppOps = new AppOpsManager();",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1, 2, \"foo\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(\"foo\", 1, \"bar\");",
+ " // BUG: Diagnostic contains:",
+ " mAppOps.finishOp(1);",
+ " }",
+ "}")
+ .doTest();
+ }
+
+
+
+}
diff --git a/errorprone/tests/res/android/app/AppOpsManager.java b/errorprone/tests/res/android/app/AppOpsManager.java
new file mode 100644
index 000000000000..216270cb65d7
--- /dev/null
+++ b/errorprone/tests/res/android/app/AppOpsManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+public class AppOpsManager {
+
+ public int noteOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(String op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOp(int op, int uid, String packageName,
+ String attributionTag, String message) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOp(int op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int noteProxyOpNoThrow(String op, String proxiedPackageName,
+ int proxiedUid) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(String op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void finishOp(int op, int uid, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 81aeec07db9e..abf7e9911086 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1272,11 +1272,10 @@ public class Canvas extends BaseCanvas {
* in a way similar to quickReject, in that it tells you that drawing
* outside of these bounds will be clipped out.
*
- * @param bounds Return the clip bounds here. If it is null, ignore it but
- * still return true if the current clip is non-empty.
+ * @param bounds Return the clip bounds here.
* @return true if the current clip is non-empty.
*/
- public boolean getClipBounds(@Nullable Rect bounds) {
+ public boolean getClipBounds(@NonNull Rect bounds) {
return nGetClipBounds(mNativeCanvasWrapper, bounds);
}
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 93a336e7a408..96b33259e739 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,7 @@ import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
+import android.util.ArraySet;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +38,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -120,7 +122,23 @@ public class FontListParser {
}
}
- private static FontConfig readFamilies(
+ /**
+ * Parses the familyset tag in font.xml
+ * @param parser a XML pull parser
+ * @param fontDir A system font directory, e.g. "/system/fonts"
+ * @param customization A OEM font customization
+ * @param updatableFontMap A map of updated font files
+ * @param lastModifiedDate A date that the system font is updated.
+ * @param configVersion A version of system font config.
+ * @param allowNonExistingFile true if allowing non-existing font files during parsing fonts.xml
+ * @return result of fonts.xml
+ *
+ * @throws XmlPullParserException
+ * @throws IOException
+ *
+ * @hide
+ */
+ public static FontConfig readFamilies(
@NonNull XmlPullParser parser,
@NonNull String fontDir,
@NonNull FontCustomizationParser.Result customization,
@@ -159,7 +177,24 @@ public class FontListParser {
}
families.addAll(oemNamedFamilies.values());
- return new FontConfig(families, aliases, lastModifiedDate, configVersion);
+
+ // Filters aliases that point to non-existing families.
+ Set<String> namedFamilies = new ArraySet<>();
+ for (int i = 0; i < families.size(); ++i) {
+ String name = families.get(i).getName();
+ if (name != null) {
+ namedFamilies.add(name);
+ }
+ }
+ List<FontConfig.Alias> filtered = new ArrayList<>();
+ for (int i = 0; i < aliases.size(); ++i) {
+ FontConfig.Alias alias = aliases.get(i);
+ if (namedFamilies.contains(alias.getOriginal())) {
+ filtered.add(alias);
+ }
+ }
+
+ return new FontConfig(families, filtered, lastModifiedDate, configVersion);
}
private static boolean keepReading(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index c3b1cd74d29b..8894fa32b480 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -752,22 +752,14 @@ public class HardwareRenderer {
nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
- private ASurfaceTransactionCallback mASurfaceTransactionCallback;
-
/** @hide */
- public void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) {
- // ensure callback is kept alive on the java side since weak ref is used in native code
- mASurfaceTransactionCallback = callback;
+ protected void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) {
nSetASurfaceTransactionCallback(mNativeProxy, callback);
}
- private PrepareSurfaceControlForWebviewCallback mAPrepareSurfaceControlForWebviewCallback;
-
/** @hide */
- public void setPrepareSurfaceControlForWebviewCallback(
+ protected void setPrepareSurfaceControlForWebviewCallback(
PrepareSurfaceControlForWebviewCallback callback) {
- // ensure callback is kept alive on the java side since weak ref is used in native code
- mAPrepareSurfaceControlForWebviewCallback = callback;
nSetPrepareSurfaceControlForWebviewCallback(mNativeProxy, callback);
}
@@ -1075,6 +1067,43 @@ public class HardwareRenderer {
ProcessInitializer.sInstance.setContext(context);
}
+ /**
+ * Returns true if HardwareRender will produce output.
+ *
+ * This value is global to the process and affects all uses of HardwareRenderer,
+ * including
+ * those created by the system such as those used by the View tree when using hardware
+ * accelerated rendering.
+ *
+ * Default is true in all production environments, but may be false in testing-focused
+ * emulators or if {@link #setDrawingEnabled(boolean)} is used.
+ */
+ public static boolean isDrawingEnabled() {
+ return nIsDrawingEnabled();
+ }
+
+ /**
+ * Toggles whether or not HardwareRenderer will produce drawing output globally in the current
+ * process.
+ *
+ * This applies to all HardwareRenderer instances, including those created by the platform such
+ * as those used by the system for hardware accelerated View rendering.
+ *
+ * The capability to disable drawing output is intended for test environments, primarily
+ * headless ones. By setting this to false, tests that launch activities or interact with Views
+ * can be quicker with less RAM usage by skipping the final step of View drawing. All View
+ * lifecycle events will occur as normal, only the final step of rendering on the GPU to the
+ * display will be skipped.
+ *
+ * This can be toggled on and off at will, so screenshot tests can also run in this same
+ * environment by toggling drawing back on and forcing a frame to be drawn such as by calling
+ * view#invalidate(). Once drawn and the screenshot captured, this can then be turned back off.
+ */
+ // TODO(b/194195794): Add link to androidx's Screenshot library for help with this
+ public static void setDrawingEnabled(boolean drawingEnabled) {
+ nSetDrawingEnabled(drawingEnabled);
+ }
+
private static final class DestroyContextRunnable implements Runnable {
private final long mNativeInstance;
@@ -1262,7 +1291,7 @@ public class HardwareRenderer {
/**
* @hide
*/
- public static native boolean isWebViewOverlaysEnabled();
+ protected static native boolean isWebViewOverlaysEnabled();
/** @hide */
protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
@@ -1393,4 +1422,8 @@ public class HardwareRenderer {
private static native void nInitDisplayInfo(int width, int height, float refreshRate,
int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos);
+
+ private static native void nSetDrawingEnabled(boolean drawingEnabled);
+
+ private static native boolean nIsDrawingEnabled();
}
diff --git a/graphics/java/android/graphics/HardwareRendererObserver.java b/graphics/java/android/graphics/HardwareRendererObserver.java
index e2a05723166f..d5a6a2fe158a 100644
--- a/graphics/java/android/graphics/HardwareRendererObserver.java
+++ b/graphics/java/android/graphics/HardwareRendererObserver.java
@@ -21,12 +21,14 @@ import android.os.Handler;
import com.android.internal.util.VirtualRefBasePtr;
+import java.lang.ref.WeakReference;
+
/**
* Provides streaming access to frame stats information from HardwareRenderer to apps.
*
* @hide
*/
-public class HardwareRendererObserver {
+public final class HardwareRendererObserver {
private final long[] mFrameMetrics;
private final Handler mHandler;
private final OnFrameMetricsAvailableListener mListener;
@@ -74,15 +76,14 @@ public class HardwareRendererObserver {
mFrameMetrics = frameMetrics;
mHandler = handler;
mListener = listener;
- mNativePtr = new VirtualRefBasePtr(nCreateObserver(waitForPresentTime));
+ mNativePtr = new VirtualRefBasePtr(nCreateObserver(
+ new WeakReference<>(this), waitForPresentTime));
}
/*package*/ long getNativeInstance() {
return mNativePtr.get();
}
- // Called by native on the provided Handler
- @SuppressWarnings("unused")
private void notifyDataAvailable() {
mHandler.post(() -> {
boolean hasMoreData = true;
@@ -98,6 +99,21 @@ public class HardwareRendererObserver {
});
}
- private native long nCreateObserver(boolean waitForPresentTime);
+ /**
+ * called by native
+ * @hide
+ * @return true to keep listening, false if this is a dead observer
+ */
+ static boolean invokeDataAvailable(WeakReference<HardwareRendererObserver> weakObserver) {
+ HardwareRendererObserver observer = weakObserver.get();
+ if (observer != null) {
+ observer.notifyDataAvailable();
+ return true;
+ }
+ return false;
+ }
+
+ private static native long nCreateObserver(WeakReference<HardwareRendererObserver> observer,
+ boolean waitForPresentTime);
private static native int nGetNextBuffer(long nativePtr, long[] data);
}
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 01fd231d011f..5fd53adc409a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -35,6 +35,7 @@ import libcore.util.NativeAllocationRegistry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
/**
* <p>RenderNode is used to build hardware accelerated rendering hierarchies. Each RenderNode
@@ -263,7 +264,6 @@ public final class RenderNode {
* @hide
*/
public interface PositionUpdateListener {
-
/**
* Called by native by a Rendering Worker thread to update window position
*
@@ -272,6 +272,21 @@ public final class RenderNode {
void positionChanged(long frameNumber, int left, int top, int right, int bottom);
/**
+ * Called by JNI
+ *
+ * @hide */
+ static boolean callPositionChanged(WeakReference<PositionUpdateListener> weakListener,
+ long frameNumber, int left, int top, int right, int bottom) {
+ final PositionUpdateListener listener = weakListener.get();
+ if (listener != null) {
+ listener.positionChanged(frameNumber, left, top, right, bottom);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Call to apply a stretch effect to any child SurfaceControl layers
*
* TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
@@ -286,6 +301,26 @@ public final class RenderNode {
float childRelativeTop, float childRelativeRight, float childRelativeBottom) { }
/**
+ * Called by JNI
+ *
+ * @hide */
+ static boolean callApplyStretch(WeakReference<PositionUpdateListener> weakListener,
+ long frameNumber, float width, float height,
+ float vecX, float vecY,
+ float maxStretchX, float maxStretchY, float childRelativeLeft,
+ float childRelativeTop, float childRelativeRight, float childRelativeBottom) {
+ final PositionUpdateListener listener = weakListener.get();
+ if (listener != null) {
+ listener.applyStretch(frameNumber, width, height, vecX, vecY, maxStretchX,
+ maxStretchY, childRelativeLeft, childRelativeTop, childRelativeRight,
+ childRelativeBottom);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Called by native on RenderThread to notify that the view is no longer in the
* draw tree. UI thread is blocked at this point.
*
@@ -293,6 +328,21 @@ public final class RenderNode {
*/
void positionLost(long frameNumber);
+ /**
+ * Called by JNI
+ *
+ * @hide */
+ static boolean callPositionLost(WeakReference<PositionUpdateListener> weakListener,
+ long frameNumber) {
+ final PositionUpdateListener listener = weakListener.get();
+ if (listener != null) {
+ listener.positionLost(frameNumber);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
}
private static final class CompositePositionUpdateListener implements PositionUpdateListener {
@@ -353,7 +403,7 @@ public final class RenderNode {
comp = comp.with(listener);
}
mCompositePositionUpdateListener = comp;
- nRequestPositionUpdates(mNativeRenderNode, comp);
+ nRequestPositionUpdates(mNativeRenderNode, new WeakReference<>(comp));
}
/**
@@ -368,7 +418,7 @@ public final class RenderNode {
if (comp != null) {
comp = comp.without(listener);
mCompositePositionUpdateListener = comp;
- nRequestPositionUpdates(mNativeRenderNode, comp);
+ nRequestPositionUpdates(mNativeRenderNode, new WeakReference<>(comp));
}
}
@@ -1575,7 +1625,7 @@ public final class RenderNode {
private static native int nGetAllocatedSize(long renderNode);
private static native void nRequestPositionUpdates(long renderNode,
- PositionUpdateListener callback);
+ WeakReference<PositionUpdateListener> callback);
// Animations
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index dd64327ebfd8..82b3f6875d67 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -48,6 +48,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -494,7 +495,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
if (mAnimationCallbacks == null) {
mAnimationCallbacks = new ArrayList<Animatable2.AnimationCallback>();
- nSetOnAnimationEndListener(mState.mNativePtr, this);
+ nSetOnAnimationEndListener(mState.mNativePtr, new WeakReference<>(this));
}
if (!mAnimationCallbacks.contains(callback)) {
@@ -562,6 +563,13 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
* callback, so no need to post.
*/
@SuppressWarnings("unused")
+ private static void callOnAnimationEnd(WeakReference<AnimatedImageDrawable> weakDrawable) {
+ AnimatedImageDrawable drawable = weakDrawable.get();
+ if (drawable != null) {
+ drawable.onAnimationEnd();
+ }
+ }
+
private void onAnimationEnd() {
if (mAnimationCallbacks != null) {
for (Animatable2.AnimationCallback callback : mAnimationCallbacks) {
@@ -603,7 +611,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 {
private static native void nSetRepeatCount(long nativePtr, int repeatCount);
// Pass the drawable down to native so it can call onAnimationEnd.
private static native void nSetOnAnimationEndListener(long nativePtr,
- @Nullable AnimatedImageDrawable drawable);
+ @Nullable WeakReference<AnimatedImageDrawable> drawable);
@FastNative
private static native long nNativeByteSize(long nativePtr);
@FastNative
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 3616a4d66399..166a795e1661 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -723,8 +723,17 @@ public class GradientDrawable extends Drawable {
*/
@Nullable
public int[] getColors() {
- return mGradientState.mGradientColors == null ?
- null : mGradientState.mGradientColors.clone();
+ if (mGradientState.mGradientColors == null) {
+ return null;
+ } else {
+ int[] colors = new int[mGradientState.mGradientColors.length];
+ for (int i = 0; i < mGradientState.mGradientColors.length; i++) {
+ if (mGradientState.mGradientColors[i] != null) {
+ colors[i] = mGradientState.mGradientColors[i].getDefaultColor();
+ }
+ }
+ return colors;
+ }
}
@Override
@@ -1277,7 +1286,15 @@ public class GradientDrawable extends Drawable {
mRect.set(bounds.left + inset, bounds.top + inset,
bounds.right - inset, bounds.bottom - inset);
- final int[] gradientColors = st.mGradientColors;
+ int[] gradientColors = null;
+ if (st.mGradientColors != null) {
+ gradientColors = new int[st.mGradientColors.length];
+ for (int i = 0; i < gradientColors.length; i++) {
+ if (st.mGradientColors[i] != null) {
+ gradientColors[i] = st.mGradientColors[i].getDefaultColor();
+ }
+ }
+ }
if (gradientColors != null) {
final RectF r = mRect;
final float x0, x1, y0, y1;
@@ -1439,6 +1456,14 @@ public class GradientDrawable extends Drawable {
state.mStrokeColors = state.mStrokeColors.obtainForTheme(t);
}
+ if (state.mGradientColors != null) {
+ for (int i = 0; i < state.mGradientColors.length; i++) {
+ if (state.mGradientColors[i] != null && state.mGradientColors[i].canApplyTheme()) {
+ state.mGradientColors[i] = state.mGradientColors[i].obtainForTheme(t);
+ }
+ }
+ }
+
applyThemeChildElements(t);
updateLocalState(t.getResources());
@@ -1726,38 +1751,46 @@ public class GradientDrawable extends Drawable {
st.mGradient = a.getInt(
R.styleable.GradientDrawableGradient_type, st.mGradient);
+ ColorStateList startCSL = a.getColorStateList(
+ R.styleable.GradientDrawableGradient_startColor);
+ ColorStateList centerCSL = a.getColorStateList(
+ R.styleable.GradientDrawableGradient_centerColor);
+ ColorStateList endCSL = a.getColorStateList(
+ R.styleable.GradientDrawableGradient_endColor);
+
final boolean hasGradientColors = st.mGradientColors != null;
final boolean hasGradientCenter = st.hasCenterColor();
- final int prevStart = hasGradientColors ? st.mGradientColors[0] : 0;
- final int prevCenter = hasGradientCenter ? st.mGradientColors[1] : 0;
- final int prevEnd;
- if (st.hasCenterColor()) {
+ int startColor = startCSL != null ? startCSL.getDefaultColor() : 0;
+ int centerColor = centerCSL != null ? centerCSL.getDefaultColor() : 0;
+ int endColor = endCSL != null ? endCSL.getDefaultColor() : 0;
+
+ if (hasGradientColors && st.mGradientColors[0] != null) {
+ startColor = st.mGradientColors[0].getDefaultColor();
+ }
+ if (hasGradientCenter && st.mGradientColors[1] != null) {
+ centerColor = st.mGradientColors[1].getDefaultColor();
+ }
+ if (hasGradientCenter && st.mGradientColors[2] != null) {
// if there is a center color, the end color is the last of the 3 values
- prevEnd = st.mGradientColors[2];
- } else if (hasGradientColors) {
+ endColor = st.mGradientColors[2].getDefaultColor();
+ } else if (hasGradientColors && st.mGradientColors[1] != null) {
// if there is not a center color but there are already colors configured, then
// the end color is the 2nd value in the array
- prevEnd = st.mGradientColors[1];
- } else {
- // otherwise, there isn't a previously configured end color
- prevEnd = 0;
+ endColor = st.mGradientColors[1].getDefaultColor();
}
- final int startColor = a.getColor(
- R.styleable.GradientDrawableGradient_startColor, prevStart);
final boolean hasCenterColor = a.hasValue(
R.styleable.GradientDrawableGradient_centerColor) || hasGradientCenter;
- final int centerColor = a.getColor(
- R.styleable.GradientDrawableGradient_centerColor, prevCenter);
- final int endColor = a.getColor(
- R.styleable.GradientDrawableGradient_endColor, prevEnd);
if (hasCenterColor) {
- st.mGradientColors = new int[3];
- st.mGradientColors[0] = startColor;
- st.mGradientColors[1] = centerColor;
- st.mGradientColors[2] = endColor;
+ st.mGradientColors = new ColorStateList[3];
+ st.mGradientColors[0] =
+ startCSL != null ? startCSL : ColorStateList.valueOf(startColor);
+ st.mGradientColors[1] =
+ centerCSL != null ? centerCSL : ColorStateList.valueOf(centerColor);
+ st.mGradientColors[2] =
+ endCSL != null ? endCSL : ColorStateList.valueOf(endColor);
st.mPositions = new float[3];
st.mPositions[0] = 0.0f;
@@ -1765,9 +1798,11 @@ public class GradientDrawable extends Drawable {
st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
st.mPositions[2] = 1f;
} else {
- st.mGradientColors = new int[2];
- st.mGradientColors[0] = startColor;
- st.mGradientColors[1] = endColor;
+ st.mGradientColors = new ColorStateList[2];
+ st.mGradientColors[0] =
+ startCSL != null ? startCSL : ColorStateList.valueOf(startColor);
+ st.mGradientColors[1] =
+ endCSL != null ? endCSL : ColorStateList.valueOf(endColor);
}
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
@@ -1981,7 +2016,7 @@ public class GradientDrawable extends Drawable {
public ColorStateList mSolidColors;
public ColorStateList mStrokeColors;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124050917)
- public @ColorInt int[] mGradientColors;
+ public ColorStateList[] mGradientColors; // no support for state-based color
public @ColorInt int[] mTempColors; // no need to copy
public float[] mTempPositions; // no need to copy
@UnsupportedAppUsage
@@ -2188,6 +2223,13 @@ public class GradientDrawable extends Drawable {
@Override
public boolean canApplyTheme() {
+ boolean mGradientColorState = mGradientColors != null;
+ if (mGradientColors != null) {
+ for (int i = 0; i < mGradientColors.length; i++) {
+ mGradientColorState |= (mGradientColors[i] != null && mGradientColors[i]
+ .canApplyTheme());
+ }
+ }
return mThemeAttrs != null
|| mAttrSize != null || mAttrGradient != null
|| mAttrSolid != null || mAttrStroke != null
@@ -2195,6 +2237,7 @@ public class GradientDrawable extends Drawable {
|| (mTint != null && mTint.canApplyTheme())
|| (mStrokeColors != null && mStrokeColors.canApplyTheme())
|| (mSolidColors != null && mSolidColors.canApplyTheme())
+ || mGradientColorState
|| super.canApplyTheme();
}
@@ -2246,7 +2289,18 @@ public class GradientDrawable extends Drawable {
}
public void setGradientColors(@Nullable int[] colors) {
- mGradientColors = colors;
+ if (colors == null) {
+ mGradientColors = null;
+ } else {
+ // allocate new CSL array only if the size of the current array is different
+ // from the size of the given parameter
+ if (mGradientColors == null || mGradientColors.length != colors.length) {
+ mGradientColors = new ColorStateList[colors.length];
+ }
+ for (int i = 0; i < colors.length; i++) {
+ mGradientColors[i] = ColorStateList.valueOf(colors[i]);
+ }
+ }
mSolidColors = null;
computeOpacity();
}
@@ -2263,7 +2317,8 @@ public class GradientDrawable extends Drawable {
if (mGradientColors != null) {
for (int i = 0; i < mGradientColors.length; i++) {
- if (!isOpaque(mGradientColors[i])) {
+ if (mGradientColors[i] != null
+ && !isOpaque(mGradientColors[i].getDefaultColor())) {
return;
}
}
diff --git a/graphics/java/android/graphics/text/TextRunShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
index 8459e7bfe191..19ea04a48046 100644
--- a/graphics/java/android/graphics/text/TextRunShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -24,8 +24,6 @@ import android.text.TextUtils;
import com.android.internal.util.Preconditions;
-import dalvik.annotation.optimization.FastNative;
-
/**
* Provides conversion from a text into glyph array.
*
@@ -116,12 +114,10 @@ public class TextRunShaper {
}
}
- @FastNative
private static native long nativeShapeTextRun(
char[] text, int start, int count, int contextStart, int contextCount,
boolean isRtl, long nativePaint);
- @FastNative
private static native long nativeShapeTextRun(
String text, int start, int count, int contextStart, int contextCount,
boolean isRtl, long nativePaint);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
index 5619585d9c3c..b24a22dbc0ec 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -102,11 +102,9 @@ public class AndroidKeyStoreKey implements Key {
final int prime = 31;
int result = 1;
- result = prime * result + ((mDescriptor == null) ? 0 : mDescriptor.hashCode());
+ result = prime * result + getClass().hashCode();
result = prime * result + (int) (mKeyId >>> 32);
result = prime * result + (int) (mKeyId & 0xffffffff);
- result = prime * result + ((mAuthorizations == null) ? 0 : mAuthorizations.hashCode());
- result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
return result;
}
@@ -122,10 +120,6 @@ public class AndroidKeyStoreKey implements Key {
return false;
}
AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj;
- if (mKeyId != other.mKeyId) {
- return false;
- }
-
- return true;
+ return mKeyId == other.mKeyId;
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index db3e567cb6b4..4842984e8c6a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -23,6 +23,7 @@ import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyMetadata;
import java.security.PublicKey;
+import java.util.Objects;
/**
* {@link PublicKey} backed by Android Keystore.
@@ -75,9 +76,14 @@ public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implem
if (!super.equals(obj)) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- return true;
+
+ /*
+ * getClass().equals(ojb.getClass()) is implied by the call to super.equals() above. This
+ * means we can cast obj to AndroidKeyStorePublicKey here.
+ */
+ final AndroidKeyStorePublicKey other = (AndroidKeyStorePublicKey) obj;
+
+ return Objects.equals(mCertificate, other.mCertificate) && Objects.equals(mCertificateChain,
+ other.mCertificateChain);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
index b7a60392c512..ce4e10364ba2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -18,6 +18,10 @@ package androidx.window.extensions;
import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.organizer.EmbeddingExtensionImpl;
+
/**
* Provider class that will instantiate the library implementation. It must be included in the
* vendor library, and the vendor implementation must match the signature of this class.
@@ -31,6 +35,12 @@ public class ExtensionProvider {
return new SampleExtensionImpl(context);
}
+ /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */
+ public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl(
+ @NonNull Context context) {
+ return new EmbeddingExtensionImpl();
+ }
+
/**
* The support library will use this method to check API version compatibility.
* @return API version string in MAJOR.MINOR.PATCH-description format.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
new file mode 100644
index 000000000000..9a8961f1d460
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.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 androidx.window.extensions.organizer;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Reference implementation of the activity embedding interface defined in WM Jetpack.
+ */
+public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent {
+
+ private final SplitController mSplitController;
+
+ public EmbeddingExtensionImpl() {
+ mSplitController = new SplitController();
+ }
+
+ @Override
+ public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
+ mSplitController.setEmbeddingRules(rules);
+ }
+
+ @Override
+ public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
+ mSplitController.setEmbeddingCallback(consumer);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
new file mode 100644
index 000000000000..dd00189c3bf1
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.Activity;
+import android.app.WindowConfiguration.WindowingMode;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentCreationParams;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizer;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Platform default Extensions implementation of {@link TaskFragmentOrganizer} to organize
+ * task fragments.
+ *
+ * All calls into methods of this class are expected to be on the UI thread.
+ */
+class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
+
+ /** Mapping from the client assigned unique token to the {@link TaskFragmentInfo}. */
+ private final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
+
+ /** Mapping from the client assigned unique token to the TaskFragment {@link SurfaceControl}. */
+ private final Map<IBinder, SurfaceControl> mFragmentLeashes = new ArrayMap<>();
+
+ /**
+ * Mapping from the client assigned unique token to the TaskFragment parent
+ * {@link Configuration}.
+ */
+ final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
+
+ private final TaskFragmentCallback mCallback;
+
+ /**
+ * Callback that notifies the controller about changes to task fragments.
+ */
+ interface TaskFragmentCallback {
+ void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo);
+ void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
+ void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
+ void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
+ @NonNull Configuration parentConfig);
+ }
+
+ /**
+ * @param executor callbacks from WM Core are posted on this executor. It should be tied to the
+ * UI thread that all other calls into methods of this class are also on.
+ */
+ JetpackTaskFragmentOrganizer(@NonNull Executor executor, TaskFragmentCallback callback) {
+ super(executor);
+ mCallback = callback;
+ }
+
+ /**
+ * Starts a new Activity and puts it into split with an existing Activity side-by-side.
+ * @param launchingFragmentToken token for the launching TaskFragment. If it exists, it will
+ * be resized based on {@param launchingFragmentBounds}.
+ * Otherwise, we will create a new TaskFragment with the given
+ * token for the {@param launchingActivity}.
+ * @param launchingFragmentBounds the initial bounds for the launching TaskFragment.
+ * @param launchingActivity the Activity to put on the left hand side of the split as the
+ * primary.
+ * @param secondaryFragmentToken token to create the secondary TaskFragment with.
+ * @param secondaryFragmentBounds the initial bounds for the secondary TaskFragment
+ * @param activityIntent Intent to start the secondary Activity with.
+ * @param activityOptions ActivityOptions to start the secondary Activity with.
+ */
+ void startActivityToSide(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
+ @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
+ @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
+ @Nullable Bundle activityOptions) {
+ final IBinder ownerToken = launchingActivity.getActivityToken();
+
+ // Create or resize the launching TaskFragment.
+ if (mFragmentInfos.containsKey(launchingFragmentToken)) {
+ resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds);
+ } else {
+ createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
+ launchingFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, launchingActivity);
+ }
+
+ // Create a TaskFragment for the secondary activity.
+ createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken,
+ secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent,
+ activityOptions);
+
+ // Set adjacent to each other so that the containers below will be invisible.
+ wct.setAdjacentTaskFragments(launchingFragmentToken, secondaryFragmentToken);
+ }
+
+ /**
+ * Expands an existing TaskFragment to fill parent.
+ * @param wct WindowContainerTransaction in which the task fragment should be resized.
+ * @param fragmentToken token of an existing TaskFragment.
+ */
+ void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
+ resizeTaskFragment(wct, fragmentToken, new Rect());
+ wct.setAdjacentTaskFragments(fragmentToken, null);
+ }
+
+ /**
+ * Expands an existing TaskFragment to fill parent.
+ * @param fragmentToken token of an existing TaskFragment.
+ */
+ void expandTaskFragment(IBinder fragmentToken) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ expandTaskFragment(wct, fragmentToken);
+ applyTransaction(wct);
+ }
+
+ /**
+ * Expands an Activity to fill parent by moving it to a new TaskFragment.
+ * @param fragmentToken token to create new TaskFragment with.
+ * @param activity activity to move to the fill-parent TaskFragment.
+ */
+ void expandActivity(IBinder fragmentToken, Activity activity) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ createTaskFragmentAndReparentActivity(
+ wct, fragmentToken, activity.getActivityToken(), new Rect(),
+ WINDOWING_MODE_UNDEFINED, activity);
+ applyTransaction(wct);
+ }
+
+ /**
+ * @param ownerToken The token of the activity that creates this task fragment. It does not
+ * have to be a child of this task fragment, but must belong to the same task.
+ */
+ void createTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken,
+ IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
+ final TaskFragmentCreationParams fragmentOptions =
+ createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
+ wct.createTaskFragment(fragmentOptions);
+ }
+
+ /**
+ * @param ownerToken The token of the activity that creates this task fragment. It does not
+ * have to be a child of this task fragment, but must belong to the same task.
+ */
+ private void createTaskFragmentAndReparentActivity(
+ WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
+ @NonNull Rect bounds, @WindowingMode int windowingMode, Activity activity) {
+ createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
+ wct.reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken());
+ }
+
+ /**
+ * @param ownerToken The token of the activity that creates this task fragment. It does not
+ * have to be a child of this task fragment, but must belong to the same task.
+ */
+ private void createTaskFragmentAndStartActivity(
+ WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
+ @NonNull Rect bounds, @WindowingMode int windowingMode, Intent activityIntent,
+ @Nullable Bundle activityOptions) {
+ createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
+ wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions);
+ }
+
+ TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
+ Rect bounds, @WindowingMode int windowingMode) {
+ if (mFragmentInfos.containsKey(fragmentToken)) {
+ throw new IllegalArgumentException(
+ "There is an existing TaskFragment with fragmentToken=" + fragmentToken);
+ }
+
+ return new TaskFragmentCreationParams.Builder(
+ getOrganizerToken(),
+ fragmentToken,
+ ownerToken)
+ .setInitialBounds(bounds)
+ .setWindowingMode(windowingMode)
+ .build();
+ }
+
+ void resizeTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken,
+ @Nullable Rect bounds) {
+ if (!mFragmentInfos.containsKey(fragmentToken)) {
+ throw new IllegalArgumentException(
+ "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
+ }
+ if (bounds == null) {
+ bounds = new Rect();
+ }
+ wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds);
+ }
+
+ void deleteTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
+ if (!mFragmentInfos.containsKey(fragmentToken)) {
+ throw new IllegalArgumentException(
+ "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken);
+ }
+ wct.deleteTaskFragment(mFragmentInfos.get(fragmentToken).getToken());
+ }
+
+ @Override
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
+ final TaskFragmentInfo info = taskFragmentAppearedInfo.getTaskFragmentInfo();
+ final IBinder fragmentToken = info.getFragmentToken();
+ final SurfaceControl leash = taskFragmentAppearedInfo.getLeash();
+ mFragmentInfos.put(fragmentToken, info);
+ mFragmentLeashes.put(fragmentToken, leash);
+
+ if (mCallback != null) {
+ mCallback.onTaskFragmentAppeared(taskFragmentAppearedInfo);
+ }
+ }
+
+ @Override
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
+ mFragmentInfos.put(fragmentToken, taskFragmentInfo);
+
+ if (mCallback != null) {
+ mCallback.onTaskFragmentInfoChanged(taskFragmentInfo);
+ }
+ }
+
+ @Override
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
+ mFragmentLeashes.remove(taskFragmentInfo.getFragmentToken());
+ mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
+
+ if (mCallback != null) {
+ mCallback.onTaskFragmentVanished(taskFragmentInfo);
+ }
+ }
+
+ @Override
+ public void onTaskFragmentParentInfoChanged(
+ @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
+ mFragmentParentConfigs.put(fragmentToken, parentConfig);
+
+ if (mCallback != null) {
+ mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
new file mode 100644
index 000000000000..8fd710a7986d
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitPlaceholderRule;
+import androidx.window.extensions.embedding.SplitRule;
+
+/**
+ * Client-side descriptor of a split that holds two containers.
+ */
+class SplitContainer {
+ private final TaskFragmentContainer mPrimaryContainer;
+ private final TaskFragmentContainer mSecondaryContainer;
+ private final SplitRule mSplitRule;
+
+ SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
+ @NonNull Activity primaryActivity,
+ @NonNull TaskFragmentContainer secondaryContainer,
+ @NonNull SplitRule splitRule) {
+ mPrimaryContainer = primaryContainer;
+ mSecondaryContainer = secondaryContainer;
+ mSplitRule = splitRule;
+
+ final boolean isPlaceholderContainer = isPlaceholderContainer();
+ final boolean shouldFinishPrimaryWithSecondary = (mSplitRule instanceof SplitPairRule)
+ && ((SplitPairRule) mSplitRule).shouldFinishPrimaryWithSecondary();
+ final boolean shouldFinishSecondaryWithPrimary = (mSplitRule instanceof SplitPairRule)
+ && ((SplitPairRule) mSplitRule).shouldFinishSecondaryWithPrimary();
+
+ if (shouldFinishPrimaryWithSecondary || isPlaceholderContainer) {
+ mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
+ }
+ if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) {
+ mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
+ }
+ }
+
+ @NonNull
+ TaskFragmentContainer getPrimaryContainer() {
+ return mPrimaryContainer;
+ }
+
+ @NonNull
+ TaskFragmentContainer getSecondaryContainer() {
+ return mSecondaryContainer;
+ }
+
+ @NonNull
+ SplitRule getSplitRule() {
+ return mSplitRule;
+ }
+
+ boolean isPlaceholderContainer() {
+ return (mSplitRule instanceof SplitPlaceholderRule);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
new file mode 100644
index 000000000000..05c6792a3fc7
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityClient;
+import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Pair;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.window.extensions.embedding.ActivityRule;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitPlaceholderRule;
+import androidx.window.extensions.embedding.SplitRule;
+import androidx.window.extensions.embedding.TaskFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Main controller class that manages split states and presentation.
+ */
+public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback {
+
+ private final SplitPresenter mPresenter;
+
+ // Currently applied split configuration.
+ private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
+ private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+ private final List<SplitContainer> mSplitContainers = new ArrayList<>();
+
+ // Callback to Jetpack to notify about changes to split states.
+ private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
+
+ public SplitController() {
+ mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
+ ActivityThread activityThread = ActivityThread.currentActivityThread();
+ // Register a callback to be notified about activities being created.
+ activityThread.getApplication().registerActivityLifecycleCallbacks(
+ new LifecycleCallbacks());
+ // Intercept activity starts to route activities to new containers if necessary.
+ Instrumentation instrumentation = activityThread.getInstrumentation();
+ instrumentation.addMonitor(new ActivityStartMonitor());
+ }
+
+ /** Updates the embedding rules applied to future activity launches. */
+ public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
+ mSplitRules.clear();
+ mSplitRules.addAll(rules);
+ }
+
+ @NonNull
+ public List<EmbeddingRule> getSplitRules() {
+ return mSplitRules;
+ }
+
+ /**
+ * Starts an activity to side of the launchingActivity with the provided split config.
+ */
+ public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
+ @Nullable Bundle options, @NonNull SplitRule sideRule,
+ @NonNull Consumer<Exception> failureCallback) {
+ try {
+ mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
+ } catch (Exception e) {
+ failureCallback.accept(e);
+ }
+ }
+
+ /**
+ * Registers the split organizer callback to notify about changes to active splits.
+ */
+ public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+ mEmbeddingCallback = callback;
+ updateCallbackIfNecessary();
+ }
+
+ @Override
+ public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {
+ TaskFragmentContainer container = getContainer(
+ taskFragmentAppearedInfo.getTaskFragmentInfo().getFragmentToken());
+ if (container == null) {
+ return;
+ }
+
+ container.setInfo(taskFragmentAppearedInfo.getTaskFragmentInfo());
+ }
+
+ @Override
+ public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
+ if (container == null) {
+ return;
+ }
+
+ container.setInfo(taskFragmentInfo);
+ // Check if there are no running activities - consider the container empty if there are no
+ // non-finishing activities left.
+ if (!taskFragmentInfo.hasRunningActivity()) {
+ mPresenter.cleanupContainer(container, true /* shouldFinishDependent */);
+ updateCallbackIfNecessary();
+ }
+ }
+
+ @Override
+ public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
+ TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
+ if (container == null) {
+ return;
+ }
+
+ mPresenter.cleanupContainer(container, true /* shouldFinishDependent */);
+ updateCallbackIfNecessary();
+ }
+
+ @Override
+ public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
+ @NonNull Configuration parentConfig) {
+ TaskFragmentContainer container = getContainer(fragmentToken);
+ if (container != null) {
+ mPresenter.updateContainer(container);
+ updateCallbackIfNecessary();
+ }
+ }
+
+ /**
+ * Checks if the activity start should be routed to a particular container. It can create a new
+ * container for the activity and a new split container if necessary.
+ */
+ // TODO(b/190433398): Break down into smaller functions.
+ void onActivityCreated(@NonNull Activity launchedActivity) {
+ final List<EmbeddingRule> splitRules = getSplitRules();
+ final TaskFragmentContainer currentContainer = getContainerWithActivity(
+ launchedActivity.getActivityToken(), launchedActivity);
+
+ // Check if the activity is configured to always be expanded.
+ if (shouldExpand(launchedActivity, splitRules)) {
+ if (shouldContainerBeExpanded(currentContainer)) {
+ // Make sure that the existing container is expanded
+ mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
+ } else {
+ // Put activity into a new expanded container
+ final TaskFragmentContainer newContainer = newContainer(launchedActivity);
+ mPresenter.expandActivity(newContainer.getTaskFragmentToken(),
+ launchedActivity);
+ }
+ return;
+ }
+
+ // Check if activity requires a placeholder
+ if (launchPlaceholderIfNecessary(launchedActivity)) {
+ return;
+ }
+
+ // TODO(b/190433398): Check if it is a placeholder and there is already another split
+ // created by the primary activity. This is necessary for the case when the primary activity
+ // launched another secondary in the split, but the placeholder was still launched by the
+ // logic above. We didn't prevent the placeholder launcher because we didn't know that
+ // another secondary activity is coming up.
+
+ // Check if the activity should form a split with the activity below in the same task
+ // fragment.
+ Activity activityBelow = null;
+ if (currentContainer != null) {
+ final List<Activity> containerActivities = currentContainer.collectActivities();
+ final int index = containerActivities.indexOf(launchedActivity);
+ if (index > 0) {
+ activityBelow = containerActivities.get(index - 1);
+ }
+ }
+ if (activityBelow == null) {
+ IBinder belowToken = ActivityClient.getInstance().getActivityTokenBelow(
+ launchedActivity.getActivityToken());
+ if (belowToken != null) {
+ activityBelow = ActivityThread.currentActivityThread().getActivity(belowToken);
+ }
+ }
+ if (activityBelow == null) {
+ return;
+ }
+
+ // Check if the split is already set.
+ final TaskFragmentContainer activityBelowContainer = getContainerWithActivity(
+ activityBelow.getActivityToken());
+ if (currentContainer != null && activityBelowContainer != null) {
+ final SplitContainer existingSplit = getActiveSplitForContainers(currentContainer,
+ activityBelowContainer);
+ if (existingSplit != null) {
+ // There is already an active split with the activity below.
+ return;
+ }
+ }
+
+ final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity,
+ splitRules);
+ if (splitPairRule == null) {
+ return;
+ }
+
+ mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
+ splitPairRule);
+
+ updateCallbackIfNecessary();
+ }
+
+ private void onActivityConfigurationChanged(@NonNull Activity activity) {
+ final TaskFragmentContainer currentContainer = getContainerWithActivity(
+ activity.getActivityToken());
+
+ if (currentContainer != null) {
+ // Changes to activities in controllers are handled in
+ // onTaskFragmentParentInfoChanged
+ return;
+ }
+
+ // Check if activity requires a placeholder
+ launchPlaceholderIfNecessary(activity);
+ }
+
+ /**
+ * Returns a container that this activity is registered with. An activity can only belong to one
+ * container, or no container at all.
+ */
+ @Nullable
+ TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
+ return getContainerWithActivity(activityToken, null /* activityToAdd */);
+ }
+
+ /**
+ * This method can only be called from {@link #onActivityCreated(Activity)}, use
+ * {@link #getContainerWithActivity(IBinder) } otherwise.
+ *
+ * Returns a container that this activity is registered with. The activity could be created
+ * before the container appeared, adding the activity to the container if so.
+ */
+ @Nullable
+ private TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken,
+ Activity activityToAdd) {
+ final IBinder taskFragmentToken = ActivityThread.currentActivityThread().getActivityClient(
+ activityToken).mInitialTaskFragmentToken;
+ for (TaskFragmentContainer container : mContainers) {
+ if (container.hasActivity(activityToken)) {
+ return container;
+ } else if (container.getTaskFragmentToken().equals(taskFragmentToken)) {
+ if (activityToAdd != null) {
+ container.addPendingAppearedActivity(activityToAdd);
+ }
+ return container;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates and registers a new organized container with an optional activity that will be
+ * re-parented to it in a WCT.
+ */
+ TaskFragmentContainer newContainer(@Nullable Activity activity) {
+ TaskFragmentContainer container = new TaskFragmentContainer(activity);
+ mContainers.add(container);
+ return container;
+ }
+
+ /**
+ * Creates and registers a new split with the provided containers and configuration. Finishes
+ * existing secondary containers if found for the given primary container.
+ */
+ void registerSplit(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
+ @NonNull TaskFragmentContainer secondaryContainer,
+ @NonNull SplitRule splitRule) {
+ if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
+ removeExistingSecondaryContainers(wct, primaryContainer);
+ }
+ SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
+ secondaryContainer, splitRule);
+ mSplitContainers.add(splitContainer);
+ }
+
+ /**
+ * Removes the container from bookkeeping records.
+ */
+ void removeContainer(@NonNull TaskFragmentContainer container) {
+ // Remove all split containers that included this one
+ mContainers.remove(container);
+ List<SplitContainer> containersToRemove = new ArrayList<>();
+ for (SplitContainer splitContainer : mSplitContainers) {
+ if (container.equals(splitContainer.getSecondaryContainer())
+ || container.equals(splitContainer.getPrimaryContainer())) {
+ containersToRemove.add(splitContainer);
+ }
+ }
+ mSplitContainers.removeAll(containersToRemove);
+ }
+
+ /**
+ * Removes a secondary container for the given primary container if an existing split is
+ * already registered.
+ */
+ void removeExistingSecondaryContainers(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer primaryContainer) {
+ // If the primary container was already in a split - remove the secondary container that
+ // is now covered by the new one that replaced it.
+ final SplitContainer existingSplitContainer = getActiveSplitForContainer(
+ primaryContainer);
+ if (existingSplitContainer == null
+ || primaryContainer == existingSplitContainer.getSecondaryContainer()) {
+ return;
+ }
+
+ existingSplitContainer.getSecondaryContainer().finish(
+ false /* shouldFinishDependent */, mPresenter, wct, this);
+ }
+
+ /**
+ * Returns the topmost not finished container.
+ */
+ @Nullable
+ TaskFragmentContainer getTopActiveContainer() {
+ for (int i = mContainers.size() - 1; i >= 0; i--) {
+ TaskFragmentContainer container = mContainers.get(i);
+ if (!container.isFinished()) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Updates the presentation of the container. If the container is part of the split or should
+ * have a placeholder, it will also update the other part of the split.
+ */
+ void updateContainer(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container) {
+ if (launchPlaceholderIfNecessary(container)) {
+ // Placeholder was launched, the positions will be updated when the activity is added
+ // to the secondary container.
+ return;
+ }
+ if (shouldContainerBeExpanded(container)) {
+ if (container.getInfo() != null) {
+ mPresenter.expandTaskFragment(wct, container.getTaskFragmentToken());
+ }
+ // If the info is not available yet the task fragment will be expanded when it's ready
+ return;
+ }
+ SplitContainer splitContainer = getActiveSplitForContainer(container);
+ if (splitContainer == null) {
+ return;
+ }
+ if (splitContainer != mSplitContainers.get(mSplitContainers.size() - 1)) {
+ // Skip position update - it isn't the topmost split.
+ return;
+ }
+ if (splitContainer.getPrimaryContainer().isEmpty()
+ || splitContainer.getSecondaryContainer().isEmpty()) {
+ // Skip position update - one or both containers are empty.
+ return;
+ }
+ if (dismissPlaceholderIfNecessary(splitContainer)) {
+ // Placeholder was finished, the positions will be updated when its container is emptied
+ return;
+ }
+ mPresenter.updateSplitContainer(splitContainer, container, wct);
+ }
+
+ /**
+ * Returns the top active split container that has the provided container, if available.
+ */
+ @Nullable
+ private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) {
+ for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
+ SplitContainer splitContainer = mSplitContainers.get(i);
+ if (container.equals(splitContainer.getSecondaryContainer())
+ || container.equals(splitContainer.getPrimaryContainer())) {
+ return splitContainer;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the active split that has the provided containers as primary and secondary or as
+ * secondary and primary, if available.
+ */
+ @Nullable
+ private SplitContainer getActiveSplitForContainers(
+ @NonNull TaskFragmentContainer firstContainer,
+ @NonNull TaskFragmentContainer secondContainer) {
+ for (int i = mSplitContainers.size() - 1; i >= 0; i--) {
+ SplitContainer splitContainer = mSplitContainers.get(i);
+ final TaskFragmentContainer primary = splitContainer.getPrimaryContainer();
+ final TaskFragmentContainer secondary = splitContainer.getSecondaryContainer();
+ if ((firstContainer == secondary && secondContainer == primary)
+ || (firstContainer == primary && secondContainer == secondary)) {
+ return splitContainer;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks if the container requires a placeholder and launches it if necessary.
+ */
+ private boolean launchPlaceholderIfNecessary(@NonNull TaskFragmentContainer container) {
+ final Activity topActivity = container.getTopNonFinishingActivity();
+ if (topActivity == null) {
+ return false;
+ }
+
+ return launchPlaceholderIfNecessary(topActivity);
+ }
+
+ boolean launchPlaceholderIfNecessary(@NonNull Activity activity) {
+ final TaskFragmentContainer container = getContainerWithActivity(
+ activity.getActivityToken());
+
+ SplitContainer splitContainer = container != null ? getActiveSplitForContainer(container)
+ : null;
+ if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) {
+ // Don't launch placeholder in primary split container
+ return false;
+ }
+
+ // Check if there is enough space for launch
+ final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity);
+ if (placeholderRule == null || !mPresenter.shouldShowSideBySide(
+ mPresenter.getParentContainerBounds(activity), placeholderRule)) {
+ return false;
+ }
+
+ // TODO(b/190433398): Handle failed request
+ startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null,
+ placeholderRule, null);
+ return true;
+ }
+
+ private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
+ if (!splitContainer.isPlaceholderContainer()) {
+ return false;
+ }
+
+ if (mPresenter.shouldShowSideBySide(splitContainer)) {
+ return false;
+ }
+
+ mPresenter.cleanupContainer(splitContainer.getSecondaryContainer(),
+ false /* shouldFinishDependent */);
+ return true;
+ }
+
+ /**
+ * Returns the rule to launch a placeholder for the activity with the provided component name
+ * if it is configured in the split config.
+ */
+ private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) {
+ for (EmbeddingRule rule : mSplitRules) {
+ if (!(rule instanceof SplitPlaceholderRule)) {
+ continue;
+ }
+ SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
+ if (placeholderRule.getActivityPredicate().test(activity)) {
+ return placeholderRule;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Notifies listeners about changes to split states if necessary.
+ */
+ private void updateCallbackIfNecessary() {
+ if (mEmbeddingCallback == null) {
+ return;
+ }
+ // TODO(b/190433398): Check if something actually changed
+ mEmbeddingCallback.accept(getActiveSplitStates());
+ }
+
+ /**
+ * Returns a list of descriptors for currently active split states.
+ */
+ private List<SplitInfo> getActiveSplitStates() {
+ List<SplitInfo> splitStates = new ArrayList<>();
+ for (SplitContainer container : mSplitContainers) {
+ TaskFragment primaryContainer =
+ new TaskFragment(
+ container.getPrimaryContainer().collectActivities());
+ TaskFragment secondaryContainer =
+ new TaskFragment(
+ container.getSecondaryContainer().collectActivities());
+ SplitInfo splitState = new SplitInfo(primaryContainer,
+ secondaryContainer, container.getSplitRule().getSplitRatio());
+ splitStates.add(splitState);
+ }
+ return splitStates;
+ }
+
+ /**
+ * Returns {@code true} if the container is expanded to occupy full task size.
+ * Returns {@code false} if the container is included in an active split.
+ */
+ boolean shouldContainerBeExpanded(@Nullable TaskFragmentContainer container) {
+ if (container == null) {
+ return false;
+ }
+ for (SplitContainer splitContainer : mSplitContainers) {
+ if (container.equals(splitContainer.getPrimaryContainer())
+ || container.equals(splitContainer.getSecondaryContainer())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a split rule for the provided pair of primary activity and secondary activity intent
+ * if available.
+ */
+ @Nullable
+ private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
+ @NonNull Intent secondaryActivityIntent, @NonNull List<EmbeddingRule> splitRules) {
+ for (EmbeddingRule rule : splitRules) {
+ if (!(rule instanceof SplitPairRule)) {
+ continue;
+ }
+ SplitPairRule pairRule = (SplitPairRule) rule;
+ if (pairRule.getActivityIntentPredicate().test(
+ new Pair(primaryActivity, secondaryActivityIntent))) {
+ return pairRule;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a split rule for the provided pair of primary and secondary activities if available.
+ */
+ @Nullable
+ private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
+ @NonNull Activity secondaryActivity, @NonNull List<EmbeddingRule> splitRules) {
+ for (EmbeddingRule rule : splitRules) {
+ if (!(rule instanceof SplitPairRule)) {
+ continue;
+ }
+ SplitPairRule pairRule = (SplitPairRule) rule;
+ final Intent intent = secondaryActivity.getIntent();
+ if (pairRule.getActivityPairPredicate().test(
+ new Pair(primaryActivity, secondaryActivity))
+ && (intent == null || pairRule.getActivityIntentPredicate().test(
+ new Pair(primaryActivity, intent)))) {
+ return pairRule;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
+ for (TaskFragmentContainer container : mContainers) {
+ if (container.getTaskFragmentToken().equals(fragmentToken)) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if an Activity with the provided component name should always be
+ * expanded to occupy full task bounds. Such activity must not be put in a split.
+ */
+ private static boolean shouldExpand(@NonNull Activity activity,
+ List<EmbeddingRule> splitRules) {
+ if (splitRules == null) {
+ return false;
+ }
+ for (EmbeddingRule rule : splitRules) {
+ if (!(rule instanceof ActivityRule)) {
+ continue;
+ }
+ ActivityRule activityRule = (ActivityRule) rule;
+ if (!activityRule.shouldAlwaysExpand()) {
+ continue;
+ }
+ if (activityRule.getActivityPredicate().test(activity)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) {
+ // Calling after Activity#onCreate is complete to allow the app launch something
+ // first. In case of a configured placeholder activity we want to make sure
+ // that we don't launch it if an activity itself already requested something to be
+ // launched to side.
+ SplitController.this.onActivityCreated(activity);
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ }
+
+ @Override
+ public void onActivityConfigurationChanged(Activity activity) {
+ SplitController.this.onActivityConfigurationChanged(activity);
+ }
+ }
+
+ /** Executor that posts on the main application thread. */
+ private static class MainThreadExecutor implements Executor {
+ private final Handler handler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void execute(Runnable r) {
+ handler.post(r);
+ }
+ }
+
+ /**
+ * A monitor that intercepts all activity start requests originating in the client process and
+ * can amend them to target a specific task fragment to form a split.
+ */
+ private class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
+
+ @Override
+ public Instrumentation.ActivityResult onStartActivity(@NonNull Context who,
+ @NonNull Intent intent, @NonNull Bundle options) {
+ // TODO(b/190433398): Check if the activity is configured to always be expanded.
+
+ // Check if activity should be put in a split with the activity that launched it.
+ if (!(who instanceof Activity)) {
+ return super.onStartActivity(who, intent, options);
+ }
+ final Activity launchingActivity = (Activity) who;
+
+ if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
+ setLaunchingInSameContainer(launchingActivity, intent, options);
+ }
+
+ return super.onStartActivity(who, intent, options);
+ }
+
+ /**
+ * Returns {@code true} if the activity that is going to be started via the
+ * {@code intent} should be paired with the {@code launchingActivity} and is set to be
+ * launched in an empty side container.
+ */
+ private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent,
+ Bundle options) {
+ final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent,
+ getSplitRules());
+ if (splitPairRule == null) {
+ return false;
+ }
+
+ // Create a new split with an empty side container
+ final TaskFragmentContainer secondaryContainer = mPresenter
+ .createNewSplitWithEmptySideContainer(launchingActivity, splitPairRule);
+
+ // Amend the request to let the WM know that the activity should be placed in the
+ // dedicated container.
+ options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ secondaryContainer.getTaskFragmentToken());
+ return true;
+ }
+
+ /**
+ * Checks if the activity that is going to be started via the {@code intent} should be
+ * paired with the existing top activity which is currently paired with the
+ * {@code launchingActivity}. If so, set the activity to be launched in the same
+ * container of the {@code launchingActivity}.
+ */
+ private void setLaunchingInSameContainer(Activity launchingActivity, Intent intent,
+ Bundle options) {
+ final TaskFragmentContainer launchingContainer = getContainerWithActivity(
+ launchingActivity.getActivityToken());
+ if (launchingContainer == null) {
+ return;
+ }
+
+ final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer);
+ if (splitContainer == null) {
+ return;
+ }
+
+ if (splitContainer.getSecondaryContainer() != launchingContainer) {
+ return;
+ }
+
+ // The launching activity is on the secondary container. Retrieve the primary
+ // activity from the other container.
+ Activity primaryActivity =
+ splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+ if (primaryActivity == null) {
+ return;
+ }
+
+ final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent,
+ getSplitRules());
+ if (splitPairRule == null) {
+ return;
+ }
+
+ // Amend the request to let the WM know that the activity should be placed in the
+ // dedicated container. This is necessary for the case that the activity is started
+ // into a new Task, or new Task will be escaped from the current host Task and be
+ // displayed in fullscreen.
+ options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+ launchingContainer.getTaskFragmentToken());
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
new file mode 100644
index 000000000000..a7bce20cae8b
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.WindowInsets;
+import android.view.WindowMetrics;
+import android.window.TaskFragmentCreationParams;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitRule;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Controls the visual presentation of the splits according to the containers formed by
+ * {@link SplitController}.
+ */
+class SplitPresenter extends JetpackTaskFragmentOrganizer {
+ private static final int POSITION_LEFT = 0;
+ private static final int POSITION_RIGHT = 1;
+ private static final int POSITION_FILL = 2;
+
+ @IntDef(value = {
+ POSITION_LEFT,
+ POSITION_RIGHT,
+ POSITION_FILL,
+ })
+ private @interface Position {}
+
+ private final SplitController mController;
+
+ SplitPresenter(@NonNull Executor executor, SplitController controller) {
+ super(executor, controller);
+ mController = controller;
+ registerOrganizer();
+ }
+
+ /**
+ * Updates the presentation of the provided container.
+ */
+ void updateContainer(TaskFragmentContainer container) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mController.updateContainer(wct, container);
+ applyTransaction(wct);
+ }
+
+ /**
+ * Deletes the specified container and all other associated and dependent containers in the same
+ * transaction.
+ */
+ void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ container.finish(shouldFinishDependent, this, wct, mController);
+
+ final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer();
+ if (newTopContainer != null) {
+ mController.updateContainer(wct, newTopContainer);
+ }
+
+ applyTransaction(wct);
+ }
+
+ /**
+ * Creates a new split with the primary activity and an empty secondary container.
+ * @return The newly created secondary container.
+ */
+ TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity,
+ @NonNull SplitPairRule rule) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ final Rect parentBounds = getParentContainerBounds(primaryActivity);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
+ primaryActivity, primaryRectBounds, null);
+
+ // Create new empty task fragment
+ TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
+ primaryActivity.getActivityToken(), secondaryRectBounds,
+ WINDOWING_MODE_MULTI_WINDOW);
+ secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
+
+ // Set adjacent to each other so that the containers below will be invisible.
+ wct.setAdjacentTaskFragments(
+ primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken());
+
+ mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+
+ applyTransaction(wct);
+
+ return secondaryContainer;
+ }
+
+ /**
+ * Creates a new split container with the two provided activities.
+ * @param primaryActivity An activity that should be in the primary container. If it is not
+ * currently in an existing container, a new one will be created and the
+ * activity will be re-parented to it.
+ * @param secondaryActivity An activity that should be in the secondary container. If it is not
+ * currently in an existing container, or if it is currently in the
+ * same container as the primary activity, a new container will be
+ * created and the activity will be re-parented to it.
+ * @param rule The split rule to be applied to the container.
+ */
+ void createNewSplitContainer(@NonNull Activity primaryActivity,
+ @NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+ final Rect parentBounds = getParentContainerBounds(primaryActivity);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
+ primaryActivity, primaryRectBounds, null);
+
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+ final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
+ secondaryActivity, secondaryRectBounds, primaryContainer);
+
+ // Set adjacent to each other so that the containers below will be invisible.
+ wct.setAdjacentTaskFragments(
+ primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken());
+
+ mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+
+ applyTransaction(wct);
+ }
+
+ /**
+ * Creates a new container or resizes an existing container for activity to the provided bounds.
+ * @param activity The activity to be re-parented to the container if necessary.
+ * @param containerToAvoid Re-parent from this container if an activity is already in it.
+ */
+ private TaskFragmentContainer prepareContainerForActivity(
+ @NonNull WindowContainerTransaction wct, @NonNull Activity activity,
+ @NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) {
+ TaskFragmentContainer container = mController.getContainerWithActivity(
+ activity.getActivityToken());
+ if (container == null || container == containerToAvoid) {
+ container = mController.newContainer(activity);
+
+ final TaskFragmentCreationParams fragmentOptions =
+ createFragmentOptions(
+ container.getTaskFragmentToken(),
+ activity.getActivityToken(),
+ bounds,
+ WINDOWING_MODE_MULTI_WINDOW);
+ wct.createTaskFragment(fragmentOptions);
+
+ wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
+ activity.getActivityToken());
+
+ container.setLastRequestedBounds(bounds);
+ } else {
+ resizeTaskFragmentIfRegistered(wct, container, bounds);
+ }
+
+ return container;
+ }
+
+ /**
+ * Starts a new activity to the side, creating a new split container. A new container will be
+ * created for the activity that will be started.
+ * @param launchingActivity An activity that should be in the primary container. If it is not
+ * currently in an existing container, a new one will be created and
+ * the activity will be re-parented to it.
+ * @param activityIntent The intent to start the new activity.
+ * @param activityOptions The options to apply to new activity start.
+ * @param rule The split rule to be applied to the container.
+ */
+ void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
+ @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
+ final Rect parentBounds = getParentContainerBounds(launchingActivity);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+
+ TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
+ launchingActivity.getActivityToken());
+ if (primaryContainer == null) {
+ primaryContainer = mController.newContainer(launchingActivity);
+ }
+
+ TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
+ rule);
+ startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
+ launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
+ activityIntent, activityOptions);
+ applyTransaction(wct);
+
+ primaryContainer.setLastRequestedBounds(primaryRectBounds);
+ secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
+ }
+
+ /**
+ * Updates the positions of containers in an existing split.
+ * @param splitContainer The split container to be updated.
+ * @param updatedContainer The task fragment that was updated and caused this split update.
+ * @param wct WindowContainerTransaction that this update should be performed with.
+ */
+ void updateSplitContainer(@NonNull SplitContainer splitContainer,
+ @NonNull TaskFragmentContainer updatedContainer,
+ @NonNull WindowContainerTransaction wct) {
+ // Getting the parent bounds using the updated container - it will have the recent value.
+ final Rect parentBounds = getParentContainerBounds(updatedContainer);
+ final SplitRule rule = splitContainer.getSplitRule();
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+
+ // If the task fragments are not registered yet, the positions will be updated after they
+ // are created again.
+ resizeTaskFragmentIfRegistered(wct, splitContainer.getPrimaryContainer(),
+ primaryRectBounds);
+ resizeTaskFragmentIfRegistered(wct, splitContainer.getSecondaryContainer(),
+ secondaryRectBounds);
+ }
+
+ /**
+ * Resizes the task fragment if it was already registered. Skips the operation if the container
+ * creation has not been reported from the server yet.
+ */
+ // TODO(b/190433398): Handle resize if the fragment hasn't appeared yet.
+ void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container,
+ @Nullable Rect bounds) {
+ if (container.getInfo() == null) {
+ return;
+ }
+ resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+ }
+
+ @Override
+ void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
+ @Nullable Rect bounds) {
+ TaskFragmentContainer container = mController.getContainer(fragmentToken);
+ if (container == null) {
+ throw new IllegalStateException(
+ "Resizing a task fragment that is not registered with controller.");
+ }
+
+ if (container.areLastRequestedBoundsEqual(bounds)) {
+ // Return early if the provided bounds were already requested
+ return;
+ }
+
+ container.setLastRequestedBounds(bounds);
+ super.resizeTaskFragment(wct, fragmentToken, bounds);
+ }
+
+ boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
+ final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
+ return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule());
+ }
+
+ boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) {
+ // TODO(b/190433398): Supply correct insets.
+ final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
+ new WindowInsets(new Rect()));
+ return rule.getParentWindowMetricsPredicate().test(parentMetrics);
+ }
+
+ @NonNull
+ private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
+ @NonNull SplitRule rule) {
+ if (!shouldShowSideBySide(parentBounds, rule)) {
+ return new Rect();
+ }
+
+ float splitRatio = rule.getSplitRatio();
+ switch (position) {
+ case POSITION_LEFT:
+ return new Rect(
+ parentBounds.left,
+ parentBounds.top,
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.bottom);
+ case POSITION_RIGHT:
+ return new Rect(
+ (int) (parentBounds.left + parentBounds.width() * splitRatio),
+ parentBounds.top,
+ parentBounds.right,
+ parentBounds.bottom);
+ case POSITION_FILL:
+ return parentBounds;
+ }
+ return parentBounds;
+ }
+
+ @NonNull
+ Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
+ final Configuration parentConfig = mFragmentParentConfigs.get(
+ container.getTaskFragmentToken());
+ if (parentConfig != null) {
+ return parentConfig.windowConfiguration.getBounds();
+ }
+
+ // If there is no parent yet - then assuming that activities are running in full task bounds
+ final Activity topActivity = container.getTopNonFinishingActivity();
+ final Rect bounds = topActivity != null ? getParentContainerBounds(topActivity) : null;
+
+ if (bounds == null) {
+ throw new IllegalStateException("Unknown parent bounds");
+ }
+ return bounds;
+ }
+
+ @NonNull
+ Rect getParentContainerBounds(@NonNull Activity activity) {
+ final TaskFragmentContainer container = mController.getContainerWithActivity(
+ activity.getActivityToken());
+ if (container != null) {
+ final Configuration parentConfig = mFragmentParentConfigs.get(
+ container.getTaskFragmentToken());
+ if (parentConfig != null) {
+ return parentConfig.windowConfiguration.getBounds();
+ }
+ }
+
+ // TODO(b/190433398): Check if the client-side available info about parent bounds is enough.
+ if (!activity.isInMultiWindowMode()) {
+ // In fullscreen mode the max bounds should correspond to the task bounds.
+ return activity.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+ }
+ return activity.getResources().getConfiguration().windowConfiguration.getBounds();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
new file mode 100644
index 000000000000..a4f5c75276f5
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.organizer;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.window.TaskFragmentInfo;
+import android.window.WindowContainerTransaction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Client-side container for a stack of activities. Corresponds to an instance of TaskFragment
+ * on the server side.
+ */
+class TaskFragmentContainer {
+ /**
+ * Client-created token that uniquely identifies the task fragment container instance.
+ */
+ @NonNull
+ private final IBinder mToken;
+
+ /**
+ * Server-provided task fragment information.
+ */
+ private TaskFragmentInfo mInfo;
+
+ /**
+ * Activities that are being reparented or being started to this container, but haven't been
+ * added to {@link #mInfo} yet.
+ */
+ private final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
+
+ /** Containers that are dependent on this one and should be completely destroyed on exit. */
+ private final List<TaskFragmentContainer> mContainersToFinishOnExit =
+ new ArrayList<>();
+
+ /** Individual associated activities in different containers that should be finished on exit. */
+ private final List<Activity> mActivitiesToFinishOnExit = new ArrayList<>();
+
+ /** Indicates whether the container was cleaned up after the last activity was removed. */
+ private boolean mIsFinished;
+
+ /**
+ * Bounds that were requested last via {@link android.window.WindowContainerTransaction}.
+ */
+ private final Rect mLastRequestedBounds = new Rect();
+
+ /**
+ * Creates a container with an existing activity that will be re-parented to it in a window
+ * container transaction.
+ */
+ TaskFragmentContainer(@Nullable Activity activity) {
+ mToken = new Binder("TaskFragmentContainer");
+ if (activity != null) {
+ addPendingAppearedActivity(activity);
+ }
+ }
+
+ /**
+ * Returns the client-created token that uniquely identifies this container.
+ */
+ @NonNull
+ IBinder getTaskFragmentToken() {
+ return mToken;
+ }
+
+ /** List of activities that belong to this container and live in this process. */
+ @NonNull
+ List<Activity> collectActivities() {
+ // Add the re-parenting activity, in case the server has not yet reported the task
+ // fragment info update with it placed in this container. We still want to apply rules
+ // in this intermediate state.
+ List<Activity> allActivities = new ArrayList<>();
+ if (!mPendingAppearedActivities.isEmpty()) {
+ allActivities.addAll(mPendingAppearedActivities);
+ }
+ // Add activities reported from the server.
+ if (mInfo == null) {
+ return allActivities;
+ }
+ ActivityThread activityThread = ActivityThread.currentActivityThread();
+ for (IBinder token : mInfo.getActivities()) {
+ Activity activity = activityThread.getActivity(token);
+ if (activity != null && !allActivities.contains(activity)) {
+ allActivities.add(activity);
+ }
+ }
+ return allActivities;
+ }
+
+ void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
+ mPendingAppearedActivities.add(pendingAppearedActivity);
+ }
+
+ boolean hasActivity(@NonNull IBinder token) {
+ if (mInfo != null && mInfo.getActivities().contains(token)) {
+ return true;
+ }
+ for (Activity activity : mPendingAppearedActivities) {
+ if (activity.getActivityToken().equals(token)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ TaskFragmentInfo getInfo() {
+ return mInfo;
+ }
+
+ void setInfo(@Nullable TaskFragmentInfo info) {
+ mInfo = info;
+ if (mInfo == null || mPendingAppearedActivities.isEmpty()) {
+ return;
+ }
+ // Cleanup activities that were being re-parented
+ List<IBinder> infoActivities = mInfo.getActivities();
+ for (int i = mPendingAppearedActivities.size() - 1; i >= 0; --i) {
+ final Activity activity = mPendingAppearedActivities.get(i);
+ if (infoActivities.contains(activity.getActivityToken())) {
+ mPendingAppearedActivities.remove(i);
+ }
+ }
+ }
+
+ @Nullable
+ Activity getTopNonFinishingActivity() {
+ List<Activity> activities = collectActivities();
+ if (activities.isEmpty()) {
+ return null;
+ }
+ int i = activities.size() - 1;
+ while (i >= 0 && activities.get(i).isFinishing()) {
+ i--;
+ }
+ return i >= 0 ? activities.get(i) : null;
+ }
+
+ boolean isEmpty() {
+ return mPendingAppearedActivities.isEmpty() && (mInfo == null || mInfo.isEmpty());
+ }
+
+ /**
+ * Adds a container that should be finished when this container is finished.
+ */
+ void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
+ mContainersToFinishOnExit.add(containerToFinish);
+ }
+
+ /**
+ * Adds an activity that should be finished when this container is finished.
+ */
+ void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
+ mActivitiesToFinishOnExit.add(activityToFinish);
+ }
+
+ /**
+ * Removes all activities that belong to this process and finishes other containers/activities
+ * configured to finish together.
+ */
+ void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
+ @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
+ if (mIsFinished) {
+ return;
+ }
+ mIsFinished = true;
+
+ // Finish own activities
+ for (Activity activity : collectActivities()) {
+ activity.finish();
+ }
+
+ // Cleanup the visuals
+ presenter.deleteTaskFragment(wct, getTaskFragmentToken());
+ // Cleanup the records
+ controller.removeContainer(this);
+
+ if (!shouldFinishDependent) {
+ return;
+ }
+
+ // Finish dependent containers
+ for (TaskFragmentContainer container : mContainersToFinishOnExit) {
+ container.finish(true /* shouldFinishDependent */, presenter,
+ wct, controller);
+ }
+ mContainersToFinishOnExit.clear();
+
+ // Finish associated activities
+ for (Activity activity : mActivitiesToFinishOnExit) {
+ activity.finish();
+ }
+ mActivitiesToFinishOnExit.clear();
+
+ // Finish activities that were being re-parented to this container.
+ for (Activity activity : mPendingAppearedActivities) {
+ activity.finish();
+ }
+ mPendingAppearedActivities.clear();
+ }
+
+ boolean isFinished() {
+ return mIsFinished;
+ }
+
+ /**
+ * Checks if last requested bounds are equal to the provided value.
+ */
+ boolean areLastRequestedBoundsEqual(@Nullable Rect bounds) {
+ return (bounds == null && mLastRequestedBounds.isEmpty())
+ || mLastRequestedBounds.equals(bounds);
+ }
+
+ /**
+ * Updates the last requested bounds.
+ */
+ void setLastRequestedBounds(@Nullable Rect bounds) {
+ if (bounds == null) {
+ mLastRequestedBounds.setEmpty();
+ } else {
+ mLastRequestedBounds.set(bounds);
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index be6652d43fb2..097febf9770a 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
index 8710fb8ac69b..96d2d7c954d8 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
@@ -18,7 +18,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
- android:color="@android:color/system_neutral1_900"
+ android:color="@android:color/system_neutral1_800"
/>
<corners android:radius="20dp" />
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
index c09ae53746da..0cf6d73162d2 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
@@ -17,13 +17,13 @@
<com.android.wm.shell.common.AlphaOptimizedButton
xmlns:android="http://schemas.android.com/apk/res/android"
style="@android:style/Widget.DeviceDefault.Button.Borderless"
- android:id="@+id/settings_button"
+ android:id="@+id/manage_button"
android:layout_gravity="start"
android:layout_width="wrap_content"
- android:layout_height="40dp"
- android:layout_marginTop="8dp"
- android:layout_marginLeft="16dp"
- android:layout_marginBottom="8dp"
+ android:layout_height="@dimen/bubble_manage_button_height"
+ android:layout_marginStart="@dimen/bubble_manage_button_margin"
+ android:layout_marginTop="@dimen/bubble_manage_button_margin"
+ android:layout_marginBottom="@dimen/bubble_manage_button_margin"
android:focusable="true"
android:text="@string/manage_bubbles_text"
android:textSize="@*android:dimen/text_size_body_2_material"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index f4b3aca33dd7..298ad3025b00 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -25,15 +25,15 @@
android:id="@+id/bubble_manage_menu_dismiss_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
android:orientation="horizontal">
<ImageView
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
android:src="@drawable/ic_remove_no_shadow"
android:tint="@color/bubbles_icon_tint"/>
@@ -50,15 +50,15 @@
android:id="@+id/bubble_manage_menu_dont_bubble_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
android:orientation="horizontal">
<ImageView
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
android:src="@drawable/bubble_ic_stop_bubble"
android:tint="@color/bubbles_icon_tint"/>
@@ -75,16 +75,16 @@
android:id="@+id/bubble_manage_menu_settings_container"
android:background="@drawable/bubble_manage_menu_row"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/bubble_menu_item_height"
android:gravity="center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
+ android:paddingStart="@dimen/bubble_menu_padding"
+ android:paddingEnd="@dimen/bubble_menu_padding"
android:orientation="horizontal">
<ImageView
android:id="@+id/bubble_manage_menu_settings_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/bubble_menu_icon_size"
+ android:layout_height="@dimen/bubble_menu_icon_size"
android:src="@drawable/ic_remove_no_shadow"/>
<TextView
diff --git a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
index fd4c3ba87026..87deb8b5a1fd 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
@@ -21,7 +21,6 @@
android:layout_width="wrap_content"
android:paddingTop="48dp"
android:paddingBottom="48dp"
- android:paddingStart="@dimen/bubble_stack_user_education_side_inset"
android:paddingEnd="16dp"
android:layout_marginEnd="24dp"
android:orientation="vertical"
diff --git a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
index c5c42fca323d..fafe40e924f5 100644
--- a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
@@ -23,7 +23,6 @@
android:clickable="true"
android:paddingTop="28dp"
android:paddingBottom="16dp"
- android:paddingStart="@dimen/bubble_expanded_view_padding"
android:paddingEnd="48dp"
android:layout_marginEnd="24dp"
android:orientation="vertical"
@@ -66,27 +65,21 @@
android:id="@+id/button_layout"
android:orientation="horizontal" >
- <com.android.wm.shell.common.AlphaOptimizedButton
- style="@android:style/Widget.Material.Button.Borderless"
- android:id="@+id/manage"
- android:layout_gravity="start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="true"
- android:clickable="false"
- android:text="@string/manage_bubbles_text"
- android:textColor="@android:color/system_neutral1_900"
+ <include
+ layout="@layout/bubble_manage_button"
/>
<com.android.wm.shell.common.AlphaOptimizedButton
- style="@android:style/Widget.Material.Button.Borderless"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
android:id="@+id/got_it"
android:layout_gravity="start"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/bubble_manage_button_height"
android:focusable="true"
android:text="@string/bubbles_user_education_got_it"
+ android:textSize="@*android:dimen/text_size_body_2_material"
android:textColor="@android:color/system_neutral1_900"
+ android:background="@drawable/bubble_manage_btn_bg"
/>
</LinearLayout>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
new file mode 100644
index 000000000000..4e2a77f213a0
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -0,0 +1,26 @@
+<?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.splitscreen.OutlineRoot
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.wm.shell.splitscreen.OutlineView
+ android:id="@+id/split_outline"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+
+</com.android.wm.shell.splitscreen.OutlineRoot>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index ea634cfa907c..69aa31ee861a 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Slaan oor na volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Slaan oor na vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Verander grootte"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Hou vas"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Laat los"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Program sal dalk nie met verdeelde skerm werk nie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Program steun nie verdeelde skerm nie."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Program sal dalk nie op \'n sekondêre skerm werk nie."</string>
@@ -69,4 +71,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..c754e3ca4dd2 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ወደ ቀጣይ ዝለል"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ወደ ቀዳሚ ዝለል"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"መጠን ይቀይሩ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"መተግበሪያ ከተከፈለ ማያ ገጽ ጋር ላይሠራ ይችላል"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"መተግበሪያው የተከፈለ ማያ ገጽን አይደግፍም።"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"መተግበሪያ በሁለተኛ ማሳያ ላይ ላይሠራ ይችላል።"</string>
@@ -69,4 +71,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..ac72a3d7a367 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"التخطي إلى التالي"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"التخطي إلى السابق"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغيير الحجم"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"إخفاء"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"إظهار"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"قد لا يعمل التطبيق بشكل سليم في وضع \"تقسيم الشاشة\"."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"التطبيق لا يتيح تقسيم الشاشة."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"قد لا يعمل التطبيق على شاشة عرض ثانوية."</string>
@@ -69,4 +71,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..1ace3cdacb46 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
@@ -69,4 +71,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..6d3e0a92d2ce 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Növbətiyə keçin"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Əvvəlkinə keçin"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ölçüsünü dəyişin"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Güvənli məkanda saxlayın"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Güvənli məkandan çıxarın"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Tətbiq bölünmüş ekran ilə işləməyə bilər."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Tətbiq ekran bölünməsini dəstəkləmir."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Tətbiq ikinci ekranda işləməyə bilər."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Yuxarı 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Yuxarı 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Aşağı tam ekran"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bir əlli rejimdən istifadə edilir"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Birəlli rejim istifadəsi"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bir əlli rejimi başladın"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bir əlli rejimdən çıxın"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Birəlli rejim başlasın"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Birəlli rejimdən çıxın"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> yumrucuqları üçün ayarlar"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Kənara çıxma"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Yenidən dəstəyə əlavə edin"</string>
@@ -69,4 +71,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..358da25d92c0 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pređi na sledeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pređi na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promenite veličinu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavite u tajnu memoriju"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Uklonite iz tajne memorije"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi sa podeljenim ekranom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podeljeni ekran."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionisati na sekundarnom ekranu."</string>
@@ -69,4 +71,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..7a934ccb1e9a 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перайсці да наступнага"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перайсці да папярэдняга"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змяніць памер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Схаваць"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Паказаць"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Праграма можа не працаваць у рэжыме падзеленага экрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Праграма не падтрымлівае функцыю дзялення экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Праграма можа не працаваць на дадатковых экранах."</string>
@@ -69,4 +71,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..02930b1db277 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Към следващия елемент"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Към предишния елемент"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Преоразмеряване"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Съхраняване"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Отмяна на съхраняването"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Приложението може да не работи в режим на разделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложението не поддържа разделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Възможно е приложението да не работи на алтернативни дисплеи."</string>
@@ -69,4 +71,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..b35e17919cc2 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"স্ট্যাস করুন"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
@@ -69,4 +71,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..14d90a488352 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeći"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodni"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stavljanje u stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vađenje iz stasha"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće raditi na podijeljenom ekranu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava dijeljenje ekrana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće raditi na sekundarnom ekranu."</string>
@@ -69,4 +71,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..9cffbdd7159e 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ves al següent"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Torna a l\'anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Canvia la mida"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Amaga"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Deixa d\'amagar"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'aplicació no admet la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"És possible que l\'aplicació no funcioni en una pantalla secundària."</string>
@@ -69,4 +71,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..9b5206a1da5c 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Přeskočit na další"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Přeskočit na předchozí"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Změnit velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Uložit"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušit uložení"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikace v režimu rozdělené obrazovky nemusí fungovat."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikace nepodporuje režim rozdělené obrazovky."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikace na sekundárním displeji nemusí fungovat."</string>
@@ -69,4 +71,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..a06abf10cb23 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Gå videre til næste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Gå til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Rediger størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skjul"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Vis"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen fungerer muligvis ikke i opdelt skærm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen understøtter ikke opdelt skærm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer muligvis ikke på sekundære skærme."</string>
@@ -69,4 +71,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..c5e79f87ca50 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Vorwärts springen"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Rückwärts springen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Größe anpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"In Stash legen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Aus Stash entfernen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Die App funktioniert unter Umständen bei geteiltem Bildschirmmodus nicht."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Das Teilen des Bildschirms wird in dieser App nicht unterstützt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Die App funktioniert auf einem sekundären Display möglicherweise nicht."</string>
@@ -60,13 +62,15 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, \"Bubbles\" genannt. Wenn du die Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Bubble-Einstellungen festlegen"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf \"Verwalten\", um Bubbles für diese App zu deaktivieren"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tippe auf „Verwalten“, um Bubbles für diese App zu deaktivieren"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
<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..fc397c5569a8 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Μετάβαση στο επόμενο"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Μετάβαση στο προηγούμενο"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Αλλαγή μεγέθους"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Απόκρυψη"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Κατάργηση απόκρυψης"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Η εφαρμογή ενδέχεται να μην λειτουργεί με διαχωρισμό οθόνης."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Η εφαρμογή δεν υποστηρίζει διαχωρισμό οθόνης."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Η εφαρμογή ίσως να μην λειτουργήσει σε δευτερεύουσα οθόνη."</string>
@@ -69,4 +71,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..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,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..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,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..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,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..a4f287f1bdb4 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Skip to next"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Skip to previous"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"App may not work with split-screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App does not support split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App may not work on a secondary display."</string>
@@ -69,4 +71,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..87210d5ecbec 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎Skip to next‎‏‎‎‏‎"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎Skip to previous‎‏‎‎‏‎"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎Resize‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Stash‎‏‎‎‏‎"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‎‎Unstash‎‏‎‎‏‎"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎App may not work with split-screen.‎‏‎‎‏‎"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎App does not support split-screen.‎‏‎‎‏‎"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎App may not work on a secondary display.‎‏‎‎‏‎"</string>
@@ -69,4 +71,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..ebe41e88f08c 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar el tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Almacenar de manera segura"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Dejar de almacenar de manera segura"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la app no funcione en el modo de pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La app no es compatible con la función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la app no funcione en una pantalla secundaria."</string>
@@ -58,7 +60,7 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbujas"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
@@ -69,4 +71,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..5949099bd6a3 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Saltar al siguiente"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Volver al anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"No esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Es posible que la aplicación no funcione con la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"La aplicación no admite la pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Es posible que la aplicación no funcione en una pantalla secundaria."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Superior 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Superior 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Pantalla inferior completa"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Utilizar el modo una mano"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Usar Modo una mano"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar modo una mano"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del modo una mano"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar Modo una mano"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Salir del Modo una mano"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Ajustes de las burbujas de <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menú adicional"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Volver a añadir a la pila"</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca para abrir la burbuja. Arrastra para moverla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamadas \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controla las burbujas"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Toca Gestionar para desactivar las burbujas de esta aplicación"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
@@ -69,4 +71,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">"Entendido"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 0ccfcfee85d6..83309810311c 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Järgmise juurde"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Eelmise juurde"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Suuruse muutmine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Pane hoidlasse"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Eemalda hoidlast"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Rakendus ei pruugi poolitatud ekraaniga töötada."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Rakendus ei toeta jagatud ekraani."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Rakendus ei pruugi teisesel ekraanil töötada."</string>
@@ -69,4 +71,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..664976983fdc 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -25,9 +25,11 @@
<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="accessibility_action_pip_stash" msgid="4060775037619702641">"Gorde"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ez gorde"</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>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Baliteke aplikazioak ez funtzionatzea bigarren mailako pantailetan."</string>
@@ -69,4 +71,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..f646039df1f8 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"رد شدن به بعدی"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"رد شدن به قبلی"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"تغییر اندازه"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"مخفی‌سازی"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"لغو مخفی‌سازی"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن است برنامه با «صفحهٔ دونیمه» کار نکند."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"برنامه از تقسیم صفحه پشتیبانی نمی‌کند."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن است برنامه در نمایشگر ثانویه کار نکند."</string>
@@ -43,7 +45,7 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"تمام‌صفحه پایین"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از «حالت تک حرکت»"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از حالت یک‌دستی"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحه‌نمایش تند به‌طرف بالا بکشید یا در هر جایی از بالای برنامه که می‌خواهید ضربه بزنید"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت تک حرکت»"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت تک حرکت»"</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها ضربه بزنید. برای جابه‌جایی، آن را بکشید."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"کنترل حبابک‌ها در هرزمانی"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن «حبابک‌ها» از این برنامه، روی «مدیریت» ضربه بزنید"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"برای خاموش کردن حبابک‌ها از این برنامه، روی «مدیریت» ضربه بزنید"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجه‌ام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌ها اخیر و حبابک‌ها ردشده اینجا ظاهر خواهند شد"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابک‌های اخیر و حبابک‌های ردشده اینجا ظاهر خواهند شد"</string>
<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..5f871639a202 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Siirry seuraavaan"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Siirry edelliseen"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Muuta kokoa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Lisää turvasäilytykseen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poista turvasäilytyksestä"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Sovellus ei ehkä toimi jaetulla näytöllä."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Sovellus ei tue jaetun näytön tilaa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Sovellus ei ehkä toimi toissijaisella näytöllä."</string>
@@ -69,4 +71,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..68df5917eab4 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Revenir au précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ajouter à la réserve"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Retirer de la réserve"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'application n\'est pas compatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -69,4 +71,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..eecc9cbbba43 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passer au contenu suivant"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passer au contenu précédent"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionner"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Il est possible que l\'application ne fonctionne pas en mode Écran partagé."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Application incompatible avec l\'écran partagé."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Il est possible que l\'application ne fonctionne pas sur un écran secondaire."</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôler les paramètres des bulles"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Contrôlez les bulles à tout moment"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Appuyez sur \"Gérer\" pour désactiver les bulles de cette application"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
@@ -69,4 +71,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..3583cafdfb9f 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ir ao seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ir ao anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Cambiar tamaño"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Esconder"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Non esconder"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Pode que a aplicación non funcione coa pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A aplicación non é compatible coa función de pantalla dividida."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É posible que a aplicación non funcione nunha pantalla secundaria."</string>
@@ -69,4 +71,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..e0654bd6bfef 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"છુપાવો"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
@@ -69,4 +71,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..55a30f2358fb 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अगले पर जाएं"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"पिछले पर जाएं"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदलें"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"छिपाएं"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"दिखाएं"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ऐप्लिकेशन शायद स्प्लिट स्क्रीन मोड में काम न करे."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ऐप विभाजित स्‍क्रीन का समर्थन नहीं करता है."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"हो सकता है कि ऐप प्राइमरी (मुख्य) डिस्प्ले के अलावा बाकी दूसरे डिस्प्ले पर काम न करे."</string>
@@ -67,6 +69,8 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के बबल्स मौजूद नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"प्रबंधित करें"</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..f6acb5cb2d33 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na sljedeće"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prethodno"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Promjena veličine"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sakrijte"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Poništite sakrivanje"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija možda neće funkcionirati s podijeljenim zaslonom."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podržava podijeljeni zaslon."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija možda neće funkcionirati na sekundarnom zaslonu."</string>
@@ -69,4 +71,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..0c1c8a40c8bf 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ugrás a következőre"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ugrás az előzőre"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Átméretezés"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Félretevés"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Félretevés megszüntetése"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Lehet, hogy az alkalmazás nem működik osztott képernyős nézetben."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Az alkalmazás nem támogatja az osztott képernyős nézetet."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Előfordulhat, hogy az alkalmazás nem működik másodlagos kijelzőn."</string>
@@ -69,4 +71,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..36204c1a6599 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Անցնել հաջորդին"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Վերադառնալ նախորդին"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Փոխել չափը"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Թաքցնել"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ցուցադրել"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Հավելվածը չի կարող աշխատել տրոհված էկրանի ռեժիմում։"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Հավելվածը չի աջակցում էկրանի տրոհումը:"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Հավելվածը կարող է չաշխատել լրացուցիչ էկրանի վրա"</string>
@@ -69,4 +71,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..de962c43b137 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lewati ke berikutnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lewati ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah ukuran"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Batalkan stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikasi mungkin tidak berfungsi dengan layar terpisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App tidak mendukung layar terpisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikasi mungkin tidak berfungsi pada layar sekunder."</string>
@@ -69,4 +71,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..c205d22e7ae8 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Fara á næsta"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Fara á fyrra"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Breyta stærð"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Geymsla"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Taka úr geymslu"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Hugsanlega virkar forritið ekki með skjáskiptingu."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Forritið styður ekki að skjánum sé skipt."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Hugsanlegt er að forritið virki ekki á öðrum skjá."</string>
@@ -69,4 +71,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..c788a03a5b29 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Passa ai contenuti successivi"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Passa ai contenuti precedenti"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ridimensiona"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Accantona"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Annulla accantonamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"L\'app potrebbe non funzionare con lo schermo diviso."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"L\'app non supporta la modalità Schermo diviso."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"L\'app potrebbe non funzionare su un display secondario."</string>
@@ -60,7 +62,7 @@
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono visualizzate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Controlla le bolle quando vuoi"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tocca Gestisci per disattivare le bolle dall\'app"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
@@ -69,4 +71,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..b0c03edf168d 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -18,16 +18,18 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"הרחב"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולכבות את התכונה."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"אם אינך רוצה שהתכונה הזו תשמש את <xliff:g id="NAME">%s</xliff:g>, יש להקיש כדי לפתוח את ההגדרות ולהשבית את התכונה."</string>
<string name="pip_play" msgid="3496151081459417097">"הפעלה"</string>
- <string name="pip_pause" msgid="690688849510295232">"השהה"</string>
+ <string name="pip_pause" msgid="690688849510295232">"השהיה"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"אפשר לדלג אל הבא"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"אפשר לדלג אל הקודם"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"שינוי גודל"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"הסתרה זמנית"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ביטול ההסתרה הזמנית"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ייתכן שהאפליקציה לא תפעל במסך מפוצל."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"האפליקציה אינה תומכת במסך מפוצל."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ייתכן שהאפליקציה לא תפעל במסך משני."</string>
@@ -41,14 +43,14 @@
<string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"מסך עליון מלא"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"עליון 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"עליון 50%"</string>
- <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"עליון 30%"</string>
+ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"למעלה 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"מסך תחתון מלא"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש במצב שימוש ביד אחת"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"איך להשתמש בתכונה \'מצב שימוש ביד אחת\'"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"הפעלה של מצב שימוש ביד אחת"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"יציאה ממצב שימוש ביד אחת"</string>
- <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות בשביל בועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"גלישה"</string>
+ <string name="bubbles_settings_button_description" msgid="1301286017420516912">"הגדרות לבועות של <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"אפשרויות נוספות"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"הוספה בחזרה לערימה"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מהאפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
<string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> מ-<xliff:g id="APP_NAME">%2$s</xliff:g> ועוד <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
@@ -69,4 +71,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-iw/strings_tv.xml b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
index 8ca54e0a5473..ef98a9c41cf2 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings_tv.xml
@@ -19,6 +19,6 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"תמונה בתוך תמונה"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(תוכנית ללא כותרת)"</string>
- <string name="pip_close" msgid="9135220303720555525">"‏סגור PIP"</string>
+ <string name="pip_close" msgid="9135220303720555525">"‏סגירת PIP"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"מסך מלא"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fbb2951a06e1..36700bd81717 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"次へスキップ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"前へスキップ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"サイズ変更"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"非表示"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"表示"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"アプリは分割画面では動作しないことがあります。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"アプリで分割画面がサポートされていません。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"アプリはセカンダリ ディスプレイでは動作しないことがあります。"</string>
@@ -61,7 +63,7 @@
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
- <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"いつでもバブルを管理"</string>
+ <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"バブルはいつでも管理可能"</string>
<string name="bubbles_user_education_manage" msgid="3460756219946517198">"このアプリからのバブルを OFF にするには、[管理] をタップしてください"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
@@ -69,4 +71,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..af1377a2f1e0 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"შემდეგზე გადასვლა"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"წინაზე გადასვლა"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ზომის შეცვლა"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"გადანახვა"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"გადანახვის გაუქმება"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"აპმა შეიძლება არ იმუშაოს გაყოფილი ეკრანის რეჟიმში."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ეკრანის გაყოფა არ არის მხარდაჭერილი აპის მიერ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"აპმა შეიძლება არ იმუშაოს მეორეულ ეკრანზე."</string>
@@ -69,4 +71,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..6deb0b892316 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Келесіге өту"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Алдыңғысына оралу"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлшемін өзгерту"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Жасыру"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Көрсету"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Қолданба экранды бөлу режимінде жұмыс істемеуі мүмкін."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Қодланба бөлінген экранды қолдамайды."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Қолданба қосымша дисплейде жұмыс істемеуі мүмкін."</string>
@@ -68,5 +70,7 @@
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
<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="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..c59d0fc4f60d 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"រំលងទៅបន្ទាប់"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"រំលងទៅក្រោយ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ប្ដូរ​ទំហំ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"លាក់ជាបណ្ដោះអាសន្ន"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ឈប់លាក់ជាបណ្ដោះអាសន្ន"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"កម្មវិធី​អាចនឹងមិន​ដំណើរការ​ជាមួយ​មុខងារបំបែកអេក្រង់​ទេ។"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"កម្មវិធីមិនគាំទ្រអេក្រង់បំបែកជាពីរទេ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"កម្មវិធីនេះ​ប្រហែល​ជាមិនដំណើរការ​នៅលើ​អេក្រង់បន្ទាប់បន្សំទេ។"</string>
@@ -69,4 +71,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..5e655b4c7bee 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ಮುಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ಹಿಂದಕ್ಕೆ ಸ್ಕಿಪ್‌ ಮಾಡಿ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ಅನ್‌ಸ್ಟ್ಯಾಶ್ ಮಾಡಿ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ವಿಭಜಿಸಿದ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ಆ್ಯಪ್ ಕೆಲಸ ಮಾಡದೇ ಇರಬಹುದು."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ಅಪ್ಲಿಕೇಶನ್ ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್‌ ಕಾರ್ಯ ನಿರ್ವಹಿಸದೇ ಇರಬಹುದು."</string>
@@ -69,4 +71,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..af34ef48cb31 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"다음으로 건너뛰기"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"이전으로 건너뛰기"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"크기 조절"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"숨기기"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"숨기기 취소"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"앱이 분할 화면에서 작동하지 않을 수 있습니다."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"앱이 화면 분할을 지원하지 않습니다."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"앱이 보조 디스플레이에서 작동하지 않을 수도 있습니다."</string>
@@ -69,4 +71,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..8056d15de3f4 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Кийинкисине өткөрүп жиберүү"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Мурункусуна өткөрүп жиберүү"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Өлчөмүн өзгөртүү"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сейфке салуу"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Сейфтен чыгаруу"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Колдонмодо экран бөлүнбөшү мүмкүн."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Колдонмодо экран бөлүнбөйт."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Колдонмо кошумча экранда иштебей коюшу мүмкүн."</string>
@@ -44,7 +46,7 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Үстүнкү экранды 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Ылдыйкы экранды толук экран режимине өткөрүү"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"Бир кол режимин колдонуу"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Чыгуу үчүн экранды ылдый жагынан өйдө көздөй сүрүңүз же колдонмонун өйдө жагын басыңыз"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Чыгуу үчүн экранды ылдый жагынан өйдө сүрүңүз же колдонмонун өйдө жагын басыңыз"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Бир кол режимин баштоо"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Бир кол режиминен чыгуу"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> калкып чыкма билдирмелер жөндөөлөрү"</string>
@@ -62,11 +64,13 @@
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн таптап коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Калкып чыкма билдирмелерди каалаган убакта көзөмөлдөңүз"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн, \"Башкарууну\" басыңыз"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Бул колдонмодогу калкып чыкма билдирмелерди өчүрүү үчүн \"Башкарууну\" басыңыз"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
<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..a578b0a3f4cd 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ຂ້າມໄປລາຍການໜ້າ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ຂ້າມໄປລາຍການກ່ອນນີ້"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ປ່ຽນຂະໜາດ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ເກັບໄວ້ບ່ອນເກັບສ່ວນຕົວ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ເອົາອອກຈາກບ່ອນເກັບສ່ວນຕົວ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ແອັບອາດໃຊ້ບໍ່ໄດ້ກັບການແບ່ງໜ້າຈໍ."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ແອັບບໍ່ຮອງຮັບໜ້າຈໍແບບແຍກກັນ."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ແອັບອາດບໍ່ສາມາດໃຊ້ໄດ້ໃນໜ້າຈໍທີສອງ."</string>
@@ -69,4 +71,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..e037839a54db 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Praleisti ir eiti į kitą"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Praleisti ir eiti į ankstesnį"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Pakeisti dydį"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslėpti"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Nebeslėpti"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Programa gali neveikti naudojant išskaidyto ekrano režimą."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programoje nepalaikomas skaidytas ekranas."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Programa gali neveikti antriniame ekrane."</string>
@@ -69,4 +71,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..05472e454f77 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pāriet uz nākamo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pāriet uz iepriekšējo"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Mainīt lielumu"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Paslēpt"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Rādīt"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Iespējams, lietotne nedarbosies ekrāna sadalīšanas režīmā."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Lietotnē netiek atbalstīta ekrāna sadalīšana."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Lietotne, iespējams, nedarbosies sekundārajā displejā."</string>
@@ -69,4 +71,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..9cb2c6906c70 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Прескокни до следната"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Прескокни до претходната"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промени големина"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сокријте"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Прикажете"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликацијата може да не работи со поделен екран."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликацијата не поддржува поделен екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликацијата може да не функционира на друг екран."</string>
@@ -69,4 +71,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..f0bf513e264c 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"അടുത്തതിലേക്ക് പോകുക"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"മുമ്പത്തേതിലേക്ക് പോകുക"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"വലുപ്പം മാറ്റുക"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"സ്റ്റാഷ് ചെയ്യൽ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"അൺസ്റ്റാഷ് ചെയ്യൽ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"സ്‌ക്രീൻ വിഭജന മോഡിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"സ്പ്ലിറ്റ്-സ്ക്രീനിനെ ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"രണ്ടാം ഡിസ്‌പ്ലേയിൽ ആപ്പ് പ്രവർത്തിച്ചേക്കില്ല."</string>
@@ -66,7 +68,9 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
- <string name="notification_bubble_title" msgid="6082910224488253378">"ബബ്ൾ"</string>
+ <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="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..68822cb4f51b 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Дараагийн медиад очих"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Өмнөх медиад очих"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Хэмжээг өөрчлөх"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Нуух"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Ил гаргах"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апп хуваагдсан дэлгэц дээр ажиллахгүй байж болзошгүй."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Энэ апп нь дэлгэц хуваах тохиргоог дэмждэггүй."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апп хоёрдогч дэлгэцэд ажиллахгүй."</string>
@@ -69,4 +71,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..a4b7be4d52c7 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्टॅश करा"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
@@ -69,4 +71,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..2f33bfa41d83 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Langkau ke seterusnya"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Langkau ke sebelumnya"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ubah saiz"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Sembunyikan"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Tunjukkan"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Apl mungkin tidak berfungsi dengan skrin pisah."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Apl tidak menyokong skrin pisah."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Apl mungkin tidak berfungsi pada paparan kedua."</string>
@@ -69,4 +71,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..018ffc02556d 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"နောက်တစ်ခုသို့ ကျော်ရန်"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ယခင်တစ်ခုသို့ ပြန်သွားရန်"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"အရွယ်အစားပြောင်းရန်"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"သိုဝှက်ရန်"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"မသိုဝှက်ရန်"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းဖြင့် အက်ပ်သည် အလုပ်မလုပ်ပါ။"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"အက်ပ်သည် မျက်နှာပြင်ခွဲပြရန် ပံ့ပိုးထားခြင်းမရှိပါ။"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ဤအက်ပ်အနေဖြင့် ဒုတိယဖန်သားပြင်ပေါ်တွင် အလုပ်လုပ်မည် မဟုတ်ပါ။"</string>
@@ -69,4 +71,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..a23ad9068ea9 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hopp til neste"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hopp til forrige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Endre størrelse"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Oppbevar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Avslutt oppbevaring"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Det kan hende at appen ikke fungerer med delt skjerm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen støtter ikke delt skjerm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen fungerer kanskje ikke på en sekundær skjerm."</string>
@@ -69,4 +71,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..5b9b87205428 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -28,9 +28,11 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"अर्कोमा जानुहोस्"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"अघिल्लोमा जानुहोस्"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदल्नुहोस्"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्ट्यास गर्नुहोस्"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्ट्यास गर्नुहोस्"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"एप विभाजित स्क्रिनमा काम नगर्न सक्छ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अनुप्रयोगले विभाजित-स्क्रिनलाई समर्थन गर्दैन।"</string>
- <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो अनुप्रयोगले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
+ <string name="forced_resizable_secondary_display" msgid="1768046938673582671">"यो एपले सहायक प्रदर्शनमा काम नगर्नसक्छ।"</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"अनुप्रयोगले सहायक प्रदर्शनहरूमा लञ्च सुविधालाई समर्थन गर्दैन।"</string>
<string name="accessibility_divider" msgid="703810061635792791">"विभाजित-स्क्रिन छुट्याउने"</string>
<string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"बायाँ भाग फुल स्क्रिन"</string>
@@ -69,4 +71,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..06aaad7d65ca 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -22,12 +22,14 @@
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
- <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en schakel je de functie uit."</string>
+ <string name="pip_notification_message" msgid="8854051911700302620">"Als je niet wilt dat <xliff:g id="NAME">%s</xliff:g> deze functie gebruikt, tik je om de instellingen te openen en zet je de functie uit."</string>
<string name="pip_play" msgid="3496151081459417097">"Afspelen"</string>
<string name="pip_pause" msgid="690688849510295232">"Onderbreken"</string>
<string name="pip_skip_to_next" msgid="8403429188794867653">"Doorgaan naar volgende"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Teruggaan naar vorige"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Formaat aanpassen"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Verbergen"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Niet meer verbergen"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"De app werkt mogelijk niet met gesplitst scherm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"App biedt geen ondersteuning voor gesplitst scherm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"App werkt mogelijk niet op een secundair scherm."</string>
@@ -43,10 +45,10 @@
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string>
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string>
- <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met één hand gebruiken"</string>
+ <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met één hand starten"</string>
- <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met één hand afsluiten"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string>
+ <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met 1 hand afsluiten"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string>
<string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string>
@@ -58,15 +60,17 @@
<string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
<string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
- <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels weergeven"</string>
+ <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
- <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden weergegeven als zwevende iconen of \'bubbels\'. Tik om een bubbel te openen. Sleep om de bubbel te verplaatsen."</string>
+ <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
<string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Beheer bubbels wanneer je wilt"</string>
- <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te schakelen"</string>
+ <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Tik op Beheren om bubbels van deze app uit te zetten"</string>
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
- <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels worden hier weergegeven"</string>
+ <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
<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..ac1e84a6e283 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ରିସାଇଜ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ଲୁଚାନ୍ତୁ"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ଦେଖାନ୍ତୁ"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ସ୍ପ୍ଲିଟ୍-ସ୍କ୍ରିନରେ ଆପ୍ କାମ କରିନପାରେ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
@@ -69,4 +71,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..bf5b733c22ea 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ਅਗਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ਪਿਛਲੇ \'ਤੇ ਜਾਓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ਆਕਾਰ ਬਦਲੋ"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"ਸਟੈਸ਼"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ਅਣਸਟੈਸ਼"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ਐਪ ਸਪਲਿਟ-ਸਕ੍ਰੀਨ ਨੂੰ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਐਪ ਸੈਕੰਡਰੀ ਡਿਸਪਲੇ \'ਤੇ ਕੰਮ ਨਾ ਕਰੇ।"</string>
@@ -69,4 +71,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..cd659ba86319 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Dalej"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Wstecz"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmień rozmiar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Przenieś do schowka"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zabierz ze schowka"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacja może nie działać przy podzielonym ekranie."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacja nie obsługuje dzielonego ekranu."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacja może nie działać na dodatkowym ekranie."</string>
@@ -69,4 +71,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..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,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..1f5b0abd80d9 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Mudar para o seguinte"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Mudar para o anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Armazenar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do armazenamento"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"A app pode não funcionar com o ecrã dividido."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"A app não é compatível com o ecrã dividido."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"A app pode não funcionar num ecrã secundário."</string>
@@ -69,4 +71,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..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
@@ -69,4 +71,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..d694be1cdd18 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Treceți la următorul"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Treceți la cel anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionați"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stocați"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulați stocarea"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Este posibil ca aplicația să nu funcționeze cu ecranul împărțit."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplicația nu acceptă ecranul împărțit."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Este posibil ca aplicația să nu funcționeze pe un ecran secundar."</string>
@@ -69,4 +71,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..e9bfffbad399 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти к следующему"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти к предыдущему"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Изменить размер"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Скрыть"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показать"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"В режиме разделения экрана приложение может работать нестабильно."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Приложение не поддерживает разделение экрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Приложение может не работать на дополнительном экране"</string>
@@ -69,4 +71,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..ba178f03efbb 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ඊළඟ එකට පනින්න"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"පෙර එකට පනින්න"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ප්‍රතිප්‍රමාණ කරන්න"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"සඟවා තබන්න"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"සඟවා තැබීම ඉවත් කරන්න"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"යෙදුම බෙදුම් තිරය සමග ක්‍රියා නොකළ හැකිය"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"යෙදුම බෙදුණු-තිරය සඳහා සහාය නොදක්වයි."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"යෙදුම ද්විතියික සංදර්ශකයක ක්‍රියා නොකළ හැකිය."</string>
@@ -69,4 +71,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..e048ca15b91f 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskočiť na ďalšie"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskočiť na predchádzajúce"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Zmeniť veľkosť"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Skryť"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Zrušiť skrytie"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikácia nemusí fungovať s rozdelenou obrazovkou."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikácia nepodporuje rozdelenú obrazovku."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikácia nemusí fungovať na sekundárnej obrazovke."</string>
@@ -69,4 +71,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..ed05908c6e03 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Preskoči na naslednjega"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Preskoči na prejšnjega"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Spremeni velikost"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Zakrij"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Razkrij"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacija morda ne deluje v načinu razdeljenega zaslona."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacija ne podpira načina razdeljenega zaslona."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacija morda ne bo delovala na sekundarnem zaslonu."</string>
@@ -69,4 +71,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..13e830cbd100 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Kalo te tjetra"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Kalo tek e mëparshmja"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ndrysho përmasat"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Fshih"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Mos e fshih"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Aplikacioni mund të mos funksionojë me ekranin e ndarë."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Aplikacioni nuk mbështet ekranin e ndarë."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Aplikacioni mund të mos funksionojë në një ekran dytësor."</string>
@@ -69,4 +71,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..be6857b1f1a8 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Пређи на следеће"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Пређи на претходно"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Промените величину"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ставите у тајну меморију"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Уклоните из тајне меморије"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Апликација можда неће радити са подељеним екраном."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Апликација не подржава подељени екран."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Апликација можда неће функционисати на секундарном екрану."</string>
@@ -69,4 +71,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..e61e69b51c5f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Hoppa till nästa"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Hoppa till föregående"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Ändra storlek"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Utför stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Återställ stash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Appen kanske inte fungerar med delad skärm."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Appen har inte stöd för delad skärm."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Appen kanske inte fungerar på en sekundär skärm."</string>
@@ -69,4 +71,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..476af11cd3ad 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Ruka ufikie inayofuata"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Ruka ufikie iliyotangulia"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Badilisha ukubwa"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ficha"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Fichua"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Huenda programu isifanye kazi kwenye skrini inayogawanywa."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Programu haiwezi kutumia skrini iliyogawanywa."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Huenda programu isifanye kazi kwenye dirisha lingine."</string>
@@ -69,4 +71,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..bc27389c6116 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"அடுத்ததற்குச் செல்"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"முந்தையதற்குச் செல்"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"அளவு மாற்று"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"திரைப் பிரிப்பு அம்சத்தில் ஆப்ஸ் செயல்படாமல் போகக்கூடும்."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"திரையைப் பிரிப்பதைப் ஆப்ஸ் ஆதரிக்கவில்லை."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"இரண்டாம்நிலைத் திரையில் ஆப்ஸ் வேலை செய்யாமல் போகக்கூடும்."</string>
@@ -69,4 +71,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..c2b6ffbd1048 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -28,8 +28,10 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"దాటవేసి తర్వాత దానికి వెళ్లు"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"దాటవేసి మునుపటి దానికి వెళ్లు"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"పరిమాణం మార్చు"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
- <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
+ <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ప్రత్యామ్నాయ డిస్‌ప్లేలో యాప్ పని చేయకపోవచ్చు."</string>
<string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ప్రత్యామ్నాయ డిస్‌ప్లేల్లో ప్రారంభానికి యాప్ మద్దతు లేదు."</string>
<string name="accessibility_divider" msgid="703810061635792791">"విభజన స్క్రీన్ విభాగిని"</string>
@@ -69,4 +71,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..9017b3f6b326 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"ข้ามไปรายการถัดไป"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"ข้ามไปรายการก่อนหน้า"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"ปรับขนาด"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"เก็บเข้าที่เก็บส่วนตัว"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"เอาออกจากที่เก็บส่วนตัว"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"แอปอาจใช้ไม่ได้กับโหมดแบ่งหน้าจอ"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"แอปไม่สนับสนุนการแยกหน้าจอ"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"แอปอาจไม่ทำงานในจอแสดงผลรอง"</string>
@@ -69,4 +71,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..c484cafb191a 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Lumaktaw sa susunod"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Lumaktaw sa nakaraan"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"I-resize"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"I-stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"I-unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Posibleng hindi gumana ang app sa split screen."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Hindi sinusuportahan ng app ang split-screen."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Maaaring hindi gumana ang app sa pangalawang display."</string>
@@ -69,4 +71,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..ca856a1fe93b 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Sonrakine atla"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Öncekine atla"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Yeniden boyutlandır"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Depola"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Depolama"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Uygulama bölünmüş ekranda çalışmayabilir."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uygulama bölünmüş ekranı desteklemiyor."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uygulama ikincil ekranda çalışmayabilir."</string>
@@ -69,4 +71,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..08e8d2942d57 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Перейти далі"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Перейти назад"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Змінити розмір"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Сховати"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Показати"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Додаток може не працювати в режимі розділеного екрана."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Додаток не підтримує розділення екрана."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Додаток може не працювати на додатковому екрані."</string>
@@ -69,4 +71,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..06c09276e717 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"نظرانداز کرکے اگلے پر جائیں"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"نظرانداز کرکے پچھلے پر جائیں"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"سائز تبدیل کریں"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Stash"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Unstash"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"ممکن ہے کہ ایپ اسپلٹ اسکرین کے ساتھ کام نہ کرے۔"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ایپ سپلٹ اسکرین کو سپورٹ نہیں کرتی۔"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ممکن ہے ایپ ثانوی ڈسپلے پر کام نہ کرے۔"</string>
@@ -69,4 +71,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..6a873a3e185e 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Keyingisiga o‘tish"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Avvalgisiga qaytish"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Oʻlchamini oʻzgartirish"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Berkitish"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Chiqarish"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Bu ilova ekranni ikkiga ajratish rejimini dastaklamaydi."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Bu ilova ekranni bo‘lish xususiyatini qo‘llab-quvvatlamaydi."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Bu ilova qo‘shimcha ekranda ishlamasligi mumkin."</string>
@@ -69,4 +71,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..4d4eebcfd68b 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Chuyển tới mục tiếp theo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Chuyển về mục trước"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Đổi kích thước"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ẩn"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Hiện"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Ứng dụng có thể không hoạt động với tính năng chia đôi màn hình."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Ứng dụng không hỗ trợ chia đôi màn hình."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Ứng dụng có thể không hoạt động trên màn hình phụ."</string>
@@ -69,4 +71,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..3b8c8894c4e7 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一个"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一个"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"调整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"隐藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消隐藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"应用可能无法在分屏模式下正常运行。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"应用不支持分屏。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"应用可能无法在辅显示屏上正常运行。"</string>
@@ -69,4 +71,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..9ba82b5ddf72 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"保護"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消保護"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示屏上運作。"</string>
@@ -69,4 +71,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..aa666531996d 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"跳到下一個"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"跳到上一個"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"調整大小"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"暫時隱藏"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"取消暫時隱藏"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"應用程式可能無法在分割畫面中運作。"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"這個應用程式不支援分割畫面。"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"應用程式可能無法在次要顯示器上運作。"</string>
@@ -44,7 +46,7 @@
<string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</string>
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"以全螢幕顯示底部畫面"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"使用單手模式"</string>
- <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上的任何位置"</string>
+ <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string>
<string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"啟動單手模式"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"結束單手模式"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string>
@@ -69,4 +71,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..c8199c844d5b 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -28,6 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Yeqela kokulandelayo"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Yeqela kokwangaphambilini"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Shintsha usayizi"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Yenza isiteshi"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Susa isiteshi"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"Izinhlelo zokusebenza kungenzeka zingasebenzi ngesikrini esihlukanisiwe."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"Uhlelo lokusebenza alusekeli isikrini esihlukanisiwe."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"Uhlelo lokusebenza kungenzeka lungasebenzi kusibonisi sesibili."</string>
@@ -69,4 +71,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/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f28ee820eb35..130f741def86 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -100,6 +100,8 @@
<dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
<!-- How much space to leave between the flyout text and the avatar displayed in the flyout. -->
<dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
+ <!-- If the screen percentage is smaller than this, we'll use this value instead. -->
+ <dimen name="bubbles_flyout_min_width_large_screen">200dp</dimen>
<!-- Padding between status bar and bubbles when displayed in expanded state -->
<dimen name="bubble_padding_top">16dp</dimen>
<!-- Space between bubbles when expanded. -->
@@ -122,7 +124,7 @@
should also be updated. -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- On large screens the width of the expanded view is restricted to this size. -->
- <dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
+ <dimen name="bubble_expanded_view_phone_landscape_overflow_width">412dp</dimen>
<!-- Inset to apply to the icon in the overflow button. -->
<dimen name="bubble_overflow_icon_inset">30dp</dimen>
<!-- Default (and minimum) height of bubble overflow -->
@@ -149,9 +151,17 @@
<!-- Extra padding around the dismiss target for bubbles -->
<dimen name="bubble_dismiss_slop">16dp</dimen>
<!-- Height of button allowing users to adjust settings for bubbles. -->
- <dimen name="bubble_manage_button_height">56dp</dimen>
+ <dimen name="bubble_manage_button_height">36dp</dimen>
+ <!-- Height of manage button including margins. -->
+ <dimen name="bubble_manage_button_total_height">68dp</dimen>
+ <!-- The margin around the outside of the manage button. -->
+ <dimen name="bubble_manage_button_margin">16dp</dimen>
<!-- Height of an item in the bubble manage menu. -->
<dimen name="bubble_menu_item_height">60dp</dimen>
+ <!-- Padding applied to the bubble manage menu. -->
+ <dimen name="bubble_menu_padding">16dp</dimen>
+ <!-- Size of the icons in the manage menu. -->
+ <dimen name="bubble_menu_icon_size">24dp</dimen>
<!-- Max width of the message bubble-->
<dimen name="bubble_message_max_width">144dp</dimen>
<!-- Min width of the message bubble -->
@@ -174,14 +184,8 @@
<dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
<dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
<dimen name="bubble_manage_menu_elevation">4dp</dimen>
-
- <!-- Bubbles user education views -->
- <dimen name="bubbles_manage_education_width">160dp</dimen>
- <!-- The inset from the top bound of the manage button to place the user education. -->
- <dimen name="bubbles_manage_education_top_inset">65dp</dimen>
- <!-- Size of padding for the user education cling, this should at minimum be larger than
- individual_bubble_size + some padding. -->
- <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
+ <!-- Size of user education views on large screens (phone is just match parent). -->
+ <dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
<!-- The width/height of the size compat restart button. -->
<dimen name="size_compat_button_size">48dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 34c66a4f4b82..bf074b0337ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -97,6 +97,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
b.setParent(sc);
}
+ public void setPosition(@NonNull SurfaceControl.Transaction tx, int displayId, int x, int y) {
+ final SurfaceControl sc = mLeashes.get(displayId);
+ if (sc == null) {
+ throw new IllegalArgumentException("can't find display" + displayId);
+ }
+ tx.setPosition(sc, x, y);
+ }
+
@Override
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
@NonNull SurfaceControl leash) {
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 0b941b59b3db..9113c79d40f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -103,6 +103,8 @@ public final class ShellCommandHandlerImpl {
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
return runRemoveFromSideStage(args, pw);
+ case "setSideStageOutline":
+ return runSetSideStageOutline(args, pw);
case "setSideStagePosition":
return runSetSideStagePosition(args, pw);
case "setSideStageVisibility":
@@ -161,6 +163,18 @@ public final class ShellCommandHandlerImpl {
return true;
}
+ private boolean runSetSideStageOutline(String[] args, PrintWriter pw) {
+ if (args.length < 3) {
+ // First arguments are "WMShell" and command name.
+ pw.println("Error: whether to enable or disable side stage outline border should be"
+ + " provided as arguments");
+ return false;
+ }
+ final boolean enable = new Boolean(args[2]);
+ mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable));
+ return true;
+ }
+
private boolean runSetSideStagePosition(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
@@ -175,7 +189,7 @@ public final class ShellCommandHandlerImpl {
private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.
- pw.println("Error: side stage position should be provided as arguments");
+ pw.println("Error: side stage visibility should be provided as arguments");
return false;
}
final Boolean visible = new Boolean(args[2]);
@@ -197,6 +211,8 @@ public final class ShellCommandHandlerImpl {
pw.println(" Move a task with given id in split-screen mode.");
pw.println(" removeFromSideStage <taskId>");
pw.println(" Remove a task with given id in split-screen mode.");
+ pw.println(" setSideStageOutline <true/false>");
+ pw.println(" Enable/Disable outline on the side-stage.");
pw.println(" setSideStagePosition <SideStagePosition>");
pw.println(" Sets the position of the side-stage.");
pw.println(" setSideStageVisibility <true/false>");
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 d1fbf31e2b99..df4f2383c062 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -20,10 +20,13 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCR
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
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.freeform.FreeformTaskListener;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -38,7 +41,9 @@ import java.util.Optional;
public class ShellInitImpl {
private static final String TAG = ShellInitImpl.class.getSimpleName();
+ private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<BubbleController> mBubblesOptional;
@@ -47,13 +52,17 @@ public class ShellInitImpl {
private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
+ private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
private final StartingWindowController mStartingWindow;
private final InitImpl mImpl = new InitImpl();
- public ShellInitImpl(DisplayImeController displayImeController,
+ public ShellInitImpl(
+ DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
@@ -62,10 +71,13 @@ public class ShellInitImpl {
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
+ Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
Transitions transitions,
StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
+ mDisplayController = displayController;
mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
mDragAndDropController = dragAndDropController;
mShellTaskOrganizer = shellTaskOrganizer;
mBubblesOptional = bubblesOptional;
@@ -74,6 +86,7 @@ public class ShellInitImpl {
mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
+ mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
mTransitions = transitions;
mMainExecutor = mainExecutor;
mStartingWindow = startingWindow;
@@ -84,7 +97,9 @@ public class ShellInitImpl {
}
private void init() {
- // Start listening for display changes
+ // Start listening for display and insets changes
+ mDisplayController.initialize();
+ mDisplayInsetsController.initialize();
mDisplayImeController.startMonitorDisplays();
// Setup the shell organizer
@@ -108,6 +123,11 @@ public class ShellInitImpl {
// controller instead of the feature interface, can just initialize the touch handler if
// needed
mPipTouchHandlerOptional.ifPresent((handler) -> handler.init());
+
+ // Initialize optional freeform
+ mFreeformTaskListenerOptional.ifPresent(f ->
+ mShellTaskOrganizer.addListenerForType(
+ f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
}
@ExternalThread
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 ba0ab6db1003..b5dffba7a0f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,6 +31,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.LocusId;
+import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -46,6 +47,7 @@ import android.window.TaskOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
@@ -71,12 +73,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2;
public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
public static final int TASK_LISTENER_TYPE_PIP = -4;
+ public static final int TASK_LISTENER_TYPE_FREEFORM = -5;
@IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
TASK_LISTENER_TYPE_UNDEFINED,
TASK_LISTENER_TYPE_FULLSCREEN,
TASK_LISTENER_TYPE_MULTI_WINDOW,
TASK_LISTENER_TYPE_PIP,
+ TASK_LISTENER_TYPE_FREEFORM,
})
public @interface TaskListenerType {}
@@ -486,14 +490,40 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
@Override
+ public void onSizeCompatRestartButtonAppeared(int taskId) {
+ final TaskAppearedInfo info;
+ synchronized (mLock) {
+ info = mTasks.get(taskId);
+ }
+ if (info == null) {
+ return;
+ }
+ logSizeCompatRestartButtonEventReported(info,
+ FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__APPEARED);
+ }
+
+ @Override
public void onSizeCompatRestartButtonClicked(int taskId) {
final TaskAppearedInfo info;
synchronized (mLock) {
info = mTasks.get(taskId);
}
- if (info != null) {
- restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
+ if (info == null) {
+ return;
+ }
+ logSizeCompatRestartButtonEventReported(info,
+ FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__CLICKED);
+ restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
+ }
+
+ private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
+ int event) {
+ ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
+ if (topActivityInfo == null) {
+ return;
}
+ FrameworkStatsLog.write(FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED,
+ topActivityInfo.applicationInfo.uid, event);
}
/**
@@ -572,6 +602,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
case WINDOWING_MODE_PINNED:
return TASK_LISTENER_TYPE_PIP;
case WINDOWING_MODE_FREEFORM:
+ return TASK_LISTENER_TYPE_FREEFORM;
case WINDOWING_MODE_UNDEFINED:
default:
return TASK_LISTENER_TYPE_UNDEFINED;
@@ -586,6 +617,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
return "TASK_LISTENER_TYPE_MULTI_WINDOW";
case TASK_LISTENER_TYPE_PIP:
return "TASK_LISTENER_TYPE_PIP";
+ case TASK_LISTENER_TYPE_FREEFORM:
+ return "TASK_LISTENER_TYPE_FREEFORM";
case TASK_LISTENER_TYPE_UNDEFINED:
return "TASK_LISTENER_TYPE_UNDEFINED";
default:
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 1861e48482b8..2f3214d1d1ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -40,6 +40,8 @@ import android.view.ViewTreeObserver;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -74,6 +76,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final ShellTaskOrganizer mTaskOrganizer;
private final Executor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
@@ -89,11 +92,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private final Rect mTmpRootRect = new Rect();
private final int[] mTmpLocation = new int[2];
- public TaskView(Context context, ShellTaskOrganizer organizer) {
+ public TaskView(Context context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
+ mSyncQueue = syncQueue;
setUseAlpha();
getHolder().addCallback(this);
mGuard.open("release");
@@ -189,8 +193,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mTaskToken, mTmpRect);
- // TODO(b/151449487): Enable synchronization
- mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.queue(wct);
}
/**
@@ -236,14 +239,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
private void updateTaskVisibility() {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
- mTaskOrganizer.applyTransaction(wct);
- // TODO(b/151449487): Only call callback once we enable synchronization
- if (mListener != null) {
- final int taskId = mTaskInfo.taskId;
+ mSyncQueue.queue(wct);
+ if (mListener == null) {
+ return;
+ }
+ int taskId = mTaskInfo.taskId;
+ mSyncQueue.runInSync((t) -> {
mListenerExecutor.execute(() -> {
mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
});
- }
+ });
}
@Override
@@ -264,10 +269,12 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
updateTaskVisibility();
}
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
- // TODO: Synchronize show with the resize
onLocationChanged();
if (taskInfo.taskDescription != null) {
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ int backgroundColor = taskInfo.taskDescription.getBackgroundColor();
+ mSyncQueue.runInSync((t) -> {
+ setResizeBackgroundColor(t, backgroundColor);
+ });
}
if (mListener != null) {
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 58ca1fbaba24..8286d102791e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskViewFactoryController.java
@@ -20,8 +20,8 @@ import android.annotation.UiContext;
import android.content.Context;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -30,12 +30,14 @@ import java.util.function.Consumer;
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
+ private final SyncTransactionQueue mSyncQueue;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
- ShellExecutor shellExecutor) {
+ ShellExecutor shellExecutor, SyncTransactionQueue syncQueue) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
+ mSyncQueue = syncQueue;
}
public TaskViewFactory asTaskViewFactory() {
@@ -44,7 +46,7 @@ public class TaskViewFactoryController {
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
- TaskView taskView = new TaskView(context, mTaskOrganizer);
+ TaskView taskView = new TaskView(context, mTaskOrganizer, mSyncQueue);
executor.execute(() -> {
onCreate.accept(taskView);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 8aca01d2467b..2aead9392e59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -62,4 +62,10 @@ public class Interpolators {
*/
public static final Interpolator PANEL_CLOSE_ACCELERATED =
new PathInterpolator(0.3f, 0, 0.5f, 1);
+
+ public static final PathInterpolator SLOWDOWN_INTERPOLATOR =
+ new PathInterpolator(0.5f, 1f, 0.5f, 1f);
+
+ public static final PathInterpolator DIM_INTERPOLATOR =
+ new PathInterpolator(.23f, .87f, .52f, -0.11f);
}
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 e6d088e6537d..3800b8d234f3 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
@@ -19,6 +19,7 @@ package com.android.wm.shell.apppairs;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
@@ -181,12 +182,13 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
// TODO: Is there more we need to do here?
mSyncQueue.runInSync(t -> {
- t.setLayer(dividerLeash, Integer.MAX_VALUE)
+ t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
mTaskInfo1.positionInParent.y)
.setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
mTaskInfo2.positionInParent.y)
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
+ .show(dividerLeash)
.show(mRootTaskLeash)
.show(mTaskLeash1)
.show(mTaskLeash2);
@@ -212,9 +214,12 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
}
mRootTaskInfo = taskInfo;
- if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
- onBoundsChanged(mSplitLayout);
+ if (mSplitLayout != null) {
+ if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+ onLayoutChanged(mSplitLayout);
+ }
+ // updateConfiguration re-inits the dividerbar, so show it now
+ mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
@@ -295,17 +300,24 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
}
@Override
- public void onBoundsChanging(SplitLayout layout) {
+ public void onLayoutChanging(SplitLayout layout) {
mSyncQueue.runInSync(t ->
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
}
@Override
- public void onBoundsChanged(SplitLayout layout) {
+ public void onLayoutChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t ->
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
}
+
+ @Override
+ public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ layout.applyLayoutShifted(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2);
+ mController.getTaskOrganizer().applyTransaction(wct);
+ }
}
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 09fcb86e56de..95b80df7fcbd 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
@@ -56,7 +56,6 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -85,6 +84,7 @@ import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -97,7 +97,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -137,6 +136,7 @@ public class BubbleController {
private final TaskStackListenerImpl mTaskStackListener;
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
+ private final SyncTransactionQueue mSyncQueue;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -209,7 +209,8 @@ public class BubbleController {
ShellTaskOrganizer organizer,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
BubbleLogger logger = new BubbleLogger(uiEventLogger);
BubblePositioner positioner = new BubblePositioner(context, windowManager);
BubbleData data = new BubbleData(context, logger, positioner, mainExecutor);
@@ -217,7 +218,7 @@ public class BubbleController {
new BubbleDataRepository(context, launcherApps, mainExecutor),
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
logger, taskStackListener, organizer, positioner, displayController, mainExecutor,
- mainHandler);
+ mainHandler, syncQueue);
}
/**
@@ -239,7 +240,8 @@ public class BubbleController {
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor mainExecutor,
- Handler mainHandler) {
+ Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
mContext = context;
mLauncherApps = launcherApps;
mBarService = statusBarService == null
@@ -262,6 +264,7 @@ public class BubbleController {
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mBubbleIconFactory = new BubbleIconFactory(context);
mDisplayController = displayController;
+ mSyncQueue = syncQueue;
}
public void initialize() {
@@ -561,6 +564,10 @@ public class BubbleController {
return mTaskOrganizer;
}
+ SyncTransactionQueue getSyncTransactionQueue() {
+ return mSyncQueue;
+ }
+
/** Contains information to help position things on the screen. */
BubblePositioner getPositioner() {
return mBubblePositioner;
@@ -572,7 +579,7 @@ public class BubbleController {
/**
* BubbleStackView is lazily created by this method the first time a Bubble is added. This
- * method initializes the stack view and adds it to the StatusBar just above the scrim.
+ * method initializes the stack view and adds it to window manager.
*/
private void ensureStackViewCreated() {
if (mStackView == null) {
@@ -620,7 +627,6 @@ public class BubbleController {
try {
mAddedToWindowManager = true;
mBubbleData.getOverflow().initialize(this);
- mStackView.addView(mBubbleScrim);
mWindowManager.addView(mStackView, mWmLayoutParams);
// Position info is dependent on us being attached to a window
mBubblePositioner.update();
@@ -630,10 +636,16 @@ public class BubbleController {
}
}
- /** For the overflow to be focusable & receive key events the flags must be update. **/
- void updateWindowFlagsForOverflow(boolean showingOverflow) {
+ /**
+ * In some situations bubble's should be able to receive key events for back:
+ * - when the bubble overflow is showing
+ * - when the user education for the stack is showing.
+ *
+ * @param interceptBack whether back should be intercepted or not.
+ */
+ void updateWindowFlagsForBackpress(boolean interceptBack) {
if (mStackView != null && mAddedToWindowManager) {
- mWmLayoutParams.flags = showingOverflow
+ mWmLayoutParams.flags = interceptBack
? 0
: WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -652,7 +664,6 @@ public class BubbleController {
mAddedToWindowManager = false;
if (mStackView != null) {
mWindowManager.removeView(mStackView);
- mStackView.removeView(mBubbleScrim);
mBubbleData.getOverflow().cleanUpExpandedState();
} else {
Log.w(TAG, "StackView added to WindowManager, but was null when removing!");
@@ -754,13 +765,6 @@ public class BubbleController {
}
}
- private void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
- mBubbleScrim = view;
- callback.accept(mMainExecutor, mMainExecutor.executeBlockingForResult(() -> {
- return Looper.myLooper();
- }, Looper.class));
- }
-
private void setSysuiProxy(Bubbles.SysuiProxy proxy) {
mSysuiProxy = proxy;
}
@@ -897,8 +901,7 @@ public class BubbleController {
* Fills the overflow bubbles by loading them from disk.
*/
void loadOverflowBubblesFromDisk() {
- if (!mBubbleData.getOverflowBubbles().isEmpty() && !mOverflowDataLoadNeeded) {
- // we don't need to load overflow bubbles from disk if it is already in memory
+ if (!mOverflowDataLoadNeeded) {
return;
}
mOverflowDataLoadNeeded = false;
@@ -1566,13 +1569,6 @@ public class BubbleController {
}
@Override
- public void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
- mMainExecutor.execute(() -> {
- BubbleController.this.setBubbleScrim(view, callback);
- });
- }
-
- @Override
public void setExpandListener(BubbleExpandListener listener) {
mMainExecutor.execute(() -> {
BubbleController.this.setExpandListener(listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index d73ce6951e6d..b48bda3a6e48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -699,10 +699,9 @@ public class BubbleData {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "setSelectedBubbleInternal: " + bubble);
}
- if (!mShowingOverflow && Objects.equals(bubble, mSelectedBubble)) {
+ if (Objects.equals(bubble, mSelectedBubble)) {
return;
}
- // Otherwise, if we are showing the overflow menu, return to the previously selected bubble.
boolean isOverflow = bubble != null && BubbleOverflow.KEY.equals(bubble.getKey());
if (bubble != null
&& !mBubbles.contains(bubble)
@@ -771,6 +770,10 @@ public class BubbleData {
Log.e(TAG, "Attempt to expand stack without selected bubble!");
return;
}
+ if (mSelectedBubble.getKey().equals(mOverflow.getKey()) && !mBubbles.isEmpty()) {
+ // Show previously selected bubble instead of overflow menu when expanding.
+ setSelectedBubbleInternal(mBubbles.get(0));
+ }
if (mSelectedBubble instanceof Bubble) {
((Bubble) mSelectedBubble).markAsAccessedAt(mTimeSource.currentTimeMillis());
}
@@ -779,16 +782,6 @@ public class BubbleData {
// Apply ordering and grouping rules from expanded -> collapsed, then save
// the result.
mStateChange.orderChanged |= repackAll();
- // Save the state which should be returned to when expanded (with no other changes)
-
- if (mShowingOverflow) {
- // Show previously selected bubble instead of overflow menu on next expansion.
- if (!mSelectedBubble.getKey().equals(mOverflow.getKey())) {
- setSelectedBubbleInternal(mSelectedBubble);
- } else {
- setSelectedBubbleInternal(mBubbles.get(0));
- }
- }
if (mBubbles.indexOf(mSelectedBubble) > 0) {
// Move the selected bubble to the top while collapsed.
int index = mBubbles.indexOf(mSelectedBubble);
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 9687ec6a8168..7d7bfb2a92a3 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
@@ -25,6 +25,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -60,7 +61,6 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
import com.android.wm.shell.common.AlphaOptimizedButton;
@@ -77,7 +77,6 @@ public class BubbleExpandedView extends LinearLayout {
// The triangle pointing to the expanded view
private View mPointerView;
- private int mPointerMargin;
@Nullable private int[] mExpandedViewContainerLocation;
private AlphaOptimizedButton mManageButton;
@@ -102,9 +101,6 @@ public class BubbleExpandedView extends LinearLayout {
*/
private boolean mIsAlphaAnimating = false;
- private int mMinHeight;
- private int mOverflowHeight;
- private int mManageButtonHeight;
private int mPointerWidth;
private int mPointerHeight;
private float mPointerRadius;
@@ -232,7 +228,7 @@ public class BubbleExpandedView extends LinearLayout {
@Override
public void onBackPressedOnTaskRoot(int taskId) {
if (mTaskId == taskId && mStackView.isExpanded()) {
- mController.collapseStack();
+ mStackView.onBackPressed();
}
}
};
@@ -338,7 +334,8 @@ public class BubbleExpandedView extends LinearLayout {
bringChildToFront(mOverflowView);
mManageButton.setVisibility(GONE);
} else {
- mTaskView = new TaskView(mContext, mController.getTaskOrganizer());
+ mTaskView = new TaskView(mContext, mController.getTaskOrganizer(),
+ mController.getSyncTransactionQueue());
mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener);
mExpandedViewContainer.addView(mTaskView);
bringChildToFront(mTaskView);
@@ -347,12 +344,8 @@ public class BubbleExpandedView extends LinearLayout {
void updateDimensions() {
Resources res = getResources();
- mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
- mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
-
updateFontSize();
- mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerRadius = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_radius);
@@ -368,7 +361,6 @@ public class BubbleExpandedView extends LinearLayout {
updatePointerView();
}
- mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
if (mManageButton != null) {
int visibility = mManageButton.getVisibility();
removeView(mManageButton);
@@ -632,12 +624,11 @@ public class BubbleExpandedView extends LinearLayout {
}
if ((mBubble != null && mTaskView != null) || mIsOverflow) {
- float desiredHeight = mIsOverflow
- ? mPositioner.isLargeScreen() ? getMaxExpandedHeight() : mOverflowHeight
- : mBubble.getDesiredHeight(mContext);
- desiredHeight = Math.max(desiredHeight, mMinHeight);
- float height = Math.min(desiredHeight, getMaxExpandedHeight());
- height = Math.max(height, mMinHeight);
+ float desiredHeight = mPositioner.getExpandedViewHeight(mBubble);
+ int maxHeight = mPositioner.getMaxExpandedViewHeight(mIsOverflow);
+ float height = desiredHeight == MAX_HEIGHT
+ ? maxHeight
+ : Math.min(desiredHeight, maxHeight);
FrameLayout.LayoutParams lp = mIsOverflow
? (FrameLayout.LayoutParams) mOverflowView.getLayoutParams()
: (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
@@ -661,23 +652,6 @@ public class BubbleExpandedView extends LinearLayout {
}
}
- private int getMaxExpandedHeight() {
- int expandedContainerY = mExpandedViewContainerLocation != null
- // Remove top insets back here because availableRect.height would account for that
- ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top
- : 0;
- int settingsHeight = mIsOverflow ? 0 : mManageButtonHeight;
- int pointerHeight = mPositioner.showBubblesVertically()
- ? mPointerWidth
- : (int) (mPointerHeight - mPointerOverlap + mPointerMargin);
- return mPositioner.getAvailableRect().height()
- - expandedContainerY
- - getPaddingTop()
- - getPaddingBottom()
- - settingsHeight
- - pointerHeight;
- }
-
/**
* Update appearance of the expanded view being displayed.
*
@@ -722,19 +696,18 @@ public class BubbleExpandedView extends LinearLayout {
? mPointerHeight - mPointerOverlap
: 0;
final float paddingRight = (showVertically && !onLeft)
- ? mPointerHeight - mPointerOverlap : 0;
- final float paddingTop = showVertically ? 0
+ ? mPointerHeight - mPointerOverlap
+ : 0;
+ final float paddingTop = showVertically
+ ? 0
: mPointerHeight - mPointerOverlap;
setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);
- final float expandedViewY = mPositioner.getExpandedViewY();
- // TODO: I don't understand why it works but it does - why normalized in portrait
- // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
- final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
- mPositioner.getBubbleSize());
- final float bubbleCenter = showVertically
- ? bubblePosition + (mPositioner.getBubbleSize() / 2f) - expandedViewY
- : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+ // Subtract the expandedViewY here because the pointer is placed within the expandedView.
+ float pointerPosition = mPositioner.getPointerPosition(bubblePosition);
+ final float bubbleCenter = mPositioner.showBubblesVertically()
+ ? pointerPosition - mPositioner.getExpandedViewY(mBubble, bubblePosition)
+ : pointerPosition;
// Post because we need the width of the view
post(() -> {
float pointerY;
@@ -764,6 +737,10 @@ public class BubbleExpandedView extends LinearLayout {
mManageButton.getBoundsOnScreen(rect);
}
+ public int getManageButtonMargin() {
+ return ((LinearLayout.LayoutParams) mManageButton.getLayoutParams()).getMarginStart();
+ }
+
/**
* Cleans up anything related to the task and {@code TaskView}. If this view should be reused
* after this method is called, then
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 35a4f33ecf72..9374da4c4fab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -56,9 +56,6 @@ import com.android.wm.shell.common.TriangleShape;
* transform into the 'new' dot, which is used during flyout dismiss animations/gestures.
*/
public class BubbleFlyoutView extends FrameLayout {
- /** Max width of the flyout, in terms of percent of the screen width. */
- private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
-
/** Translation Y of fade animation. */
private static final float FLYOUT_FADE_Y = 40f;
@@ -68,6 +65,8 @@ public class BubbleFlyoutView extends FrameLayout {
// Whether the flyout view should show a pointer to the bubble.
private static final boolean SHOW_POINTER = false;
+ private BubblePositioner mPositioner;
+
private final int mFlyoutPadding;
private final int mFlyoutSpaceFromBubble;
private final int mPointerSize;
@@ -156,10 +155,11 @@ public class BubbleFlyoutView extends FrameLayout {
/** Callback to run when the flyout is hidden. */
@Nullable private Runnable mOnHide;
- public BubbleFlyoutView(Context context) {
+ public BubbleFlyoutView(Context context, BubblePositioner positioner) {
super(context);
- LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
+ mPositioner = positioner;
+ LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
mSenderText = findViewById(R.id.bubble_flyout_name);
mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
@@ -230,11 +230,11 @@ public class BubbleFlyoutView extends FrameLayout {
/*
* Fade animation for consecutive flyouts.
*/
- void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos,
+ void animateUpdate(Bubble.FlyoutMessage flyoutMessage, PointF stackPos,
boolean hideDot, Runnable onHide) {
mOnHide = onHide;
final Runnable afterFadeOut = () -> {
- updateFlyoutMessage(flyoutMessage, parentWidth);
+ updateFlyoutMessage(flyoutMessage);
// Wait for TextViews to layout with updated height.
post(() -> {
fade(true /* in */, stackPos, hideDot, () -> {} /* after */);
@@ -266,7 +266,7 @@ public class BubbleFlyoutView extends FrameLayout {
.withEndAction(afterFade);
}
- private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) {
+ private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage) {
final Drawable senderAvatar = flyoutMessage.senderAvatar;
if (senderAvatar != null && flyoutMessage.isGroupChat) {
mSenderAvatar.setVisibility(VISIBLE);
@@ -278,8 +278,7 @@ public class BubbleFlyoutView extends FrameLayout {
mSenderText.setTranslationX(0);
}
- final int maxTextViewWidth =
- (int) (parentWidth * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2;
+ final int maxTextViewWidth = (int) mPositioner.getMaxFlyoutSize() - mFlyoutPadding * 2;
// Name visibility
if (!TextUtils.isEmpty(flyoutMessage.senderName)) {
@@ -328,22 +327,20 @@ public class BubbleFlyoutView extends FrameLayout {
void setupFlyoutStartingAsDot(
Bubble.FlyoutMessage flyoutMessage,
PointF stackPos,
- float parentWidth,
boolean arrowPointingLeft,
int dotColor,
@Nullable Runnable onLayoutComplete,
@Nullable Runnable onHide,
float[] dotCenter,
- boolean hideDot,
- BubblePositioner positioner) {
+ boolean hideDot) {
- mBubbleSize = positioner.getBubbleSize();
+ mBubbleSize = mPositioner.getBubbleSize();
mOriginalDotSize = SIZE_PERCENTAGE * mBubbleSize;
mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
mNewDotSize = mNewDotRadius * 2f;
- updateFlyoutMessage(flyoutMessage, parentWidth);
+ updateFlyoutMessage(flyoutMessage);
mArrowPointingLeft = arrowPointingLeft;
mDotColor = dotColor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index ede42285d9cd..5e9d97f23c57 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -142,7 +142,7 @@ public class BubbleOverflowContainerView extends LinearLayout {
super.onAttachedToWindow();
if (mController != null) {
// For the overflow to get key events (e.g. back press) we need to adjust the flags
- mController.updateWindowFlagsForOverflow(true);
+ mController.updateWindowFlagsForBackpress(true);
}
setOnKeyListener(mKeyListener);
}
@@ -151,7 +151,7 @@ public class BubbleOverflowContainerView extends LinearLayout {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mController != null) {
- mController.updateWindowFlagsForOverflow(false);
+ mController.updateWindowFlagsForBackpress(false);
}
setOnKeyListener(null);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c600f56ba0c5..306224bd316c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -34,6 +34,7 @@ import android.view.WindowMetrics;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import java.lang.annotation.Retention;
@@ -58,23 +59,40 @@ public class BubblePositioner {
/** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
public static final int NUM_VISIBLE_WHEN_RESTING = 2;
+ /** Indicates a bubble's height should be the maximum available space. **/
+ public static final int MAX_HEIGHT = -1;
+ /** The max percent of screen width to use for the flyout on large screens. */
+ public static final float FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN = 0.3f;
+ /** The max percent of screen width to use for the flyout on phone. */
+ public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f;
+ /** The percent of screen width that should be used for the expanded view on a large screen. **/
+ public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f;
private Context mContext;
private WindowManager mWindowManager;
private Rect mPositionRect;
+ private Rect mScreenRect;
private @Surface.Rotation int mRotation = Surface.ROTATION_0;
private Insets mInsets;
private int mDefaultMaxBubbles;
private int mMaxBubbles;
private int mBubbleSize;
- private int mBubbleBadgeSize;
private int mSpacingBetweenBubbles;
+
+ private int mExpandedViewMinHeight;
private int mExpandedViewLargeScreenWidth;
+ private int mExpandedViewLargeScreenInset;
+
+ private int mOverflowWidth;
private int mExpandedViewPadding;
private int mPointerMargin;
- private float mPointerWidth;
- private float mPointerHeight;
+ private int mPointerWidth;
+ private int mPointerHeight;
+ private int mPointerOverlap;
+ private int mManageButtonHeight;
+ private int mOverflowHeight;
+ private int mMinimumFlyoutWidthLargeScreen;
private PointF mPinLocation;
private PointF mRestingStackPosition;
@@ -143,6 +161,7 @@ public class BubblePositioner {
mRotation = rotation;
mInsets = insets;
+ mScreenRect = new Rect(bounds);
mPositionRect = new Rect(bounds);
mPositionRect.left += mInsets.left;
mPositionRect.top += mInsets.top;
@@ -151,16 +170,27 @@ public class BubblePositioner {
Resources res = mContext.getResources();
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
- mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size);
mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
-
- mExpandedViewLargeScreenWidth = res.getDimensionPixelSize(
- R.dimen.bubble_expanded_view_tablet_width);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
+ mExpandedViewLargeScreenWidth = (int) (bounds.width()
+ * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT);
+ mExpandedViewLargeScreenInset = mIsLargeScreen
+ ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2
+ : mExpandedViewPadding;
+ mOverflowWidth = mIsLargeScreen
+ ? mExpandedViewLargeScreenWidth
+ : res.getDimensionPixelSize(
+ R.dimen.bubble_expanded_view_phone_landscape_overflow_width);
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
+ mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
+ mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height);
+ mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
+ mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
+ mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize(
+ R.dimen.bubbles_flyout_min_width_large_screen);
mMaxBubbles = calculateMaxBubbles();
@@ -225,6 +255,13 @@ public class BubblePositioner {
}
/**
+ * @return a rect of the screen size.
+ */
+ public Rect getScreenRect() {
+ return mScreenRect;
+ }
+
+ /**
* @return the relevant insets (status bar, nav bar, cutouts). If taskbar is showing, its
* inset is not included here.
*/
@@ -266,46 +303,226 @@ public class BubblePositioner {
}
/**
- * Calculates the left & right padding for the bubble expanded view.
+ * Calculates the padding for the bubble expanded view.
*
- * On larger screens the width of the expanded view is restricted via this padding.
- * On landscape the bubble overflow expanded view is also restricted via this padding.
+ * Some specifics:
+ * On large screens the width of the expanded view is restricted via this padding.
+ * On phone landscape the bubble overflow expanded view is also restricted via this padding.
+ * On large screens & landscape no top padding is set, the top position is set via translation.
+ * On phone portrait top padding is set as the space between the tip of the pointer and the
+ * bubble.
+ * When the overflow is shown it doesn't have the manage button to pad out the bottom so
+ * padding is added.
*/
- public int[] getExpandedViewPadding(boolean onLeft, boolean isOverflow) {
- int leftPadding = mInsets.left + mExpandedViewPadding;
- int rightPadding = mInsets.right + mExpandedViewPadding;
- final boolean isLargeOrOverflow = mIsLargeScreen || isOverflow;
- if (showBubblesVertically()) {
- if (!onLeft) {
- rightPadding += mBubbleSize - mPointerHeight;
- leftPadding += isLargeOrOverflow
- ? (mPositionRect.width() - rightPadding - mExpandedViewLargeScreenWidth)
- : 0;
- } else {
- leftPadding += mBubbleSize - mPointerHeight;
- rightPadding += isLargeOrOverflow
- ? (mPositionRect.width() - leftPadding - mExpandedViewLargeScreenWidth)
- : 0;
+ public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) {
+ final int pointerTotalHeight = mPointerHeight - mPointerOverlap;
+ if (mIsLargeScreen) {
+ // [left, top, right, bottom]
+ mPaddings[0] = onLeft
+ ? mExpandedViewLargeScreenInset - pointerTotalHeight
+ : mExpandedViewLargeScreenInset;
+ mPaddings[1] = 0;
+ mPaddings[2] = onLeft
+ ? mExpandedViewLargeScreenInset
+ : mExpandedViewLargeScreenInset - pointerTotalHeight;
+ // Overflow doesn't show manage button / get padding from it so add padding here for it
+ mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
+ return mPaddings;
+ } else {
+ int leftPadding = mInsets.left + mExpandedViewPadding;
+ int rightPadding = mInsets.right + mExpandedViewPadding;
+ final float expandedViewWidth = isOverflow
+ ? mOverflowWidth
+ : mExpandedViewLargeScreenWidth;
+ if (showBubblesVertically()) {
+ if (!onLeft) {
+ rightPadding += mBubbleSize - pointerTotalHeight;
+ leftPadding += isOverflow
+ ? (mPositionRect.width() - rightPadding - expandedViewWidth)
+ : 0;
+ } else {
+ leftPadding += mBubbleSize - pointerTotalHeight;
+ rightPadding += isOverflow
+ ? (mPositionRect.width() - leftPadding - expandedViewWidth)
+ : 0;
+ }
}
+ // [left, top, right, bottom]
+ mPaddings[0] = leftPadding;
+ mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
+ mPaddings[2] = rightPadding;
+ mPaddings[3] = 0;
+ return mPaddings;
}
- // [left, top, right, bottom]
- mPaddings[0] = leftPadding;
- mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
- mPaddings[2] = rightPadding;
- mPaddings[3] = 0;
- return mPaddings;
}
- /** Calculates the y position of the expanded view when it is expanded. */
- public float getExpandedViewY() {
+ /** Gets the y position of the expanded view if it was top-aligned. */
+ private float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
- return top - mPointerWidth;
+ return top - mPointerWidth + mExpandedViewPadding;
} else {
return top + mBubbleSize + mPointerMargin;
}
}
+ public float getExpandedBubblesY() {
+ return getAvailableRect().top + mExpandedViewPadding;
+ }
+
+ /**
+ * Calculate the maximum height the expanded view can be depending on where it's placed on
+ * the screen and the size of the elements around it (e.g. padding, pointer, manage button).
+ */
+ public int getMaxExpandedViewHeight(boolean isOverflow) {
+ // Subtract top insets because availableRect.height would account for that
+ int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+ int paddingTop = showBubblesVertically()
+ ? 0
+ : mPointerHeight;
+ // Subtract pointer size because it's laid out in LinearLayout with the expanded view.
+ int pointerSize = showBubblesVertically()
+ ? mPointerWidth
+ : (mPointerHeight + mPointerMargin);
+ int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
+ return getAvailableRect().height()
+ - expandedContainerY
+ - paddingTop
+ - pointerSize
+ - bottomPadding;
+ }
+
+ /**
+ * Determines the height for the bubble, ensuring a minimum height. If the height should be as
+ * big as available, returns {@link #MAX_HEIGHT}.
+ */
+ public float getExpandedViewHeight(BubbleViewProvider bubble) {
+ boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+ if (isOverflow && showBubblesVertically() && !mIsLargeScreen) {
+ // overflow in landscape on phone is max
+ return MAX_HEIGHT;
+ }
+ float desiredHeight = isOverflow
+ ? mOverflowHeight
+ : ((Bubble) bubble).getDesiredHeight(mContext);
+ desiredHeight = Math.max(desiredHeight, mExpandedViewMinHeight);
+ if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) {
+ return MAX_HEIGHT;
+ }
+ return desiredHeight;
+ }
+
+ /**
+ * Gets the y position for the expanded view. This is the position on screen of the top
+ * horizontal line of the expanded view.
+ *
+ * @param bubble the bubble being positioned.
+ * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+ * bubble if showing vertically.
+ * @return the y position for the expanded view.
+ */
+ public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
+ boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+ float expandedViewHeight = getExpandedViewHeight(bubble);
+ float topAlignment = getExpandedViewYTopAligned();
+ if (!showBubblesVertically() || expandedViewHeight == MAX_HEIGHT) {
+ // Top-align when bubbles are shown at the top or are max size.
+ return topAlignment;
+ }
+ // If we're here, we're showing vertically & developer has made height less than maximum.
+ int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
+ float pointerPosition = getPointerPosition(bubblePosition);
+ float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
+ float topIfCentered = pointerPosition - (expandedViewHeight / 2);
+ if (topIfCentered > mPositionRect.top && mPositionRect.bottom > bottomIfCentered) {
+ // Center it
+ return pointerPosition - mPointerWidth - (expandedViewHeight / 2f);
+ } else if (topIfCentered <= mPositionRect.top) {
+ // Top align
+ return topAlignment;
+ } else {
+ // Bottom align
+ return mPositionRect.bottom - manageButtonHeight - expandedViewHeight - mPointerWidth;
+ }
+ }
+
+ /**
+ * The position the pointer points to, the center of the bubble.
+ *
+ * @param bubblePosition the x position of the bubble if showing on top, the y position of the
+ * bubble if showing vertically.
+ * @return the position the tip of the pointer points to. The x position if showing on top, the
+ * y position if showing vertically.
+ */
+ public float getPointerPosition(float bubblePosition) {
+ // TODO: I don't understand why it works but it does - why normalized in portrait
+ // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
+ final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
+ getBubbleSize());
+ return showBubblesVertically()
+ ? bubblePosition + (getBubbleSize() / 2f)
+ : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
+ }
+
+ /**
+ * Returns the position of the bubble on-screen when the stack is expanded.
+ *
+ * @param index the index of the bubble in the stack.
+ * @param numberOfBubbles the total number of bubbles in the stack.
+ * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed.
+ * @return the x, y position of the bubble on-screen when the stack is expanded.
+ */
+ public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) {
+ final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
+ final float expandedStackSize = (numberOfBubbles * mBubbleSize)
+ + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ final float centerPosition = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ // alignment - centered on the edge
+ final float rowStart = centerPosition - (expandedStackSize / 2f);
+ float x;
+ float y;
+ if (showBubblesVertically()) {
+ y = rowStart + positionInRow;
+ int left = mIsLargeScreen
+ ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize
+ : mPositionRect.left;
+ int right = mIsLargeScreen
+ ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
+ : mPositionRect.right - mBubbleSize;
+ x = onLeftEdge
+ ? left
+ : right;
+ } else {
+ y = mPositionRect.top + mExpandedViewPadding;
+ x = rowStart + positionInRow;
+ }
+ return new PointF(x, y);
+ }
+
+ /**
+ * @return the width of the bubble flyout (message originating from the bubble).
+ */
+ public float getMaxFlyoutSize() {
+ if (isLargeScreen()) {
+ return Math.max(mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN,
+ mMinimumFlyoutWidthLargeScreen);
+ }
+ return mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT;
+ }
+
+ /**
+ * @return whether the stack is considered on the left side of the screen.
+ */
+ public boolean isStackOnLeft(PointF currentStackPosition) {
+ if (currentStackPosition == null) {
+ currentStackPosition = getRestingPosition();
+ }
+ final int stackCenter = (int) currentStackPosition.x + mBubbleSize / 2;
+ return stackCenter < mScreenRect.width() / 2;
+ }
+
/**
* Sets the stack's most recent position along the edge of the screen. This is saved when the
* last bubble is removed, so that the stack can be restored in its previous position.
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 c0df06f2954f..5a51eed04e1a 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
@@ -19,6 +19,8 @@ package com.android.wm.shell.bubbles;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -33,11 +35,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -106,14 +108,8 @@ public class BubbleStackView extends FrameLayout
*/
private static final float FLYOUT_OVERSCROLL_ATTENUATION_FACTOR = 8f;
- /** Duration of the flyout alpha animations. */
- private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
-
private static final int FADE_IN_DURATION = 320;
- /** Percent to darken the bubbles when they're in the dismiss target. */
- private static final float DARKEN_PERCENT = 0.3f;
-
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -122,6 +118,10 @@ public class BubbleStackView extends FrameLayout
private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
+ private static final int MANAGE_MENU_SCRIM_ANIM_DURATION = 150;
+
+ private static final float SCRIM_ALPHA = 0.6f;
+
/**
* How long to wait to animate the stack temporarily invisible after a drag/flyout hide
* animation ends, if we are in fact temporarily invisible.
@@ -195,7 +195,8 @@ public class BubbleStackView extends FrameLayout
private StackAnimationController mStackAnimationController;
private ExpandedAnimationController mExpandedAnimationController;
- private View mTaskbarScrim;
+ private View mScrim;
+ private View mManageMenuScrim;
private FrameLayout mExpandedViewContainer;
/** Matrix used to scale the expanded view container with a given pivot point. */
@@ -555,7 +556,7 @@ public class BubbleStackView extends FrameLayout
if (mBubbleData.isExpanded()) {
if (mManageEduView != null) {
- mManageEduView.hide(false /* show */);
+ mManageEduView.hide();
}
// If we're expanded, tell the animation controller to prepare to drag this bubble,
@@ -576,20 +577,17 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.setActiveController(mStackAnimationController);
hideFlyoutImmediate();
- if (!mPositioner.showingInTaskbar()) {
- // Also, save the magnetized stack so we can dispatch touch events to it.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(
- mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
- } else {
+ if (mPositioner.showingInTaskbar()) {
// In taskbar, the stack isn't draggable so we shouldn't dispatch touch events.
mMagnetizedObject = null;
+ } else {
+ // Save the magnetized stack so we can dispatch touch events to it.
+ mMagnetizedObject = mStackAnimationController.getMagnetizedStack();
+ mMagnetizedObject.clearAllTargets();
+ mMagnetizedObject.addTarget(mMagneticTarget);
+ mMagnetizedObject.setMagnetListener(mStackMagnetListener);
}
- // Also, save the magnetized stack so we can dispatch touch events to it.
- mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget);
- mMagnetizedObject.setMagnetListener(mStackMagnetListener);
-
mIsDraggingStack = true;
// Cancel animations to make the stack temporarily invisible, since we're now
@@ -780,8 +778,8 @@ public class BubbleStackView extends FrameLayout
floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut,
this::animateShadows /* onStackAnimationFinished */, mPositioner);
- mExpandedAnimationController = new ExpandedAnimationController(
- mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
+ mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
+ onBubbleAnimatedOut);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -796,8 +794,6 @@ public class BubbleStackView extends FrameLayout
mBubbleContainer.setClipChildren(false);
addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- updateUserEdu();
-
mExpandedViewContainer = new FrameLayout(context);
mExpandedViewContainer.setElevation(elevation);
mExpandedViewContainer.setClipChildren(false);
@@ -861,11 +857,20 @@ public class BubbleStackView extends FrameLayout
mBubbleData.setExpanded(true);
});
- mTaskbarScrim = new View(getContext());
- mTaskbarScrim.setBackgroundColor(Color.BLACK);
- addView(mTaskbarScrim);
- mTaskbarScrim.setAlpha(0f);
- mTaskbarScrim.setVisibility(GONE);
+ mScrim = new View(getContext());
+ mScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mScrim.setBackgroundDrawable(new ColorDrawable(
+ getResources().getColor(android.R.color.system_neutral1_1000)));
+ addView(mScrim);
+ mScrim.setAlpha(0f);
+
+ mManageMenuScrim = new View(getContext());
+ mManageMenuScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
+ getResources().getColor(android.R.color.system_neutral1_1000)));
+ addView(mManageMenuScrim, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ mManageMenuScrim.setAlpha(0f);
+ mManageMenuScrim.setVisibility(INVISIBLE);
mOrientationChangedListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -881,7 +886,6 @@ public class BubbleStackView extends FrameLayout
mRelativeStackPositionBeforeRotation = null;
}
- setUpDismissView();
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
@@ -890,8 +894,10 @@ public class BubbleStackView extends FrameLayout
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
} /* after */);
+ final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+ getBubbleIndex(mExpandedBubble));
mExpandedViewContainer.setTranslationX(0f);
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+ mExpandedViewContainer.setTranslationY(translationY);
mExpandedViewContainer.setAlpha(1f);
}
removeOnLayoutChangeListener(mOrientationChangedListener);
@@ -921,8 +927,10 @@ public class BubbleStackView extends FrameLayout
setOnClickListener(view -> {
if (mShowingManage) {
showManageMenu(false /* show */);
+ } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+ mManageEduView.hide();
} else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
- mStackEduView.hide(false);
+ mStackEduView.hide(false /* isExpanding */);
} else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
}
@@ -1043,10 +1051,9 @@ public class BubbleStackView extends FrameLayout
contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
// Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects.
+ // MagnetizedObjects when the dismiss view gets shown.
mMagneticTarget = new MagnetizedObject.MagneticTarget(
mDismissView.getCircle(), dismissRadius);
-
mBubbleContainer.bringToFront();
}
@@ -1122,10 +1129,10 @@ public class BubbleStackView extends FrameLayout
return;
}
if (mManageEduView == null) {
- mManageEduView = new ManageEducationView(mContext);
+ mManageEduView = new ManageEducationView(mContext, mPositioner);
addView(mManageEduView);
}
- mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect);
+ mManageEduView.show(mExpandedBubble.getExpandedView());
}
/**
@@ -1153,21 +1160,27 @@ public class BubbleStackView extends FrameLayout
return false;
}
if (mStackEduView == null) {
- mStackEduView = new StackEducationView(mContext);
+ mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
addView(mStackEduView);
}
mBubbleContainer.bringToFront();
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
+ // Recreates & shows the education views. Call when a theme/config change happens.
private void updateUserEdu() {
- maybeShowStackEdu();
- if (mManageEduView != null) {
- mManageEduView.invalidate();
+ if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ removeView(mStackEduView);
+ mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
+ addView(mStackEduView);
+ mBubbleContainer.bringToFront(); // Stack appears on top of the stack education
+ mStackEduView.show(mPositioner.getDefaultStartPosition());
}
- maybeShowManageEdu();
- if (mStackEduView != null) {
- mStackEduView.invalidate();
+ if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+ removeView(mManageEduView);
+ mManageEduView = new ManageEducationView(mContext, mPositioner);
+ addView(mManageEduView);
+ mManageEduView.show(mExpandedBubble.getExpandedView());
}
}
@@ -1176,7 +1189,7 @@ public class BubbleStackView extends FrameLayout
if (mFlyout != null) {
removeView(mFlyout);
}
- mFlyout = new BubbleFlyoutView(getContext());
+ mFlyout = new BubbleFlyoutView(getContext(), mPositioner);
mFlyout.setVisibility(GONE);
mFlyout.setOnClickListener(mFlyoutClickListener);
mFlyout.setOnTouchListener(mFlyoutTouchListener);
@@ -1223,6 +1236,10 @@ public class BubbleStackView extends FrameLayout
updateOverflow();
updateUserEdu();
updateExpandedViewTheme();
+ mScrim.setBackgroundDrawable(new ColorDrawable(
+ getResources().getColor(android.R.color.system_neutral1_1000)));
+ mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
+ getResources().getColor(android.R.color.system_neutral1_1000)));
}
/**
@@ -1260,6 +1277,7 @@ public class BubbleStackView extends FrameLayout
setUpManageMenu();
setUpFlyout();
setUpDismissView();
+ updateUserEdu();
mBubbleSize = mPositioner.getBubbleSize();
for (Bubble b : mBubbleData.getBubbles()) {
if (b.getIconView() == null) {
@@ -1540,6 +1558,7 @@ public class BubbleStackView extends FrameLayout
bubble.cleanupViews();
}
updatePointerPosition();
+ updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
}
@@ -1715,6 +1734,21 @@ public class BubbleStackView extends FrameLayout
notifyExpansionChanged(mExpandedBubble, mIsExpanded);
}
+ /**
+ * Called when back press occurs while bubbles are expanded.
+ */
+ public void onBackPressed() {
+ if (mIsExpanded) {
+ if (mShowingManage) {
+ showManageMenu(false);
+ } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+ mManageEduView.hide();
+ } else {
+ setExpanded(false);
+ }
+ }
+ }
+
void setBubbleVisibility(Bubble b, boolean visible) {
if (b.getIconView() != null) {
b.getIconView().setVisibility(visible ? VISIBLE : GONE);
@@ -1801,6 +1835,20 @@ public class BubbleStackView extends FrameLayout
mExpandedViewAlphaAnimator.start();
}
+ private void showScrim(boolean show) {
+ if (show) {
+ mScrim.animate()
+ .setInterpolator(ALPHA_IN)
+ .alpha(SCRIM_ALPHA)
+ .start();
+ } else {
+ mScrim.animate()
+ .alpha(0f)
+ .setInterpolator(ALPHA_OUT)
+ .start();
+ }
+ }
+
private void animateExpansion() {
cancelDelayedExpandCollapseSwitchAnimations();
final boolean showVertically = mPositioner.showBubblesVertically();
@@ -1810,6 +1858,7 @@ public class BubbleStackView extends FrameLayout
}
beforeExpandedViewAnimation();
+ showScrim(true);
updateZOrder();
updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -1820,37 +1869,28 @@ public class BubbleStackView extends FrameLayout
maybeShowManageEdu();
}
} /* after */);
-
- if (mPositioner.showingInTaskbar()
- // Don't need the scrim when the bar is at the bottom
- && mPositioner.getTaskbarPosition() != BubblePositioner.TASKBAR_POSITION_BOTTOM) {
- mTaskbarScrim.getLayoutParams().width = mPositioner.getTaskbarSize();
- mTaskbarScrim.setTranslationX(mStackOnLeftOrWillBe
- ? 0f
- : mPositioner.getAvailableRect().right - mPositioner.getTaskbarSize());
- mTaskbarScrim.setVisibility(VISIBLE);
- mTaskbarScrim.animate().alpha(1f).start();
- }
-
- mExpandedViewContainer.setTranslationX(0f);
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
- mExpandedViewContainer.setAlpha(1f);
-
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
index = mBubbleData.getBubbles().size();
} else {
index = getBubbleIndex(mExpandedBubble);
}
- // Position of the bubble we're expanding, once it's settled in its row.
- final float bubbleWillBeAt =
- mExpandedAnimationController.getBubbleXOrYForOrientation(index);
+ PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(),
+ mStackOnLeftOrWillBe);
+ final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+ mPositioner.showBubblesVertically() ? p.y : p.x);
+ mExpandedViewContainer.setTranslationX(0f);
+ mExpandedViewContainer.setTranslationY(translationY);
+ mExpandedViewContainer.setAlpha(1f);
// How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
// that are animating farther, so that the expanded view doesn't move as much.
final float relevantStackPosition = showVertically
? mStackAnimationController.getStackPosition().y
: mStackAnimationController.getStackPosition().x;
+ final float bubbleWillBeAt = showVertically
+ ? p.y
+ : p.x;
final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
// Wait for the path animation target to reach its end, and add a small amount of extra time
@@ -1867,22 +1907,22 @@ public class BubbleStackView extends FrameLayout
// Set the pivot point for the scale, so the expanded view animates out from the bubble.
if (showVertically) {
float pivotX;
- float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) {
- pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+ pivotX = p.x + mBubbleSize + mExpandedViewPadding;
} else {
- pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+ pivotX = p.x - mExpandedViewPadding;
}
mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- pivotX, pivotY);
+ pivotX,
+ p.y + mBubbleSize / 2f);
} else {
mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- bubbleWillBeAt + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ p.x + mBubbleSize / 2f,
+ p.y + mBubbleSize + mExpandedViewPadding);
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -1919,6 +1959,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
+ mExpandedViewContainer.setAnimationMatrix(null);
afterExpandedViewAnimation();
if (mExpandedBubble != null
&& mExpandedBubble.getExpandedView() != null) {
@@ -1934,12 +1975,17 @@ public class BubbleStackView extends FrameLayout
private void animateCollapse() {
cancelDelayedExpandCollapseSwitchAnimations();
+ if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+ mManageEduView.hide();
+ }
// Hide the menu if it's visible.
showManageMenu(false);
mIsExpanded = false;
mIsExpansionAnimating = true;
+ showScrim(false);
+
mBubbleContainer.cancelAllAnimations();
// If we were in the middle of swapping, the animating-out surface would have been scaling
@@ -1957,10 +2003,6 @@ public class BubbleStackView extends FrameLayout
/* collapseTo */,
() -> mBubbleContainer.setActiveController(mStackAnimationController));
- if (mTaskbarScrim.getVisibility() == VISIBLE) {
- mTaskbarScrim.animate().alpha(0f).start();
- }
-
int index;
if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
index = mBubbleData.getBubbles().size();
@@ -1968,12 +2010,11 @@ public class BubbleStackView extends FrameLayout
index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
}
// Value the bubble is animating from (back into the stack).
- final float expandingFromBubbleAt =
- mExpandedAnimationController.getBubbleXOrYForOrientation(index);
- final boolean showVertically = mPositioner.showBubblesVertically();
+ final PointF p = mPositioner.getExpandedBubbleXY(index,
+ mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
if (mPositioner.showBubblesVertically()) {
float pivotX;
- float pivotY = expandingFromBubbleAt + mBubbleSize / 2f;
+ float pivotY = p.y + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) {
pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
} else {
@@ -1985,8 +2026,8 @@ public class BubbleStackView extends FrameLayout
} else {
mExpandedViewContainerMatrix.setScale(
1f, 1f,
- expandingFromBubbleAt + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ p.x + mBubbleSize / 2f,
+ p.y + mBubbleSize + mExpandedViewPadding);
}
mExpandedViewAlphaAnimator.reverse();
@@ -2013,7 +2054,7 @@ public class BubbleStackView extends FrameLayout
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
if (mManageEduView != null) {
- mManageEduView.hide(false /* fromExpansion */);
+ mManageEduView.hide();
}
if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -2028,10 +2069,6 @@ public class BubbleStackView extends FrameLayout
if (previouslySelected != null) {
previouslySelected.setTaskViewVisibility(false);
}
-
- if (mPositioner.showingInTaskbar()) {
- mTaskbarScrim.setVisibility(GONE);
- }
})
.start();
}
@@ -2068,32 +2105,31 @@ public class BubbleStackView extends FrameLayout
boolean isOverflow = mExpandedBubble != null
&& mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
- float expandingFromBubbleDestination =
- mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow
- ? getBubbleCount()
- : mBubbleData.getBubbles().indexOf(mExpandedBubble));
-
+ PointF p = mPositioner.getExpandedBubbleXY(isOverflow
+ ? mBubbleContainer.getChildCount() - 1
+ : mBubbleData.getBubbles().indexOf(mExpandedBubble),
+ mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
mExpandedViewContainer.setAlpha(1f);
mExpandedViewContainer.setVisibility(View.VISIBLE);
if (mPositioner.showBubblesVertically()) {
float pivotX;
- float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f;
+ float pivotY = p.y + mBubbleSize / 2f;
if (mStackOnLeftOrWillBe) {
- pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+ pivotX = p.x + mBubbleSize + mExpandedViewPadding;
} else {
- pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
-
+ pivotX = p.x - mExpandedViewPadding;
}
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
- expandingFromBubbleDestination + mBubbleSize / 2f,
- mPositioner.getExpandedViewY());
+ p.x + mBubbleSize / 2f,
+ p.y + mBubbleSize + mExpandedViewPadding);
}
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -2118,6 +2154,7 @@ public class BubbleStackView extends FrameLayout
.withEndActions(() -> {
mExpandedViewTemporarilyHidden = false;
mIsBubbleSwitchAnimating = false;
+ mExpandedViewContainer.setAnimationMatrix(null);
})
.start();
}, 25);
@@ -2408,20 +2445,19 @@ public class BubbleStackView extends FrameLayout
if (mFlyout.getVisibility() == View.VISIBLE) {
- mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
+ mFlyout.animateUpdate(bubble.getFlyoutMessage(),
mStackAnimationController.getStackPosition(), !bubble.showDot(),
mAfterFlyoutHidden /* onHide */);
} else {
mFlyout.setVisibility(INVISIBLE);
mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
- mStackAnimationController.getStackPosition(), getWidth(),
+ mStackAnimationController.getStackPosition(),
mStackAnimationController.isStackOnLeftSide(),
bubble.getIconView().getDotColor() /* dotColor */,
expandFlyoutAfterDelay /* onLayoutComplete */,
mAfterFlyoutHidden /* onHide */,
bubble.getIconView().getDotCenter(),
- !bubble.showDot(),
- mPositioner);
+ !bubble.showDot());
}
mFlyout.bringToFront();
});
@@ -2506,6 +2542,24 @@ public class BubbleStackView extends FrameLayout
return;
}
+ if (show) {
+ mManageMenuScrim.setVisibility(VISIBLE);
+ mManageMenuScrim.setTranslationZ(mManageMenu.getElevation() - 1f);
+ }
+ Runnable endAction = () -> {
+ if (!show) {
+ mManageMenuScrim.setVisibility(INVISIBLE);
+ mManageMenuScrim.setTranslationZ(0f);
+ }
+ };
+
+ mManageMenuScrim.animate()
+ .setDuration(MANAGE_MENU_SCRIM_ANIM_DURATION)
+ .setInterpolator(show ? ALPHA_IN : ALPHA_OUT)
+ .alpha(show ? SCRIM_ALPHA : 0f)
+ .withEndAction(endAction)
+ .start();
+
// If available, update the manage menu's settings option with the expanded bubble's app
// name and icon.
if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
@@ -2515,7 +2569,6 @@ public class BubbleStackView extends FrameLayout
R.string.bubbles_app_settings, bubble.getAppName()));
}
- mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
if (mExpandedBubble.getExpandedView().getTaskView() != null) {
mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
? new Rect(0, 0, getWidth(), getHeight())
@@ -2527,7 +2580,11 @@ public class BubbleStackView extends FrameLayout
// When the menu is open, it should be at these coordinates. The menu pops out to the right
// in LTR and to the left in RTL.
- final float targetX = isLtr ? mTempRect.left : mTempRect.right - mManageMenu.getWidth();
+ mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+ final float margin = mExpandedBubble.getExpandedView().getManageButtonMargin();
+ final float targetX = isLtr
+ ? mTempRect.left - margin
+ : mTempRect.right + margin - mManageMenu.getWidth();
final float targetY = mTempRect.bottom - mManageMenu.getHeight();
final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
@@ -2707,14 +2764,17 @@ public class BubbleStackView extends FrameLayout
}
boolean isOverflowExpanded = mExpandedBubble != null
&& BubbleOverflow.KEY.equals(mExpandedBubble.getKey());
- int[] paddings = mPositioner.getExpandedViewPadding(
+ int[] paddings = mPositioner.getExpandedViewContainerPadding(
mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded);
mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]);
if (mIsExpansionAnimating) {
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
}
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY());
+ PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
+ mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
+ mPositioner.showBubblesVertically() ? p.y : p.x));
mExpandedViewContainer.setTranslationX(0f);
mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen());
@@ -2797,8 +2857,13 @@ public class BubbleStackView extends FrameLayout
if (index == -1) {
return;
}
- float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index);
- mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, mStackOnLeftOrWillBe);
+ PointF bubblePosition = mPositioner.getExpandedBubbleXY(index,
+ mBubbleContainer.getChildCount(),
+ mStackOnLeftOrWillBe);
+ mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically()
+ ? bubblePosition.y
+ : bubblePosition.x,
+ mStackOnLeftOrWillBe);
}
/**
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 c73b5eebc5c2..9b7eb2f1cfb3 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
@@ -24,12 +24,10 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.os.Looper;
import android.service.notification.NotificationListenerService.RankingMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
-import android.view.View;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
@@ -43,7 +41,6 @@ import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
@@ -160,14 +157,6 @@ public interface Bubbles {
/** Set the proxy to commnuicate with SysUi side components. */
void setSysuiProxy(SysuiProxy proxy);
- /**
- * Set the scrim view for bubbles.
- *
- * @param callback The callback made with the executor and the executor's looper that the view
- * will be running on.
- **/
- void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback);
-
/** Set a listener to be notified of bubble expand events. */
void setExpandListener(BubbleExpandListener listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 4cc67025fff4..eb4737ac6c63 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -18,12 +18,13 @@ package com.android.wm.shell.bubbles
import android.content.Context
import android.graphics.Color
import android.graphics.Rect
+import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.internal.util.ContrastColorUtil
+import com.android.internal.R.color.system_neutral1_900
import com.android.wm.shell.R
import com.android.wm.shell.animation.Interpolators
@@ -31,21 +32,22 @@ import com.android.wm.shell.animation.Interpolators
* User education view to highlight the manage button that allows a user to configure the settings
* for the bubble. Shown only the first time a user expands a bubble.
*/
-class ManageEducationView constructor(context: Context) : LinearLayout(context) {
+class ManageEducationView constructor(context: Context, positioner: BubblePositioner)
+ : LinearLayout(context) {
- private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
+ private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "ManageEducationView"
else BubbleDebugConfig.TAG_BUBBLES
private val ANIMATE_DURATION: Long = 200
- private val ANIMATE_DURATION_SHORT: Long = 40
- private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
- private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+ private val positioner: BubblePositioner = positioner
+ private val manageView by lazy { findViewById<ViewGroup>(R.id.manage_education_view) }
+ private val manageButton by lazy { findViewById<Button>(R.id.manage_button) }
private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
- private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
- private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
private var isHiding = false
+ private var realManageButtonRect = Rect()
+ private var bubbleExpandedView: BubbleExpandedView? = null
init {
LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
@@ -66,18 +68,17 @@ class ManageEducationView constructor(context: Context) : LinearLayout(context)
override fun onFinishInflate() {
super.onFinishInflate()
layoutDirection = resources.configuration.layoutDirection
- setTextColor()
}
- private fun setTextColor() {
- val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
- android.R.attr.textColorPrimaryInverse))
- val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
- var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+ private fun setButtonColor() {
+ val typedArray = mContext.obtainStyledAttributes(intArrayOf(
+ com.android.internal.R.attr.colorAccentPrimary))
+ val buttonColor = typedArray.getColor(0 /* index */, Color.TRANSPARENT)
typedArray.recycle()
- textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
- titleTextView.setTextColor(textColor)
- descTextView.setTextColor(textColor)
+
+ manageButton.setTextColor(mContext.getColor(system_neutral1_900))
+ manageButton.setBackgroundDrawable(ColorDrawable(buttonColor))
+ gotItButton.setBackgroundDrawable(ColorDrawable(buttonColor))
}
private fun setDrawableDirection() {
@@ -91,30 +92,39 @@ class ManageEducationView constructor(context: Context) : LinearLayout(context)
* If necessary, toggles the user education view for the manage button. This is shown when the
* bubble stack is expanded for the first time.
*
- * @param show whether the user education view should show or not.
+ * @param expandedView the expandedView the user education is shown on top of.
*/
- fun show(expandedView: BubbleExpandedView, rect: Rect) {
+ fun show(expandedView: BubbleExpandedView) {
+ setButtonColor()
if (visibility == VISIBLE) return
+ bubbleExpandedView = expandedView
+ expandedView.taskView?.setObscuredTouchRect(Rect(positioner.screenRect))
+
+ layoutParams.width = if (positioner.isLargeScreen)
+ context.resources.getDimensionPixelSize(
+ R.dimen.bubbles_user_education_width_large_screen)
+ else ViewGroup.LayoutParams.MATCH_PARENT
+
alpha = 0f
visibility = View.VISIBLE
+ expandedView.getManageButtonBoundsOnScreen(realManageButtonRect)
+ manageView.setPadding(realManageButtonRect.left - expandedView.manageButtonMargin,
+ manageView.paddingTop, manageView.paddingRight, manageView.paddingBottom)
post {
- expandedView.getManageButtonBoundsOnScreen(rect)
-
manageButton
.setOnClickListener {
- expandedView.findViewById<View>(R.id.settings_button).performClick()
- hide(true /* isStackExpanding */)
+ hide()
+ expandedView.findViewById<View>(R.id.manage_button).performClick()
}
- gotItButton.setOnClickListener { hide(true /* isStackExpanding */) }
- setOnClickListener { hide(true /* isStackExpanding */) }
-
- with(manageView) {
- translationX = 0f
- val inset = resources.getDimensionPixelSize(
- R.dimen.bubbles_manage_education_top_inset)
- translationY = (rect.top - manageView.height + inset).toFloat()
- }
+ gotItButton.setOnClickListener { hide() }
+ setOnClickListener { hide() }
+
+ val offsetViewBounds = Rect()
+ manageButton.getDrawingRect(offsetViewBounds)
+ manageView.offsetDescendantRectToMyCoords(manageButton, offsetViewBounds)
+ translationX = 0f
+ translationY = (realManageButtonRect.top - offsetViewBounds.top).toFloat()
bringToFront()
animate()
.setDuration(ANIMATE_DURATION)
@@ -124,13 +134,14 @@ class ManageEducationView constructor(context: Context) : LinearLayout(context)
setShouldShow(false)
}
- fun hide(isStackExpanding: Boolean) {
+ fun hide() {
+ bubbleExpandedView?.taskView?.setObscuredTouchRect(null)
if (visibility != VISIBLE || isHiding) return
animate()
.withStartAction { isHiding = true }
.alpha(0f)
- .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+ .setDuration(ANIMATE_DURATION)
.withEndAction {
isHiding = false
visibility = GONE
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 0a2cfc4089ed..f6a90b7a76cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -18,8 +18,11 @@ package com.android.wm.shell.bubbles
import android.content.Context
import android.graphics.Color
import android.graphics.PointF
+import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
+import android.view.View.OnKeyListener
+import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
@@ -30,7 +33,12 @@ import com.android.wm.shell.animation.Interpolators
* User education view to highlight the collapsed stack of bubbles.
* Shown only the first time a user taps the stack.
*/
-class StackEducationView constructor(context: Context) : LinearLayout(context) {
+class StackEducationView constructor(
+ context: Context,
+ positioner: BubblePositioner,
+ controller: BubbleController
+)
+ : LinearLayout(context) {
private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
else BubbleDebugConfig.TAG_BUBBLES
@@ -38,6 +46,9 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) {
private val ANIMATE_DURATION: Long = 200
private val ANIMATE_DURATION_SHORT: Long = 40
+ private val positioner: BubblePositioner = positioner
+ private val controller: BubbleController = controller
+
private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) }
@@ -67,6 +78,28 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) {
setTextColor()
}
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ setFocusableInTouchMode(true)
+ setOnKeyListener(object : OnKeyListener {
+ override fun onKey(v: View?, keyCode: Int, event: KeyEvent): Boolean {
+ // if the event is a key down event on the enter button
+ if (event.action == KeyEvent.ACTION_UP &&
+ keyCode == KeyEvent.KEYCODE_BACK && !isHiding) {
+ hide(false)
+ return true
+ }
+ return false
+ }
+ })
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ setOnKeyListener(null)
+ controller.updateWindowFlagsForBackpress(false /* interceptBack */)
+ }
+
private fun setTextColor() {
val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
android.R.attr.textColorPrimaryInverse))
@@ -94,13 +127,25 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) {
fun show(stackPosition: PointF): Boolean {
if (visibility == VISIBLE) return false
+ controller.updateWindowFlagsForBackpress(true /* interceptBack */)
+ layoutParams.width = if (positioner.isLargeScreen)
+ context.resources.getDimensionPixelSize(
+ R.dimen.bubbles_user_education_width_large_screen)
+ else ViewGroup.LayoutParams.MATCH_PARENT
+
setAlpha(0f)
setVisibility(View.VISIBLE)
post {
+ requestFocus()
with(view) {
- val bubbleSize = context.resources.getDimensionPixelSize(
- R.dimen.bubble_size)
- translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2
+ if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+ setPadding(positioner.bubbleSize + paddingRight, paddingTop, paddingRight,
+ paddingBottom)
+ } else {
+ setPadding(paddingLeft, paddingTop, positioner.bubbleSize + paddingLeft,
+ paddingBottom)
+ }
+ translationY = stackPosition.y + positioner.bubbleSize / 2 - getHeight() / 2
}
animate()
.setDuration(ANIMATE_DURATION)
@@ -114,15 +159,16 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) {
/**
* If necessary, hides the stack education view.
*
- * @param fromExpansion if true this indicates the hide is happening due to the bubble being
+ * @param isExpanding if true this indicates the hide is happening due to the bubble being
* expanded, false if due to a touch outside of the bubble stack.
*/
- fun hide(fromExpansion: Boolean) {
+ fun hide(isExpanding: Boolean) {
if (visibility != VISIBLE || isHiding) return
+ controller.updateWindowFlagsForBackpress(false /* interceptBack */)
animate()
.alpha(0f)
- .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+ .setDuration(if (isExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
.withEndAction { visibility = GONE }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index df2b440c19df..c32be98866cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -64,9 +64,6 @@ public class ExpandedAnimationController
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
- /** What percentage of the screen to use when centering the bubbles in landscape. */
- private static final float CENTER_BUBBLES_LANDSCAPE_PERCENT = 0.66f;
-
/**
* Velocity required to dismiss an individual bubble without dragging it into the dismiss
* target.
@@ -79,16 +76,8 @@ public class ExpandedAnimationController
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
- /** Space between status bar and bubbles in the expanded state. */
- private float mBubblePaddingTop;
/** Size of each bubble. */
private float mBubbleSizePx;
- /** Max number of bubbles shown in row above expanded view. */
- private int mBubblesMaxRendered;
- /** Max amount of space to have between bubbles when expanded. */
- private int mBubblesMaxSpace;
- /** Amount of space between the bubbles when expanded. */
- private float mSpaceBetweenBubbles;
/** Whether the expand / collapse animation is running. */
private boolean mAnimatingExpand = false;
@@ -127,8 +116,6 @@ public class ExpandedAnimationController
/** The bubble currently being dragged out of the row (to potentially be dismissed). */
private MagnetizedObject<View> mMagnetizedBubbleDraggingOut;
- private int mExpandedViewPadding;
-
/**
* Callback to run whenever any bubble is animated out. The BubbleStackView will check if the
* end of this animation means we have no bubbles left, and notify the BubbleController.
@@ -137,11 +124,10 @@ public class ExpandedAnimationController
private BubblePositioner mPositioner;
- public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding,
+ public ExpandedAnimationController(BubblePositioner positioner,
Runnable onBubbleAnimatedOutAction) {
mPositioner = positioner;
updateResources();
- mExpandedViewPadding = expandedViewPadding;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
mCollapsePoint = mPositioner.getDefaultStartPosition();
}
@@ -208,11 +194,8 @@ public class ExpandedAnimationController
return;
}
Resources res = mLayout.getContext().getResources();
- mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubbleSizePx = mPositioner.getBubbleSize();
- mBubblesMaxRendered = mPositioner.getMaxBubbles();
- mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
}
/**
@@ -256,31 +239,22 @@ public class ExpandedAnimationController
final Path path = new Path();
path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
- final float expandedY = mPositioner.showBubblesVertically()
- ? getBubbleXOrYForOrientation(index)
- : getExpandedY();
+ boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
+ final PointF p = mPositioner.getExpandedBubbleXY(index,
+ mLayout.getChildCount(),
+ onLeft);
if (expanding) {
- // If we're expanding, first draw a line from the bubble's current position to the
- // top of the screen.
- path.lineTo(bubble.getTranslationX(), expandedY);
+ // If we're expanding, first draw a line from the bubble's current position to where
+ // it'll end up
+ path.lineTo(bubble.getTranslationX(), p.y);
// Then, draw a line across the screen to the bubble's resting position.
- if (mPositioner.showBubblesVertically()) {
- Rect availableRect = mPositioner.getAvailableRect();
- boolean onLeft = mCollapsePoint != null
- && mCollapsePoint.x < (availableRect.width() / 2f);
- float translationX = onLeft
- ? availableRect.left
- : availableRect.right - mBubbleSizePx;
- path.lineTo(translationX, getBubbleXOrYForOrientation(index));
- } else {
- path.lineTo(getBubbleXOrYForOrientation(index), expandedY);
- }
+ path.lineTo(p.x, p.y);
} else {
final float stackedX = mCollapsePoint.x;
// If we're collapsing, draw a line from the bubble's current position to the side
// of the screen where the bubble will be stacked.
- path.lineTo(stackedX, expandedY);
+ path.lineTo(stackedX, p.y);
// Then, draw a line down to the stack position.
path.lineTo(stackedX, mCollapsePoint.y
@@ -390,8 +364,9 @@ public class ExpandedAnimationController
bubbleView.setTranslationY(y);
}
+ final float expandedY = mPositioner.getExpandedBubblesY();
final boolean draggedOutEnough =
- y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+ y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) {
updateBubblePositions();
mBubbleDraggedOutEnough = draggedOutEnough;
@@ -435,9 +410,10 @@ public class ExpandedAnimationController
return;
}
final int index = mLayout.indexOfChild(bubbleView);
-
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(),
+ mPositioner.isStackOnLeft(mCollapsePoint));
animationForChildAtIndex(index)
- .position(getBubbleXOrYForOrientation(index), getExpandedY())
+ .position(p.x, p.y)
.withPositionStartVelocities(velX, velY)
.start(() -> bubbleView.setTranslationZ(0f) /* after */);
@@ -454,17 +430,13 @@ public class ExpandedAnimationController
}
/**
- * Animates the bubbles to {@link #getExpandedY()} position. Used in response to IME showing.
+ * Animates the bubbles to the y position. Used in response to IME showing.
*/
public void updateYPosition(Runnable after) {
if (mLayout == null) return;
animationsForChildrenFromIndex(
- 0, (i, anim) -> anim.translationY(getExpandedY())).startAll(after);
- }
-
- /** The Y value of the row of expanded bubbles. */
- public float getExpandedY() {
- return mPositioner.getAvailableRect().top + mBubblePaddingTop;
+ 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY()))
+ .startAll(after);
}
/** Description of current animation controller state. */
@@ -522,35 +494,36 @@ public class ExpandedAnimationController
startOrUpdatePathAnimation(true /* expanding */);
} else if (mAnimatingCollapse) {
startOrUpdatePathAnimation(false /* expanding */);
- } else if (mPositioner.showBubblesVertically()) {
- child.setTranslationY(getBubbleXOrYForOrientation(index));
- if (!mPreparingToCollapse) {
- // Only animate if we're not collapsing as that animation will handle placing the
- // new bubble in the stacked position.
- Rect availableRect = mPositioner.getAvailableRect();
- boolean onLeft = mCollapsePoint != null
- && mCollapsePoint.x < (availableRect.width() / 2f);
- float fromX = onLeft
- ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
- : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
- float toX = onLeft
- ? availableRect.left + mExpandedViewPadding
- : availableRect.right - mBubbleSizePx - mExpandedViewPadding;
- animationForChild(child)
- .translationX(fromX, toX)
- .start();
- updateBubblePositions();
- }
} else {
- child.setTranslationX(getBubbleXOrYForOrientation(index));
+ boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
+ final PointF p = mPositioner.getExpandedBubbleXY(index,
+ mLayout.getChildCount(),
+ onLeft);
+ if (mPositioner.showBubblesVertically()) {
+ child.setTranslationY(p.y);
+ } else {
+ child.setTranslationX(p.x);
+ }
if (!mPreparingToCollapse) {
// Only animate if we're not collapsing as that animation will handle placing the
// new bubble in the stacked position.
- float toY = getExpandedY();
- float fromY = getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
- animationForChild(child)
- .translationY(fromY, toY)
- .start();
+ if (mPositioner.showBubblesVertically()) {
+ Rect availableRect = mPositioner.getAvailableRect();
+ float fromX = onLeft
+ ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
+ : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationX(fromX, p.y)
+ .start();
+ } else {
+ // Only animate if we're not collapsing as that animation will handle placing
+ // the new bubble in the stacked position.
+ float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx
+ * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationY(fromY, p.y)
+ .start();
+ }
updateBubblePositions();
}
}
@@ -599,7 +572,7 @@ public class ExpandedAnimationController
if (mAnimatingExpand || mAnimatingCollapse) {
return;
}
-
+ boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
for (int i = 0; i < mLayout.getChildCount(); i++) {
final View bubble = mLayout.getChildAt(i);
@@ -609,49 +582,11 @@ public class ExpandedAnimationController
return;
}
- if (mPositioner.showBubblesVertically()) {
- Rect availableRect = mPositioner.getAvailableRect();
- boolean onLeft = mCollapsePoint != null
- && mCollapsePoint.x < (availableRect.width() / 2f);
- animationForChild(bubble)
- .translationX(onLeft
- ? availableRect.left
- : availableRect.right - mBubbleSizePx)
- .translationY(getBubbleXOrYForOrientation(i))
- .start();
- } else {
- animationForChild(bubble)
- .translationX(getBubbleXOrYForOrientation(i))
- .translationY(getExpandedY())
- .start();
- }
- }
- }
-
- // TODO - could move to method on bubblePositioner if mSpaceBetweenBubbles gets moved
- /**
- * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
- * row. When in landscape or on a large screen, they show at the left or right side in a
- * vertical row. This method accounts for screen orientation and will return an x or y value
- * for the position of the bubble in the row.
- *
- * @param index Bubble index in row.
- * @return the y position of the bubble if showing vertically and the x position if showing
- * horizontally.
- */
- public float getBubbleXOrYForOrientation(int index) {
- if (mLayout == null) {
- return 0;
+ final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft);
+ animationForChild(bubble)
+ .translationX(p.x)
+ .translationY(p.y)
+ .start();
}
- final float positionInBar = index * (mBubbleSizePx + mSpaceBetweenBubbles);
- Rect availableRect = mPositioner.getAvailableRect();
- final boolean isLandscape = mPositioner.showBubblesVertically();
- final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx)
- + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
- final float centerPosition = isLandscape
- ? availableRect.centerY()
- : availableRect.centerX();
- final float rowStart = centerPosition - (expandedStackSize / 2f);
- return rowStart + positionInBar;
}
}
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 0802fb59a008..9a08190675b6 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
@@ -305,10 +305,7 @@ public class StackAnimationController extends
if (mLayout == null || !isStackPositionSet()) {
return true; // Default to left, which is where it starts by default.
}
-
- float stackCenter = mStackPosition.x + mBubbleSize / 2;
- float screenCenter = mLayout.getWidth() / 2;
- return stackCenter < screenCenter;
+ return mPositioner.isStackOnLeft(mStackPosition);
}
/**
@@ -1024,11 +1021,9 @@ public class StackAnimationController extends
}
/**
- * Returns the {@link MagnetizedObject} instance for the bubble stack, with the provided
- * {@link MagnetizedObject.MagneticTarget} added as a target.
+ * Returns the {@link MagnetizedObject} instance for the bubble stack.
*/
- public MagnetizedObject<StackAnimationController> getMagnetizedStack(
- MagnetizedObject.MagneticTarget target) {
+ public MagnetizedObject<StackAnimationController> getMagnetizedStack() {
if (mMagnetizedStack == null) {
mMagnetizedStack = new MagnetizedObject<StackAnimationController>(
mLayout.getContext(),
@@ -1053,7 +1048,6 @@ public class StackAnimationController extends
loc[1] = (int) mStackPosition.y;
}
};
- mMagnetizedStack.addTarget(target);
mMagnetizedStack.setHapticsEnabled(true);
mMagnetizedStack.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 3a7b534f3c17..ffda1f92ec90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.common;
import android.os.RemoteException;
+import android.util.Slog;
import android.view.IDisplayWindowRotationCallback;
import android.view.IDisplayWindowRotationController;
import android.view.IWindowManager;
@@ -27,6 +28,7 @@ import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -35,14 +37,14 @@ import java.util.ArrayList;
* rotation.
*/
public class DisplayChangeController {
+ private static final String TAG = DisplayChangeController.class.getSimpleName();
private final ShellExecutor mMainExecutor;
private final IWindowManager mWmService;
private final IDisplayWindowRotationController mControllerImpl;
- private final ArrayList<OnDisplayChangingListener> mRotationListener =
- new ArrayList<>();
- private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
+ private final CopyOnWriteArrayList<OnDisplayChangingListener> mRotationListener =
+ new CopyOnWriteArrayList<>();
public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -59,34 +61,26 @@ public class DisplayChangeController {
* Adds a display rotation controller.
*/
public void addRotationListener(OnDisplayChangingListener listener) {
- synchronized (mRotationListener) {
- mRotationListener.add(listener);
- }
+ mRotationListener.add(listener);
}
/**
* Removes a display rotation controller.
*/
public void removeRotationListener(OnDisplayChangingListener listener) {
- synchronized (mRotationListener) {
- mRotationListener.remove(listener);
- }
+ mRotationListener.remove(listener);
}
private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
IDisplayWindowRotationCallback callback) {
WindowContainerTransaction t = new WindowContainerTransaction();
- synchronized (mRotationListener) {
- mTmpListeners.clear();
- // Make a local copy in case the handlers add/remove themselves.
- mTmpListeners.addAll(mRotationListener);
- }
- for (OnDisplayChangingListener c : mTmpListeners) {
+ for (OnDisplayChangingListener c : mRotationListener) {
c.onRotateDisplay(displayId, fromRotation, toRotation, t);
}
try {
callback.continueRotateDisplay(toRotation, t);
} catch (RemoteException e) {
+ Slog.e(TAG, "Failed to continue rotation", e);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index ba9ba5e5883a..9a3bdab9f418 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -26,6 +26,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.IDisplayWindowListener;
import android.view.IWindowManager;
+import android.view.InsetsState;
import androidx.annotation.BinderThread;
@@ -52,14 +53,6 @@ public class DisplayController {
private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
- /**
- * Gets a display by id from DisplayManager.
- */
- public Display getDisplay(int displayId) {
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- return displayManager.getDisplay(displayId);
- }
-
public DisplayController(Context context, IWindowManager wmService,
ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -67,14 +60,28 @@ public class DisplayController {
mWmService = wmService;
mChangeController = new DisplayChangeController(mWmService, mainExecutor);
mDisplayContainerListener = new DisplayWindowListenerImpl();
+ }
+
+ /**
+ * Initializes the window listener.
+ */
+ public void initialize() {
try {
mWmService.registerDisplayWindowListener(mDisplayContainerListener);
} catch (RemoteException e) {
- throw new RuntimeException("Unable to register hierarchy listener");
+ throw new RuntimeException("Unable to register display controller");
}
}
/**
+ * Gets a display by id from DisplayManager.
+ */
+ public Display getDisplay(int displayId) {
+ final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ return displayManager.getDisplay(displayId);
+ }
+
+ /**
* Gets the DisplayLayout associated with a display.
*/
public @Nullable DisplayLayout getDisplayLayout(int displayId) {
@@ -91,6 +98,16 @@ public class DisplayController {
}
/**
+ * Updates the insets for a given display.
+ */
+ public void updateDisplayInsets(int displayId, InsetsState state) {
+ final DisplayRecord r = mDisplays.get(displayId);
+ if (r != null) {
+ r.setInsets(state);
+ }
+ }
+
+ /**
* Add a display window-container listener. It will get notified whenever a display's
* configuration changes or when displays are added/removed from the WM hierarchy.
*/
@@ -134,17 +151,18 @@ public class DisplayController {
if (mDisplays.get(displayId) != null) {
return;
}
- Display display = getDisplay(displayId);
+ final Display display = getDisplay(displayId);
if (display == null) {
// It's likely that the display is private to some app and thus not
// accessible by system-ui.
return;
}
- DisplayRecord record = new DisplayRecord();
- record.mDisplayId = displayId;
- record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
+
+ final Context context = (displayId == Display.DEFAULT_DISPLAY)
+ ? mContext
: mContext.createDisplayContext(display);
- record.mDisplayLayout = new DisplayLayout(record.mContext, display);
+ final DisplayRecord record = new DisplayRecord(displayId);
+ record.setDisplayLayout(context, new DisplayLayout(context, display));
mDisplays.put(displayId, record);
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
@@ -154,24 +172,23 @@ public class DisplayController {
private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
synchronized (mDisplays) {
- DisplayRecord dr = mDisplays.get(displayId);
+ final DisplayRecord dr = mDisplays.get(displayId);
if (dr == null) {
Slog.w(TAG, "Skipping Display Configuration change on non-added"
+ " display.");
return;
}
- Display display = getDisplay(displayId);
+ final Display display = getDisplay(displayId);
if (display == null) {
Slog.w(TAG, "Skipping Display Configuration change on invalid"
+ " display. It may have been removed.");
return;
}
- Context perDisplayContext = mContext;
- if (displayId != Display.DEFAULT_DISPLAY) {
- perDisplayContext = mContext.createDisplayContext(display);
- }
- dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
- dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
+ final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY)
+ ? mContext
+ : mContext.createDisplayContext(display);
+ final Context context = perDisplayContext.createConfigurationContext(newConfig);
+ dr.setDisplayLayout(context, new DisplayLayout(context, display));
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
displayId, newConfig);
@@ -219,9 +236,25 @@ public class DisplayController {
}
private static class DisplayRecord {
- int mDisplayId;
- Context mContext;
- DisplayLayout mDisplayLayout;
+ private int mDisplayId;
+ private Context mContext;
+ private DisplayLayout mDisplayLayout;
+ private InsetsState mInsetsState = new InsetsState();
+
+ private DisplayRecord(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ private void setDisplayLayout(Context context, DisplayLayout displayLayout) {
+ mContext = context;
+ mDisplayLayout = displayLayout;
+ mDisplayLayout.setInsets(mContext.getResources(), mInsetsState);
+ }
+
+ private void setInsets(InsetsState state) {
+ mInsetsState = state;
+ mDisplayLayout.setInsets(mContext.getResources(), state);
+ }
}
@BinderThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index a7996f056785..a7052bc49699 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -33,6 +33,7 @@ import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowInsets;
@@ -68,14 +69,17 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
protected final Executor mMainExecutor;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
Executor mainExecutor, TransactionPool transactionPool) {
mWmService = wmService;
mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
mMainExecutor = mainExecutor;
mTransactionPool = transactionPool;
}
@@ -109,11 +113,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
@Override
public void onDisplayRemoved(int displayId) {
- try {
- mWmService.setDisplayWindowInsetsController(displayId, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+ PerDisplay pd = mImePerDisplay.get(displayId);
+ if (pd == null) {
+ return;
}
+ pd.unregister();
mImePerDisplay.remove(displayId);
}
@@ -195,11 +199,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
/** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
- public class PerDisplay {
+ public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
final int mDisplayId;
final InsetsState mInsetsState = new InsetsState();
- protected final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
- new DisplayWindowInsetsControllerImpl();
+ final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
InsetsSourceControl mImeSourceControl = null;
int mAnimationDirection = DIRECTION_NONE;
ValueAnimator mAnimation = null;
@@ -214,14 +217,15 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
public void register() {
- try {
- mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
- } catch (RemoteException e) {
- Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
- }
+ mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
}
- protected void insetsChanged(InsetsState insetsState) {
+ public void unregister() {
+ mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -239,8 +243,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
+ @Override
@VisibleForTesting
- protected void insetsControlChanged(InsetsState insetsState,
+ public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
insetsChanged(insetsState);
InsetsSourceControl imeSourceControl = null;
@@ -279,9 +284,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
if (!mImeShowing) {
removeImeSurface();
}
- }
- if (mImeSourceControl != null) {
- mImeSourceControl.release(SurfaceControl::release);
+ if (mImeSourceControl != null) {
+ mImeSourceControl.release(SurfaceControl::release);
+ }
}
mImeSourceControl = imeSourceControl;
}
@@ -301,7 +306,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- protected void showInsets(int types, boolean fromIme) {
+ @Override
+ public void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
@@ -309,8 +315,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
startAnimation(true /* show */, false /* forceRestart */);
}
-
- protected void hideInsets(int types, boolean fromIme) {
+ @Override
+ public void hideInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
@@ -318,6 +324,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
startAnimation(false /* show */, false /* forceRestart */);
}
+ @Override
public void topFocusedWindowChanged(String packageName) {
// Do nothing
}
@@ -327,8 +334,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*/
private void setVisibleDirectly(boolean visible) {
mInsetsState.getSource(InsetsState.ITYPE_IME).setVisible(visible);
+ mRequestedVisibilities.setVisibility(InsetsState.ITYPE_IME, visible);
try {
- mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
+ mWmService.updateDisplayWindowRequestedVisibilities(mDisplayId,
+ mRequestedVisibilities);
} catch (RemoteException e) {
}
}
@@ -489,47 +498,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
dispatchVisibilityChanged(mDisplayId, isShowing);
}
}
-
- @VisibleForTesting
- @BinderThread
- public class DisplayWindowInsetsControllerImpl
- extends IDisplayWindowInsetsController.Stub {
- @Override
- public void topFocusedWindowChanged(String packageName) throws RemoteException {
- mMainExecutor.execute(() -> {
- PerDisplay.this.topFocusedWindowChanged(packageName);
- });
- }
-
- @Override
- public void insetsChanged(InsetsState insetsState) throws RemoteException {
- mMainExecutor.execute(() -> {
- PerDisplay.this.insetsChanged(insetsState);
- });
- }
-
- @Override
- public void insetsControlChanged(InsetsState insetsState,
- InsetsSourceControl[] activeControls) throws RemoteException {
- mMainExecutor.execute(() -> {
- PerDisplay.this.insetsControlChanged(insetsState, activeControls);
- });
- }
-
- @Override
- public void showInsets(int types, boolean fromIme) throws RemoteException {
- mMainExecutor.execute(() -> {
- PerDisplay.this.showInsets(types, fromIme);
- });
- }
-
- @Override
- public void hideInsets(int types, boolean fromIme) throws RemoteException {
- mMainExecutor.execute(() -> {
- PerDisplay.this.hideInsets(types, fromIme);
- });
- }
- }
}
void removeImeSurface() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
new file mode 100644
index 000000000000..5f3de7ec35c0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.common;
+
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Manages insets from the core.
+ */
+public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener {
+ private static final String TAG = "DisplayInsetsController";
+
+ private final IWindowManager mWmService;
+ private final ShellExecutor mMainExecutor;
+ private final DisplayController mDisplayController;
+ private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>();
+ private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners =
+ new SparseArray<>();
+
+ public DisplayInsetsController(IWindowManager wmService, DisplayController displayController,
+ ShellExecutor mainExecutor) {
+ mWmService = wmService;
+ mDisplayController = displayController;
+ mMainExecutor = mainExecutor;
+ }
+
+ /**
+ * Starts listening for insets for each display.
+ **/
+ public void initialize() {
+ mDisplayController.addDisplayWindowListener(this);
+ }
+
+ /**
+ * Adds a callback to listen for insets changes for a particular display. Note that the
+ * listener will not be updated with the existing state of the insets on that display.
+ */
+ public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
+ if (listeners == null) {
+ listeners = new CopyOnWriteArrayList<>();
+ mListeners.put(displayId, listeners);
+ }
+ if (!listeners.contains(listener)) {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Removes a callback listening for insets changes from a particular display.
+ */
+ public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
+ if (listeners == null) {
+ return;
+ }
+ listeners.remove(listener);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ PerDisplay pd = new PerDisplay(displayId);
+ pd.register();
+ mInsetsPerDisplay.put(displayId, pd);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ PerDisplay pd = mInsetsPerDisplay.get(displayId);
+ if (pd == null) {
+ return;
+ }
+ pd.unregister();
+ mInsetsPerDisplay.remove(displayId);
+ }
+
+ /**
+ * An implementation of {@link IDisplayWindowInsetsController} for a given display id.
+ **/
+ public class PerDisplay {
+ private final int mDisplayId;
+ private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
+ new DisplayWindowInsetsControllerImpl();
+
+ public PerDisplay(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ public void register() {
+ try {
+ mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
+ }
+ }
+
+ public void unregister() {
+ try {
+ mWmService.setDisplayWindowInsetsController(mDisplayId, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId);
+ }
+ }
+
+ private void insetsChanged(InsetsState insetsState) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+ if (listeners == null) {
+ return;
+ }
+ mDisplayController.updateDisplayInsets(mDisplayId, insetsState);
+ for (OnInsetsChangedListener listener : listeners) {
+ listener.insetsChanged(insetsState);
+ }
+ }
+
+ private void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+ if (listeners == null) {
+ return;
+ }
+ for (OnInsetsChangedListener listener : listeners) {
+ listener.insetsControlChanged(insetsState, activeControls);
+ }
+ }
+
+ private void showInsets(int types, boolean fromIme) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+ if (listeners == null) {
+ return;
+ }
+ for (OnInsetsChangedListener listener : listeners) {
+ listener.showInsets(types, fromIme);
+ }
+ }
+
+ private void hideInsets(int types, boolean fromIme) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+ if (listeners == null) {
+ return;
+ }
+ for (OnInsetsChangedListener listener : listeners) {
+ listener.hideInsets(types, fromIme);
+ }
+ }
+
+ private void topFocusedWindowChanged(String packageName) {
+ CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+ if (listeners == null) {
+ return;
+ }
+ for (OnInsetsChangedListener listener : listeners) {
+ listener.topFocusedWindowChanged(packageName);
+ }
+ }
+
+ @BinderThread
+ private class DisplayWindowInsetsControllerImpl
+ extends IDisplayWindowInsetsController.Stub {
+ @Override
+ public void topFocusedWindowChanged(String packageName) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.topFocusedWindowChanged(packageName);
+ });
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.insetsChanged(insetsState);
+ });
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.insetsControlChanged(insetsState, activeControls);
+ });
+ }
+
+ @Override
+ public void showInsets(int types, boolean fromIme) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.showInsets(types, fromIme);
+ });
+ }
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ mMainExecutor.execute(() -> {
+ PerDisplay.this.hideInsets(types, fromIme);
+ });
+ }
+ }
+ }
+
+ /**
+ * Gets notified whenever the insets change.
+ *
+ * @see IDisplayWindowInsetsController
+ */
+ @ShellMainThread
+ public interface OnInsetsChangedListener {
+ /**
+ * Called when top focused window changes to determine whether or not to take over insets
+ * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+ * @param packageName: Passes the top package name
+ */
+ void topFocusedWindowChanged(String packageName);
+
+ /**
+ * Called when the window insets configuration has changed.
+ */
+ void insetsChanged(InsetsState insetsState);
+
+ /**
+ * Called when this window retrieved control over a specified set of insets sources.
+ */
+ void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls);
+
+ /**
+ * Called when a set of insets source window should be shown by policy.
+ *
+ * @param types internal insets types (WindowInsets.Type.InsetsType) to show
+ * @param fromIme true if this request originated from IME (InputMethodService).
+ */
+ void showInsets(int types, boolean fromIme);
+
+ /**
+ * Called when a set of insets source window should be hidden by policy.
+ *
+ * @param types internal insets types (WindowInsets.Type.InsetsType) to hide
+ * @param fromIme true if this request originated from IME (InputMethodService).
+ */
+ void hideInsets(int types, boolean fromIme);
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index b7235a31af03..962aca122b4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -25,6 +25,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON
import static android.util.RotationUtils.rotateBounds;
import static android.util.RotationUtils.rotateInsets;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
@@ -44,7 +45,10 @@ import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.Surface;
+import android.view.WindowInsets;
import com.android.internal.R;
@@ -82,6 +86,10 @@ public class DisplayLayout {
private boolean mHasNavigationBar = false;
private boolean mHasStatusBar = false;
private int mNavBarFrameHeight = 0;
+ private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
+ private boolean mNavigationBarCanMove = false;
+ private boolean mReverseDefaultRotation = false;
+ private InsetsState mInsetsState = new InsetsState();
@Override
public boolean equals(Object o) {
@@ -98,14 +106,20 @@ public class DisplayLayout {
&& Objects.equals(mStableInsets, other.mStableInsets)
&& mHasNavigationBar == other.mHasNavigationBar
&& mHasStatusBar == other.mHasStatusBar
- && mNavBarFrameHeight == other.mNavBarFrameHeight;
+ && mAllowSeamlessRotationDespiteNavBarMoving
+ == other.mAllowSeamlessRotationDespiteNavBarMoving
+ && mNavigationBarCanMove == other.mNavigationBarCanMove
+ && mReverseDefaultRotation == other.mReverseDefaultRotation
+ && mNavBarFrameHeight == other.mNavBarFrameHeight
+ && Objects.equals(mInsetsState, other.mInsetsState);
}
@Override
public int hashCode() {
return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
- mNavBarFrameHeight);
+ mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
+ mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState);
}
/**
@@ -150,9 +164,13 @@ public class DisplayLayout {
mDensityDpi = dl.mDensityDpi;
mHasNavigationBar = dl.mHasNavigationBar;
mHasStatusBar = dl.mHasStatusBar;
+ mAllowSeamlessRotationDespiteNavBarMoving = dl.mAllowSeamlessRotationDespiteNavBarMoving;
+ mNavigationBarCanMove = dl.mNavigationBarCanMove;
+ mReverseDefaultRotation = dl.mReverseDefaultRotation;
mNavBarFrameHeight = dl.mNavBarFrameHeight;
mNonDecorInsets.set(dl.mNonDecorInsets);
mStableInsets.set(dl.mStableInsets);
+ mInsetsState.set(dl.mInsetsState, true /* copySources */);
}
private void init(DisplayInfo info, Resources res, boolean hasNavigationBar,
@@ -165,12 +183,24 @@ public class DisplayLayout {
mDensityDpi = info.logicalDensityDpi;
mHasNavigationBar = hasNavigationBar;
mHasStatusBar = hasStatusBar;
+ mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean(
+ R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
+ mNavigationBarCanMove = res.getBoolean(R.bool.config_navBarCanMove);
+ mReverseDefaultRotation = res.getBoolean(R.bool.config_reverseDefaultRotation);
+ recalcInsets(res);
+ }
+
+ /**
+ * Updates the current insets.
+ */
+ public void setInsets(Resources res, InsetsState state) {
+ mInsetsState = state;
recalcInsets(res);
}
private void recalcInsets(Resources res) {
- computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets,
- mHasNavigationBar);
+ computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mInsetsState, mUiMode,
+ mNonDecorInsets, mHasNavigationBar);
mStableInsets.set(mNonDecorInsets);
if (mHasStatusBar) {
convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
@@ -244,11 +274,33 @@ public class DisplayLayout {
return mWidth > mHeight;
}
- /** Get the navbar frame height (used by ime). */
+ /** Get the navbar frame (or window) height (used by ime). */
public int navBarFrameHeight() {
return mNavBarFrameHeight;
}
+ /** @return whether we can seamlessly rotate even if nav-bar can change sides. */
+ public boolean allowSeamlessRotationDespiteNavBarMoving() {
+ return mAllowSeamlessRotationDespiteNavBarMoving;
+ }
+
+ /** @return whether the navigation bar will change sides during rotation. */
+ public boolean navigationBarCanMove() {
+ return mNavigationBarCanMove;
+ }
+
+ /** @return the rotation that would make the physical display "upside down". */
+ public int getUpsideDownRotation() {
+ boolean displayHardwareIsLandscape = mWidth > mHeight;
+ if ((mRotation % 2) != 0) {
+ displayHardwareIsLandscape = !displayHardwareIsLandscape;
+ }
+ if (displayHardwareIsLandscape) {
+ return mReverseDefaultRotation ? Surface.ROTATION_270 : Surface.ROTATION_90;
+ }
+ return Surface.ROTATION_180;
+ }
+
/** Gets the orientation of this layout */
public int getOrientation() {
return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
@@ -291,21 +343,29 @@ public class DisplayLayout {
* @param outInsets the insets to return
*/
static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth,
- int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
- boolean hasNavigationBar) {
+ int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
+ Rect outInsets, boolean hasNavigationBar) {
outInsets.setEmpty();
// Only navigation bar
if (hasNavigationBar) {
+ final InsetsSource extraNavBar = insetsState.getSource(ITYPE_EXTRA_NAVIGATION_BAR);
+ final boolean hasExtraNav = extraNavBar != null && extraNavBar.isVisible();
int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation);
int navBarSize =
getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode);
if (position == NAV_BAR_BOTTOM) {
- outInsets.bottom = navBarSize;
+ outInsets.bottom = hasExtraNav
+ ? Math.max(navBarSize, extraNavBar.getFrame().height())
+ : navBarSize;
} else if (position == NAV_BAR_RIGHT) {
- outInsets.right = navBarSize;
+ outInsets.right = hasExtraNav
+ ? Math.max(navBarSize, extraNavBar.getFrame().width())
+ : navBarSize;
} else if (position == NAV_BAR_LEFT) {
- outInsets.left = navBarSize;
+ outInsets.left = hasExtraNav
+ ? Math.max(navBarSize, extraNavBar.getFrame().width())
+ : navBarSize;
}
}
@@ -327,13 +387,13 @@ public class DisplayLayout {
* @param outInsets the insets to return
*/
static void computeStableInsets(Resources res, int displayRotation, int displayWidth,
- int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
- boolean hasNavigationBar, boolean hasStatusBar) {
+ int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
+ Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) {
outInsets.setEmpty();
// Navigation bar and status bar.
computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout,
- uiMode, outInsets, hasNavigationBar);
+ insetsState, uiMode, outInsets, hasNavigationBar);
convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight,
hasStatusBar);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index 33beab5ee3f1..f3a8620b1693 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -18,13 +18,15 @@ package com.android.wm.shell.common;
import android.annotation.BinderThread;
import android.annotation.NonNull;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
import android.window.WindowOrganizer;
-import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.transition.LegacyTransitions;
import java.util.ArrayList;
@@ -77,6 +79,21 @@ public final class SyncTransactionQueue {
}
/**
+ * Queues a legacy transition to be sent serially to WM
+ */
+ public void queue(LegacyTransitions.ILegacyTransition transition,
+ @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
+ SyncCallback cb = new SyncCallback(transition, type, wct);
+ synchronized (mQueue) {
+ if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct);
+ mQueue.add(cb);
+ if (mQueue.size() == 1) {
+ cb.send();
+ }
+ }
+ }
+
+ /**
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
* Otherwise just returns without queueing.
* @return {@code true} if queued, {@code false} if not.
@@ -118,12 +135,12 @@ public final class SyncTransactionQueue {
// Synchronized on mQueue
private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
if (DEBUG) Slog.d(TAG, " Running " + mRunnables.size() + " sync runnables");
- for (int i = 0, n = mRunnables.size(); i < n; ++i) {
+ final int n = mRunnables.size();
+ for (int i = 0; i < n; ++i) {
mRunnables.get(i).runWithTransaction(t);
}
- mRunnables.clear();
- t.apply();
- t.close();
+ // More runnables may have been added, so only remove the ones that ran.
+ mRunnables.subList(0, n).clear();
}
/** Task to run with transaction. */
@@ -135,20 +152,38 @@ public final class SyncTransactionQueue {
private class SyncCallback extends WindowContainerTransactionCallback {
int mId = -1;
final WindowContainerTransaction mWCT;
+ final LegacyTransitions.LegacyTransition mLegacyTransition;
SyncCallback(WindowContainerTransaction wct) {
mWCT = wct;
+ mLegacyTransition = null;
+ }
+
+ SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition,
+ @WindowManager.TransitionType int type, WindowContainerTransaction wct) {
+ mWCT = wct;
+ mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition);
}
// Must be sychronized on mQueue
void send() {
+ if (mInFlight == this) {
+ // This was probably queued up and sent during a sync runnable of the last callback.
+ // Don't queue it again.
+ return;
+ }
if (mInFlight != null) {
throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
+ mInFlight.mId + " - " + mInFlight.mWCT);
}
mInFlight = this;
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
- mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+ if (mLegacyTransition != null) {
+ mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
+ mLegacyTransition.getAdapter(), this, mWCT);
+ } else {
+ mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+ }
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
}
@@ -169,6 +204,16 @@ public final class SyncTransactionQueue {
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
mQueue.remove(this);
onTransactionReceived(t);
+ if (mLegacyTransition != null) {
+ try {
+ mLegacyTransition.getSyncCallback().onTransactionReady(mId, t);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e);
+ }
+ } else {
+ t.apply();
+ t.close();
+ }
if (!mQueue.isEmpty()) {
mQueue.get(0).send();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
index 9f6dd1f27b62..9e012598554b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
@@ -303,6 +303,13 @@ abstract class MagnetizedObject<T : Any>(
}
/**
+ * Removes all associated targets from this object.
+ */
+ fun clearAllTargets() {
+ associatedTargets.clear()
+ }
+
+ /**
* Provide this method with all motion events that move the magnetized object. If the
* location of the motion events moves within the magnetic field of a target, or indicate a
* fling-to-target gesture, this method will return true and you should not move the object
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 5b158d2063ba..c32dddf6dca0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -16,11 +16,19 @@
package com.android.wm.shell.common.split;
+import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
+import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -30,6 +38,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.WindowInsets;
@@ -39,6 +48,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
@@ -78,6 +88,7 @@ public final class SplitLayout {
private final int mDividerInsets;
private final int mDividerSize;
+ private final Rect mTempRect = new Rect();
private final Rect mRootBounds = new Rect();
private final Rect mDividerBounds = new Rect();
private final Rect mBounds1 = new Rect();
@@ -86,24 +97,30 @@ public final class SplitLayout {
private final SplitWindowManager mSplitWindowManager;
private final DisplayImeController mDisplayImeController;
private final ImePositionProcessor mImePositionProcessor;
+ private final DismissingParallaxPolicy mDismissingParallaxPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private Context mContext;
private DividerSnapAlgorithm mDividerSnapAlgorithm;
private int mDividePosition;
private boolean mInitialized = false;
+ private int mOrientation;
+ private int mRotation;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) {
mContext = context.createConfigurationContext(configuration);
+ mOrientation = configuration.orientation;
+ mRotation = configuration.windowConfiguration.getRotation();
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
mSplitWindowManager = new SplitWindowManager(
windowName, mContext, configuration, parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
+ mDismissingParallaxPolicy = new DismissingParallaxPolicy();
final Resources resources = context.getResources();
mDividerWindowWidth = resources.getDimensionPixelSize(
@@ -142,27 +159,47 @@ public final class SplitLayout {
return mDividePosition;
}
+ /**
+ * Returns the divider position as a fraction from 0 to 1.
+ */
+ public float getDividerPositionAsFraction() {
+ return Math.min(1f, Math.max(0f, isLandscape()
+ ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right
+ : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
+ }
+
/** Applies new configuration, returns {@code false} if there's no effect to the layout. */
public boolean updateConfiguration(Configuration configuration) {
- final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (mRootBounds.equals(rootBounds)) {
- return false;
+ boolean affectsLayout = false;
+
+ // Make sure to render the divider bar with proper resources that matching the screen
+ // orientation.
+ final int orientation = configuration.orientation;
+ if (orientation != mOrientation) {
+ mOrientation = orientation;
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ affectsLayout = true;
}
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- resetDividerPosition();
+ // Update the split bounds when necessary. Besides root bounds changed, split bounds need to
+ // be updated when the rotation changed to cover the case that users rotated the screen 180
+ // degrees.
+ final int rotation = configuration.windowConfiguration.getRotation();
+ final Rect rootBounds = configuration.windowConfiguration.getBounds();
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ mRootBounds.set(rootBounds);
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ resetDividerPosition();
+ affectsLayout = true;
+ }
- // Don't inflate divider bar if it is not initialized.
- if (!mInitialized) {
- return false;
+ if (mInitialized) {
+ release();
+ init();
}
- release();
- init();
- return true;
+ return affectsLayout;
}
/** Updates recording bounds of divider window and both of the splits. */
@@ -170,7 +207,8 @@ public final class SplitLayout {
mDividerBounds.set(mRootBounds);
mBounds1.set(mRootBounds);
mBounds2.set(mRootBounds);
- if (isLandscape(mRootBounds)) {
+ final boolean isLandscape = isLandscape(mRootBounds);
+ if (isLandscape) {
position += mRootBounds.left;
mDividerBounds.left = position - mDividerInsets;
mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth;
@@ -183,6 +221,7 @@ public final class SplitLayout {
mBounds1.bottom = position;
mBounds2.top = mBounds1.bottom + mDividerSize;
}
+ mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
}
/** Inflates {@link DividerView} on the root surface. */
@@ -209,19 +248,20 @@ public final class SplitLayout {
void updateDivideBounds(int position) {
updateBounds(position);
mSplitWindowManager.setResizingSplits(true);
- mSplitLayoutHandler.onBoundsChanging(this);
+ mSplitLayoutHandler.onLayoutChanging(this);
}
void setDividePosition(int position) {
mDividePosition = position;
updateBounds(mDividePosition);
- mSplitLayoutHandler.onBoundsChanged(this);
+ mSplitLayoutHandler.onLayoutChanged(this);
mSplitWindowManager.setResizingSplits(false);
}
/** Resets divider position. */
public void resetDividerPosition() {
mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+ mSplitWindowManager.setResizingSplits(false);
updateBounds(mDividePosition);
}
@@ -232,15 +272,15 @@ public final class SplitLayout {
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) {
case FLAG_DISMISS_START:
- mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
- mSplitWindowManager.setResizingSplits(false);
+ flingDividePosition(currentPosition, snapTarget.position,
+ () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */));
break;
case FLAG_DISMISS_END:
- mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
- mSplitWindowManager.setResizingSplits(false);
+ flingDividePosition(currentPosition, snapTarget.position,
+ () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */));
break;
default:
- flingDividePosition(currentPosition, snapTarget.position);
+ flingDividePosition(currentPosition, snapTarget.position, null);
break;
}
}
@@ -270,8 +310,13 @@ public final class SplitLayout {
isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
- private void flingDividePosition(int from, int to) {
- if (from == to) return;
+ @VisibleForTesting
+ void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
+ if (from == to) {
+ // No animation run, it should stop resizing here.
+ mSplitWindowManager.setResizingSplits(false);
+ return;
+ }
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
@@ -282,6 +327,9 @@ public final class SplitLayout {
@Override
public void onAnimationEnd(Animator animation) {
setDividePosition(to);
+ if (flingFinishedCallback != null) {
+ flingFinishedCallback.run();
+ }
}
@Override
@@ -296,42 +344,91 @@ public final class SplitLayout {
return context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics()
.getWindowInsets()
- .getInsets(WindowInsets.Type.navigationBars()
- | WindowInsets.Type.statusBars()
- | WindowInsets.Type.displayCutout()).toRect();
+ .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
+ .toRect();
}
private static boolean isLandscape(Rect bounds) {
return bounds.width() > bounds.height();
}
+ /**
+ * Return if this layout is landscape.
+ */
+ public boolean isLandscape() {
+ return isLandscape(mRootBounds);
+ }
+
/** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
- final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds);
- final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1);
- final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2);
final SurfaceControl dividerLeash = getDividerLeash();
if (dividerLeash != null) {
- t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
- // Resets layer of divider bar to make sure it is always on top.
- .setLayer(dividerLeash, Integer.MAX_VALUE);
+ t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top);
+ // Resets layer of divider bar to make sure it is always on top.
+ t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
+ }
+ t.setPosition(leash1, mBounds1.left, mBounds1.top)
+ .setWindowCrop(leash1, mBounds1.width(), mBounds1.height());
+ t.setPosition(leash2, mBounds2.left, mBounds2.top)
+ .setWindowCrop(leash2, mBounds2.width(), mBounds2.height());
+
+ if (mImePositionProcessor.adjustSurfaceLayoutForIme(
+ t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) {
+ return;
}
- t.setPosition(leash1, bounds1.left, bounds1.top)
- .setWindowCrop(leash1, bounds1.width(), bounds1.height());
-
- t.setPosition(leash2, bounds2.left, bounds2.top)
- .setWindowCrop(leash2, bounds2.width(), bounds2.height());
-
- mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
+ mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
}
/** Apply recorded task layout to the {@link WindowContainerTransaction}. */
public void applyTaskChanges(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
- wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1))
- .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2));
+ if (mImePositionProcessor.applyTaskLayoutForIme(wct, task1.token, task2.token)) {
+ return;
+ }
+
+ wct.setBounds(task1.token, mBounds1)
+ .setBounds(task2.token, mBounds2);
+ }
+
+ /**
+ * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And
+ * restore shifted configuration bounds if it's no longer shifted.
+ */
+ public void applyLayoutShifted(WindowContainerTransaction wct, int offsetX, int offsetY,
+ ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) {
+ if (offsetX == 0 && offsetY == 0) {
+ wct.setBounds(taskInfo1.token, mBounds1);
+ wct.setAppBounds(taskInfo1.token, null);
+ wct.setScreenSizeDp(taskInfo1.token,
+ SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+
+ wct.setBounds(taskInfo2.token, mBounds2);
+ wct.setAppBounds(taskInfo2.token, null);
+ wct.setScreenSizeDp(taskInfo2.token,
+ SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+ } else {
+ mTempRect.set(taskInfo1.configuration.windowConfiguration.getBounds());
+ mTempRect.offset(offsetX, offsetY);
+ wct.setBounds(taskInfo1.token, mTempRect);
+ mTempRect.set(taskInfo1.configuration.windowConfiguration.getAppBounds());
+ mTempRect.offset(offsetX, offsetY);
+ wct.setAppBounds(taskInfo1.token, mTempRect);
+ wct.setScreenSizeDp(taskInfo1.token,
+ taskInfo1.configuration.screenWidthDp,
+ taskInfo1.configuration.screenHeightDp);
+
+ mTempRect.set(taskInfo2.configuration.windowConfiguration.getBounds());
+ mTempRect.offset(offsetX, offsetY);
+ wct.setBounds(taskInfo2.token, mTempRect);
+ mTempRect.set(taskInfo2.configuration.windowConfiguration.getAppBounds());
+ mTempRect.offset(offsetX, offsetY);
+ wct.setAppBounds(taskInfo2.token, mTempRect);
+ wct.setScreenSizeDp(taskInfo2.token,
+ taskInfo2.configuration.screenWidthDp,
+ taskInfo2.configuration.screenHeightDp);
+ }
}
/** Handles layout change event. */
@@ -341,10 +438,18 @@ public final class SplitLayout {
void onSnappedToDismiss(boolean snappedToEnd);
/** Calls when the bounds is changing due to animation or dragging divider bar. */
- void onBoundsChanging(SplitLayout layout);
+ void onLayoutChanging(SplitLayout layout);
/** Calls when the target bounds changed. */
- void onBoundsChanged(SplitLayout layout);
+ void onLayoutChanged(SplitLayout layout);
+
+ /**
+ * Notifies when the layout shifted. So the layout handler can shift configuration
+ * bounds correspondingly to make sure client apps won't get configuration changed or
+ * relaunch. If the layout is no longer shifted, layout handler should restore shifted
+ * configuration bounds.
+ */
+ void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout);
/** Calls when user double tapped on the divider bar. */
default void onDoubleTappedDivider() {
@@ -355,6 +460,106 @@ public final class SplitLayout {
int getSplitItemPosition(WindowContainerToken token);
}
+ /**
+ * Calculates and applies proper dismissing parallax offset and dimming value to hint users
+ * dismissing gesture.
+ */
+ private class DismissingParallaxPolicy {
+ // The current dismissing side.
+ int mDismissingSide = DOCKED_INVALID;
+
+ // The parallax offset to hint the dismissing side and progress.
+ final Point mDismissingParallaxOffset = new Point();
+
+ // The dimming value to hint the dismissing side and progress.
+ float mDismissingDimValue = 0.0f;
+
+ /**
+ * Applies a parallax to the task to hint dismissing progress.
+ *
+ * @param position the split position to apply dismissing parallax effect
+ * @param isLandscape indicates whether it's splitting horizontally or vertically
+ */
+ void applyDividerPosition(int position, boolean isLandscape) {
+ mDismissingSide = DOCKED_INVALID;
+ mDismissingParallaxOffset.set(0, 0);
+ mDismissingDimValue = 0;
+
+ int totalDismissingDistance = 0;
+ if (position <= mDividerSnapAlgorithm.getFirstSplitTarget().position) {
+ mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+ totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
+ - mDividerSnapAlgorithm.getFirstSplitTarget().position;
+ } else if (position >= mDividerSnapAlgorithm.getLastSplitTarget().position) {
+ mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+ totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
+ - mDividerSnapAlgorithm.getDismissEndTarget().position;
+ }
+
+ if (mDismissingSide != DOCKED_INVALID) {
+ float fraction = Math.max(0,
+ Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
+ mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
+ fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+ if (isLandscape) {
+ mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+ } else {
+ mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+ }
+ }
+ }
+
+ /**
+ * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+ * slowing down parallax effect
+ */
+ private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
+ float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+
+ // Less parallax at the top, just because.
+ if (dockSide == WindowManager.DOCKED_TOP) {
+ result /= 2f;
+ }
+ return result;
+ }
+
+ /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
+ boolean adjustDismissingSurface(SurfaceControl.Transaction t,
+ SurfaceControl leash1, SurfaceControl leash2,
+ SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+ SurfaceControl targetLeash, targetDimLayer;
+ switch (mDismissingSide) {
+ case DOCKED_TOP:
+ case DOCKED_LEFT:
+ targetLeash = leash1;
+ targetDimLayer = dimLayer1;
+ mTempRect.set(mBounds1);
+ break;
+ case DOCKED_BOTTOM:
+ case DOCKED_RIGHT:
+ targetLeash = leash2;
+ targetDimLayer = dimLayer2;
+ mTempRect.set(mBounds2);
+ break;
+ case DOCKED_INVALID:
+ default:
+ t.setAlpha(dimLayer1, 0).hide(dimLayer1);
+ t.setAlpha(dimLayer2, 0).hide(dimLayer2);
+ return false;
+ }
+
+ t.setPosition(targetLeash,
+ mTempRect.left + mDismissingParallaxOffset.x,
+ mTempRect.top + mDismissingParallaxOffset.y);
+ // Transform the screen-based split bounds to surface-based crop bounds.
+ mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
+ t.setWindowCrop(targetLeash, mTempRect);
+ t.setAlpha(targetDimLayer, mDismissingDimValue)
+ .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
+ return true;
+ }
+ }
+
/** Records IME top offset changes and updates SplitLayout correspondingly. */
private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
/**
@@ -409,6 +614,18 @@ public final class SplitLayout {
&& !isFloating && !isLandscape(mRootBounds) && showing;
mTargetYOffset = needOffset ? getTargetYOffset() : 0;
+ if (mTargetYOffset != mLastYOffset) {
+ // Freeze the configuration size with offset to prevent app get a configuration
+ // changed or relaunch. This is required to make sure client apps will calculate
+ // insets properly after layout shifted.
+ if (mTargetYOffset == 0) {
+ mSplitLayoutHandler.onLayoutShifted(0, 0, SplitLayout.this);
+ } else {
+ mSplitLayoutHandler.onLayoutShifted(0, mTargetYOffset - mLastYOffset,
+ SplitLayout.this);
+ }
+ }
+
// Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
// ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
// because DividerView won't receive onImeVisibilityChanged callback after it being
@@ -423,7 +640,7 @@ public final class SplitLayout {
public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
if (displayId != mDisplayId) return;
onProgress(getProgress(imeTop));
- mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+ mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
}
@Override
@@ -431,7 +648,7 @@ public final class SplitLayout {
SurfaceControl.Transaction t) {
if (displayId != mDisplayId || cancel) return;
onProgress(1.0f);
- mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+ mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
}
@Override
@@ -441,7 +658,7 @@ public final class SplitLayout {
if (!controlling && mImeShown) {
reset();
mSplitWindowManager.setInteractive(true);
- mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+ mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
}
}
@@ -473,24 +690,61 @@ public final class SplitLayout {
return start + (end - start) * progress;
}
- private void reset() {
+ void reset() {
mImeShown = false;
mYOffsetForIme = mLastYOffset = mTargetYOffset = 0;
mDimValue1 = mLastDim1 = mTargetDim1 = 0.0f;
mDimValue2 = mLastDim2 = mTargetDim2 = 0.0f;
}
- /* Adjust bounds with IME offset. */
- private Rect adjustForIme(Rect bounds) {
- final Rect temp = new Rect(bounds);
- if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme);
- return temp;
+ /**
+ * Applies adjusted task layout for showing IME.
+ *
+ * @return {@code false} if there's no need to adjust, otherwise {@code true}
+ */
+ boolean applyTaskLayoutForIme(WindowContainerTransaction wct,
+ WindowContainerToken token1, WindowContainerToken token2) {
+ if (mYOffsetForIme == 0) return false;
+
+ mTempRect.set(mBounds1);
+ mTempRect.offset(0, mYOffsetForIme);
+ wct.setBounds(token1, mTempRect);
+
+ mTempRect.set(mBounds2);
+ mTempRect.offset(0, mYOffsetForIme);
+ wct.setBounds(token2, mTempRect);
+
+ return true;
}
- private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
- SurfaceControl dimLayer2) {
+ /**
+ * Adjusts surface layout while showing IME.
+ *
+ * @return {@code false} if there's no need to adjust, otherwise {@code true}
+ */
+ boolean adjustSurfaceLayoutForIme(SurfaceControl.Transaction t,
+ SurfaceControl dividerLeash, SurfaceControl leash1, SurfaceControl leash2,
+ SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+ if (mYOffsetForIme == 0) return false;
+
+ if (dividerLeash != null) {
+ mTempRect.set(mDividerBounds);
+ mTempRect.offset(0, mYOffsetForIme);
+ t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+ }
+
+ mTempRect.set(mBounds1);
+ mTempRect.offset(0, mYOffsetForIme);
+ t.setPosition(leash1, mTempRect.left, mTempRect.top);
+
+ mTempRect.set(mBounds2);
+ mTempRect.offset(0, mYOffsetForIme);
+ t.setPosition(leash2, mTempRect.left, mTempRect.top);
+
t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
+
+ return true;
}
}
}
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 0cea0efc0057..b7bbe807cf0c 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
@@ -95,7 +95,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName(TAG)
- .setHidden(false)
+ .setHidden(true)
.setCallsite("SplitWindowManager#attachToParentSurface");
mParentContainerCallbacks.attachToParentSurface(builder);
mLeash = builder.build();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 58bf22ad29b2..0c12d6c7bca2 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
@@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.res.Configuration;
@@ -48,6 +49,8 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
@@ -67,14 +70,17 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
private final Context mContext;
private final DisplayController mDisplayController;
+ private final DragAndDropEventLogger mLogger;
private SplitScreenController mSplitScreen;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
- public DragAndDropController(Context context, DisplayController displayController) {
+ public DragAndDropController(Context context, DisplayController displayController,
+ UiEventLogger uiEventLogger) {
mContext = context;
mDisplayController = displayController;
+ mLogger = new DragAndDropEventLogger(uiEventLogger);
}
public void initialize(Optional<SplitScreenController> splitscreen) {
@@ -175,9 +181,10 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
Slog.w(TAG, "Unexpected drag start during an active drag");
return false;
}
+ InstanceId loggerSessionId = mLogger.logStart(event);
pd.activeDragCount++;
pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId),
- event.getClipData());
+ event.getClipData(), loggerSessionId);
setDropTargetWindowVisibility(pd, View.VISIBLE);
break;
case ACTION_DRAG_ENTERED:
@@ -198,7 +205,9 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
case ACTION_DRAG_ENDED:
// TODO(b/169894807): Ensure sure it's not possible to get ENDED without DROP
// or EXITED
- if (!pd.dragLayout.hasDropped()) {
+ if (pd.dragLayout.hasDropped()) {
+ mLogger.logDrop();
+ } else {
pd.activeDragCount--;
pd.dragLayout.hide(event, () -> {
if (pd.activeDragCount == 0) {
@@ -208,6 +217,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange
}
});
}
+ mLogger.logEnd();
break;
}
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
new file mode 100644
index 000000000000..6e4b81563441
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.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 com.android.wm.shell.draganddrop;
+
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.pm.ActivityInfo;
+import android.view.DragEvent;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+/**
+ * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
+ */
+public class DragAndDropEventLogger {
+
+ private final UiEventLogger mUiEventLogger;
+ // Used to generate instance ids for this drag if one is not provided
+ private final InstanceIdSequence mIdSequence;
+
+ // Tracks the current drag session
+ private ActivityInfo mActivityInfo;
+ private InstanceId mInstanceId;
+
+ public DragAndDropEventLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Logs the start of a drag.
+ */
+ public InstanceId logStart(DragEvent event) {
+ final ClipDescription description = event.getClipDescription();
+ final ClipData data = event.getClipData();
+ final ClipData.Item item = data.getItemAt(0);
+ mInstanceId = item.getIntent().getParcelableExtra(
+ ClipDescription.EXTRA_LOGGING_INSTANCE_ID);
+ if (mInstanceId == null) {
+ mInstanceId = mIdSequence.newInstanceId();
+ }
+ mActivityInfo = item.getActivityInfo();
+ mUiEventLogger.logWithInstanceId(getStartEnum(description),
+ mActivityInfo.applicationInfo.uid,
+ mActivityInfo.applicationInfo.packageName, mInstanceId);
+ return mInstanceId;
+ }
+
+ /**
+ * Logs a successful drop.
+ */
+ public void logDrop() {
+ mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_DROPPED,
+ mActivityInfo.applicationInfo.uid,
+ mActivityInfo.applicationInfo.packageName, mInstanceId);
+ }
+
+ /**
+ * Logs the end of a drag.
+ */
+ public void logEnd() {
+ mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_END,
+ mActivityInfo.applicationInfo.uid,
+ mActivityInfo.applicationInfo.packageName, mInstanceId);
+ }
+
+ /**
+ * Returns the start logging enum for the given drag description.
+ */
+ private DragAndDropUiEventEnum getStartEnum(ClipDescription description) {
+ if (description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)) {
+ return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY;
+ } else if (description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)) {
+ return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_SHORTCUT;
+ } else if (description.hasMimeType(MIMETYPE_APPLICATION_TASK)) {
+ return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_TASK;
+ }
+ throw new IllegalArgumentException("Not an app drag");
+ }
+
+ /**
+ * Enums for logging Drag & Drop UiEvents
+ */
+ public enum DragAndDropUiEventEnum implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Starting a global drag and drop of an activity")
+ GLOBAL_APP_DRAG_START_ACTIVITY(884),
+
+ @UiEvent(doc = "Starting a global drag and drop of a shortcut")
+ GLOBAL_APP_DRAG_START_SHORTCUT(885),
+
+ @UiEvent(doc = "Starting a global drag and drop of a task")
+ GLOBAL_APP_DRAG_START_TASK(888),
+
+ @UiEvent(doc = "A global app drag was successfully dropped")
+ GLOBAL_APP_DRAG_DROPPED(887),
+
+ @UiEvent(doc = "Ending a global app drag and drop")
+ GLOBAL_APP_DRAG_END(886);
+
+ private final int mId;
+
+ DragAndDropUiEventEnum(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+}
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 9bcc3acf7a57..102b90ff5d3d 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
@@ -63,6 +63,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -86,6 +87,7 @@ public class DragAndDropPolicy {
private final SplitScreenController mSplitScreen;
private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
+ private InstanceId mLoggerSessionId;
private DragSession mSession;
public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
@@ -104,7 +106,8 @@ public class DragAndDropPolicy {
/**
* Starts a new drag session with the given initial drag data.
*/
- void start(DisplayLayout displayLayout, ClipData data) {
+ void start(DisplayLayout displayLayout, ClipData data, InstanceId loggerSessionId) {
+ mLoggerSessionId = loggerSessionId;
mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data);
// TODO(b/169894807): Also update the session data with task stack changes
mSession.update();
@@ -151,10 +154,8 @@ public class DragAndDropPolicy {
final Rect rightHitRegion = new Rect();
final Rect rightDrawRegion = bottomOrRightBounds;
- displayRegion.splitVertically(leftHitRegion, fullscreenHitRegion, rightHitRegion);
+ displayRegion.splitVertically(leftHitRegion, rightHitRegion);
- mTargets.add(
- new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion));
@@ -165,10 +166,8 @@ public class DragAndDropPolicy {
final Rect bottomDrawRegion = bottomOrRightBounds;
displayRegion.splitHorizontally(
- topHitRegion, fullscreenHitRegion, bottomHitRegion);
+ topHitRegion, bottomHitRegion);
- mTargets.add(
- new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion));
mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion));
}
@@ -211,6 +210,8 @@ public class DragAndDropPolicy {
// Launch in the side stage if we are not in split-screen already.
stage = STAGE_TYPE_SIDE;
}
+ // Add some data for logging splitscreen once it is invoked
+ mSplitScreen.logOnDroppedToSplit(position, mLoggerSessionId);
}
final ClipDescription description = data.getDescription();
@@ -269,7 +270,6 @@ public class DragAndDropPolicy {
* Updates the session data based on the current state of the system.
*/
void update() {
-
List<ActivityManager.RunningTaskInfo> tasks =
mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */);
if (!tasks.isEmpty()) {
@@ -299,7 +299,12 @@ public class DragAndDropPolicy {
@StageType int stage, @SplitPosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
- void exitSplitScreen();
+
+ /**
+ * Exits splitscreen, with an associated exit trigger from the SplitscreenUIChanged proto
+ * for logging.
+ */
+ void exitSplitScreen(int exitTrigger);
}
/**
@@ -352,7 +357,7 @@ public class DragAndDropPolicy {
}
@Override
- public void exitSplitScreen() {
+ public void exitSplitScreen(int exitTrigger) {
throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
}
}
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 b3423362347f..efc9ed0f75b2 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
@@ -38,6 +38,7 @@ import android.view.WindowInsets.Type;
import androidx.annotation.NonNull;
+import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
@@ -98,8 +99,9 @@ public class DragLayout extends View {
return mHasDropped;
}
- public void prepare(DisplayLayout displayLayout, ClipData initialData) {
- mPolicy.start(displayLayout, initialData);
+ public void prepare(DisplayLayout displayLayout, ClipData initialData,
+ InstanceId loggerSessionId) {
+ mPolicy.start(displayLayout, initialData, loggerSessionId);
mHasDropped = false;
mCurrentTarget = null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
new file mode 100644
index 000000000000..5fb3297aa6d3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.freeform;
+
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.io.PrintWriter;
+
+/**
+ * {@link ShellTaskOrganizer.TaskListener} for {@link
+ * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
+ */
+public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = "FreeformTaskListener";
+
+ private final SyncTransactionQueue mSyncQueue;
+
+ private final SparseArray<State> mTasks = new SparseArray<>();
+
+ private static class State {
+ RunningTaskInfo mTaskInfo;
+ SurfaceControl mLeash;
+ }
+
+ public FreeformTaskListener(SyncTransactionQueue syncQueue) {
+ mSyncQueue = syncQueue;
+ }
+
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mTasks.get(taskInfo.taskId) != null) {
+ throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
+ taskInfo.taskId);
+ final State state = new State();
+ state.mTaskInfo = taskInfo;
+ state.mLeash = leash;
+ mTasks.put(taskInfo.taskId, state);
+
+ final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ mSyncQueue.runInSync(t -> {
+ Point taskPosition = taskInfo.positionInParent;
+ t.setPosition(leash, taskPosition.x, taskPosition.y)
+ .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
+ .show(leash);
+ });
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ State state = mTasks.get(taskInfo.taskId);
+ if (state == null) {
+ Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+ return;
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
+ taskInfo.taskId);
+ mTasks.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ State state = mTasks.get(taskInfo.taskId);
+ if (state == null) {
+ throw new RuntimeException(
+ "Task info changed before appearing: #" + taskInfo.taskId);
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
+ taskInfo.taskId);
+ state.mTaskInfo = taskInfo;
+
+ final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ final SurfaceControl leash = state.mLeash;
+ mSyncQueue.runInSync(t -> {
+ Point taskPosition = taskInfo.positionInParent;
+ t.setPosition(leash, taskPosition.x, taskPosition.y)
+ .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
+ .show(leash);
+ });
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + this);
+ pw.println(innerPrefix + mTasks.size() + " tasks");
+ }
+
+ @Override
+ public String toString() {
+ return TAG;
+ }
+
+ /**
+ * Checks if freeform support is enabled in system.
+ *
+ * @param context context used to check settings and package manager.
+ * @return {@code true} if freeform is enabled, {@code false} if not.
+ */
+ public static boolean isFreeformEnabled(Context context) {
+ return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+ || Settings.Global.getInt(context.getContentResolver(),
+ DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ }
+
+ /**
+ * Creates {@link FreeformTaskListener} if freeform is enabled.
+ */
+ public static FreeformTaskListener create(Context context,
+ SyncTransactionQueue syncQueue) {
+ if (!isFreeformEnabled(context)) {
+ return null;
+ }
+
+ return new FreeformTaskListener(syncQueue);
+ }
+}
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 362b40f33e89..067f80800ed5 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
@@ -20,6 +20,8 @@ import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
import static android.view.WindowManager.DOCKED_RIGHT;
+import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
@@ -100,10 +102,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private static final float MINIMIZE_DOCK_SCALE = 0f;
private static final float ADJUSTED_FOR_IME_SCALE = 0.5f;
- private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
- new PathInterpolator(0.5f, 1f, 0.5f, 1f);
- private static final PathInterpolator DIM_INTERPOLATOR =
- new PathInterpolator(.23f, .87f, .52f, -0.11f);
private static final Interpolator IME_ADJUST_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 0.1f, 1f);
@@ -460,6 +458,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void stopDragging() {
mHandle.setTouching(false, true /* animate */);
mWindowManager.setSlippery(true);
+ mWindowManagerProxy.setResizing(false);
releaseBackground();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index d9409ec2dc17..b1fa2ac25fe7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -204,7 +204,8 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mPendingDismiss && transition != mPendingEnter) {
// If we're not in split-mode, just abort
@@ -239,12 +240,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
if (change.getParent() != null) {
// This is probably reparented, so we want the parent to be immediately visible
final TransitionInfo.Change parentChange = info.getChange(change.getParent());
- t.show(parentChange.getLeash());
- t.setAlpha(parentChange.getLeash(), 1.f);
+ startTransaction.show(parentChange.getLeash());
+ startTransaction.setAlpha(parentChange.getLeash(), 1.f);
// and then animate this layer outside the parent (since, for example, this is
// the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRootLeash());
- t.setLayer(leash, info.getChanges().size() - i);
+ startTransaction.reparent(leash, info.getRootLeash());
+ startTransaction.setLayer(leash, info.getChanges().size() - i);
// build the finish reparent/reposition
mFinishTransaction.reparent(leash, parentChange.getLeash());
mFinishTransaction.setPosition(leash,
@@ -271,12 +272,12 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
if (transition == mPendingEnter
&& mListener.mPrimary.token.equals(change.getContainer())
|| mListener.mSecondary.token.equals(change.getContainer())) {
- t.setWindowCrop(leash, change.getStartAbsBounds().width(),
+ startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(),
change.getStartAbsBounds().height());
if (mListener.mPrimary.token.equals(change.getContainer())) {
// Move layer to top since we want it above the oversized home task during
// animation even though home task is on top in hierarchy.
- t.setLayer(leash, info.getChanges().size() + 1);
+ startTransaction.setLayer(leash, info.getChanges().size() + 1);
}
}
boolean isOpening = Transitions.isOpeningType(info.getType());
@@ -289,7 +290,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
// Dismissing via snap-to-top/bottom means that the dismissed task is already
// not-visible (usually cropped to oblivion) so immediately set its alpha to 0
// and don't animate it so it doesn't pop-in when reparented.
- t.setAlpha(leash, 0.f);
+ startTransaction.setAlpha(leash, 0.f);
} else {
startExampleAnimation(leash, false /* show */);
}
@@ -311,7 +312,7 @@ public class LegacySplitScreenTransitions implements Transitions.TransitionHandl
}
mSplitScreen.finishEnterSplitTransition(homeIsVisible);
}
- t.apply();
+ startTransaction.apply();
onFinish();
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index d3274706631b..9e1c61aac868 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.view.ContextThemeWrapper;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.animation.LinearInterpolator;
@@ -33,7 +34,6 @@ import android.window.DisplayAreaOrganizer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.view.ContextThemeWrapper;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 7cf4fb7a811d..ff333c8c659d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -171,9 +171,22 @@ public final class OneHandedSettingsUtil {
* @return true if user enabled one-handed shortcut in settings, false otherwise.
*/
public boolean getShortcutEnabled(ContentResolver resolver, int userId) {
- final String targets = Settings.Secure.getStringForUser(resolver,
+ // Checks SOFTWARE_SHORTCUT_KEY
+ final String targetsSwKey = Settings.Secure.getStringForUser(resolver,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
- return TextUtils.isEmpty(targets) ? false : targets.contains(ONE_HANDED_MODE_TARGET_NAME);
+ if (!TextUtils.isEmpty(targetsSwKey) && targetsSwKey.contains(
+ ONE_HANDED_MODE_TARGET_NAME)) {
+ return true;
+ }
+
+ // Checks HARDWARE_SHORTCUT_KEY
+ final String targetsHwKey = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ if (!TextUtils.isEmpty(targetsHwKey) && targetsHwKey.contains(
+ ONE_HANDED_MODE_TARGET_NAME)) {
+ return true;
+ }
+ return false;
}
/**
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 f58c6b173af9..88f33755fa2d 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
@@ -32,6 +32,8 @@ import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
@@ -44,7 +46,6 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
-import androidx.appcompat.view.ContextThemeWrapper;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
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 200af7415eb1..05111a3d4436 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
@@ -38,6 +38,7 @@ import android.view.SurfaceSession;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.transition.Transitions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -617,14 +618,28 @@ public class PipAnimationController {
setCurrentValue(bounds);
final Rect insets = computeInsets(fraction);
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;
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ if (rotationDelta == ROTATION_90) {
+ degree = 90 * (1 - fraction);
+ x = fraction * (end.left - start.left)
+ + start.left + start.right * (1 - fraction);
+ y = fraction * (end.top - start.top) + start.top;
+ } else {
+ degree = -90 * (1 - fraction);
+ x = fraction * (end.left - start.left) + start.left;
+ y = fraction * (end.top - start.top)
+ + start.top + start.bottom * (1 - fraction);
+ }
} else {
- degree = -90 * fraction;
- x = fraction * (end.left - start.left) + start.left;
- y = fraction * (end.bottom - start.top) + start.top;
+ 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, initialContainerRect, bounds,
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 728794de0865..180e3fb48c9d 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
@@ -23,6 +23,7 @@ import android.graphics.RectF;
import android.view.SurfaceControl;
import com.android.wm.shell.R;
+import com.android.wm.shell.transition.Transitions;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
@@ -137,7 +138,8 @@ public class PipSurfaceTransactionHelper {
// destination are different.
final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
final Rect crop = mTmpDestinationRect;
- crop.set(0, 0, destW, destH);
+ crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH
+ : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH);
// Inverse scale for crop to fit in screen coordinates.
crop.scale(1 / scale);
crop.offset(insets.left, insets.top);
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 f2bad6caf3e8..96867761cc7e 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
@@ -114,38 +114,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
- // Not a complete set of states but serves what we want right now.
- private enum State {
- UNDEFINED(0),
- TASK_APPEARED(1),
- ENTRY_SCHEDULED(2),
- ENTERING_PIP(3),
- ENTERED_PIP(4),
- EXITING_PIP(5);
-
- private final int mStateValue;
-
- State(int value) {
- mStateValue = value;
- }
-
- private boolean isInPip() {
- return mStateValue >= TASK_APPEARED.mStateValue
- && mStateValue != EXITING_PIP.mStateValue;
- }
-
- /**
- * Resize request can be initiated in other component, ignore if we are no longer in PIP,
- * still waiting for animation or we're exiting from it.
- *
- * @return {@code true} if the resize request should be blocked/ignored.
- */
- private boolean shouldBlockResizeRequest() {
- return mStateValue < ENTERING_PIP.mStateValue
- || mStateValue == EXITING_PIP.mStateValue;
- }
- }
-
private final Context mContext;
private final SyncTransactionQueue mSyncTransactionQueue;
private final PipBoundsState mPipBoundsState;
@@ -169,11 +137,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void onPipAnimationStart(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- // TODO (b//169221267): Add jank listener for transactions without buffer updates.
- //InteractionJankMonitor.getInstance().begin(
- // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
- }
sendOnPipTransitionStarted(direction);
}
@@ -201,7 +164,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
final boolean isExitPipDirection = isOutPipDirection(direction)
|| isRemovePipDirection(direction);
- if (mState != State.EXITING_PIP || isExitPipDirection) {
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP
+ || isExitPipDirection) {
// Finish resize as long as we're not exiting PIP, or, if we are, only if this is
// the end of an exit PIP animation.
// This is necessary in case there was a resize animation ongoing when exit PIP
@@ -244,7 +208,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private ActivityManager.RunningTaskInfo mDeferredTaskInfo;
private WindowContainerToken mToken;
private SurfaceControl mLeash;
- private State mState = State.UNDEFINED;
+ private PipTransitionState mPipTransitionState;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private long mLastOneShotAlphaAnimationTime;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -274,21 +238,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private @Surface.Rotation int mCurrentRotation;
/**
- * 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.
- * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}.
- */
- private boolean mInSwipePipToHomeTransition;
-
- /**
* An optional overlay used to mask content changing between an app in/out of PiP, only set if
- * {@link #mInSwipePipToHomeTransition} is true.
+ * {@link PipTransitionState#getInSwipePipToHomeTransition()} is true.
*/
private SurfaceControl mSwipePipToHomeOverlay;
public PipTaskOrganizer(Context context,
@NonNull SyncTransactionQueue syncTransactionQueue,
+ @NonNull PipTransitionState pipTransitionState,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipBoundsAlgorithm boundsHandler,
@NonNull PipMenuController pipMenuController,
@@ -302,6 +259,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mSyncTransactionQueue = syncTransactionQueue;
+ mPipTransitionState = pipTransitionState;
mPipBoundsState = pipBoundsState;
mPipBoundsAlgorithm = boundsHandler;
mPipMenuController = pipMenuController;
@@ -337,14 +295,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
public boolean isInPip() {
- return mState.isInPip();
+ return mPipTransitionState.isInPip();
}
/**
* Returns whether the entry animation is waiting to be started.
*/
public boolean isEntryScheduled() {
- return mState == State.ENTRY_SCHEDULED;
+ return mPipTransitionState.getTransitionState() == PipTransitionState.ENTRY_SCHEDULED;
}
/**
@@ -372,7 +330,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams) {
- mInSwipePipToHomeTransition = true;
+ mPipTransitionState.setInSwipePipToHomeTransition(true);
sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
return mPipBoundsAlgorithm.getEntryDestinationBounds();
@@ -385,7 +343,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds,
SurfaceControl overlay) {
// do nothing if there is no startSwipePipToHome being called before
- if (mInSwipePipToHomeTransition) {
+ if (mPipTransitionState.getInSwipePipToHomeTransition()) {
mPipBoundsState.setBounds(destinationBounds);
mSwipePipToHomeOverlay = overlay;
}
@@ -412,9 +370,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* @param animationDurationMs duration in millisecond for the exiting PiP transition
*/
public void exitPip(int animationDurationMs) {
- if (!mState.isInPip() || mState == State.EXITING_PIP || mToken == null) {
+ if (!mPipTransitionState.isInPip()
+ || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
+ || mToken == null) {
Log.wtf(TAG, "Not allowed to exitPip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -438,7 +398,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setBoundsChangeTransaction(mToken, tx);
// Set the exiting state first so if there is fixed rotation later, the running animation
// won't be interrupted by alpha animation for existing PiP.
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.startTransition(destinationBounds, wct);
+ return;
+ }
mSyncTransactionQueue.queue(wct);
mSyncTransactionQueue.runInSync(t -> {
// Make sure to grab the latest source hint rect as it could have been
@@ -476,9 +441,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* Removes PiP immediately.
*/
public void removePip() {
- if (!mState.isInPip() || mToken == null) {
+ if (!mPipTransitionState.isInPip() || mToken == null) {
Log.wtf(TAG, "Not allowed to removePip in current state"
- + " mState=" + mState + " mToken=" + mToken);
+ + " mState=" + mPipTransitionState.getTransitionState() + " mToken=" + mToken);
return;
}
@@ -492,10 +457,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
animator.setDuration(mExitAnimationDuration);
animator.setInterpolator(Interpolators.ALPHA_OUT);
animator.start();
- mState = State.EXITING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP);
}
private void removePipImmediately() {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setBounds(mToken, null);
+ wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+ wct.reorder(mToken, false);
+ mPipTransitionController.startTransition(null, wct);
+ return;
+ }
+
try {
// Reset the task bounds first to ensure the activity configuration is reset as well
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -514,7 +488,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Objects.requireNonNull(info, "Requires RunningTaskInfo");
mTaskInfo = info;
mToken = mTaskInfo.token;
- mState = State.TASK_APPEARED;
+ mPipTransitionState.setTransitionState(PipTransitionState.TASK_APPEARED);
mLeash = leash;
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
@@ -530,7 +504,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mOnDisplayIdChangeCallback.accept(info.displayId);
}
- if (mInSwipePipToHomeTransition) {
+ if (mPipTransitionState.getInSwipePipToHomeTransition()) {
if (!mWaitForFixedRotation) {
onEndOfSwipePipToHomeTransition();
} else {
@@ -557,6 +531,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
mPipMenuController.attach(mLeash);
+ } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
}
return;
}
@@ -568,7 +544,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */,
sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration,
null /* updateBoundsCallback */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -595,7 +571,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
animateResizePip(currentBounds, destinationBounds, sourceHintRect,
TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, 0 /* startingAngle */);
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
/**
@@ -620,7 +596,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceControlTransactionFactory.getTransaction();
tx.setAlpha(mLeash, 0f);
tx.apply();
- mState = State.ENTRY_SCHEDULED;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
applyEnterPipSyncTransaction(destinationBounds, () -> {
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f)
@@ -631,11 +607,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
.start();
// mState is set right after the animation is kicked off to block any resize
// requests such as offsetPip that may have been called prior to the transition.
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}, null /* boundsChangeTransaction */);
}
private void onEndOfSwipePipToHomeTransition() {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mSwipePipToHomeOverlay = null;
+ return;
+ }
+
final Rect destinationBounds = mPipBoundsState.getBounds();
final SurfaceControl swipeToHomeOverlay = mSwipePipToHomeOverlay;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -655,7 +636,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
null /* callback */, false /* withStartDelay */);
}
}, tx);
- mInSwipePipToHomeTransition = false;
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
mSwipePipToHomeOverlay = null;
}
@@ -679,7 +660,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private void sendOnPipTransitionStarted(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERING_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
}
mPipTransitionController.sendOnPipTransitionStarted(direction);
}
@@ -688,7 +669,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
void sendOnPipTransitionFinished(
@PipAnimationController.TransitionDirection int direction) {
if (direction == TRANSITION_DIRECTION_TO_PIP) {
- mState = State.ENTERED_PIP;
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
}
mPipTransitionController.sendOnPipTransitionFinished(direction);
// Apply the deferred RunningTaskInfo if applicable after all proper callbacks are sent.
@@ -713,7 +694,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
- if (mState == State.UNDEFINED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
return;
}
final WindowContainerToken token = info.token;
@@ -723,9 +704,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
return;
}
clearWaitForFixedRotation();
- mInSwipePipToHomeTransition = false;
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
mPictureInPictureParams = null;
- mState = State.UNDEFINED;
+ mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
// Re-set the PIP bounds to none.
mPipBoundsState.setBounds(new Rect());
mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -750,8 +731,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
- if (mState != State.ENTERED_PIP && mState != State.EXITING_PIP) {
- Log.d(TAG, "Defer onTaskInfoChange in current state: " + mState);
+ if (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP
+ && mPipTransitionState.getTransitionState() != PipTransitionState.EXITING_PIP) {
+ Log.d(TAG, "Defer onTaskInfoChange in current state: "
+ + mPipTransitionState.getTransitionState());
// Defer applying PiP parameters if the task is entering PiP to avoid disturbing
// the animation.
mDeferredTaskInfo = info;
@@ -784,7 +767,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mNextRotation = newRotation;
mWaitForFixedRotation = true;
- if (mState.isInPip()) {
+ if (mPipTransitionState.isInPip()) {
// Fade out the existing PiP to avoid jump cut during seamless rotation.
fadeExistingPip(false /* show */);
}
@@ -795,17 +778,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
if (!mWaitForFixedRotation) {
return;
}
- if (mState == State.TASK_APPEARED) {
- if (mInSwipePipToHomeTransition) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.TASK_APPEARED) {
+ if (mPipTransitionState.getInSwipePipToHomeTransition()) {
onEndOfSwipePipToHomeTransition();
} else {
// Schedule a regular animation to ensure all the callbacks are still being sent.
enterPipWithAlphaAnimation(mPipBoundsAlgorithm.getEntryDestinationBounds(),
mEnterAnimationDuration);
}
- } else if (mState == State.ENTERED_PIP && mHasFadeOut) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERED_PIP
+ && mHasFadeOut) {
fadeExistingPip(true /* show */);
- } else if (mState == State.ENTERING_PIP && mDeferredAnimEndTransaction != null) {
+ } else if (mPipTransitionState.getTransitionState() == PipTransitionState.ENTERING_PIP
+ && mDeferredAnimEndTransaction != null) {
final PipAnimationController.PipTransitionAnimator<?> animator =
mPipAnimationController.getCurrentAnimator();
final Rect destinationBounds = animator.getDestinationBounds();
@@ -859,13 +844,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// note that this can be called when swipe-to-home or fixed-rotation is happening.
// Skip this entirely if that's the case.
final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation
- && (mState != State.ENTERED_PIP);
- if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) {
+ && (mPipTransitionState.getTransitionState() != PipTransitionState.ENTERED_PIP);
+ if ((mPipTransitionState.getInSwipePipToHomeTransition()
+ || waitForFixedRotationOnEnteringPip) && fromRotation) {
if (DEBUG) {
Log.d(TAG, "Skip onMovementBoundsChanged on rotation change"
- + " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition
+ + " InSwipePipToHomeTransition="
+ + mPipTransitionState.getInSwipePipToHomeTransition()
+ " mWaitForFixedRotation=" + mWaitForFixedRotation
- + " mState=" + mState);
+ + " getTransitionState=" + mPipTransitionState.getTransitionState());
}
return;
}
@@ -873,7 +860,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
- final boolean rotatingPip = mState.isInPip() && fromRotation;
+ final boolean rotatingPip = mPipTransitionState.isInPip() && fromRotation;
if (rotatingPip && mWaitForFixedRotation && mHasFadeOut) {
// The position will be used by fade-in animation when the fixed rotation is done.
mPipBoundsState.setBounds(destinationBoundsOut);
@@ -1006,7 +993,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
Rect currentBounds, Rect destinationBounds, float startingAngle, Rect sourceHintRect,
@PipAnimationController.TransitionDirection int direction, int durationMs,
Consumer<Rect> updateBoundsCallback) {
- if (!mState.isInPip()) {
+ if (!mPipTransitionState.isInPip()) {
// TODO: tend to use shouldBlockResizeRequest here as well but need to consider
// the fact that when in exitPip, scheduleAnimateResizePip is executed in the window
// container transaction callback and we want to set the mState immediately.
@@ -1036,7 +1023,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
if (mPipMenuController.isMenuVisible()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
@@ -1114,7 +1101,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
public void scheduleFinishResizePip(Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
@@ -1131,7 +1118,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, destinationBounds)
.resetScale(tx, mLeash, destinationBounds)
- .round(tx, mLeash, mState.isInPip());
+ .round(tx, mLeash, mPipTransitionState.isInPip());
return tx;
}
@@ -1140,7 +1127,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
*/
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
Consumer<Rect> updateBoundsCallback) {
- if (mState.shouldBlockResizeRequest()) {
+ if (mPipTransitionState.shouldBlockResizeRequest()) {
return;
}
if (mWaitForFixedRotation) {
@@ -1384,7 +1371,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f);
animator.setDuration(mCrossFadeAnimationDuration);
animator.addUpdateListener(animation -> {
- if (mState == State.UNDEFINED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
// Could happen if onTaskVanished happens during the animation since we may have
// set a start delay on this animation.
Log.d(TAG, "Task vanished, skip fadeOutAndRemoveOverlay");
@@ -1410,7 +1397,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
private void removeContentOverlay(SurfaceControl surface, Runnable callback) {
- if (mState == State.UNDEFINED) {
+ if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
// Avoid double removal, which is fatal.
return;
}
@@ -1432,7 +1419,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
- pw.println(innerPrefix + "mState=" + mState);
+ pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
}
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 4759550c35c0..6fec1fbda7b0 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
@@ -18,6 +18,10 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PIP;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
@@ -25,9 +29,12 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
import android.app.TaskInfo;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.Surface;
@@ -35,6 +42,7 @@ import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -49,74 +57,218 @@ import com.android.wm.shell.transition.Transitions;
*/
public class PipTransition extends PipTransitionController {
+ private final PipTransitionState mPipTransitionState;
private final int mEnterExitAnimationDuration;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
+ private Rect mExitDestinationBounds = new Rect();
public PipTransition(Context context,
- PipBoundsState pipBoundsState, PipMenuController pipMenuController,
+ PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
+ PipMenuController pipMenuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
Transitions transitions,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
super(pipBoundsState, pipMenuController, pipBoundsAlgorithm,
pipAnimationController, transitions, shellTaskOrganizer);
+ mPipTransitionState = pipTransitionState;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
}
@Override
+ public void setIsFullAnimation(boolean isFullAnimation) {
+ setOneShotAnimationType(isFullAnimation ? ANIM_TYPE_BOUNDS : ANIM_TYPE_ALPHA);
+ }
+
+ /**
+ * Sets the preferred animation type for one time.
+ * This is typically used to set the animation type to
+ * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
+ */
+ private void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+ mOneShotAnimationType = animationType;
+ }
+
+ @Override
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ if (destinationBounds != null) {
+ mExitDestinationBounds.set(destinationBounds);
+ mTransitions.startTransition(TRANSIT_EXIT_PIP, out, this);
+ } else {
+ mTransitions.startTransition(TRANSIT_REMOVE_PIP, out, this);
+ }
+ }
+
+ @Override
public boolean startAnimation(@android.annotation.NonNull IBinder transition,
@android.annotation.NonNull TransitionInfo info,
- @android.annotation.NonNull SurfaceControl.Transaction t,
+ @android.annotation.NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@android.annotation.NonNull Transitions.TransitionFinishCallback finishCallback) {
+
+ if (info.getType() == TRANSIT_EXIT_PIP && info.getChanges().size() == 1) {
+ final TransitionInfo.Change change = info.getChanges().get(0);
+ mFinishCallback = finishCallback;
+ startTransaction.apply();
+ boolean success = startExpandAnimation(change.getTaskInfo(), change.getLeash(),
+ new Rect(mExitDestinationBounds));
+ mExitDestinationBounds.setEmpty();
+ return success;
+ }
+
+ if (info.getType() == TRANSIT_REMOVE_PIP) {
+ startTransaction.apply();
+ finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
+ mPipBoundsState.getDisplayBounds());
+ finishCallback.onTransitionFinished(null, null);
+ return true;
+ }
+
+ // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+ // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+ if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) {
+ return false;
+ }
+
+ // Search for an Enter PiP transition (along with a show wallpaper one)
+ TransitionInfo.Change enterPip = null;
+ TransitionInfo.Change wallpaper = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().configuration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_PINNED) {
- mFinishCallback = finishCallback;
- return startEnterAnimation(change.getTaskInfo(), change.getLeash(), t);
+ enterPip = change;
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ wallpaper = change;
}
}
- return false;
+ if (enterPip == null) {
+ return false;
+ }
+
+ // Show the wallpaper if there is a wallpaper change.
+ if (wallpaper != null) {
+ startTransaction.show(wallpaper.getLeash());
+ startTransaction.setAlpha(wallpaper.getLeash(), 1.f);
+ }
+
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
+ mFinishCallback = finishCallback;
+ return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
+ startTransaction, finishTransaction, enterPip.getStartRotation(),
+ enterPip.getEndRotation());
}
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@NonNull TransitionRequestInfo request) {
- return null;
+ if (request.getType() == TRANSIT_PIP) {
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
+ if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+ wct.setActivityWindowingMode(request.getTriggerTask().token,
+ WINDOWING_MODE_UNDEFINED);
+ final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+ wct.setBounds(request.getTriggerTask().token, destinationBounds);
+ }
+ return wct;
+ } else {
+ return null;
+ }
}
@Override
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
SurfaceControl.Transaction tx) {
+
+ if (isInPipDirection(direction)) {
+ mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
prepareFinishResizeTransaction(taskInfo, destinationBounds,
direction, tx, wct);
- mFinishCallback.onTransitionFinished(wct, null);
+ mFinishCallback.onTransitionFinished(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) {
+ t.merge(tx);
+ t.apply();
+ }
+ });
finishResizeForMenu(destinationBounds);
}
+ private boolean startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
+ final Rect destinationBounds) {
+ PipAnimationController.PipTransitionAnimator animator =
+ mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(),
+ mPipBoundsState.getBounds(), destinationBounds, null,
+ TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
+
+ animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP)
+ .setPipAnimationCallback(mPipAnimationCallback)
+ .setDuration(mEnterExitAnimationDuration)
+ .start();
+
+ return true;
+ }
+
private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash,
- final SurfaceControl.Transaction t) {
+ final SurfaceControl.Transaction startTransaction,
+ final SurfaceControl.Transaction finishTransaction,
+ final int startRotation, final int endRotation) {
setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams,
taskInfo.topActivityInfo);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
PipAnimationController.PipTransitionAnimator animator;
+ finishTransaction.setPosition(leash, destinationBounds.left, destinationBounds.top);
+ if (taskInfo.pictureInPictureParams != null
+ && taskInfo.pictureInPictureParams.isAutoEnterEnabled()
+ && mPipTransitionState.getInSwipePipToHomeTransition()) {
+ mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+ // PiP menu is attached late in the process here to avoid any artifacts on the leash
+ // caused by addShellRoot when in gesture navigation mode.
+ mPipMenuController.attach(leash);
+ SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+ tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, new float[9])
+ .setPosition(leash, destinationBounds.left, destinationBounds.top)
+ .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
+ startTransaction.merge(tx);
+ startTransaction.apply();
+ mPipBoundsState.setBounds(destinationBounds);
+ onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx);
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+ mFinishCallback = null;
+ mPipTransitionState.setInSwipePipToHomeTransition(false);
+ return true;
+ }
+
+ int rotationDelta = deltaRotation(endRotation, startRotation);
+ if (rotationDelta != Surface.ROTATION_0) {
+ Matrix tmpTransform = new Matrix();
+ tmpTransform.postRotate(rotationDelta == Surface.ROTATION_90
+ ? Surface.ROTATION_270 : Surface.ROTATION_90);
+ startTransaction.setMatrix(leash, tmpTransform, new float[9]);
+ }
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
final Rect sourceHintRect =
PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds);
animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP,
- 0 /* startingAngle */, Surface.ROTATION_0);
+ 0 /* startingAngle */, rotationDelta);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
- t.setAlpha(leash, 0f);
- t.apply();
+ startTransaction.setAlpha(leash, 0f);
+ // PiP menu is attached late in the process here to avoid any artifacts on the leash
+ // caused by addShellRoot when in gesture navigation mode.
+ mPipMenuController.attach(leash);
animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
0f, 1f);
mOneShotAnimationType = ANIM_TYPE_BOUNDS;
@@ -124,10 +276,12 @@ public class PipTransition extends PipTransitionController {
throw new RuntimeException("Unrecognized animation type: "
+ mOneShotAnimationType);
}
+ startTransaction.apply();
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
+
return true;
}
@@ -158,6 +312,5 @@ public class PipTransition extends PipTransitionController {
}
wct.setBounds(taskInfo.token, taskBounds);
- wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index d801c918973a..dbf603ca72d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -19,7 +19,6 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
-import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
@@ -29,6 +28,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.transition.Transitions;
@@ -46,6 +46,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
protected final PipBoundsState mPipBoundsState;
protected final ShellTaskOrganizer mShellTaskOrganizer;
protected final PipMenuController mPipMenuController;
+ protected final Transitions mTransitions;
private final Handler mMainHandler;
private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -55,12 +56,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
public void onPipAnimationStart(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
final int direction = animator.getTransitionDirection();
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- // TODO (b//169221267): Add jank listener for transactions without buffer
- // updates.
- //InteractionJankMonitor.getInstance().begin(
- // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
- }
sendOnPipTransitionStarted(direction);
}
@@ -74,12 +69,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
}
onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
sendOnPipTransitionFinished(direction);
- if (direction == TRANSITION_DIRECTION_TO_PIP) {
- // TODO (b//169221267): Add jank listener for transactions without buffer
- // updates.
- //InteractionJankMonitor.getInstance().end(
- // InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
- }
}
@Override
@@ -98,6 +87,22 @@ public abstract class PipTransitionController implements Transitions.TransitionH
SurfaceControl.Transaction tx) {
}
+ /**
+ * Called to inform the transition that the animation should start with the assumption that
+ * PiP is not animating from its original bounds, but rather a continuation of another
+ * animation. For example, gesture navigation would first fade out the PiP activity, and the
+ * transition should be responsible to animate in (such as fade in) the PiP.
+ */
+ public void setIsFullAnimation(boolean isFullAnimation) {
+ }
+
+ /**
+ * Called when the Shell wants to starts a transition/animation.
+ */
+ public void startTransition(Rect destinationBounds, WindowContainerTransaction out) {
+ // Default implementation does nothing.
+ }
+
public PipTransitionController(PipBoundsState pipBoundsState,
PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController, Transitions transitions,
@@ -107,6 +112,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
mShellTaskOrganizer = shellTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipAnimationController = pipAnimationController;
+ mTransitions = transitions;
mMainHandler = new Handler(Looper.getMainLooper());
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.addHandler(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java
new file mode 100644
index 000000000000..85e56b7dd99f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.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 com.android.wm.shell.pip;
+
+import android.annotation.IntDef;
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to keep track of PiP leash state as it appears and animates by {@link PipTaskOrganizer} and
+ * {@link PipTransition}.
+ */
+public class PipTransitionState {
+
+ public static final int UNDEFINED = 0;
+ public static final int TASK_APPEARED = 1;
+ public static final int ENTRY_SCHEDULED = 2;
+ public static final int ENTERING_PIP = 3;
+ public static final int ENTERED_PIP = 4;
+ public static final int EXITING_PIP = 5;
+
+ /**
+ * 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.
+ * See also {@link PipTaskOrganizer#startSwipePipToHome(ComponentName, ActivityInfo,
+ * PictureInPictureParams)}.
+ */
+ private boolean mInSwipePipToHomeTransition;
+
+ // Not a complete set of states but serves what we want right now.
+ @IntDef(prefix = { "TRANSITION_STATE_" }, value = {
+ UNDEFINED,
+ TASK_APPEARED,
+ ENTRY_SCHEDULED,
+ ENTERING_PIP,
+ ENTERED_PIP,
+ EXITING_PIP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransitionState {}
+
+ private @TransitionState int mState;
+
+ public PipTransitionState() {
+ mState = UNDEFINED;
+ }
+
+ public void setTransitionState(@TransitionState int state) {
+ mState = state;
+ }
+
+ public @TransitionState int getTransitionState() {
+ return mState;
+ }
+
+ public boolean isInPip() {
+ return mState >= TASK_APPEARED
+ && mState != EXITING_PIP;
+ }
+
+ public void setInSwipePipToHomeTransition(boolean inSwipePipToHomeTransition) {
+ mInSwipePipToHomeTransition = inSwipePipToHomeTransition;
+ }
+
+ public boolean getInSwipePipToHomeTransition() {
+ return mInSwipePipToHomeTransition;
+ }
+ /**
+ * Resize request can be initiated in other component, ignore if we are no longer in PIP,
+ * still waiting for animation or we're exiting from it.
+ *
+ * @return {@code true} if the resize request should be blocked/ignored.
+ */
+ public boolean shouldBlockResizeRequest() {
+ return mState < ENTERING_PIP
+ || mState == EXITING_PIP;
+ }
+}
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 4f3ec96968b2..ac02075a49d8 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
@@ -21,7 +21,16 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import android.app.ActivityManager;
@@ -52,6 +61,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
@@ -67,6 +77,7 @@ import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
@@ -74,6 +85,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.Objects;
@@ -445,11 +457,18 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return;
}
Runnable updateDisplayLayout = () -> {
+ final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
+ && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
mPipBoundsState.setDisplayLayout(layout);
+ final WindowContainerTransaction wct =
+ fromRotation ? new WindowContainerTransaction() : null;
updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, false /* fromImeAdjustment */,
+ fromRotation, false /* fromImeAdjustment */,
false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
+ wct /* windowContainerTransaction */);
+ if (wct != null) {
+ mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+ }
};
if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
@@ -528,6 +547,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private void setPinnedStackAnimationType(int animationType) {
mPipTaskOrganizer.setOneShotAnimationType(animationType);
+ mPipTransitionController.setIsFullAnimation(
+ animationType == PipAnimationController.ANIM_TYPE_BOUNDS);
}
private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
@@ -564,8 +585,37 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds, overlay);
}
+ private String getTransitionTag(int direction) {
+ switch (direction) {
+ case TRANSITION_DIRECTION_TO_PIP:
+ return "TRANSITION_TO_PIP";
+ case TRANSITION_DIRECTION_LEAVE_PIP:
+ return "TRANSITION_LEAVE_PIP";
+ case TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN:
+ return "TRANSITION_LEAVE_PIP_TO_SPLIT_SCREEN";
+ case TRANSITION_DIRECTION_REMOVE_STACK:
+ return "TRANSITION_REMOVE_STACK";
+ case TRANSITION_DIRECTION_SNAP_AFTER_RESIZE:
+ return "TRANSITION_SNAP_AFTER_RESIZE";
+ case TRANSITION_DIRECTION_USER_RESIZE:
+ return "TRANSITION_USER_RESIZE";
+ case TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND:
+ return "TRANSITION_EXPAND_OR_UNEXPAND";
+ default:
+ return "TRANSITION_LEAVE_UNKNOWN";
+ }
+ }
+
@Override
public void onPipTransitionStarted(int direction, Rect pipBounds) {
+ // Begin InteractionJankMonitor with PIP transition CUJs
+ final InteractionJankMonitor.Configuration.Builder builder =
+ InteractionJankMonitor.Configuration.Builder.withSurface(
+ CUJ_PIP_TRANSITION, mContext, mPipTaskOrganizer.getSurfaceControl())
+ .setTag(getTransitionTag(direction))
+ .setTimeout(2000);
+ InteractionJankMonitor.getInstance().begin(builder);
+
if (isOutPipDirection(direction)) {
// Exiting PIP, save the reentry state to restore to when re-entering.
saveReentryState(pipBounds);
@@ -604,6 +654,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
private void onPipTransitionFinishedOrCanceled(int direction) {
+ // End InteractionJankMonitor with PIP transition by CUJs
+ InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION);
+
// Re-enable touches after the animation completes
mTouchHandler.setTouchEnabled(true);
mTouchHandler.onPinnedStackAnimationEnded(direction);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 1da9577fe49a..82092ac5ac3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -158,14 +158,16 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
- mMainExecutor.executeDelayed(() -> {
- mMotionHelper.notifyDismissalPending();
- mMotionHelper.animateDismiss();
- hideDismissTargetMaybe();
-
- mPipUiEventLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
- }, 0);
+ if (mEnableDismissDragToEdge) {
+ mMainExecutor.executeDelayed(() -> {
+ mMotionHelper.notifyDismissalPending();
+ mMotionHelper.animateDismiss();
+ hideDismissTargetMaybe();
+
+ mPipUiEventLogger.log(
+ PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
+ }, 0);
+ }
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index b7caf72641a3..551476dc9d54 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -58,7 +58,8 @@ public class TvPipTransition extends PipTransitionController {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @android.annotation.NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
return false;
}
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 1fc4d12def1f..ab3cbd655ea1 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
@@ -47,6 +47,8 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
/** Callback for size compat UI interaction. */
public interface SizeCompatUICallback {
+ /** Called when the size compat restart button appears. */
+ void onSizeCompatRestartButtonAppeared(int taskId);
/** Called when the size compat restart button is clicked. */
void onSizeCompatRestartButtonClicked(int taskId);
}
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
index a5e96d14dde6..7cf95593dbaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
@@ -45,7 +46,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
class SizeCompatUILayout {
private static final String TAG = "SizeCompatUILayout";
- private final SyncTransactionQueue mSyncQueue;
+ final SyncTransactionQueue mSyncQueue;
private final SizeCompatUIController.SizeCompatUICallback mCallback;
private Context mContext;
private Configuration mTaskConfig;
@@ -105,6 +106,8 @@ class SizeCompatUILayout {
mShouldShowHint = false;
createSizeCompatHint();
}
+
+ mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
}
/** Creates the restart button hint window. */
@@ -306,6 +309,10 @@ class SizeCompatUILayout {
private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
mSyncQueue.runInSync(t -> {
+ if (!leash.isValid()) {
+ Log.w(TAG, "The leash has been released.");
+ return;
+ }
t.setPosition(leash, positionX, positionY);
// The size compat UI should be the topmost child of the Task in case there can be more
// than one children.
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
index f634c4586e39..82f69c3e2985 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
@@ -110,7 +110,8 @@ class SizeCompatUIWindowManager extends WindowlessWindowManager {
}
if (mLeash != null) {
- new SurfaceControl.Transaction().remove(mLeash).apply();
+ final SurfaceControl leash = mLeash;
+ mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
mLeash = null;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 8f0892fdcbba..6ec514bd8331 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.window.IRemoteTransition;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
@@ -77,9 +79,24 @@ interface ISplitScreen {
int position, in Bundle options) = 9;
/**
- * Starts tasks simultaneously in one transition. The first task in the list will be in the
- * main-stage and on the left/top.
+ * Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
in Bundle sideOptions, int sidePosition, in IRemoteTransition remoteTransition) = 10;
+
+ /**
+ * Version of startTasks using legacy transition system.
+ */
+ oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+ int sideTaskId, in Bundle sideOptions, int sidePosition,
+ in RemoteAnimationAdapter adapter) = 11;
+
+ /**
+ * Blocking call that notifies and gets additional split-screen targets when entering
+ * recents (for example: the dividerBar).
+ * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
+ * @param appTargets apps that will be re-parented to display area
+ */
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+ in RemoteAnimationTarget[] appTargets) = 12;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
new file mode 100644
index 000000000000..0b763f2d05f7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.splitscreen;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+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.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.view.WindowlessWindowManager;
+
+import com.android.wm.shell.R;
+
+/**
+ * Handles drawing outline of the bounds of provided root surface. The outline will be drown with
+ * the consideration of display insets like status bar, navigation bar and display cutout.
+ */
+class OutlineManager extends WindowlessWindowManager {
+ private static final String WINDOW_NAME = "SplitOutlineLayer";
+ private final Context mContext;
+ private final Rect mOutlineBounds = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private SurfaceControlViewHost mViewHost;
+ private SurfaceControl mHostLeash;
+ private SurfaceControl mLeash;
+ private int mOutlineColor;
+
+ OutlineManager(Context context, Configuration configuration) {
+ super(configuration, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
+ null /* options */);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ b.setParent(mHostLeash);
+ }
+
+ boolean drawOutlineBounds(Rect rootBounds) {
+ if (mLeash == null || mViewHost == null) return false;
+
+ computeOutlineBounds(mContext, rootBounds, mTmpBounds);
+ if (mOutlineBounds.equals(mTmpBounds)) {
+ return false;
+ }
+ mOutlineBounds.set(mTmpBounds);
+
+ ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
+ final WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ mViewHost.relayout(lp);
+
+ return true;
+ }
+
+ void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
+ if (mLeash != null || mViewHost != null) return;
+
+ mHostLeash = hostLeash;
+ mOutlineColor = color;
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
+ .inflate(R.layout.split_outline, null);
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.token = new Binder();
+ lp.setTitle(WINDOW_NAME);
+ lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
+ // TRUSTED_OVERLAY for windowless window without input channel.
+ mViewHost.setView(rootView, lp);
+ mLeash = getSurfaceControl(mViewHost.getWindowToken());
+ t.setLayer(mLeash, Integer.MAX_VALUE);
+ }
+
+ void release() {
+ if (mViewHost != null) {
+ mViewHost.release();
+ }
+ }
+
+ private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
+ computeDisplayStableBounds(context, outBounds);
+ outBounds.intersect(rootBounds);
+ // Offset the coordinate from screen based to surface based.
+ outBounds.offset(-rootBounds.left, -rootBounds.top);
+ }
+
+ private static void computeDisplayStableBounds(Context context, Rect outBounds) {
+ final WindowMetrics windowMetrics =
+ context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
+ outBounds.set(windowMetrics.getBounds());
+ outBounds.inset(windowMetrics.getWindowInsets().getInsets(
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
new file mode 100644
index 000000000000..71d48eeca71d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/** Root layout for holding split outline. */
+public class OutlineRoot extends FrameLayout {
+ public OutlineRoot(@NonNull Context context) {
+ super(context);
+ }
+
+ public OutlineRoot(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private OutlineView mOutlineView;
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mOutlineView = findViewById(R.id.split_outline);
+ }
+
+ void updateOutlineBounds(Rect bounds, int color) {
+ mOutlineView.updateOutlineBounds(bounds, color);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
new file mode 100644
index 000000000000..ea66180e3dd2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.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.wm.shell.splitscreen;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.R;
+
+/** View for drawing split outline. */
+public class OutlineView extends View {
+ private final Paint mPaint = new Paint();
+ private final Rect mBounds = new Rect();
+
+ public OutlineView(@NonNull Context context) {
+ super(context);
+ }
+
+ public OutlineView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(getResources()
+ .getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
+ }
+
+ void updateOutlineBounds(Rect bounds, int color) {
+ if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
+ mBounds.set(bounds);
+ mPaint.setColor(color);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBounds.isEmpty()) return;
+ final Path path = new Region(mBounds).getBoundaryPath();
+ canvas.drawPath(path, mPaint);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 82f95a4f32ea..2b19bb965fed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,7 +16,10 @@
package com.android.wm.shell.splitscreen;
+import android.annotation.CallSuper;
import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -28,15 +31,19 @@ import com.android.wm.shell.common.SyncTransactionQueue;
/**
* Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
* here. All other task are launch in the {@link MainStage}.
+ *
* @see StageCoordinator
*/
class SideStage extends StageTaskListener {
private static final String TAG = SideStage.class.getSimpleName();
+ private final Context mContext;
+ private OutlineManager mOutlineManager;
- SideStage(ShellTaskOrganizer taskOrganizer, int displayId,
+ SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
SurfaceSession surfaceSession) {
super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ mContext = context;
}
void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds,
@@ -44,7 +51,7 @@ class SideStage extends StageTaskListener {
final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.setBounds(rootToken, rootBounds)
.reparent(task.token, rootToken, true /* onTop*/)
- // Moving the root task to top after the child tasks were repareted , or the root
+ // Moving the root task to top after the child tasks were reparented , or the root
// task cannot be visible and focused.
.reorder(rootToken, true /* onTop */);
}
@@ -69,4 +76,34 @@ class SideStage extends StageTaskListener {
wct.reparent(task.token, newParent, false /* onTop */);
return true;
}
+
+ void enableOutline(boolean enable) {
+ if (enable) {
+ if (mOutlineManager == null && mRootTaskInfo != null) {
+ mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration);
+ mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW));
+ updateOutlineBounds();
+ }
+ } else {
+ if (mOutlineManager != null) {
+ mOutlineManager.release();
+ mOutlineManager = null;
+ }
+ }
+ }
+
+ private void updateOutlineBounds() {
+ if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
+ mOutlineManager.drawOutlineBounds(
+ mRootTaskInfo.configuration.windowConfiguration.getBounds());
+ }
+
+ @Override
+ @CallSuper
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ super.onTaskInfoChanged(taskInfo);
+ if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
+ updateOutlineBounds();
+ }
+ }
}
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 002bfb6e429f..e86462f666c9 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
@@ -17,10 +17,13 @@
package com.android.wm.shell.splitscreen;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import java.util.concurrent.Executor;
+
/**
* Interface to engage split-screen feature.
* TODO: Figure out which of these are actually needed outside of the Shell
@@ -53,10 +56,18 @@ public interface SplitScreen {
/** Callback interface for listening to changes in a split-screen stage. */
interface SplitScreenListener {
- void onStagePositionChanged(@StageType int stage, @SplitPosition int position);
- void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
+ default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {}
+ default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {}
+ default void onSplitVisibilityChanged(boolean visible) {}
}
+ /** Registers listener that gets split screen callback. */
+ void registerSplitScreenListener(@NonNull SplitScreenListener listener,
+ @NonNull Executor executor);
+
+ /** Unregisters listener that gets split screen callback. */
+ void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
+
/**
* Returns a binder that can be passed to an external process to manipulate SplitScreen.
*/
@@ -64,6 +75,18 @@ public interface SplitScreen {
return null;
}
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ void onKeyguardOccludedChanged(boolean occluded);
+
+ /**
+ * Called when the visibility of the keyguard changes.
+ * @param showing Indicates if the keyguard is now visible.
+ */
+ void onKeyguardVisibilityChanged(boolean showing);
+
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
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 9a457b5fd88e..67223c3590a5 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
@@ -17,14 +17,12 @@
package com.android.wm.shell.splitscreen;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitLayout.SPLIT_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;
@@ -38,13 +36,23 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.view.SurfaceSession;
import android.window.IRemoteTransition;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.logging.InstanceId;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -55,10 +63,11 @@ import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
-import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
/**
* Class manages split-screen multitasking mode and implements the main interface
@@ -78,6 +87,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final DisplayImeController mDisplayImeController;
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
+ private final SplitscreenEventLogger mLogger;
private StageCoordinator mStageCoordinator;
@@ -94,6 +104,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mDisplayImeController = displayImeController;
mTransitions = transitions;
mTransactionPool = transactionPool;
+ mLogger = new SplitscreenEventLogger();
}
public SplitScreen asSplitScreen() {
@@ -115,7 +126,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, mTransitions,
- mTransactionPool);
+ mTransactionPool, mLogger);
}
}
@@ -140,8 +151,12 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.removeFromSideStage(taskId);
}
+ public void setSideStageOutline(boolean enable) {
+ mStageCoordinator.setSideStageOutline(enable);
+ }
+
public void setSideStagePosition(@SplitPosition int sideStagePosition) {
- mStageCoordinator.setSideStagePosition(sideStagePosition);
+ mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */);
}
public void setSideStageVisibility(boolean visible) {
@@ -153,8 +168,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
}
- public void exitSplitScreen() {
- mStageCoordinator.exitSplitScreen();
+ public void exitSplitScreen(int exitReason) {
+ mStageCoordinator.exitSplitScreen(exitReason);
+ }
+
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mStageCoordinator.onKeyguardOccludedChanged(occluded);
+ }
+
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mStageCoordinator.onKeyguardVisibilityChanged(showing);
}
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
@@ -175,7 +198,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startTask(int taskId, @SplitScreen.StageType int stage,
@SplitPosition int position, @Nullable Bundle options) {
- options = resolveStartStage(stage, position, options);
+ options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
try {
ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
@@ -187,7 +210,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startShortcut(String packageName, String shortcutId,
@SplitScreen.StageType int stage, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
- options = resolveStartStage(stage, position, options);
+ options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */);
try {
LauncherApps launcherApps =
@@ -202,64 +225,92 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void startIntent(PendingIntent intent, Intent fillInIntent,
@SplitScreen.StageType int stage, @SplitPosition int position,
@Nullable Bundle options) {
- options = resolveStartStage(stage, position, options);
-
- try {
- intent.send(mContext, 0, fillInIntent, null, null, null, options);
- } catch (PendingIntent.CanceledException e) {
- Slog.e(TAG, "Failed to launch activity", e);
+ if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ startIntentLegacy(intent, fillInIntent, stage, position, options);
+ return;
}
+ mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options,
+ null /* remote */);
}
- private Bundle resolveStartStage(@SplitScreen.StageType int stage,
- @SplitPosition int position, @Nullable Bundle options) {
- switch (stage) {
- case STAGE_TYPE_UNDEFINED: {
- // Use the stage of the specified position is valid.
- if (position != SPLIT_POSITION_UNDEFINED) {
- if (position == mStageCoordinator.getSideStagePosition()) {
- options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
- } else {
- options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
+ private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+ @SplitScreen.StageType int stage, @SplitPosition int position,
+ @Nullable Bundle options) {
+ final boolean wasInSplit = isSplitScreenVisible();
+
+ LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback,
+ SurfaceControl.Transaction t) {
+ boolean cancelled = apps == null || apps.length == 0;
+ mStageCoordinator.updateSurfaceBounds(null /* layout */, t);
+ if (cancelled) {
+ if (!wasInSplit) {
+ final WindowContainerTransaction undoWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, undoWct);
+ mSyncQueue.queue(undoWct);
+ mSyncQueue.runInSync(undoT -> {
+ // looks weird, but we want undoT to execute after t but still want the
+ // rest of the syncQueue runnables to aggregate.
+ t.merge(undoT);
+ undoT.merge(t);
+ });
+ return;
}
} else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- mStageCoordinator.exitSplitScreen();
- }
- break;
- }
- case STAGE_TYPE_SIDE: {
- if (position != SPLIT_POSITION_UNDEFINED) {
- mStageCoordinator.setSideStagePosition(position);
- } else {
- position = mStageCoordinator.getSideStagePosition();
- }
- if (options == null) {
- options = new Bundle();
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ }
+ }
}
- mStageCoordinator.updateActivityOptions(options, position);
- break;
- }
- case STAGE_TYPE_MAIN: {
- if (position != SPLIT_POSITION_UNDEFINED) {
- // Set the side stage opposite of what we want to the main stage.
- final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
- mStageCoordinator.setSideStagePosition(sideStagePosition);
- } else {
- position = mStageCoordinator.getMainStagePosition();
+ RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget();
+ if (divider.leash != null) {
+ t.show(divider.leash);
}
- if (options == null) {
- options = new Bundle();
+ t.apply();
+ if (cancelled) return;
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error finishing legacy transition: ", e);
}
- mStageCoordinator.updateActivityOptions(options, position);
- break;
}
- default:
- throw new IllegalArgumentException("Unknown stage=" + stage);
+ };
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ options = mStageCoordinator.resolveStartStage(stage, position, options, wct);
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ }
+
+ RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
+ if (!isSplitScreenVisible()) return null;
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("RecentsAnimationSplitTasks")
+ .setHidden(false)
+ .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
+ mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
+ SurfaceControl sc = builder.build();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ for (RemoteAnimationTarget appTarget : apps) {
+ // TODO(b/195958376) set the correct layer/z-order in transaction for the new surface
+ transaction.reparent(appTarget.leash, sc);
+ transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
+ appTarget.screenSpaceBounds.top);
}
+ transaction.apply();
+ transaction.close();
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ }
- return options;
+ /**
+ * Sets drag info to be logged when splitscreen is entered.
+ */
+ public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+ mStageCoordinator.logOnDroppedToSplit(position, dragSessionId);
}
public void dump(@NonNull PrintWriter pw, String prefix) {
@@ -275,6 +326,38 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@ExternalThread
private class SplitScreenImpl implements SplitScreen {
private ISplitScreenImpl mISplitScreen;
+ private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>();
+ private final SplitScreen.SplitScreenListener mListener = new SplitScreenListener() {
+ @Override
+ public void onStagePositionChanged(int stage, int position) {
+ for (int i = 0; i < mExecutors.size(); i++) {
+ final int index = i;
+ mExecutors.valueAt(index).execute(() -> {
+ mExecutors.keyAt(index).onStagePositionChanged(stage, position);
+ });
+ }
+ }
+
+ @Override
+ public void onTaskStageChanged(int taskId, int stage, boolean visible) {
+ for (int i = 0; i < mExecutors.size(); i++) {
+ final int index = i;
+ mExecutors.valueAt(index).execute(() -> {
+ mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible);
+ });
+ }
+ }
+
+ @Override
+ public void onSplitVisibilityChanged(boolean visible) {
+ for (int i = 0; i < mExecutors.size(); i++) {
+ final int index = i;
+ mExecutors.valueAt(index).execute(() -> {
+ mExecutors.keyAt(index).onSplitVisibilityChanged(visible);
+ });
+ }
+ }
+ };
@Override
public ISplitScreen createExternalInterface() {
@@ -284,6 +367,48 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
return mISplitScreen;
}
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onKeyguardOccludedChanged(occluded);
+ });
+ }
+
+ @Override
+ public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) {
+ if (mExecutors.containsKey(listener)) return;
+
+ mMainExecutor.execute(() -> {
+ if (mExecutors.size() == 0) {
+ SplitScreenController.this.registerSplitScreenListener(mListener);
+ }
+
+ mExecutors.put(listener, executor);
+ });
+
+ executor.execute(() -> {
+ mStageCoordinator.sendStatusToListener(listener);
+ });
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mMainExecutor.execute(() -> {
+ mExecutors.remove(listener);
+
+ if (mExecutors.size() == 0) {
+ SplitScreenController.this.unregisterSplitScreenListener(mListener);
+ }
+ });
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.onKeyguardVisibilityChanged(showing);
+ });
+ }
}
/**
@@ -380,7 +505,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public void exitSplitScreen() {
executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
(controller) -> {
- controller.exitSplitScreen();
+ controller.exitSplitScreen(
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
});
}
@@ -417,6 +543,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
+ public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController, "startTasks",
+ (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
+ mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
+ adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition,
@@ -444,5 +580,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
controller.startIntent(intent, fillInIntent, stage, position, options);
});
}
+
+ @Override
+ public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+ RemoteAnimationTarget[] apps) {
+ final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
+ executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
+ (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps),
+ true /* blocking */);
+ return out[0];
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index c37789ecbc9d..69d0be6abc0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -84,17 +84,19 @@ class SplitScreenTransitions {
}
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) {
mFinishCallback = finishCallback;
mAnimatingTransition = transition;
if (mRemoteHandler != null) {
- mRemoteHandler.startAnimation(transition, info, t, mRemoteFinishCB);
+ mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction,
+ mRemoteFinishCB);
mRemoteHandler = null;
return;
}
- playInternalAnimation(transition, info, t, mainRoot, sideRoot);
+ playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot);
}
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
new file mode 100644
index 000000000000..319079baaccf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.splitscreen;
+
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+
+/**
+ * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
+ */
+public class SplitscreenEventLogger {
+
+ // Used to generate instance ids for this drag if one is not provided
+ private final InstanceIdSequence mIdSequence;
+
+ // The instance id for the current splitscreen session (from start to end)
+ private InstanceId mLoggerSessionId;
+
+ // Drag info
+ private @SplitPosition int mDragEnterPosition;
+ private InstanceId mDragEnterSessionId;
+
+ // For deduping async events
+ private int mLastMainStagePosition = -1;
+ private int mLastMainStageUid = -1;
+ private int mLastSideStagePosition = -1;
+ private int mLastSideStageUid = -1;
+ private float mLastSplitRatio = -1f;
+
+ public SplitscreenEventLogger() {
+ mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Return whether a splitscreen session has started.
+ */
+ public boolean hasStartedSession() {
+ return mLoggerSessionId != null;
+ }
+
+ /**
+ * May be called before logEnter() to indicate that the session was started from a drag.
+ */
+ public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
+ mDragEnterPosition = position;
+ mDragEnterSessionId = dragSessionId;
+ }
+
+ /**
+ * Logs when the user enters splitscreen.
+ */
+ public void logEnter(float splitRatio,
+ @SplitPosition int mainStagePosition, int mainStageUid,
+ @SplitPosition int sideStagePosition, int sideStageUid,
+ boolean isLandscape) {
+ mLoggerSessionId = mIdSequence.newInstanceId();
+ int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
+ ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
+ : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+ updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+ mainStageUid);
+ updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+ sideStageUid);
+ updateSplitRatioState(splitRatio);
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
+ enterReason,
+ 0 /* exitReason */,
+ splitRatio,
+ mLastMainStagePosition,
+ mLastMainStageUid,
+ mLastSideStagePosition,
+ mLastSideStageUid,
+ mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
+ mLoggerSessionId.getId());
+ }
+
+ /**
+ * Logs when the user exits splitscreen. Only one of the main or side stages should be
+ * specified to indicate which position was focused as a part of exiting (both can be unset).
+ */
+ public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid,
+ @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
+ if (mLoggerSessionId == null) {
+ // Ignore changes until we've started logging the session
+ return;
+ }
+ if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
+ && sideStagePosition != SPLIT_POSITION_UNDEFINED)
+ || (mainStageUid != 0 && sideStageUid != 0)) {
+ throw new IllegalArgumentException("Only main or side stage should be set");
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
+ 0 /* enterReason */,
+ exitReason,
+ 0f /* splitRatio */,
+ getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+ mainStageUid,
+ getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+ sideStageUid,
+ 0 /* dragInstanceId */,
+ mLoggerSessionId.getId());
+
+ // Reset states
+ mLoggerSessionId = null;
+ mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
+ mDragEnterSessionId = null;
+ mLastMainStagePosition = -1;
+ mLastMainStageUid = -1;
+ mLastSideStagePosition = -1;
+ mLastSideStageUid = -1;
+ }
+
+ /**
+ * Logs when an app in the main stage changes.
+ */
+ public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid,
+ boolean isLandscape) {
+ if (mLoggerSessionId == null) {
+ // Ignore changes until we've started logging the session
+ return;
+ }
+ if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition,
+ isLandscape), mainStageUid)) {
+ // Ignore if there are no user perceived changes
+ return;
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
+ 0 /* enterReason */,
+ 0 /* exitReason */,
+ 0f /* splitRatio */,
+ mLastMainStagePosition,
+ mLastMainStageUid,
+ 0 /* sideStagePosition */,
+ 0 /* sideStageUid */,
+ 0 /* dragInstanceId */,
+ mLoggerSessionId.getId());
+ }
+
+ /**
+ * Logs when an app in the side stage changes.
+ */
+ public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid,
+ boolean isLandscape) {
+ if (mLoggerSessionId == null) {
+ // Ignore changes until we've started logging the session
+ return;
+ }
+ if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition,
+ isLandscape), sideStageUid)) {
+ // Ignore if there are no user perceived changes
+ return;
+ }
+
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
+ 0 /* enterReason */,
+ 0 /* exitReason */,
+ 0f /* splitRatio */,
+ 0 /* mainStagePosition */,
+ 0 /* mainStageUid */,
+ mLastSideStagePosition,
+ mLastSideStageUid,
+ 0 /* dragInstanceId */,
+ mLoggerSessionId.getId());
+ }
+
+ /**
+ * Logs when the splitscreen ratio changes.
+ */
+ public void logResize(float splitRatio) {
+ if (mLoggerSessionId == null) {
+ // Ignore changes until we've started logging the session
+ return;
+ }
+ if (splitRatio <= 0f || splitRatio >= 1f) {
+ // Don't bother reporting resizes that end up dismissing the split, that will be logged
+ // via the exit event
+ return;
+ }
+ if (!updateSplitRatioState(splitRatio)) {
+ // Ignore if there are no user perceived changes
+ return;
+ }
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
+ 0 /* enterReason */,
+ 0 /* exitReason */,
+ mLastSplitRatio,
+ 0 /* mainStagePosition */, 0 /* mainStageUid */,
+ 0 /* sideStagePosition */, 0 /* sideStageUid */,
+ 0 /* dragInstanceId */,
+ mLoggerSessionId.getId());
+ }
+
+ /**
+ * Logs when the apps in splitscreen are swapped.
+ */
+ public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
+ @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
+ if (mLoggerSessionId == null) {
+ // Ignore changes until we've started logging the session
+ return;
+ }
+
+ updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+ mainStageUid);
+ updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+ sideStageUid);
+ FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+ FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
+ 0 /* enterReason */,
+ 0 /* exitReason */,
+ 0f /* splitRatio */,
+ mLastMainStagePosition,
+ mLastMainStageUid,
+ mLastSideStagePosition,
+ mLastSideStageUid,
+ 0 /* dragInstanceId */,
+ mLoggerSessionId.getId());
+ }
+
+ private boolean updateMainStageState(int mainStagePosition, int mainStageUid) {
+ boolean changed = (mLastMainStagePosition != mainStagePosition)
+ || (mLastMainStageUid != mainStageUid);
+ if (!changed) {
+ return false;
+ }
+
+ mLastMainStagePosition = mainStagePosition;
+ mLastMainStageUid = mainStageUid;
+ return true;
+ }
+
+ private boolean updateSideStageState(int sideStagePosition, int sideStageUid) {
+ boolean changed = (mLastSideStagePosition != sideStagePosition)
+ || (mLastSideStageUid != sideStageUid);
+ if (!changed) {
+ return false;
+ }
+
+ mLastSideStagePosition = sideStagePosition;
+ mLastSideStageUid = sideStageUid;
+ return true;
+ }
+
+ private boolean updateSplitRatioState(float splitRatio) {
+ boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0;
+ if (!changed) {
+ return false;
+ }
+
+ mLastSplitRatio = splitRatio;
+ return true;
+ }
+
+ public int getDragEnterReasonFromSplitPosition(@SplitPosition int position,
+ boolean isLandscape) {
+ if (isLandscape) {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT;
+ } else {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM;
+ }
+ }
+
+ private int getMainStagePositionFromSplitPosition(@SplitPosition int position,
+ boolean isLandscape) {
+ if (position == SPLIT_POSITION_UNDEFINED) {
+ return 0;
+ }
+ if (isLandscape) {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT;
+ } else {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM;
+ }
+ }
+
+ private int getSideStagePositionFromSplitPosition(@SplitPosition int position,
+ boolean isLandscape) {
+ if (position == SPLIT_POSITION_UNDEFINED) {
+ return 0;
+ }
+ if (isLandscape) {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT;
+ } else {
+ return position == SPLIT_POSITION_TOP_OR_LEFT
+ ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP
+ : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM;
+ }
+ }
+}
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 0264c5a1c55a..736fae41f2ed 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
@@ -20,11 +20,19 @@ import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
-
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
+
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
@@ -35,6 +43,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
import static com.android.wm.shell.transition.Transitions.isClosingType;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -42,11 +51,23 @@ import static com.android.wm.shell.transition.Transitions.isOpeningType;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.app.WindowConfiguration;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -59,6 +80,7 @@ import android.window.WindowContainerTransaction;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -115,13 +137,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
private final DisplayImeController mDisplayImeController;
private final SplitScreenTransitions mSplitTransitions;
- private boolean mExitSplitScreenOnHide = true;
+ private final SplitscreenEventLogger mLogger;
+ private boolean mExitSplitScreenOnHide;
+ private boolean mKeyguardOccluded;
// TODO(b/187041611): remove this flag after totally deprecated legacy split
/** Whether the device is supporting legacy split or not. */
private boolean mUseLegacySplit;
- @SplitScreen.StageType int mDismissTop = NO_DISMISS;
+ @SplitScreen.StageType private int mDismissTop = NO_DISMISS;
+
+ /** The target stage to dismiss to when unlock after folded. */
+ @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
private final Runnable mOnTransitionAnimationComplete = () -> {
// If still playing, let it finish.
@@ -137,12 +164,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController, Transitions transitions,
- TransactionPool transactionPool) {
+ TransactionPool transactionPool, SplitscreenEventLogger logger) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mRootTDAOrganizer = rootTDAOrganizer;
mTaskOrganizer = taskOrganizer;
+ mLogger = logger;
mMainStage = new MainStage(
mTaskOrganizer,
mDisplayId,
@@ -150,6 +178,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue,
mSurfaceSession);
mSideStage = new SideStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
@@ -157,6 +186,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSurfaceSession);
mDisplayImeController = displayImeController;
mRootTDAOrganizer.registerListener(displayId, this);
+ final DeviceStateManager deviceStateManager =
+ mContext.getSystemService(DeviceStateManager.class);
+ deviceStateManager.registerCallback(taskOrganizer.getExecutor(),
+ new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged));
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
transitions.addHandler(this);
@@ -166,7 +199,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
- SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) {
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+ SplitscreenEventLogger logger) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -179,6 +213,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
+ mLogger = logger;
transitions.addHandler(this);
}
@@ -194,7 +229,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- setSideStagePosition(sideStagePosition);
+ setSideStagePosition(sideStagePosition, wct);
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct);
@@ -215,6 +250,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return result;
}
+ void setSideStageOutline(boolean enable) {
+ mSideStage.enableOutline(enable);
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition,
@@ -222,7 +261,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
sideOptions = sideOptions != null ? sideOptions : new Bundle();
- setSideStagePosition(sidePosition);
+ setSideStagePosition(sidePosition, wct);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
@@ -241,6 +280,138 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
}
+ /** Starts 2 tasks in one legacy transition. */
+ void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
+ int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
+ RemoteAnimationAdapter adapter) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // Need to add another wrapper here in shell so that we can inject the divider bar
+ // and also manage the process elevation via setRunningRemote
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ RemoteAnimationTarget[] augmentedNonApps =
+ new RemoteAnimationTarget[nonApps.length + 1];
+ for (int i = 0; i < nonApps.length; ++i) {
+ augmentedNonApps[i] = nonApps[i];
+ }
+ augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
+ finishedCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ try {
+ adapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+ };
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ }
+
+ sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ setSideStagePosition(sidePosition, wct);
+
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct);
+ mSideStage.setBounds(getSideStageBounds(), wct);
+
+ // Make sure the launch options will put tasks in the corresponding split roots
+ addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(sideOptions, mSideStage);
+
+ // Add task launch requests
+ wct.startTask(mainTaskId, mainOptions);
+ wct.startTask(sideTaskId, sideOptions);
+
+ // Using legacy transitions, so we can't use blast sync since it conflicts.
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
+ public void startIntent(PendingIntent intent, Intent fillInIntent,
+ @SplitScreen.StageType int stage, @SplitPosition int position,
+ @androidx.annotation.Nullable Bundle options,
+ @Nullable IRemoteTransition remoteTransition) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ options = resolveStartStage(stage, position, options, wct);
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ mSplitTransitions.startEnterTransition(
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this);
+ }
+
+ Bundle resolveStartStage(@SplitScreen.StageType int stage,
+ @SplitPosition int position, @androidx.annotation.Nullable Bundle options,
+ @androidx.annotation.Nullable WindowContainerTransaction wct) {
+ switch (stage) {
+ case STAGE_TYPE_UNDEFINED: {
+ // Use the stage of the specified position is valid.
+ if (position != SPLIT_POSITION_UNDEFINED) {
+ if (position == getSideStagePosition()) {
+ options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
+ } else {
+ options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct);
+ }
+ } else {
+ // Exit split-screen and launch fullscreen since stage wasn't specified.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ }
+ break;
+ }
+ case STAGE_TYPE_SIDE: {
+ if (position != SPLIT_POSITION_UNDEFINED) {
+ setSideStagePosition(position, wct);
+ } else {
+ position = getSideStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ updateActivityOptions(options, position);
+ break;
+ }
+ case STAGE_TYPE_MAIN: {
+ if (position != SPLIT_POSITION_UNDEFINED) {
+ // Set the side stage opposite of what we want to the main stage.
+ final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
+ setSideStagePosition(sideStagePosition, wct);
+ } else {
+ position = getMainStagePosition();
+ }
+ if (options == null) {
+ options = new Bundle();
+ }
+ updateActivityOptions(options, position);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown stage=" + stage);
+ }
+
+ return options;
+ }
+
@SplitLayout.SplitPosition
int getSideStagePosition() {
return mSideStagePosition;
@@ -252,18 +423,24 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
}
- void setSideStagePosition(@SplitPosition int sideStagePosition) {
- setSideStagePosition(sideStagePosition, true /* updateVisibility */);
+ void setSideStagePosition(@SplitPosition int sideStagePosition,
+ @Nullable WindowContainerTransaction wct) {
+ setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
}
private void setSideStagePosition(@SplitPosition int sideStagePosition,
- boolean updateVisibility) {
+ boolean updateBounds, @Nullable WindowContainerTransaction wct) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
- if (mSideStageListener.mVisible && updateVisibility) {
- onStageVisibilityChanged(mSideStageListener);
+ if (mSideStageListener.mVisible && updateBounds) {
+ if (wct == null) {
+ // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds.
+ onLayoutChanged(mSplitLayout);
+ } else {
+ updateWindowBounds(mSplitLayout, wct);
+ }
}
}
@@ -275,24 +452,49 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mTaskOrganizer.applyTransaction(wct);
}
- void exitSplitScreen() {
- exitSplitScreen(null /* childrenToTop */);
+ void onKeyguardOccludedChanged(boolean occluded) {
+ // Do not exit split directly, because it needs to wait for task info update to determine
+ // which task should remain on top after split dismissed.
+ mKeyguardOccluded = occluded;
+ }
+
+ void onKeyguardVisibilityChanged(boolean showing) {
+ if (!showing && mMainStage.isActive()
+ && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
+ exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
+ SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED);
+ }
+ }
+
+ void exitSplitScreen(int exitReason) {
+ exitSplitScreen(null /* childrenToTop */, exitReason);
}
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mExitSplitScreenOnHide = exitSplitScreenOnHide;
}
- private void exitSplitScreen(StageTaskListener childrenToTop) {
+ private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
// Reset divider position.
mSplitLayout.resetDividerPosition();
+ mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ if (childrenToTop != null) {
+ logExitToStage(exitReason, childrenToTop == mMainStage);
+ } else {
+ logExit(exitReason);
+ }
}
- private void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
+ /**
+ * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
+ * an existing WindowContainerTransaction (rather than applying immediately). This is intended
+ * to be used when exiting split might be bundled with other window operations.
+ */
+ void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
@@ -309,29 +511,26 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
-
- if (!mMainStage.isActive()) {
- // Activate the main stage in anticipation of an app launch.
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mMainStage.activate(getMainStageBounds(), wct);
- mSideStage.setBounds(getSideStageBounds(), wct);
- mTaskOrganizer.applyTransaction(wct);
- }
}
void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
if (mListeners.contains(listener)) return;
mListeners.add(listener);
- listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
- listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
- mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
- mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ sendStatusToListener(listener);
}
void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mListeners.remove(listener);
}
+ void sendStatusToListener(SplitScreen.SplitScreenListener listener) {
+ listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+ listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+ listener.onSplitVisibilityChanged(isSplitScreenVisible());
+ mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+ mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+ }
+
private void sendOnStagePositionChanged() {
for (int i = mListeners.size() - 1; i >= 0; --i) {
final SplitScreen.SplitScreenListener l = mListeners.get(i);
@@ -340,9 +539,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskStatusChanged(
- StageListenerImpl stageListener, int taskId, boolean present, boolean visible) {
-
+ private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
+ boolean present, boolean visible) {
int stage;
if (present) {
stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
@@ -350,12 +548,26 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// No longer on any stage
stage = STAGE_TYPE_UNDEFINED;
}
+ if (stage == STAGE_TYPE_MAIN) {
+ mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ } else {
+ mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
}
}
+ private void sendSplitVisibilityChanged() {
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ final SplitScreen.SplitScreenListener l = mListeners.get(i);
+ l.onSplitVisibilityChanged(mDividerVisible);
+ }
+ }
+
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
@@ -395,6 +607,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else {
mSplitLayout.release();
}
+ sendSplitVisibilityChanged();
}
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
@@ -403,22 +616,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Divider is only visible if both the main stage and side stages are visible
setDividerVisibility(isSplitScreenVisible());
- 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();
+ if (!mainStageVisible && !sideStageVisible) {
+ if (mExitSplitScreenOnHide
+ // Don't dismiss staged split when both stages are not visible due to sleeping display,
+ // like the cases keyguard showing or screen off.
+ || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
+ exitSplitScreen(SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
+ }
+ } else if (mKeyguardOccluded) {
+ // At least one of the stages is visible while keyguard occluded. Dismiss split because
+ // there's show-when-locked activity showing on top of keyguard. Also make sure the
+ // task contains show-when-locked activity remains on top after split dismissed.
+ final StageTaskListener toTop =
+ mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
+ exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
}
- if (mainStageVisible) {
+ // When both stage's visibility changed to visible, main stage might receives visibility
+ // changed before side stage if it has higher z-order than side stage. Make sure we only
+ // update main stage's windowing mode with the visibility changed of side stage to prevent
+ // stacking multiple windowing mode transactions which result to flicker issue.
+ if (mainStageVisible && stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideStageVisible) {
// The main stage configuration should to follow split layout when side stage is
// visible.
mMainStage.updateConfiguration(
WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
- } else {
+ } else if (!mSideStage.mRootTaskInfo.isSleeping) {
// We want the main stage configuration to be fullscreen when the side stage isn't
// visible.
+ // We should not do it when side stage are not visible due to sleeping display too.
mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
}
// TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
@@ -426,22 +654,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
mSyncQueue.runInSync(t -> {
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
final SurfaceControl mainStageLeash = mMainStage.mRootLeash;
- if (dividerLeash != null) {
- if (mDividerVisible) {
- t.show(dividerLeash)
- .setLayer(dividerLeash, Integer.MAX_VALUE)
- .setPosition(dividerLeash,
- mSplitLayout.getDividerBounds().left,
- mSplitLayout.getDividerBounds().top);
- } else {
- t.hide(dividerLeash);
- }
- }
-
if (sideStageVisible) {
final Rect sideStageBounds = getSideStageBounds();
t.show(sideStageLeash)
@@ -468,31 +683,54 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else {
t.hide(mainStageLeash);
}
+
+ applyDividerVisibility(t);
});
}
+ private void applyDividerVisibility(SurfaceControl.Transaction t) {
+ final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
+ if (dividerLeash == null) {
+ return;
+ }
+
+ if (mDividerVisible) {
+ t.show(dividerLeash)
+ .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
+ .setPosition(dividerLeash,
+ mSplitLayout.getDividerBounds().left,
+ mSplitLayout.getDividerBounds().top);
+ } else {
+ t.hide(dividerLeash);
+ }
+
+ }
+
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
if (!hasChildren) {
if (isSideStage && mMainStageListener.mVisible) {
// Exit to main stage if side stage no longer has children.
- exitSplitScreen(mMainStage);
+ exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
} else if (!isSideStage && mSideStageListener.mVisible) {
// Exit to side stage if main stage no longer has children.
- exitSplitScreen(mSideStage);
+ exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
}
} else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.setBounds(getSideStageBounds(), wct);
- // Reorder side stage to the top whenever there's a new child task appeared in side
- // stage. This is needed to prevent main stage occludes side stage and makes main stage
- // flipping between fullscreen and multi-window windowing mode.
- wct.reorder(mSideStage.mRootTaskInfo.token, true);
mTaskOrganizer.applyTransaction(wct);
}
+ if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren
+ && mSideStageListener.mHasChildren) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
}
@VisibleForTesting
@@ -511,38 +749,52 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
onSnappedToDismissTransition(mainStageToTop);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage,
+ SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER);
}
@Override
public void onDoubleTappedDivider() {
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
- ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT);
+ ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+ mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+
+ @Override
+ public void onLayoutChanging(SplitLayout layout) {
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
}
@Override
- public void onBoundsChanging(SplitLayout layout) {
+ public void onLayoutChanged(SplitLayout layout) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ updateWindowBounds(layout, wct);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
+ }
+
+ /**
+ * Populates `wct` with operations that match the split windows to the current layout.
+ * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
+ */
+ private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) {
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-
- mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
- bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
+ layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
}
- @Override
- public void onBoundsChanged(SplitLayout layout) {
+ void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) {
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo);
- mSyncQueue.queue(wct);
- mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash,
- bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer));
+ (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
+ bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer);
}
@Override
@@ -561,6 +813,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
+ public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) {
+ final StageTaskListener topLeftStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+ final StageTaskListener bottomRightStage =
+ mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ layout.applyLayoutShifted(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo,
+ bottomRightStage.mRootTaskInfo);
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
+ @Override
public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout == null) {
@@ -580,8 +844,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout != null
- && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)) {
- onBoundsChanged(mSplitLayout);
+ && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
+ && mMainStage.isActive()) {
+ onLayoutChanged(mSplitLayout);
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
+ }
+
+ private void onFoldedStateChanged(boolean folded) {
+ mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ if (!folded) return;
+
+ if (mMainStage.isFocused()) {
+ mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN;
+ } else if (mSideStage.isFocused()) {
+ mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE;
}
}
@@ -672,7 +949,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public boolean startAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition != mSplitTransitions.mPendingDismiss
&& transition != mSplitTransitions.mPendingEnter) {
@@ -717,14 +995,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
- shouldAnimate = startPendingEnterAnimation(transition, info, t);
+ shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
} else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, t);
+ shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
}
if (!shouldAnimate) return false;
- mSplitTransitions.playAnimation(transition, info, t, finishCallback,
- mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction,
+ finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
return true;
}
@@ -754,7 +1032,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Update local states (before animating).
setDividerVisibility(true);
- setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */);
+ setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
+ null /* wct */);
setSplitsVisible(true);
addDividerBarToTransition(info, t, true /* show */);
@@ -854,12 +1133,22 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Be default, make it visible. The remote animator can adjust alpha if it plans to animate.
if (show) {
t.setAlpha(leash, 1.f);
- t.setLayer(leash, Integer.MAX_VALUE);
+ t.setLayer(leash, SPLIT_DIVIDER_LAYER);
t.setPosition(leash, bounds.left, bounds.top);
t.show(leash);
}
}
+ RemoteAnimationTarget getDividerBarLegacyTarget() {
+ final Rect bounds = mSplitLayout.getDividerBounds();
+ return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
+ mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
+ null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
+ new android.graphics.Point(0, 0) /* position */, bounds, bounds,
+ new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
+ null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
@@ -884,6 +1173,36 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
}
+ /**
+ * Sets drag info to be logged when splitscreen is next entered.
+ */
+ public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+ mLogger.enterRequestedByDrag(position, dragSessionId);
+ }
+
+ /**
+ * Logs the exit of splitscreen.
+ */
+ private void logExit(int exitReason) {
+ mLogger.logExit(exitReason,
+ SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
+ SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
+ mSplitLayout.isLandscape());
+ }
+
+ /**
+ * Logs the exit of splitscreen to a specific stage. This must be called before the exit is
+ * executed.
+ */
+ private void logExitToStage(int exitReason, boolean toMainStage) {
+ mLogger.logExit(exitReason,
+ toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED,
+ toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
+ !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED,
+ !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
+ mSplitLayout.isLandscape());
+ }
+
class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
boolean mHasRootTask = false;
boolean mVisible = false;
@@ -923,7 +1242,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen();
+ StageCoordinator.this.exitSplitScreen(
+ SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
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 0fd8eca6290e..3512a0c3727b 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
@@ -71,8 +71,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
}
private final StageListenerCallbacks mCallbacks;
- private final SyncTransactionQueue mSyncQueue;
private final SurfaceSession mSurfaceSession;
+ protected final SyncTransactionQueue mSyncQueue;
protected ActivityManager.RunningTaskInfo mRootTaskInfo;
protected SurfaceControl mRootLeash;
@@ -97,6 +97,29 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return mChildrenTaskInfo.contains(taskId);
}
+ /**
+ * Returns the top activity uid for the top child task.
+ */
+ int getTopChildTaskUid() {
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
+ if (info.topActivityInfo == null) {
+ continue;
+ }
+ return info.topActivityInfo.applicationInfo.uid;
+ }
+ return 0;
+ }
+
+ /** @return {@code true} if this listener contains the currently focused task. */
+ boolean isFocused() {
+ if (mRootTaskInfo.isFocused) return true;
+ for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+ if (mChildrenTaskInfo.valueAt(i).isFocused) return true;
+ }
+ return false;
+ }
+
@Override
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
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 29326ec90e31..22865988e880 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
@@ -360,7 +360,7 @@ public class SplashscreenContentDrawer {
createIconDrawable(iconDrawable, false);
} else {
final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
- final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
+ final int densityDpi = mContext.getResources().getConfiguration().densityDpi;
final int scaledIconDpi =
(int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon");
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 fc7c86d669cb..52a3ac585aff 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
@@ -52,6 +52,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.widget.FrameLayout;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -115,6 +116,7 @@ public class StartingSurfaceDrawer {
@VisibleForTesting
final SplashscreenContentDrawer mSplashscreenContentDrawer;
private Choreographer mChoreographer;
+ private final WindowManagerGlobal mWindowManagerGlobal;
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
@@ -126,6 +128,8 @@ public class StartingSurfaceDrawer {
mSplashScreenExecutor = splashScreenExecutor;
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, pool);
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
+ mWindowManagerGlobal = WindowManagerGlobal.getInstance();
+ mDisplayManager.getDisplay(DEFAULT_DISPLAY);
}
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -137,21 +141,8 @@ public class StartingSurfaceDrawer {
private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
new SparseArray<>(1);
- /** Obtain proper context for showing splash screen on the provided display. */
- private Context getDisplayContext(Context context, int displayId) {
- if (displayId == DEFAULT_DISPLAY) {
- // The default context fits.
- return context;
- }
-
- final Display targetDisplay = mDisplayManager.getDisplay(displayId);
- if (targetDisplay == null) {
- // Failed to obtain the non-default display where splash screen should be shown,
- // lets not show at all.
- return null;
- }
-
- return context.createDisplayContext(targetDisplay);
+ private Display getDisplay(int displayId) {
+ return mDisplayManager.getDisplay(displayId);
}
private int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
@@ -186,13 +177,11 @@ public class StartingSurfaceDrawer {
+ " suggestType=" + suggestType);
}
- // Obtain proper context to launch on the right display.
- final Context displayContext = getDisplayContext(context, displayId);
- if (displayContext == null) {
+ final Display display = getDisplay(displayId);
+ if (display == null) {
// Can't show splash screen on requested display, so skip showing at all.
return;
}
- context = displayContext;
if (theme != context.getThemeResId()) {
try {
context = context.createPackageContextAsUser(activityInfo.packageName,
@@ -330,10 +319,8 @@ public class StartingSurfaceDrawer {
};
mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId,
viewSupplier::setView);
-
try {
- final WindowManager wm = context.getSystemService(WindowManager.class);
- if (addWindow(taskId, appToken, rootLayout, wm, params, suggestType)) {
+ if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
// We use the splash screen worker thread to create SplashScreenView while adding
// the window, as otherwise Choreographer#doFrame might be delayed on this thread.
// And since Choreographer#doFrame won't happen immediately after adding the window,
@@ -508,12 +495,14 @@ public class StartingSurfaceDrawer {
viewHost.getView().post(viewHost::release);
}
- protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm,
+ protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
WindowManager.LayoutParams params, @StartingWindowType int suggestType) {
boolean shouldSaveView = true;
+ final Context context = view.getContext();
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
- wm.addView(view, params);
+ mWindowManagerGlobal.addView(view, params, display,
+ null /* parentWindow */, context.getUserId());
} catch (WindowManager.BadTokenException e) {
// ignore
Slog.w(TAG, appToken + " already running, starting window not displayed. "
@@ -521,9 +510,9 @@ public class StartingSurfaceDrawer {
shouldSaveView = false;
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- if (view != null && view.getParent() == null) {
+ if (view.getParent() == null) {
Slog.w(TAG, "view not successfully added to wm, removing view");
- wm.removeViewImmediate(view);
+ mWindowManagerGlobal.removeView(view, true /* immediate */);
shouldSaveView = false;
}
}
@@ -587,10 +576,7 @@ public class StartingSurfaceDrawer {
if (hideView) {
decorView.setVisibility(View.GONE);
}
- final WindowManager wm = decorView.getContext().getSystemService(WindowManager.class);
- if (wm != null) {
- wm.removeView(decorView);
- }
+ mWindowManagerGlobal.removeView(decorView, false /* immediate */);
}
/**
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 6052d3dee891..7d011e6521a2 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
@@ -72,6 +72,7 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -205,7 +206,7 @@ public class TaskSnapshotWindow {
final SurfaceControl surfaceControl = new SurfaceControl();
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
- final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
+ final InsetsSourceControl[] tmpControls = new InsetsSourceControl[0];
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
final TaskDescription taskDescription;
@@ -225,13 +226,14 @@ public class TaskSnapshotWindow {
delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
- final InsetsState mTmpInsetsState = new InsetsState();
+ final InsetsState tmpInsetsState = new InsetsState();
+ final InsetsVisibilities tmpRequestedVisibilities = new InsetsVisibilities();
final InputChannel tmpInputChannel = new InputChannel();
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
+ tmpRequestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
@@ -244,8 +246,8 @@ public class TaskSnapshotWindow {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
- tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
- mTempControls, TMP_SURFACE_SIZE);
+ tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState,
+ tmpControls, TMP_SURFACE_SIZE);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index c6fb5af7d4be..4ba6acaba025 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -16,30 +16,55 @@
package com.android.wm.shell.transition;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.IBinder;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -48,9 +73,12 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -61,33 +89,173 @@ import java.util.ArrayList;
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static final int MAX_ANIMATION_DURATION = 3000;
+ /**
+ * Restrict ability of activities overriding transition animation in a way such that
+ * an activity can do it only when the transition happens within a same task.
+ *
+ * @see android.app.Activity#overridePendingTransition(int, int)
+ */
+ private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
+ "persist.wm.disable_custom_task_animation";
+
+ /**
+ * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
+ */
+ static boolean sDisableCustomTaskAnimationProperty =
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+
private final TransactionPool mTransactionPool;
+ private final DisplayController mDisplayController;
+ private final Context mContext;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
+
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
- DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
+ private final int mCurrentUserId;
+
+ private ScreenRotationAnimation mRotationAnimation;
+
+ DefaultTransitionHandler(@NonNull DisplayController displayController,
+ @NonNull TransactionPool transactionPool, Context context,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ mDisplayController = displayController;
mTransactionPool = transactionPool;
+ mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG);
+ mCurrentUserId = UserHandle.myUserId();
AttributeCache.init(context);
}
+ @VisibleForTesting
+ static boolean isRotationSeamless(@NonNull TransitionInfo info,
+ DisplayController displayController) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Display is rotating, check if it should be seamless.");
+ boolean checkedDisplayLayout = false;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+
+ // Only look at changing things. showing/hiding don't need to rotate.
+ if (change.getMode() != TRANSIT_CHANGE) continue;
+
+ // This container isn't rotating, so we can ignore it.
+ if (change.getEndRotation() == change.getStartRotation()) continue;
+
+ if ((change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ // In the presence of System Alert windows we can not seamlessly rotate.
+ if ((change.getFlags() & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " display has system alert windows, so not seamless.");
+ return false;
+ }
+ } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+ if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " wallpaper is participating but isn't seamless.");
+ return false;
+ }
+ } else if (change.getTaskInfo() != null) {
+ // We only enable seamless rotation if all the visible task windows requested it.
+ if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " task %s isn't requesting seamless, so not seamless.",
+ change.getTaskInfo().taskId);
+ return false;
+ }
+
+ // This is the only way to get display-id currently, so we will check display
+ // capabilities here
+ if (!checkedDisplayLayout) {
+ // only need to check display once.
+ checkedDisplayLayout = true;
+ final DisplayLayout displayLayout = displayController.getDisplayLayout(
+ change.getTaskInfo().displayId);
+ // For the upside down rotation we don't rotate seamlessly as the navigation
+ // bar moves position. Note most apps (using orientation:sensor or user as
+ // opposed to fullSensor) will not enter the reverse portrait orientation, so
+ // actually the orientation won't change at all.
+ int upsideDownRotation = displayLayout.getUpsideDownRotation();
+ if (change.getStartRotation() == upsideDownRotation
+ || change.getEndRotation() == upsideDownRotation) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " rotation involves upside-down portrait, so not seamless.");
+ return false;
+ }
+
+ // If the navigation bar can't change sides, then it will jump when we change
+ // orientations and we don't rotate seamlessly - unless that is allowed, eg.
+ // with gesture navigation where the navbar is low-profile enough that this
+ // isn't very noticeable.
+ if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
+ && (!(displayLayout.navigationBarCanMove()
+ && (change.getStartAbsBounds().width()
+ != change.getStartAbsBounds().height())))) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ " nav bar changes sides, so not seamless.");
+ return false;
+ }
+ }
+ }
+ }
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless.");
+ return true;
+ }
+
+ /**
+ * Gets the rotation animation for the topmost task. Assumes that seamless is checked
+ * elsewhere, so it will default SEAMLESS to ROTATE.
+ */
+ private int getRotationAnimation(@NonNull TransitionInfo info) {
+ // Traverse in top-to-bottom order so that the first task is top-most
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+
+ // Only look at changing things. showing/hiding don't need to rotate.
+ if (change.getMode() != TRANSIT_CHANGE) continue;
+
+ // This container isn't rotating, so we can ignore it.
+ if (change.getEndRotation() == change.getStartRotation()) continue;
+
+ if (change.getTaskInfo() != null) {
+ final int anim = change.getRotationAnimation();
+ if (anim == ROTATION_ANIMATION_UNSPECIFIED
+ // Fallback animation for seamless should also be default.
+ || anim == ROTATION_ANIMATION_SEAMLESS) {
+ return ROTATION_ANIMATION_ROTATE;
+ }
+ return anim;
+ }
+ }
+ return ROTATION_ANIMATION_ROTATE;
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"start default transition animation, info = %s", info);
+ // If keyguard goes away, we should loadKeyguardExitAnimation. Otherwise this just
+ // immediately finishes since there is no animation for screen-wake.
+ if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ return true;
+ }
+
if (mAnimations.containsKey(transition)) {
throw new IllegalStateException("Got a duplicate startAnimation call for "
+ transition);
@@ -97,19 +265,42 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
+
+ if (mRotationAnimation != null) {
+ mRotationAnimation.kill();
+ mRotationAnimation = null;
+ }
+
mAnimations.remove(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+
+ final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
+
+ if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
+ && (change.getEndRotation() != change.getStartRotation())
+ && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ final int anim = getRotationAnimation(info);
+ if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash());
+ mRotationAnimation.startAnimation(animations, onAnimFinish,
+ mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ continue;
+ }
+ }
+
if (change.getMode() == TRANSIT_CHANGE) {
// No default animation for this, so just update bounds/position.
- t.setPosition(change.getLeash(),
+ startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - change.getEndRelOffset().x,
change.getEndAbsBounds().top - change.getEndRelOffset().y);
if (change.getTaskInfo() != null) {
// Skip non-tasks since those usually have null bounds.
- t.setWindowCrop(change.getLeash(),
+ startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
}
@@ -117,12 +308,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
// Don't animate anything that isn't independent.
if (!TransitionInfo.isIndependent(change, info)) continue;
- Animation a = loadAnimation(info.getType(), info.getFlags(), change);
+ Animation a = loadAnimation(info, change, wallpaperTransit);
if (a != null) {
- startAnimInternal(animations, a, change.getLeash(), onAnimFinish);
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */);
+
+ if (info.getAnimationOptions() != null) {
+ attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions());
+ }
}
}
- t.apply();
+ startTransaction.apply();
// run finish now in-case there are no animations
onAnimFinish.run();
return true;
@@ -141,87 +337,134 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
@Nullable
- private Animation loadAnimation(int type, int flags, TransitionInfo.Change change) {
- // TODO(b/178678389): It should handle more type animation here
+ private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
+ int wallpaperTransit) {
Animation a = null;
- final boolean isOpening = Transitions.isOpeningType(type);
+ final int type = info.getType();
+ final int flags = info.getFlags();
final int changeMode = change.getMode();
final int changeFlags = change.getFlags();
+ final boolean isOpeningType = Transitions.isOpeningType(type);
+ final boolean enter = Transitions.isOpeningType(changeMode);
+ final boolean isTask = change.getTaskInfo() != null;
+ final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+ final int overrideType = options != null ? options.getType() : ANIM_NONE;
+ final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
- if (type == TRANSIT_RELAUNCH) {
- a = mTransitionAnimation.createRelaunchAnimation(
- change.getStartAbsBounds(), mInsets, change.getEndAbsBounds());
- } else if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
+ if (info.isKeyguardGoingAway()) {
a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
(changeFlags & FLAG_SHOW_WALLPAPER) != 0);
} else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
- } else if (changeMode == TRANSIT_OPEN && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskOpenEnterAnimation);
+ } else if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
+ if (isOpeningType) {
+ a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
} else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter);
- }
- } else if (changeMode == TRANSIT_TO_FRONT && isOpening) {
- if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- // This received a transferred starting window, so don't animate
- return null;
- }
-
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityOpenAnimation(true /** enter */);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToFrontEnterAnimation);
- }
- } else if (changeMode == TRANSIT_CLOSE && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else if (change.getTaskInfo() != null) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskCloseExitAnimation);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationRes(
- (changeFlags & FLAG_TRANSLUCENT) == 0
- ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit);
- }
- } else if (changeMode == TRANSIT_TO_BACK && !isOpening) {
- if ((changeFlags & FLAG_IS_VOICE_INTERACTION) != 0) {
- a = mTransitionAnimation.loadVoiceActivityExitAnimation(false /** enter */);
- } else {
- a = mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_taskToBackExitAnimation);
+ a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
}
} else if (changeMode == TRANSIT_CHANGE) {
// In the absence of a specific adapter, we just want to keep everything stationary.
a = new AlphaAnimation(1.f, 1.f);
a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION);
+ } else if (type == TRANSIT_RELAUNCH) {
+ a = mTransitionAnimation.createRelaunchAnimation(
+ change.getEndAbsBounds(), mInsets, change.getEndAbsBounds());
+ } else if (overrideType == ANIM_CUSTOM
+ && (canCustomContainer || options.getOverrideTaskTransition())) {
+ a = mTransitionAnimation.loadAnimationRes(options.getPackageName(), enter
+ ? options.getEnterResId() : options.getExitResId());
+ } else if (overrideType == ANIM_OPEN_CROSS_PROFILE_APPS && enter) {
+ a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
+ } else if (overrideType == ANIM_CLIP_REVEAL) {
+ a = mTransitionAnimation.createClipRevealAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), change.getEndAbsBounds(),
+ options.getTransitionBounds());
+ } else if (overrideType == ANIM_SCALE_UP) {
+ a = mTransitionAnimation.createScaleUpAnimationLocked(type, wallpaperTransit, enter,
+ change.getEndAbsBounds(), options.getTransitionBounds());
+ } else if (overrideType == ANIM_THUMBNAIL_SCALE_UP
+ || overrideType == ANIM_THUMBNAIL_SCALE_DOWN) {
+ final boolean scaleUp = overrideType == ANIM_THUMBNAIL_SCALE_UP;
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(enter, scaleUp,
+ change.getEndAbsBounds(), type, wallpaperTransit, options.getThumbnail(),
+ options.getTransitionBounds());
+ } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
+ // This received a transferred starting window, so don't animate
+ return null;
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
+ } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
+ } else if (type == TRANSIT_OPEN) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : R.styleable.WindowAnimation_taskOpenExitAnimation);
+ } else {
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_open_enter);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+ : R.styleable.WindowAnimation_activityOpenExitAnimation);
+ }
+ }
+ } else if (type == TRANSIT_TO_FRONT) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : R.styleable.WindowAnimation_taskToFrontExitAnimation);
+ } else if (type == TRANSIT_CLOSE) {
+ if (isTask) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : R.styleable.WindowAnimation_taskCloseExitAnimation);
+ } else {
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+ a = mTransitionAnimation.loadDefaultAnimationRes(
+ R.anim.activity_translucent_close_exit);
+ } else {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : R.styleable.WindowAnimation_activityCloseExitAnimation);
+ }
+ }
+ } else if (type == TRANSIT_TO_BACK) {
+ a = mTransitionAnimation.loadDefaultAnimationAttr(enter
+ ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : R.styleable.WindowAnimation_taskToBackExitAnimation);
}
if (a != null) {
- Rect start = change.getStartAbsBounds();
- Rect end = change.getEndAbsBounds();
+ if (!a.isInitialized()) {
+ Rect end = change.getEndAbsBounds();
+ a.initialize(end.width(), end.height(), end.width(), end.height());
+ }
a.restrictDuration(MAX_ANIMATION_DURATION);
- a.initialize(end.width(), end.height(), start.width(), start.height());
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
}
return a;
}
- private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim,
- @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) {
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ static void startSurfaceAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Animation anim, @NonNull SurfaceControl leash,
+ @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
+ @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor,
+ @Nullable Point position) {
+ final SurfaceControl.Transaction transaction = pool.acquire();
final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
final Transformation transformation = new Transformation();
final float[] matrix = new float[9];
@@ -231,14 +474,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
va.addUpdateListener(animation -> {
final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
- applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix);
+ applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
+ position);
});
final Runnable finisher = () -> {
- applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix);
+ applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
+ position);
- mTransactionPool.release(transaction);
- mMainExecutor.execute(() -> {
+ pool.release(transaction);
+ mainExecutor.execute(() -> {
animations.remove(va);
finishCallback.run();
});
@@ -255,12 +500,116 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
});
animations.add(va);
- mAnimExecutor.execute(va::start);
+ animExecutor.execute(va::start);
+ }
+
+ private void attachThumbnail(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final boolean isTask = change.getTaskInfo() != null;
+ final boolean isOpen = Transitions.isOpeningType(change.getMode());
+ final boolean isClose = Transitions.isClosingType(change.getMode());
+ if (isOpen) {
+ if (options.getType() == ANIM_OPEN_CROSS_PROFILE_APPS && isTask) {
+ attachCrossProfileThunmbnailAnimation(animations, finishCallback, change);
+ } else if (options.getType() == ANIM_THUMBNAIL_SCALE_UP) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ } else if (isClose && options.getType() == ANIM_THUMBNAIL_SCALE_DOWN) {
+ attachThumbnailAnimation(animations, finishCallback, change, options);
+ }
+ }
+
+ private void attachCrossProfileThunmbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change) {
+ final int thumbnailDrawableRes = change.getTaskInfo().userId == mCurrentUserId
+ ? R.drawable.ic_account_circle : R.drawable.ic_corp_badge;
+ final Rect bounds = change.getEndAbsBounds();
+ final HardwareBuffer thumbnail = mTransitionAnimation.createCrossProfileAppsThumbnail(
+ thumbnailDrawableRes, bounds);
+ if (thumbnail == null) {
+ return;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), thumbnail, transaction);
+ final Animation a =
+ mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds);
+ if (a == null) {
+ return;
+ }
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, mAnimExecutor, new Point(bounds.left, bounds.top));
+ }
+
+ private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, TransitionInfo.Change change,
+ TransitionInfo.AnimationOptions options) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession,
+ change.getLeash(), options.getThumbnail(), transaction);
+ final Rect bounds = change.getEndAbsBounds();
+ final int orientation = mContext.getResources().getConfiguration().orientation;
+ final Animation a = mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(bounds,
+ mInsets, options.getThumbnail(), orientation, null /* startRect */,
+ options.getTransitionBounds(), options.getType() == ANIM_THUMBNAIL_SCALE_UP);
+
+ final Runnable finisher = () -> {
+ wt.destroy(transaction);
+ mTransactionPool.release(transaction);
+
+ finishCallback.run();
+ };
+ a.restrictDuration(MAX_ANIMATION_DURATION);
+ a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ startSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
+ mMainExecutor, mAnimExecutor, null /* position */);
+ }
+
+ private static int getWallpaperTransitType(TransitionInfo info) {
+ boolean hasOpenWallpaper = false;
+ boolean hasCloseWallpaper = false;
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if ((change.getFlags() & FLAG_SHOW_WALLPAPER) != 0) {
+ if (Transitions.isOpeningType(change.getMode())) {
+ hasOpenWallpaper = true;
+ } else if (Transitions.isClosingType(change.getMode())) {
+ hasCloseWallpaper = true;
+ }
+ }
+ }
+
+ if (hasOpenWallpaper && hasCloseWallpaper) {
+ return Transitions.isOpeningType(info.getType())
+ ? WALLPAPER_TRANSITION_INTRA_OPEN : WALLPAPER_TRANSITION_INTRA_CLOSE;
+ } else if (hasOpenWallpaper) {
+ return WALLPAPER_TRANSITION_OPEN;
+ } else if (hasCloseWallpaper) {
+ return WALLPAPER_TRANSITION_CLOSE;
+ } else {
+ return WALLPAPER_TRANSITION_NONE;
+ }
}
private static void applyTransformation(long time, SurfaceControl.Transaction t,
- SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) {
+ SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
+ Point position) {
anim.getTransformation(time, transformation);
+ if (position != null) {
+ transformation.getMatrix().postTranslate(position.x, position.y);
+ }
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
new file mode 100644
index 000000000000..61e11e877b90
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.os.RemoteException;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IWindowContainerTransactionCallback;
+
+/**
+ * Utilities and interfaces for transition-like usage on top of the legacy app-transition and
+ * synctransaction tools.
+ */
+public class LegacyTransitions {
+
+ /**
+ * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation
+ * into one callback.
+ */
+ public interface ILegacyTransition {
+ /**
+ * Called when both the associated sync transaction finishes and the remote animation is
+ * ready.
+ */
+ void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t);
+ }
+
+ /**
+ * Makes sure that a remote animation and corresponding sync callback are called together
+ * such that the sync callback is called first. This assumes that both the callback receiver
+ * and the remoteanimation are in the same process so that order is preserved on both ends.
+ */
+ public static class LegacyTransition {
+ private final ILegacyTransition mLegacyTransition;
+ private int mSyncId = -1;
+ private SurfaceControl.Transaction mTransaction;
+ private int mTransit;
+ private RemoteAnimationTarget[] mApps;
+ private RemoteAnimationTarget[] mWallpapers;
+ private RemoteAnimationTarget[] mNonApps;
+ private IRemoteAnimationFinishedCallback mFinishCallback = null;
+ private boolean mCancelled = false;
+ private final SyncCallback mSyncCallback = new SyncCallback();
+ private final RemoteAnimationAdapter mAdapter =
+ new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0);
+
+ public LegacyTransition(@WindowManager.TransitionType int type,
+ @NonNull ILegacyTransition legacyTransition) {
+ mLegacyTransition = legacyTransition;
+ mTransit = type;
+ }
+
+ public @WindowManager.TransitionType int getType() {
+ return mTransit;
+ }
+
+ public IWindowContainerTransactionCallback getSyncCallback() {
+ return mSyncCallback;
+ }
+
+ public RemoteAnimationAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ private class SyncCallback extends IWindowContainerTransactionCallback.Stub {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t)
+ throws RemoteException {
+ mSyncId = id;
+ mTransaction = t;
+ checkApply();
+ }
+ }
+
+ private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+ mTransit = transit;
+ mApps = apps;
+ mWallpapers = wallpapers;
+ mNonApps = nonApps;
+ mFinishCallback = finishedCallback;
+ checkApply();
+ }
+
+ @Override
+ public void onAnimationCancelled() throws RemoteException {
+ mCancelled = true;
+ mApps = mWallpapers = mNonApps = null;
+ checkApply();
+ }
+ }
+
+
+ private void checkApply() throws RemoteException {
+ if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) return;
+ mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers,
+ mNonApps, mFinishCallback, mTransaction);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4da6664aa3dc..6bd805323aa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -57,7 +57,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
@@ -70,19 +71,24 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
- mMainExecutor.execute(
- () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
+ mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
+ finishCallback.onTransitionFinished(wct, null /* wctCB */);
+ });
}
};
try {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.startAnimation(transition, info, t, cb);
+ mRemote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (mRemote.asBinder() != null) {
@@ -102,7 +108,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(
() -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9bfb261fcb85..bda884cd80d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -56,14 +56,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
new ArrayList<>();
- private final IBinder.DeathRecipient mTransitionDeathRecipient =
- new IBinder.DeathRecipient() {
- @Override
- @BinderThread
- public void binderDied() {
- mMainExecutor.execute(() -> mFilters.clear());
- }
- };
+ private final ArrayMap<IBinder, RemoteDeathHandler> mDeathHandlers = new ArrayMap<>();
RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
@@ -71,7 +64,9 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
void addFiltered(TransitionFilter filter, IRemoteTransition remote) {
try {
- remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ RemoteDeathHandler handler = new RemoteDeathHandler(remote.asBinder());
+ remote.asBinder().linkToDeath(handler, 0 /* flags */);
+ mDeathHandlers.put(remote.asBinder(), handler);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
@@ -88,7 +83,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
}
if (removed) {
- remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
+ RemoteDeathHandler handler = mDeathHandlers.remove(remote.asBinder());
+ remote.asBinder().unlinkToDeath(handler, 0 /* flags */);
}
}
@@ -99,7 +95,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
@@ -110,6 +107,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
mFilters.get(i));
if (mFilters.get(i).first.matches(info)) {
+ Slog.d(TAG, "Found filter" + mFilters.get(i));
pendingRemote = mFilters.get(i).second;
// Add to requested list so that it can be found for merge requests.
mRequestedRemotes.put(transition, pendingRemote);
@@ -132,11 +130,15 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
};
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
if (remote.asBinder() != null) {
remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
mMainExecutor.execute(() -> {
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
@@ -146,7 +148,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
if (remote.asBinder() != null) {
remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- remote.startAnimation(transition, info, t, cb);
+ remote.startAnimation(transition, info, startTransaction, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error running remote transition.", e);
if (remote.asBinder() != null) {
@@ -170,7 +172,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
- public void onTransitionFinished(WindowContainerTransaction wct) {
+ public void onTransitionFinished(WindowContainerTransaction wct,
+ SurfaceControl.Transaction sct) {
mMainExecutor.execute(() -> {
if (!mRequestedRemotes.containsKey(mergeTarget)) {
Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
@@ -200,4 +203,25 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
+ " for %s: %s", transition, remote);
return new WindowContainerTransaction();
}
+
+ /** NOTE: binder deaths can alter the filter order */
+ private class RemoteDeathHandler implements IBinder.DeathRecipient {
+ private final IBinder mRemote;
+
+ RemoteDeathHandler(IBinder remote) {
+ mRemote = remote;
+ }
+
+ @Override
+ @BinderThread
+ public void binderDied() {
+ mMainExecutor.execute(() -> {
+ for (int i = mFilters.size() - 1; i >= 0; --i) {
+ if (mRemote.equals(mFilters.get(i).second.asBinder())) {
+ mFilters.remove(i);
+ }
+ }
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
new file mode 100644
index 000000000000..ada2ed27c114
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.hardware.HardwareBuffer.RGBA_8888;
+import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
+import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
+
+import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
+import static com.android.wm.shell.transition.Transitions.TAG;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceSession;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.window.TransitionInfo;
+
+import com.android.internal.R;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class handles the rotation animation when the device is rotated.
+ *
+ * <p>
+ * The screen rotation animation is composed of 4 different part:
+ * <ul>
+ * <li> The screenshot: <p>
+ * A screenshot of the whole screen prior the change of orientation is taken to hide the
+ * element resizing below. The screenshot is then animated to rotate and cross-fade to
+ * the new orientation with the content in the new orientation.
+ *
+ * <li> The windows on the display: <p>y
+ * Once the device is rotated, the screen and its content are in the new orientation. The
+ * animation first rotate the new content into the old orientation to then be able to
+ * animate to the new orientation
+ *
+ * <li> The Background color frame: <p>
+ * To have the animation seem more seamless, we add a color transitioning background behind the
+ * exiting and entering layouts. We compute the brightness of the start and end
+ * layouts and transition from the two brightness values as grayscale underneath the animation
+ *
+ * <li> The entering Blackframe: <p>
+ * The enter Blackframe is similar to the exit Blackframe but is only used when a custom
+ * rotation animation is used and matches the new content size instead of the screenshot.
+ * </ul>
+ */
+class ScreenRotationAnimation {
+ static final int MAX_ANIMATION_DURATION = 10 * 1000;
+
+ private final Context mContext;
+ private final TransactionPool mTransactionPool;
+ private final float[] mTmpFloats = new float[9];
+ // Complete transformations being applied.
+ private final Matrix mSnapshotInitialMatrix = new Matrix();
+ /** The leash of display. */
+ private final SurfaceControl mSurfaceControl;
+ private final Rect mStartBounds = new Rect();
+ private final Rect mEndBounds = new Rect();
+
+ private final int mStartWidth;
+ private final int mStartHeight;
+ private final int mEndWidth;
+ private final int mEndHeight;
+ private final int mStartRotation;
+ private final int mEndRotation;
+
+ /** This layer contains the actual screenshot that is to be faded out. */
+ private SurfaceControl mScreenshotLayer;
+ /**
+ * Only used for screen rotation and not custom animations. Layered behind all other layers
+ * to avoid showing any "empty" spots
+ */
+ private SurfaceControl mBackColorSurface;
+ /** The leash using to animate screenshot layer. */
+ private SurfaceControl mAnimLeash;
+ private Transaction mTransaction;
+
+ // The current active animation to move from the old to the new rotated
+ // state. Which animation is run here will depend on the old and new
+ // rotations.
+ private Animation mRotateExitAnimation;
+ private Animation mRotateEnterAnimation;
+
+ /** Intensity of light/whiteness of the layout before rotation occurs. */
+ private float mStartLuma;
+ /** Intensity of light/whiteness of the layout after rotation occurs. */
+ private float mEndLuma;
+
+ ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool,
+ Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash) {
+ mContext = context;
+ mTransactionPool = pool;
+
+ mSurfaceControl = change.getLeash();
+ mStartWidth = change.getStartAbsBounds().width();
+ mStartHeight = change.getStartAbsBounds().height();
+ mEndWidth = change.getEndAbsBounds().width();
+ mEndHeight = change.getEndAbsBounds().height();
+ mStartRotation = change.getStartRotation();
+ mEndRotation = change.getEndRotation();
+
+ mStartBounds.set(change.getStartAbsBounds());
+ mEndBounds.set(change.getEndAbsBounds());
+
+ mAnimLeash = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setEffectLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("Animation leash of screenshot rotation")
+ .build();
+
+ try {
+ SurfaceControl.LayerCaptureArgs args =
+ new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ .setCaptureSecureLayers(true)
+ .setAllowProtected(true)
+ .setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
+ .build();
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ SurfaceControl.captureLayers(args);
+ if (screenshotBuffer == null) {
+ Slog.w(TAG, "Unable to take screenshot of display");
+ return;
+ }
+
+ mBackColorSurface = new SurfaceControl.Builder(session)
+ .setParent(rootLeash)
+ .setColorLayer()
+ .setCallsite("ShellRotationAnimation")
+ .setName("BackColorSurface")
+ .build();
+
+ mScreenshotLayer = new SurfaceControl.Builder(session)
+ .setParent(mAnimLeash)
+ .setBLASTLayer()
+ .setSecure(screenshotBuffer.containsSecureLayers())
+ .setCallsite("ShellRotationAnimation")
+ .setName("RotationLayer")
+ .build();
+
+ HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+ mStartLuma = getMedianBorderLuma(hardwareBuffer, screenshotBuffer.getColorSpace());
+
+ GraphicBuffer buffer = GraphicBuffer.createFromHardwareBuffer(
+ screenshotBuffer.getHardwareBuffer());
+
+ t.setLayer(mBackColorSurface, -1);
+ t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
+ t.setAlpha(mBackColorSurface, 1);
+ t.show(mBackColorSurface);
+
+ t.setPosition(mAnimLeash, 0, 0);
+ t.setAlpha(mAnimLeash, 1);
+ t.show(mAnimLeash);
+
+ t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
+ t.setBuffer(mScreenshotLayer, buffer);
+ t.setColorSpace(mScreenshotLayer, screenshotBuffer.getColorSpace());
+ t.show(mScreenshotLayer);
+
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.w(TAG, "Unable to allocate freeze surface", e);
+ }
+
+ setRotation(t);
+ t.apply();
+ }
+
+ private void setRotation(SurfaceControl.Transaction t) {
+ // Compute the transformation matrix that must be applied
+ // to the snapshot to make it stay in the same original position
+ // with the current screen rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ createRotationMatrix(delta, mStartWidth, mStartHeight, mSnapshotInitialMatrix);
+ setRotationTransform(t, mSnapshotInitialMatrix);
+ }
+
+ private void setRotationTransform(SurfaceControl.Transaction t, Matrix matrix) {
+ if (mScreenshotLayer == null) {
+ return;
+ }
+ matrix.getValues(mTmpFloats);
+ float x = mTmpFloats[Matrix.MTRANS_X];
+ float y = mTmpFloats[Matrix.MTRANS_Y];
+ t.setPosition(mScreenshotLayer, x, y);
+ t.setMatrix(mScreenshotLayer,
+ mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
+ mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
+
+ t.setAlpha(mScreenshotLayer, (float) 1.0);
+ t.show(mScreenshotLayer);
+ }
+
+ /**
+ * Returns true if animating.
+ */
+ public boolean startAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, float animationScale,
+ @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+ if (mScreenshotLayer == null) {
+ // Can't do animation.
+ return false;
+ }
+
+ // TODO : Found a way to get right end luma and re-enable color frame animation.
+ // End luma value is very not stable so it will cause more flicker is we run background
+ // color frame animation.
+ //mEndLuma = getLumaOfSurfaceControl(mEndBounds, mSurfaceControl);
+
+ // Figure out how the screen has moved from the original rotation.
+ int delta = deltaRotation(mEndRotation, mStartRotation);
+ switch (delta) { /* Counter-Clockwise Rotations */
+ case Surface.ROTATION_0:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_0_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.rotation_animation_enter);
+ break;
+ case Surface.ROTATION_90:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_plus_90_enter);
+ break;
+ case Surface.ROTATION_180:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_180_enter);
+ break;
+ case Surface.ROTATION_270:
+ mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_exit);
+ mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
+ R.anim.screen_rotate_minus_90_enter);
+ break;
+ }
+
+ mRotateExitAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateExitAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateExitAnimation.scaleCurrentDuration(animationScale);
+ mRotateEnterAnimation.initialize(mEndWidth, mEndHeight, mStartWidth, mStartHeight);
+ mRotateEnterAnimation.restrictDuration(MAX_ANIMATION_DURATION);
+ mRotateEnterAnimation.scaleCurrentDuration(animationScale);
+
+ mTransaction = mTransactionPool.acquire();
+ startDisplayRotation(animations, finishCallback, mainExecutor, animExecutor);
+ startScreenshotRotationAnimation(animations, finishCallback, mainExecutor, animExecutor);
+ //startColorAnimation(mTransaction, animationScale);
+
+ return true;
+ }
+
+ private void startDisplayRotation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ }
+
+ private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
+ @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor,
+ @NonNull ShellExecutor animExecutor) {
+ startSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
+ mTransactionPool, mainExecutor, animExecutor, null /* position */);
+ }
+
+ private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
+ int colorTransitionMs = mContext.getResources().getInteger(
+ R.integer.config_screen_rotation_color_transition);
+ final float[] rgbTmpFloat = new float[3];
+ final int startColor = Color.rgb(mStartLuma, mStartLuma, mStartLuma);
+ final int endColor = Color.rgb(mEndLuma, mEndLuma, mEndLuma);
+ final long duration = colorTransitionMs * (long) animationScale;
+ final Transaction t = mTransactionPool.acquire();
+
+ final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
+ // Animation length is already expected to be scaled.
+ va.overrideDurationScale(1.0f);
+ va.setDuration(duration);
+ va.addUpdateListener(animation -> {
+ final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
+ final float fraction = currentPlayTime / va.getDuration();
+ applyColor(startColor, endColor, rgbTmpFloat, fraction, mBackColorSurface, t);
+ });
+ va.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+ t);
+ mTransactionPool.release(t);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ applyColor(startColor, endColor, rgbTmpFloat, 1f /* fraction */, mBackColorSurface,
+ t);
+ mTransactionPool.release(t);
+ }
+ });
+ animExecutor.execute(va::start);
+ }
+
+ public void kill() {
+ Transaction t = mTransaction != null ? mTransaction : mTransactionPool.acquire();
+ if (mAnimLeash.isValid()) {
+ t.remove(mAnimLeash);
+ }
+
+ if (mScreenshotLayer != null) {
+ if (mScreenshotLayer.isValid()) {
+ t.remove(mScreenshotLayer);
+ }
+ mScreenshotLayer = null;
+
+ if (mBackColorSurface != null) {
+ if (mBackColorSurface.isValid()) {
+ t.remove(mBackColorSurface);
+ }
+ mBackColorSurface = null;
+ }
+ }
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
+ /**
+ * Converts the provided {@link HardwareBuffer} and converts it to a bitmap to then sample the
+ * luminance at the borders of the bitmap
+ * @return the average luminance of all the pixels at the borders of the bitmap
+ */
+ private static float getMedianBorderLuma(HardwareBuffer hardwareBuffer, ColorSpace colorSpace) {
+ // Cannot read content from buffer with protected usage.
+ if (hardwareBuffer == null || hardwareBuffer.getFormat() != RGBA_8888
+ || hasProtectedContent(hardwareBuffer)) {
+ return 0;
+ }
+
+ ImageReader ir = ImageReader.newInstance(hardwareBuffer.getWidth(),
+ hardwareBuffer.getHeight(), hardwareBuffer.getFormat(), 1);
+ ir.getSurface().attachAndQueueBufferWithColorSpace(hardwareBuffer, colorSpace);
+ Image image = ir.acquireLatestImage();
+ if (image == null || image.getPlanes().length == 0) {
+ return 0;
+ }
+
+ Image.Plane plane = image.getPlanes()[0];
+ ByteBuffer buffer = plane.getBuffer();
+ int width = image.getWidth();
+ int height = image.getHeight();
+ int pixelStride = plane.getPixelStride();
+ int rowStride = plane.getRowStride();
+ float[] borderLumas = new float[2 * width + 2 * height];
+
+ // Grab the top and bottom borders
+ int l = 0;
+ for (int x = 0; x < width; x++) {
+ borderLumas[l++] = getPixelLuminance(buffer, x, 0, pixelStride, rowStride);
+ borderLumas[l++] = getPixelLuminance(buffer, x, height - 1, pixelStride, rowStride);
+ }
+
+ // Grab the left and right borders
+ for (int y = 0; y < height; y++) {
+ borderLumas[l++] = getPixelLuminance(buffer, 0, y, pixelStride, rowStride);
+ borderLumas[l++] = getPixelLuminance(buffer, width - 1, y, pixelStride, rowStride);
+ }
+
+ // Cleanup
+ ir.close();
+
+ // Oh, is this too simple and inefficient for you?
+ // How about implementing a O(n) solution? https://en.wikipedia.org/wiki/Median_of_medians
+ Arrays.sort(borderLumas);
+ return borderLumas[borderLumas.length / 2];
+ }
+
+ /**
+ * @return whether the hardwareBuffer passed in is marked as protected.
+ */
+ private static boolean hasProtectedContent(HardwareBuffer hardwareBuffer) {
+ return (hardwareBuffer.getUsage() & USAGE_PROTECTED_CONTENT) == USAGE_PROTECTED_CONTENT;
+ }
+
+ private static float getPixelLuminance(ByteBuffer buffer, int x, int y,
+ int pixelStride, int rowStride) {
+ int offset = y * rowStride + x * pixelStride;
+ int pixel = 0;
+ pixel |= (buffer.get(offset) & 0xff) << 16; // R
+ pixel |= (buffer.get(offset + 1) & 0xff) << 8; // G
+ pixel |= (buffer.get(offset + 2) & 0xff); // B
+ pixel |= (buffer.get(offset + 3) & 0xff) << 24; // A
+ return Color.valueOf(pixel).luminance();
+ }
+
+ /**
+ * Gets the average border luma by taking a screenshot of the {@param surfaceControl}.
+ * @see #getMedianBorderLuma(HardwareBuffer, ColorSpace)
+ */
+ private static float getLumaOfSurfaceControl(Rect bounds, SurfaceControl surfaceControl) {
+ if (surfaceControl == null) {
+ return 0;
+ }
+
+ Rect crop = new Rect(0, 0, bounds.width(), bounds.height());
+ SurfaceControl.ScreenshotHardwareBuffer buffer =
+ SurfaceControl.captureLayers(surfaceControl, crop, 1);
+ if (buffer == null) {
+ return 0;
+ }
+
+ return getMedianBorderLuma(buffer.getHardwareBuffer(), buffer.getColorSpace());
+ }
+
+ private static void createRotationMatrix(int rotation, int width, int height,
+ Matrix outMatrix) {
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ outMatrix.reset();
+ break;
+ case Surface.ROTATION_90:
+ outMatrix.setRotate(90, 0, 0);
+ outMatrix.postTranslate(height, 0);
+ break;
+ case Surface.ROTATION_180:
+ outMatrix.setRotate(180, 0, 0);
+ outMatrix.postTranslate(width, height);
+ break;
+ case Surface.ROTATION_270:
+ outMatrix.setRotate(270, 0, 0);
+ outMatrix.postTranslate(0, width);
+ break;
+ }
+ }
+
+ private static void applyColor(int startColor, int endColor, float[] rgbFloat,
+ float fraction, SurfaceControl surface, SurfaceControl.Transaction t) {
+ final int color = (Integer) ArgbEvaluator.getInstance().evaluate(fraction, startColor,
+ endColor);
+ Color middleColor = Color.valueOf(color);
+ rgbFloat[0] = middleColor.red();
+ rgbFloat[1] = middleColor.green();
+ rgbFloat[2] = middleColor.blue();
+ if (surface.isValid()) {
+ t.setColor(surface, rgbFloat);
+ }
+ t.apply();
+ }
+}
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 60707ccdca30..8d21ce25bcd0 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
@@ -19,6 +19,7 @@ package com.android.wm.shell.transition;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -54,6 +55,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -77,6 +79,15 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Transition type for launching 2 tasks simultaneously. */
public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
+ /** Transition type for exiting PIP via the Shell, via pressing the expand button. */
+ public static final int TRANSIT_EXIT_PIP = TRANSIT_FIRST_CUSTOM + 3;
+
+ /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */
+ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4;
+
+ /** Transition type for entering split by opening an app into side-stage. */
+ public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
@@ -91,27 +102,29 @@ public class Transitions implements RemoteCallable<Transitions> {
private float mTransitionAnimationScaleSetting = 1.0f;
private static final class ActiveTransition {
- IBinder mToken = null;
- TransitionHandler mHandler = null;
- boolean mMerged = false;
- TransitionInfo mInfo = null;
- SurfaceControl.Transaction mStartT = null;
- SurfaceControl.Transaction mFinishT = null;
+ IBinder mToken;
+ TransitionHandler mHandler;
+ boolean mMerged;
+ boolean mAborted;
+ TransitionInfo mInfo;
+ SurfaceControl.Transaction mStartT;
+ SurfaceControl.Transaction mFinishT;
}
/** Keeps track of currently playing transitions in the order of receipt. */
private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
- @NonNull Context context, @NonNull ShellExecutor mainExecutor,
- @NonNull ShellExecutor animExecutor) {
+ @NonNull DisplayController displayController, @NonNull Context context,
+ @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
mPlayerImpl = new TransitionPlayerImpl();
// The very last handler (0 in the list) should be the default one.
- mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
+ mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
+ animExecutor));
// Next lowest priority is remote transitions.
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
mHandlers.add(mRemoteTransitionHandler);
@@ -218,7 +231,7 @@ public class Transitions implements RemoteCallable<Transitions> {
public static boolean isOpeningType(@WindowManager.TransitionType int type) {
return type == TRANSIT_OPEN
|| type == TRANSIT_TO_FRONT
- || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+ || type == TRANSIT_KEYGUARD_GOING_AWAY;
}
/** @return true if the transition was triggered by closing something vs opening something */
@@ -382,7 +395,7 @@ public class Transitions implements RemoteCallable<Transitions> {
}
boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
- return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
+ return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT,
(wct, cb) -> onFinish(active.mToken, wct, cb));
}
@@ -416,17 +429,19 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Special version of finish just for dealing with no-op/invalid transitions. */
private void onAbort(IBinder transition) {
- final int activeIdx = findActiveTransition(transition);
- if (activeIdx < 0) return;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation aborted due to no-op, notifying core %s", transition);
- mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
+ onFinish(transition, null /* wct */, null /* wctCB */, true /* abort */);
}
private void onFinish(IBinder transition,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
+ onFinish(transition, wct, wctCB, false /* abort */);
+ }
+
+ private void onFinish(IBinder transition,
+ @Nullable WindowContainerTransaction wct,
+ @Nullable WindowContainerTransactionCallback wctCB,
+ boolean abort) {
int activeIdx = findActiveTransition(transition);
if (activeIdx < 0) {
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
@@ -434,28 +449,37 @@ public class Transitions implements RemoteCallable<Transitions> {
return;
} else if (activeIdx > 0) {
// This transition was merged.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
- transition);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged (abort=%b:"
+ + " %s", abort, transition);
final ActiveTransition active = mActiveTransitions.get(activeIdx);
active.mMerged = true;
+ active.mAborted = abort;
if (active.mHandler != null) {
active.mHandler.onTransitionMerged(active.mToken);
}
return;
}
+ mActiveTransitions.get(activeIdx).mAborted = abort;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation finished, notifying core %s", transition);
+ "Transition animation finished (abort=%b), notifying core %s", abort, transition);
// Merge all relevant transactions together
SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
final ActiveTransition toMerge = mActiveTransitions.get(iA);
if (!toMerge.mMerged) break;
+ // aborted transitions have no start/finish transactions
+ if (mActiveTransitions.get(iA).mStartT == null) break;
+ if (fullFinish == null) {
+ fullFinish = new SurfaceControl.Transaction();
+ }
// Include start. It will be a no-op if it was already applied. Otherwise, we need it
// to maintain consistent state.
fullFinish.merge(mActiveTransitions.get(iA).mStartT);
fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
}
- fullFinish.apply();
+ if (fullFinish != null) {
+ fullFinish.apply();
+ }
// Now perform all the finishes.
mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
@@ -464,6 +488,12 @@ public class Transitions implements RemoteCallable<Transitions> {
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
}
+ // sift through aborted transitions
+ while (mActiveTransitions.size() > activeIdx
+ && mActiveTransitions.get(activeIdx).mAborted) {
+ ActiveTransition aborted = mActiveTransitions.remove(activeIdx);
+ mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
+ }
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
+ "finished");
@@ -494,6 +524,12 @@ public class Transitions implements RemoteCallable<Transitions> {
int mergeIdx = activeIdx + 1;
while (mergeIdx < mActiveTransitions.size()) {
ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
+ if (mergeCandidate.mAborted) {
+ // transition was aborted, so we can skip for now (still leave it in the list
+ // so that it gets cleaned-up in the right order).
+ ++mergeIdx;
+ continue;
+ }
if (mergeCandidate.mMerged) {
throw new IllegalStateException("Can't merge a transition after not-merging"
+ " a preceding one.");
@@ -566,12 +602,19 @@ public class Transitions implements RemoteCallable<Transitions> {
* Starts a transition animation. This is always called if handleRequest returned non-null
* for a particular transition. Otherwise, it is only called if no other handler before
* it handled the transition.
- *
+ * @param startTransaction the transaction given to the handler to be applied before the
+ * transition animation. Note the handler is expected to call on
+ * {@link SurfaceControl.Transaction#apply()} for startTransaction.
+ * @param finishTransaction the transaction given to the handler to be applied after the
+ * transition animation. Unlike startTransaction, the handler is NOT
+ * expected to apply this transaction. The Transition system will
+ * apply it when finishCallback is called.
* @param finishCallback Call this when finished. This MUST be called on main thread.
* @return true if transition was handled, false if not (falls-back to default).
*/
boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback);
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java
new file mode 100644
index 000000000000..2c668ed3d84d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.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.wm.shell.transition;
+
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * Represents a surface that is displayed over a transition surface.
+ */
+class WindowThumbnail {
+
+ private SurfaceControl mSurfaceControl;
+
+ private WindowThumbnail() {}
+
+ /** Create a thumbnail surface and attach it over a parent surface. */
+ static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent,
+ HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) {
+ WindowThumbnail windowThumbnail = new WindowThumbnail();
+ windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+ .setParent(parent)
+ .setName("WindowThumanil : " + parent.toString())
+ .setCallsite("WindowThumanil")
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .build();
+
+ GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(thumbnailHeader);
+ t.setBuffer(windowThumbnail.mSurfaceControl, graphicBuffer);
+ t.setColorSpace(windowThumbnail.mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
+ t.setLayer(windowThumbnail.mSurfaceControl, Integer.MAX_VALUE);
+ t.show(windowThumbnail.mSurfaceControl);
+ t.apply();
+
+ return windowThumbnail;
+ }
+
+ SurfaceControl getSurface() {
+ return mSurfaceControl;
+ }
+
+ /** Remove the thumbnail surface and release the surface. */
+ void destroy(SurfaceControl.Transaction t) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+
+ t.remove(mSurfaceControl);
+ t.apply();
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 9dd25fe0e6fe..3ca5b9c38aff 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -25,11 +25,17 @@ package {
android_test {
name: "WMShellFlickerTests",
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
test_suites: ["device-tests"],
libs: ["android.test.runner"],
static_libs: [
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 c5b5b91d570b..b36468b7e9a5 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
@@ -16,97 +16,100 @@
package com.android.wm.shell.flicker
+import android.content.ComponentName
import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-fun FlickerTestParameter.appPairsDividerIsVisible() {
+fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
- this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.appPairsDividerIsInvisible() {
+fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() {
assertLayersEnd {
- this.notContains(APP_PAIR_SPLIT_DIVIDER)
+ this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.appPairsDividerBecomesVisible() {
assertLayers {
- this.isInvisible(DOCKED_STACK_DIVIDER)
+ this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isVisible(DOCKED_STACK_DIVIDER)
+ .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.dockedStackDividerIsVisible() {
+fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
assertLayers {
- this.isInvisible(DOCKED_STACK_DIVIDER)
+ this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isVisible(DOCKED_STACK_DIVIDER)
+ .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
- .isInvisible(DOCKED_STACK_DIVIDER)
+ .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.dockedStackDividerIsInvisible() {
+fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() {
assertLayersEnd {
- this.notContains(DOCKED_STACK_DIVIDER)
+ this.notContains(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
-fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
+fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
+ rotation: Int,
+ primaryComponent: ComponentName
+) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryLayerName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryComponent)
.coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryLayerName: String
+ primaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- visibleRegion(primaryLayerName)
+ val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryComponent)
.coversExactly(getPrimaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible(
+fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryLayerName: String
+ secondaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(secondaryLayerName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(secondaryComponent)
.coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
-fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryLayerName: String
+ secondaryComponent: ComponentName
) {
assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
- visibleRegion(secondaryLayerName)
+ val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(secondaryComponent)
.coversExactly(getSecondaryRegion(dividerRegion, rotation))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 03b93c74233c..ff1a6e6d9d90 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-const val IME_WINDOW_NAME = "InputMethod"
+import android.content.ComponentName
+
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
-const val DOCKED_STACK_DIVIDER = "DockedStackDivider" \ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#") \ No newline at end of file
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 ef9f7421fd60..19374ed04be5 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@ class AppPairsTestCannotPairNonResizeableApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
}
}
@@ -85,15 +84,13 @@ class AppPairsTestCannotPairNonResizeableApps(
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+ fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Presubmit
@Test
@@ -103,8 +100,8 @@ class AppPairsTestCannotPairNonResizeableApps(
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
- isInvisible(primaryApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
+ isInvisible(primaryApp.component)
}
}
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 db63c4c43523..46ee89295a4e 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,10 +24,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,10 +53,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
}
}
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -68,14 +71,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ isVisible(secondaryApp.component)
}
}
@@ -83,10 +86,10 @@ class AppPairsTestPairPrimaryAndSecondaryApps(
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryApp.defaultWindowName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.defaultWindowName)
+ visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index c8d34237231c..f7ced71afe8a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@ class AppPairsTestSupportPairNonResizeableApps(
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
}
}
@@ -77,6 +76,10 @@ class AppPairsTestSupportPairNonResizeableApps(
resetMultiWindowConfig(instrumentation)
}
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -87,7 +90,7 @@ class AppPairsTestSupportPairNonResizeableApps(
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
@@ -97,8 +100,8 @@ class AppPairsTestSupportPairNonResizeableApps(
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
- isVisible(primaryApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
+ isVisible(primaryApp.component)
}
}
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 83df83600d11..3debdd3276e4 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
@@ -25,10 +25,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,9 +51,11 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
get() = {
super.transition(this, it)
setup {
- executeShellCommand(
- composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ eachRun {
+ executeShellCommand(
+ composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+ waitAppsShown(primaryApp, secondaryApp)
+ }
}
transitions {
// TODO pair apps through normal UX flow
@@ -73,14 +75,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Presubmit
@Test
- fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+ fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Presubmit
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
- isInvisible(primaryApp.defaultWindowName)
- isInvisible(secondaryApp.defaultWindowName)
+ isInvisible(primaryApp.component)
+ isInvisible(secondaryApp.component)
}
}
@@ -88,10 +90,10 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Test
fun appsStartingBounds() {
testSpec.assertLayersStart {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- visibleRegion(primaryApp.defaultWindowName)
+ val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+ visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
- visibleRegion(secondaryApp.defaultWindowName)
+ visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
@@ -100,16 +102,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps(
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
- notContains(primaryApp.defaultWindowName)
- notContains(secondaryApp.defaultWindowName)
+ notContains(primaryApp.component)
+ notContains(secondaryApp.component)
}
}
- @FlakyTest
+ @Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 1935bb97849c..cdf89a57fde8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -30,14 +30,14 @@ import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
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.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -154,26 +154,26 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter)
@FlakyTest(bugId = 186510496)
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
@Presubmit
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 c875c0006703..3e782e608c86 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -28,10 +27,10 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -57,41 +56,43 @@ class RotateTwoLaunchedAppsInAppPairsMode(
transitions {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, true /* pair */))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
setRotation(testSpec.config.endRotation)
}
}
- @FlakyTest
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ .isVisible(secondaryApp.component)
}
}
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
- @FlakyTest(bugId = 172776659)
+ @Presubmit
@Test
- fun appPairsPrimaryBoundsIsVisible() =
- testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
- primaryApp.defaultWindowName)
+ fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ primaryApp.component)
- @FlakyTest(bugId = 172776659)
+ @FlakyTest
@Test
- fun appPairsSecondaryBoundsIsVisible() =
- testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
- secondaryApp.defaultWindowName)
+ fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ secondaryApp.component)
companion object {
@Parameterized.Parameters(name = "{0}")
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 c3360ca0f7d3..ee28c7aa6beb 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.apppairs
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -28,12 +27,10 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,48 +57,50 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
this.setRotation(testSpec.config.endRotation)
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
- SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+ waitAppsShown(primaryApp, secondaryApp)
}
}
@Presubmit
@Test
- fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+ fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
@Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+ @Presubmit
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ isVisible(primaryApp.component)
+ isVisible(secondaryApp.component)
}
}
@FlakyTest(bugId = 172776659)
@Test
- fun appPairsPrimaryBoundsIsVisible() =
- testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
- primaryApp.defaultWindowName)
+ fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ primaryApp.component)
@FlakyTest(bugId = 172776659)
@Test
- fun appPairsSecondaryBoundsIsVisible() =
- testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
- secondaryApp.defaultWindowName)
+ fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+ secondaryApp.component)
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 512fd9a58ea8..b95193a17265 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -22,7 +22,10 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.Test
abstract class RotateTwoLaunchedAppsTransition(
@@ -37,8 +40,8 @@ abstract class RotateTwoLaunchedAppsTransition(
test {
device.wakeUpAndGoToHomeScreen()
this.setRotation(Surface.ROTATION_0)
- primaryApp.launchViaIntent()
- secondaryApp.launchViaIntent()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
updateTasksId()
}
}
@@ -52,10 +55,17 @@ abstract class RotateTwoLaunchedAppsTransition(
}
}
+ @Before
+ override fun setup() {
+ // AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation.
+ assumeFalse(isShellTransitionsEnabled())
+ super.setup()
+ }
+
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsVisible() {
+ super.navBarLayerIsVisible()
}
@FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5b8cfb81016a..5a438af0b1f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
import android.content.ComponentName
import android.graphics.Region
+import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
class AppPairsHelper(
@@ -43,5 +44,17 @@ class AppPairsHelper(
companion object {
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
+
+ fun Flicker.waitAppsShown(app1: SplitScreenHelper?, app2: SplitScreenHelper?) {
+ wmHelper.waitFor("primaryAndSecondaryAppsVisible") { dump ->
+ val primaryAppVisible = app1?.let {
+ dump.wmState.isWindowSurfaceShown(app1.defaultWindowName)
+ } ?: false
+ val secondaryAppVisible = app2?.let {
+ dump.wmState.isWindowSurfaceShown(app2.defaultWindowName)
+ } ?: false
+ primaryAppVisible && secondaryAppVisible
+ }
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index 4fe69ad7fabe..f15044ef37af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -20,6 +20,7 @@ import android.app.Instrumentation
import android.content.ComponentName
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
+import android.os.SystemProperties
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.util.Log
import androidx.test.uiautomator.By
@@ -60,6 +61,9 @@ abstract class BaseAppHelper(
companion object {
private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+ fun isShellTransitionsEnabled() =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false)
+
fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
try {
SystemUtil.runShellCommand(instrumentation, cmd)
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 cac46fe676b3..086e8b792e0e 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
@@ -61,7 +61,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
device.waitForIdle()
} else {
- require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+ require(wmHelper.waitImeShown()) { "IME did not appear" }
}
}
@@ -78,7 +78,7 @@ open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
if (wmHelper == null) {
uiDevice.waitForIdle()
} else {
- require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+ require(wmHelper.waitImeGone()) { "IME did did not close" }
}
} else {
// While pressing the back button should close the IME on TV as well, it may also lead
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 f4dd7decb1b7..1529f5bcd7e1 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,11 +17,14 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import android.graphics.Rect
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 androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
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
@@ -62,7 +65,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
stringExtras: Map<String, String>
) {
super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- wmHelper.waitFor { it.wmState.hasPipWindow() }
+ wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
}
private fun focusOnObject(selector: BySelector): Boolean {
@@ -84,7 +87,11 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
clickObject(ENTER_PIP_BUTTON_ID)
// Wait on WMHelper or simply wait for 3 seconds
- wmHelper?.waitFor { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+ wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+ // when entering pip, the dismiss button is visible at the start. to ensure the pip
+ // animation is complete, wait until the pip dismiss button is no longer visible.
+ // b/176822698: dismiss-only state will be removed in the future
+ uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
}
fun clickStartMediaSessionButton() {
@@ -113,61 +120,58 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
}
}
+ private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
+ val windowRegion = wmHelper.getWindowRegion(component)
+ require(!windowRegion.isEmpty) {
+ "Unable to find a PIP window in the current state"
+ }
+ return windowRegion.bounds
+ }
+
/**
* 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 windowRect = getWindowRect(wmHelper)
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
- requireNotNull(exitPipObject) { "PIP window dismiss button not found" }
+ ?: error("PIP window dismiss button not found")
val dismissButtonBounds = exitPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
// Wait for animation to complete.
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForHomeActivityVisible()
}
/**
- * Click once on the PIP window to expand it
+ * Close the pip window by pressing the expand button
*/
- 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
+ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // Ensure WindowManagerService wait until all animations have completed
+ // search and interact with the expand button
+ val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
+ uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
+ val expandPipObject = uiDevice.findObject(expandSelector)
+ ?: error("PIP window expand button not found")
+ val expandButtonBounds = expandPipObject.visibleBounds
+ uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
- mInstrumentation.uiAutomation.syncInputTransactions()
}
/**
- * Double click on the PIP window to reopen to app
+ * Double click on the PIP window to expand it
*/
- 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
+ fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- wmHelper.waitFor { !it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 901b7a393291..2d996ca1d6f7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
import android.content.ComponentName
+import android.content.res.Resources
import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
@@ -30,6 +31,11 @@ class SplitScreenHelper(
const val TEST_REPETITIONS = 1
const val TIMEOUT_MS = 3_000L
+ // TODO: remove all legacy split screen flicker tests when legacy split screen is fully
+ // deprecated.
+ fun isUsingLegacySplit(): Boolean =
+ Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useLegacySplit)
+
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 4f12f2bb9f5f..508e93988aa6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,22 +16,24 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -60,16 +62,16 @@ class EnterSplitScreenDockActivity(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
+ splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
@@ -77,27 +79,39 @@ class EnterSplitScreenDockActivity(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
}
}
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+ supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index f91f634a00e5..12f3909b6c34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -25,7 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,24 +62,34 @@ class EnterSplitScreenFromDetachedRecentTask(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 85ded8a45233..ac85c4857c76 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,17 +24,16 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -62,22 +62,22 @@ class EnterSplitScreenLaunchToSide(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+ secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
@Presubmit
@Test
@@ -85,15 +85,35 @@ class EnterSplitScreenLaunchToSide(
@Presubmit
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(secondaryApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index e958bf39930e..964af2341439 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,12 +71,12 @@ class EnterSplitScreenNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -91,7 +92,12 @@ class EnterSplitScreenNotSupportNonResizable(
@Presubmit
@Test
- fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index d3acc82121b0..1b8afa668802 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@ import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -67,12 +68,12 @@ class EnterSplitScreenSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -88,16 +89,21 @@ class EnterSplitScreenSupportNonResizable(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index bad46836dcb7..247965f8071d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Presubmit
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -24,15 +25,13 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -67,31 +66,52 @@ class ExitLegacySplitScreenFromBottom(
}
}
transitions {
- device.exitSplitScreenFromBottom()
+ device.exitSplitScreenFromBottom(wmHelper)
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ splitScreenApp.component, secondaryApp.component,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
- @Presubmit
+ @Postsubmit
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
+ .then()
+ .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
+ }
+ }
@FlakyTest
@Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesInVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(secondaryApp.component)
+ .then()
+ .isAppWindowInvisible(secondaryApp.component)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @Presubmit
+ @Postsubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
- @Presubmit
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 76dcd8b89242..ff34364261f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,15 +25,13 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -71,31 +70,52 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ splitScreenApp.component, secondaryApp.component,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
- @FlakyTest(bugId = 175687842)
+ @Presubmit
@Test
- fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@FlakyTest
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
@FlakyTest
@Test
- fun appWindowBecomesInVisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesInVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(splitScreenApp.component)
+ .then()
+ .isAppWindowInvisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index d0a64b3774c7..95e4085db4eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,15 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -72,11 +69,11 @@ class LegacySplitScreenFromIntentNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ nonResizeableApp.component, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -92,44 +89,110 @@ class LegacySplitScreenFromIntentNotSupportNonResizable(
@Presubmit
@Test
- fun resizableAppLayerBecomesInvisible() =
- testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun resizableAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ .isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
+ /**
+ * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes
+ * invisible, it remains invisible until the end of the trace.
+ */
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun resizableAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ // when the activity gets PAUSED the window may still be marked as visible
+ // it will be updated in the next log entry. This occurs because we record 1x
+ // per frame, thus ignore activity check here
+ this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ }
+ }
+ /**
+ * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+ * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+ * visible, it remains visible until the end of the trace.
+ */
@Presubmit
@Test
- fun resizableAppWindowBecomesInvisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ // we log once per frame, upon logging, window may be visible or not depending
+ // on what was processed until that moment. Both behaviors are correct
+ .isAppWindowInvisible(nonResizeableApp.component,
+ ignoreActivity = true, isOptional = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ }
+ }
+ /**
+ * Asserts that both the app window and the activity are visible at the end of the trace
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ this.isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
- repetitions = SplitScreenHelper.TEST_REPETITIONS,
- supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index c26c05fa8db6..65346aa8ea5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,13 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,11 +69,11 @@ class LegacySplitScreenFromIntentSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ nonResizeableApp.component, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -90,27 +89,60 @@ class LegacySplitScreenFromIntentSupportNonResizable(
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
+ /**
+ * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+ * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+ * visible, it remains visible until the end of the trace.
+ */
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.notContains(nonResizeableApp.component)
+ .then()
+ // we log once per frame, upon logging, window may be visible or not depending
+ // on what was processed until that moment. Both behaviors are correct
+ .isAppWindowInvisible(nonResizeableApp.component,
+ ignoreActivity = true, isOptional = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index fb1758975442..547341a14cdd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,6 +16,8 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,16 +25,12 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -73,11 +71,11 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -93,37 +91,73 @@ class LegacySplitScreenFromRecentNotSupportNonResizable(
@Presubmit
@Test
- fun resizableAppLayerBecomesInvisible() =
- testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ fun resizableAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(splitScreenApp.component)
+ .then()
+ .isInvisible(splitScreenApp.component)
+ }
+ }
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun resizableAppWindowBecomesInvisible() =
- testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ fun resizableAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ // when the activity gets PAUSED the window may still be marked as visible
+ // it will be updated in the next log entry. This occurs because we record 1x
+ // per frame, thus ignore activity check here
+ this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ .then()
+ // immediately after the window (after onResume and before perform relayout)
+ // the activity is invisible. This may or not be logged, since we record 1x
+ // per frame, thus ignore activity check here
+ .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ }
+ }
- @Presubmit
+ @Postsubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(nonResizeableApp.component)
+ .then()
+ .isAppWindowVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
@Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index a9c28efcdf44..3f86658297fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -23,14 +24,12 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -71,11 +70,11 @@ class LegacySplitScreenFromRecentSupportNonResizable(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Before
override fun setup() {
@@ -91,27 +90,60 @@ class LegacySplitScreenFromRecentSupportNonResizable(
@Presubmit
@Test
- fun nonResizableAppLayerBecomesVisible() =
- testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(nonResizeableApp.component)
+ .then()
+ .isVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun nonResizableAppWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(nonResizeableApp.component, isOptional = true)
+ .then()
+ // if the window reappears after re-parenting it will most likely not
+ // be visible in the first log entry (because we log only 1x per frame)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(nonResizeableApp.component)
+ }
+ }
@Presubmit
@Test
- fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.defaultWindowName)
- isVisible(nonResizeableApp.defaultWindowName)
+ isVisible(splitScreenApp.component)
+ isVisible(nonResizeableApp.component)
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index a4d2ab51e358..7b4b71b41967 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -27,20 +27,18 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.exitSplitScreen
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
@@ -62,8 +60,6 @@ import org.junit.runners.Parameterized
class LegacySplitScreenToLauncher(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
- private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
private val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
@@ -90,25 +86,25 @@ class LegacySplitScreenToLauncher(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
@Presubmit
@Test
@@ -122,19 +118,39 @@ class LegacySplitScreenToLauncher(
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @Presubmit
+ @Postsubmit
@Test
fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
+ @Postsubmit
+ @Test
+ fun layerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
+
+ @Postsubmit
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
@Presubmit
@Test
- fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage())
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 151179149)
+ @Presubmit
@Test
- fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index e8d4d1e9ada2..311769313a7a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
+import android.content.ComponentName
import android.content.Context
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -32,10 +33,13 @@ import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.After
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
@@ -46,12 +50,17 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
- .launcherStrategy.supportedLauncherPackage
+ protected val LAUNCHER_COMPONENT = ComponentName("",
+ LauncherStrategyFactory.getInstance(instrumentation)
+ .launcherStrategy.supportedLauncherPackage)
private var prevDevEnableNonResizableMultiWindow = 0
@Before
open fun setup() {
+ // Only run legacy split tests when the system is using legacy split screen.
+ assumeTrue(SplitScreenHelper.isUsingLegacySplit())
+ // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+ assumeFalse(isShellTransitionsEnabled())
prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
if (prevDevEnableNonResizableMultiWindow != 0) {
// Turn off the development option
@@ -70,8 +79,9 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
*
* b/182720234
*/
- open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ open val ignoredWindows: List<ComponentName> = listOf(
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -138,9 +148,9 @@ abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestPa
}
companion object {
- internal const val LIVE_WALLPAPER_PACKAGE_NAME =
- "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
- internal const val LETTERBOX_NAME = "Letterbox"
- internal const val TOAST_NAME = "Toast"
+ internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+ "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
+ internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
+ internal val TOAST_COMPONENT = ComponentName("", "Toast")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 05eb5f49a641..ec0c73a58846 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -24,14 +25,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -62,22 +60,28 @@ class OpenAppToLegacySplitScreen(
}
}
- override val ignoredWindows: List<String>
- get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+ override val ignoredWindows: List<ComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT)
@FlakyTest
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage())
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
@@ -85,12 +89,27 @@ class OpenAppToLegacySplitScreen(
@FlakyTest
@Test
- fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage())
+ fun layerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(splitScreenApp.component)
+ .then()
+ .isVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges(splitScreenApp.`package`,
+ "recents_animation_input_consumer", "NexusLauncherActivity")
+ }
+ }
- @FlakyTest(bugId = 151179149)
+ @Presubmit
@Test
- fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`,
- "recents_animation_input_consumer", "NexusLauncherActivity")
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 3e83b6382939..d7f71a83ba7e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -28,23 +28,23 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.resizeSplitScreen
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.navBarLayerIsVisible
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.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import com.android.wm.shell.flicker.testapp.Components
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -101,16 +101,16 @@ class ResizeLegacySplitScreen(
}
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest(bugId = 156223549)
@Test
fun topAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.showsAppWindow(sSimpleActivity)
+ this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
}
}
@@ -118,18 +118,18 @@ class ResizeLegacySplitScreen(
@Test
fun bottomAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.showsAppWindow(sImeActivity)
+ this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
}
}
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
@Test
fun navBarLayerRotatesAndScales() =
@@ -142,21 +142,21 @@ class ResizeLegacySplitScreen(
@Test
fun topAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(sSimpleActivity)
+ this.isVisible(Components.SimpleActivity.COMPONENT)
}
}
@Test
fun bottomAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(sImeActivity)
+ this.isVisible(Components.ImeActivity.COMPONENT)
}
}
@Test
fun dividerLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(DOCKED_STACK_DIVIDER)
+ this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
}
}
@@ -166,7 +166,7 @@ class ResizeLegacySplitScreen(
testSpec.assertLayersStart {
val displayBounds = WindowUtils.displayBounds
val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+ layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
val topAppBounds = Region(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -174,8 +174,8 @@ class ResizeLegacySplitScreen(
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion("SimpleActivity").coversExactly(topAppBounds)
- visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
}
}
@@ -185,7 +185,7 @@ class ResizeLegacySplitScreen(
testSpec.assertLayersStart {
val displayBounds = WindowUtils.displayBounds
val dividerBounds =
- entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+ layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
val topAppBounds = Region(0, 0, dividerBounds.right,
dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -194,8 +194,8 @@ class ResizeLegacySplitScreen(
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
- visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
}
}
@@ -207,8 +207,6 @@ class ResizeLegacySplitScreen(
}
companion object {
- private const val sSimpleActivity = "SimpleActivity"
- private const val sImeActivity = "ImeActivity"
private val startRatio = Rational(1, 3)
private val stopRatio = Rational(2, 3)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 58482eaae3f5..8a2b55b6fce0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -24,18 +24,17 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -66,21 +65,21 @@ class RotateOneLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -88,16 +87,26 @@ class RotateOneLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 06828d6adb26..b3251573c942 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -24,18 +24,17 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -66,35 +65,45 @@ class RotateOneLaunchedAppInSplitScreenMode(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
- testSpec.config.startRotation, splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
+ testSpec.config.startRotation, splitScreenApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
testSpec.config.startRotation, testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(splitScreenApp.component)
+ .then()
+ .isAppWindowVisible(splitScreenApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index f8e32bf171d8..2be693631b26 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -18,26 +18,24 @@ package com.android.wm.shell.flicker.legacysplitscreen
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -69,42 +67,66 @@ class RotateTwoLaunchedAppAndEnterSplitScreen(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
testSpec.config.startRotation, testSpec.config.endRotation)
@Presubmit
@Test
- fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ // when the app is launched, first the activity becomes visible, then the
+ // SnapshotStartingWindow appears and then the app window becomes visible.
+ // Because we log WM once per frame, sometimes the activity and the window
+ // become visible in the same entry, sometimes not, thus it is not possible to
+ // assert the visibility of the activity here
+ this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ .then()
+ // during re-parenting, the window may disappear and reappear from the
+ // trace, this occurs because we log only 1x per frame
+ .notContains(secondaryApp.component, isOptional = true)
+ .then()
+ // if the window reappears after re-parenting it will most likely not
+ // be visible in the first log entry (because we log only 1x per frame)
+ .isAppWindowInvisible(secondaryApp.component, isOptional = true)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index cb246ca0b694..5782f145c00f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -24,20 +24,19 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -74,27 +73,27 @@ class RotateTwoLaunchedAppInSplitScreenMode(
@Presubmit
@Test
- fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
@Presubmit
@Test
- fun dockedStackPrimaryBoundsIsVisible() =
- testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
- splitScreenApp.defaultWindowName)
+ fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ splitScreenApp.component)
@Presubmit
@Test
- fun dockedStackSecondaryBoundsIsVisible() =
- testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
- secondaryApp.defaultWindowName)
+ fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+ testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+ secondaryApp.component)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 169271943)
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -102,16 +101,31 @@ class RotateTwoLaunchedAppInSplitScreenMode(
@FlakyTest
@Test
- fun appWindowBecomesVisible() =
- testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+ fun appWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(secondaryApp.component)
+ .then()
+ .isAppWindowVisible(secondaryApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ @Presubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 2a660747bc1d..443204c245db 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -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.
@@ -16,4 +16,4 @@
package com.android.wm.shell.flicker.pip
-internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
+internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
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 b6af26060050..f84908e7d150 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -23,8 +24,10 @@ import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,8 +35,21 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip launch.
+ * Test entering pip from an app by interacting with the app UI
+ *
* To run this test: `atest WMShellFlickerTests:EnterPipTest`
+ *
+ * Actions:
+ * Launch an app in full screen
+ * Press an "enter pip" button to put [pipApp] in pip mode
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -41,49 +57,121 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
transitions {
- pipApp.clickEnterPipButton()
- pipApp.expandPipWindow(wmHelper)
+ pipApp.clickEnterPipButton(wmHelper)
}
}
- @FlakyTest
+ /**
+ * Checks [pipApp] window remains visible throughout the animation
+ */
+ @Presubmit
@Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
+ fun pipAppWindowAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(pipApp.component)
+ }
}
+ /**
+ * Checks [pipApp] layer remains visible throughout the animation
+ */
@Presubmit
@Test
- fun pipAppWindowAlwaysVisible() {
+ fun pipAppLayerAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the pip app window remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Presubmit
+ @Test
+ fun pipWindowRemainInsideVisibleBounds() {
testSpec.assertWm {
- this.showsAppWindow(pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
- @FlakyTest
+ /**
+ * Checks that the pip app layer remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Postsubmit
@Test
- fun pipLayerBecomesVisible() {
+ fun pipLayerRemainInsideVisibleBounds() {
testSpec.assertLayers {
- this.isVisible(pipApp.windowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
- @FlakyTest
+ /**
+ * Checks that the visible region of [pipApp] always reduces during the animation
+ */
+ @Postsubmit
@Test
- fun pipWindowBecomesVisible() {
- testSpec.assertWm {
- invoke("pipWindowIsNotVisible") {
- verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
- }.then().invoke("pipWindowIsVisible") {
- verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
+ fun pipLayerReduces() {
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.coversAtMost(previous.visibleRegion.region)
}
}
}
+ /**
+ * Checks that [pipApp] window becomes pinned
+ */
+ @Presubmit
+ @Test
+ fun pipWindowBecomesPinned() {
+ testSpec.assertWm {
+ invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp.component) }
+ .then()
+ .invoke("pipWindowIsPinned") { it.isPinned(pipApp.component) }
+ }
+ }
+
+ /**
+ * Checks [LAUNCHER_COMPONENT] layer remains visible throughout the animation
+ */
+ @Presubmit
+ @Test
+ fun launcherLayerBecomesVisible() {
+ testSpec.assertLayers {
+ isInvisible(LAUNCHER_COMPONENT)
+ .then()
+ .isVisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks the focus doesn't change during the animation
+ */
+ @FlakyTest
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 3a1456e53f87..38f403b0572b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -25,7 +26,11 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -38,8 +43,22 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip with orientation changes.
- * To run this test: `atest WMShellFlickerTests:PipOrientationTest`
+ * Test entering pip while changing orientation (from app in landscape to pip window in portrait)
+ *
+ * To run this test: `atest EnterPipToOtherOrientationTest:EnterPipToOtherOrientationTest`
+ *
+ * Actions:
+ * Launch [testApp] on a fixed portrait orientation
+ * Launch [pipApp] on a fixed landscape orientation
+ * Broadcast action [ACTION_ENTER_PIP] to enter pip mode
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -53,6 +72,9 @@ class EnterPipToOtherOrientationTest(
private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
setupAndTeardown(this, configuration)
@@ -79,65 +101,126 @@ class EnterPipToOtherOrientationTest(
broadcastActionTrigger.doAction(ACTION_ENTER_PIP)
wmHelper.waitFor { it.wmState.hasPipWindow() }
wmHelper.waitForAppTransitionIdle()
+ // during rotation the status bar becomes invisible and reappears at the end
+ wmHelper.waitForNavBarStatusBarVisible()
}
}
+ /**
+ * Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at
+ * the start and end of the transition
+ */
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun navBarLayerRotatesAndScales() =
+ testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
- @FlakyTest
+ /**
+ * Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
+ * the start and end of the transition
+ */
+ @Postsubmit
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ override fun statusBarLayerRotatesScales() =
+ testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0)
- @FlakyTest
+ /**
+ * Checks that all parts of the screen are covered at the start and end of the transition
+ */
+ @Presubmit
@Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
- }
+ override fun entireScreenCovered() =
+ testSpec.entireScreenCovered(Surface.ROTATION_90, Surface.ROTATION_0, allStates = false)
+ /**
+ * Checks [pipApp] window remains visible and on top throughout the transition
+ */
@Presubmit
@Test
fun pipAppWindowIsAlwaysOnTop() {
testSpec.assertWm {
- showsAppWindowOnTop(pipApp.defaultWindowName)
+ isAppWindowOnTop(pipApp.component)
}
}
+ /**
+ * Checks that [testApp] window is not visible at the start
+ */
@Presubmit
@Test
- fun pipAppHidesTestApp() {
+ fun testAppWindowInvisibleOnStart() {
testSpec.assertWmStart {
- isInvisible(testApp.defaultWindowName)
+ isInvisible(testApp.component)
}
}
+ /**
+ * Checks that [testApp] window is visible at the end
+ */
@Presubmit
@Test
- fun testAppWindowIsVisible() {
+ fun testAppWindowVisibleOnEnd() {
testSpec.assertWmEnd {
- isVisible(testApp.defaultWindowName)
+ isVisible(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp] layer is not visible at the start
+ */
+ @Presubmit
+ @Test
+ fun testAppLayerInvisibleOnStart() {
+ testSpec.assertLayersStart {
+ isInvisible(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp] layer is visible at the end
+ */
+ @Presubmit
+ @Test
+ fun testAppLayerVisibleOnEnd() {
+ testSpec.assertLayersEnd {
+ isVisible(testApp.component)
}
}
+ /**
+ * Checks that the visible region of [pipApp] covers the full display area at the start of
+ * the transition
+ */
@Presubmit
@Test
- fun pipAppLayerHidesTestApp() {
+ fun pipAppLayerCoversFullScreenOnStart() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
- isInvisible(testApp.defaultWindowName)
+ visibleRegion(pipApp.component).coversExactly(startingBounds)
}
}
+ /**
+ * Checks that the visible region of [testApp] plus the visible region of [pipApp]
+ * cover the full display area at the end of the transition
+ */
@Presubmit
@Test
- fun testAppLayerCoversFullScreen() {
+ fun testAppPlusPipLayerCoversFullScreenOnEnd() {
testSpec.assertLayersEnd {
- visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
+ val pipRegion = visibleRegion(pipApp.component).region
+ visibleRegion(testApp.component)
+ .plus(pipRegion)
+ .coversExactly(endingBounds)
}
}
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
new file mode 100644
index 000000000000..faaaecb8da46
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.platform.test.annotations.Presubmit
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.traces.parser.toLayerName
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Test
+
+/**
+ * Base class for pip expand tests
+ */
+abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ protected val testApp = FixedAppHelper(instrumentation)
+
+ /**
+ * Checks that the pip app window remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Presubmit
+ @Test
+ open fun pipAppWindowRemainInsideVisibleBounds() {
+ testSpec.assertWm {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the pip app layer remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Presubmit
+ @Test
+ open fun pipAppLayerRemainInsideVisibleBounds() {
+ testSpec.assertLayers {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks both app windows are visible at the start of the transition (with [pipApp] on top).
+ * Then, during the transition, [testApp] becomes invisible and [pipApp] remains visible
+ */
+ @Presubmit
+ @Test
+ open fun showBothAppWindowsThenHidePip() {
+ testSpec.assertWm {
+ // when the activity is STOPPING, sometimes it becomes invisible in an entry before
+ // the window, sometimes in the same entry. This occurs because we log 1x per frame
+ // thus we ignore activity here
+ isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowOnTop(pipApp.component)
+ .then()
+ .isAppWindowInvisible(testApp.component)
+ .isAppWindowVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks both app layers are visible at the start of the transition. Then, during the
+ * transition, [testApp] becomes invisible and [pipApp] remains visible
+ */
+ @Presubmit
+ @Test
+ open fun showBothAppLayersThenHidePip() {
+ testSpec.assertLayers {
+ isVisible(testApp.component)
+ .isVisible(pipApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ .isVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the visible region of [testApp] plus the visible region of [pipApp]
+ * cover the full display area at the start of the transition
+ */
+ @Presubmit
+ @Test
+ open fun testPlusPipAppsCoverFullScreenAtStart() {
+ testSpec.assertLayersStart {
+ val pipRegion = visibleRegion(pipApp.component).region
+ visibleRegion(testApp.component)
+ .plus(pipRegion)
+ .coversExactly(displayBounds)
+ }
+ }
+
+ /**
+ * Checks that the visible region of [pipApp] covers the full display area at the end of
+ * the transition
+ */
+ @Presubmit
+ @Test
+ open fun pipAppCoversFullScreenAtEnd() {
+ testSpec.assertLayersEnd {
+ visibleRegion(pipApp.component).coversExactly(displayBounds)
+ }
+ }
+
+ /**
+ * Checks that the visible region of [pipApp] always expands during the animation
+ */
+ @Presubmit
+ @Test
+ open fun pipLayerExpands() {
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index eae7e973711c..3414031d3532 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -20,15 +20,16 @@ import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import org.junit.Test
-import org.junit.runners.Parameterized
-abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+/**
+ * Base class for exiting pip (closing pip window) without returning to the app
+ */
+abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) { configuration ->
setup {
@@ -43,37 +44,49 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio
}
}
+ /**
+ * Checks that [pipApp] window is pinned and visible at the start and then becomes
+ * unpinned and invisible at the same moment, and remains unpinned and invisible
+ * until the end of the transition
+ */
@Presubmit
@Test
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
- this.showsAppWindow(PIP_WINDOW_TITLE)
- .then()
- .hidesAppWindow(PIP_WINDOW_TITLE)
+ this.invoke("hasPipWindow") {
+ it.isPinned(pipApp.component).isVisible(pipApp.component)
+ }.then().invoke("!hasPipWindow") {
+ it.isNotPinned(pipApp.component).isInvisible(pipApp.component)
+ }
}
}
+ /**
+ * Checks that [pipApp] and [LAUNCHER_COMPONENT] layers are visible at the start
+ * of the transition. Then [pipApp] layer becomes invisible, and remains invisible
+ * until the end of the transition
+ */
@Presubmit
@Test
open fun pipLayerBecomesInvisible() {
testSpec.assertLayers {
- this.isVisible(PIP_WINDOW_TITLE)
+ this.isVisible(pipApp.component)
+ .isVisible(LAUNCHER_COMPONENT)
.then()
- .isInvisible(PIP_WINDOW_TITLE)
+ .isInvisible(pipApp.component)
+ .isVisible(LAUNCHER_COMPONENT)
}
}
+ /**
+ * Checks that the focus changes between the [pipApp] window and the launcher when
+ * closing the pip window
+ */
@FlakyTest(bugId = 151179149)
@Test
- open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ open fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
}
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
new file mode 100644
index 000000000000..596c92a70498
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.platform.test.annotations.Postsubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.parser.toWindowName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test expanding a pip window back to full screen via the expand button
+ *
+ * To run this test: `atest WMShellFlickerTests:ExitPipViaExpandButtonClickTest`
+ *
+ * Actions:
+ * Launch an app in pip mode [pipApp],
+ * Launch another full screen mode [testApp]
+ * Expand [pipApp] app to full screen by clicking on the pip window and
+ * then on the expand button
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+class ExitPipViaExpandButtonClickTest(
+ testSpec: FlickerTestParameter
+) : ExitPipToAppTransition(testSpec) {
+
+ /**
+ * Defines the transition used to run the test
+ */
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition(eachRun = true) {
+ setup {
+ eachRun {
+ // launch an app behind the pip one
+ testApp.launchViaIntent(wmHelper)
+ }
+ }
+ transitions {
+ // This will bring PipApp to fullscreen
+ pipApp.expandPipWindowToApp(wmHelper)
+ // Wait until the other app is no longer visible
+ wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
+ }
+ }
+
+ @Postsubmit
+ @Test
+ override fun pipAppCoversFullScreenAtEnd() = super.pipAppCoversFullScreenAtEnd()
+
+ @Postsubmit
+ @Test
+ override fun showBothAppLayersThenHidePip() = super.showBothAppLayersThenHidePip()
+
+ @Postsubmit
+ @Test
+ override fun showBothAppWindowsThenHidePip() = super.showBothAppWindowsThenHidePip()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ 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/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 00e50e7fe3b5..617ef221ee9a 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/ExitPipViaIntentTest.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 com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -24,88 +23,63 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.traces.parser.toWindowName
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 launch and exit.
- * To run this test: `atest WMShellFlickerTests:EnterExitPipTest`
+ * Test expanding a pip window back to full screen via an intent
+ *
+ * To run this test: `atest WMShellFlickerTests:ExitPipViaIntentTest`
+ *
+ * Actions:
+ * Launch an app in pip mode [pipApp],
+ * Launch another full screen mode [testApp]
+ * Expand [pipApp] app to full screen via an intent
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class EnterExitPipTest(
- testSpec: FlickerTestParameter
-) : PipTransition(testSpec) {
- private val testApp = FixedAppHelper(instrumentation)
+class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = true) {
setup {
eachRun {
+ // launch an app behind the pip one
testApp.launchViaIntent(wmHelper)
}
}
transitions {
// This will bring PipApp to fullscreen
pipApp.launchViaIntent(wmHelper)
+ // Wait until the other app is no longer visible
+ wmHelper.waitForSurfaceAppeared(testApp.component.toWindowName())
}
}
- @Presubmit
- @Test
- fun pipAppRemainInsideVisibleBounds() {
- testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
- }
- }
-
- @Presubmit
- @Test
- fun showBothAppWindowsThenHidePip() {
- testSpec.assertWm {
- showsAppWindow(testApp.defaultWindowName)
- .showsAppWindowOnTop(pipApp.defaultWindowName)
- .then()
- .hidesAppWindow(testApp.defaultWindowName)
- }
- }
-
- @Presubmit
- @Test
- fun showBothAppLayersThenHidePip() {
- testSpec.assertLayers {
- isVisible(testApp.defaultWindowName)
- .isVisible(pipApp.defaultWindowName)
- .then()
- .isInvisible(testApp.defaultWindowName)
- }
- }
-
- @Presubmit
- @Test
- fun testAppCoversFullScreenWithPipOnDisplay() {
- testSpec.assertLayersStart {
- visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
- }
- }
-
- @Presubmit
- @Test
- fun pipAppCoversFullScreen() {
- testSpec.assertLayersEnd {
- visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
- }
- }
-
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
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/ExitPipWithDismissButtonTest.kt
index 55e5c4128967..e3d099f6fdb5 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/ExitPipWithDismissButtonTest.kt
@@ -16,17 +16,14 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Postsubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,72 +31,58 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip launch.
- * To run this test: `atest WMShellFlickerTests:PipToAppTest`
+ * Test closing a pip window via the dismiss button
+ *
+ * To run this test: `atest WMShellFlickerTests:ExitPipWithDismissButtonTest`
+ *
+ * Actions:
+ * Launch an app in pip mode [pipApp],
+ * Click on the pip window
+ * Click on dismiss button and wait window disappear
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = buildTransition(eachRun = true) { configuration ->
- setup {
- eachRun {
- this.setRotation(configuration.startRotation)
- }
- }
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- }
+ get() = {
+ super.transition(this, it)
transitions {
- pipApp.expandPipWindowToApp(wmHelper)
+ pipApp.closePipWindow(wmHelper)
}
}
- @FlakyTest
+ @Postsubmit
@Test
- fun appReplacesPipWindow() {
- testSpec.assertWm {
- this.showsAppWindow(PIP_WINDOW_TITLE)
- .then()
- .showsAppWindowOnTop(pipApp.launcherName)
- }
- }
-
- @FlakyTest
- @Test
- fun appReplacesPipLayer() {
- testSpec.assertLayers {
- this.isVisible(PIP_WINDOW_TITLE)
- .then()
- .isVisible(pipApp.launcherName)
- }
- }
-
- @FlakyTest
- @Test
- fun testAppCoversFullScreen() {
- testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
- }
- }
+ override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
- @FlakyTest(bugId = 151179149)
+ @Postsubmit
@Test
- fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity",
- pipApp.launcherName, "NexusLauncherActivity")
+ override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 5)
+ .getConfigNonRotationTests(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/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 524a1b404591..2cdfc2bf0654 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.startRotation
@@ -33,42 +34,58 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test Pip launch.
- * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe`
+ * Test closing a pip window by swiping it to the bottom-center of the screen
+ *
+ * To run this test: `atest WMShellFlickerTests:ExitPipWithSwipeDownTest`
+ *
+ * Actions:
+ * Launch an app in pip mode [pipApp],
+ * Swipe the pip window to the bottom-center of the screen and wait it disappear
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
+class ExitPipWithSwipeDownTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = {
- super.transition(this, it)
+ get() = { args ->
+ super.transition(this, args)
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
val pipCenterX = pipRegion.centerX()
val pipCenterY = pipRegion.centerY()
val displayCenterX = device.displayWidth / 2
- device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5)
+ device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 10)
+ wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
+ wmHelper.waitForWindowSurfaceDisappeared(pipApp.component)
+ wmHelper.waitForAppTransitionIdle()
}
}
@Presubmit
@Test
- override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
@Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
@FlakyTest
@Test
@@ -85,9 +102,25 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition
@Presubmit
@Test
- override fun noUncoveredRegions() = super.noUncoveredRegions()
+ override fun entireScreenCovered() = super.entireScreenCovered()
@Presubmit
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 20)
+ }
+ }
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
new file mode 100644
index 000000000000..c525d46c83e6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -0,0 +1,175 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.parser.toLayerName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test expanding a pip window by double clicking it
+ *
+ * To run this test: `atest WMShellFlickerTests:ExpandPipOnDoubleClickTest`
+ *
+ * Actions:
+ * Launch an app in pip mode [pipApp],
+ * Expand [pipApp] app to its maximum pip size by double clicking on it
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition(eachRun = true) {
+ transitions {
+ pipApp.doubleClickPipWindow(wmHelper)
+ }
+ }
+
+ /**
+ * Checks that the pip app window remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Presubmit
+ @Test
+ fun pipWindowRemainInsideVisibleBounds() {
+ testSpec.assertWm {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the pip app layer remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Postsubmit
+ @Test
+ fun pipLayerRemainInsideVisibleBounds() {
+ testSpec.assertLayers {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks [pipApp] window remains visible throughout the animation
+ */
+ @Test
+ fun pipWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ isAppWindowVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks [pipApp] layer remains visible throughout the animation
+ */
+ @Postsubmit
+ @Test
+ fun pipLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ isVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the visible region of [pipApp] always expands during the animation
+ */
+ @Postsubmit
+ @Test
+ fun pipLayerExpands() {
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ current.visibleRegion.coversAtLeast(previous.visibleRegion.region)
+ }
+ }
+ }
+
+ /**
+ * Checks [pipApp] window remains pinned throughout the animation
+ */
+ @Presubmit
+ @Test
+ fun windowIsAlwaysPinned() {
+ testSpec.assertWm {
+ this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
+ }
+ }
+
+ /**
+ * Checks [pipApp] layer remains visible throughout the animation
+ */
+ @Presubmit
+ @Test
+ fun launcherIsAlwaysVisible() {
+ testSpec.assertLayers {
+ isVisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that the focus doesn't change between windows during the transition
+ */
+ @FlakyTest
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
new file mode 100644
index 000000000000..0ab857d755ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.traces.RegionSubject
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip movement with Launcher shelf height change (decrease).
+ *
+ * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
+ *
+ * Actions:
+ * Launch [pipApp] in pip mode
+ * Launch [testApp]
+ * Press home
+ * Check if pip window moves down (visually)
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group3
+class MovePipDownShelfHeightChangeTest(
+ testSpec: FlickerTestParameter
+) : MovePipShelfHeightTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition(eachRun = false) {
+ teardown {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ }
+ test {
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ taplInstrumentation.pressHome()
+ }
+ }
+
+ override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
+ current.isHigherOrEqual(previous.region)
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
new file mode 100644
index 000000000000..934255f73994
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.traces.RegionSubject
+import com.android.server.wm.traces.parser.toLayerName
+import com.android.server.wm.traces.parser.toWindowName
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import org.junit.Test
+
+/**
+ * Base class for pip tests with Launcher shelf height change
+ */
+abstract class MovePipShelfHeightTransition(
+ testSpec: FlickerTestParameter
+) : PipTransition(testSpec) {
+ protected val taplInstrumentation = LauncherInstrumentation()
+ protected val testApp = FixedAppHelper(instrumentation)
+
+ /**
+ * Checks if the window movement direction is valid
+ */
+ protected abstract fun assertRegionMovement(previous: RegionSubject, current: RegionSubject)
+
+ /**
+ * Checks [pipApp] window remains visible throughout the animation
+ */
+ @Postsubmit
+ @Test
+ open fun pipWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ isAppWindowVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks [pipApp] layer remains visible throughout the animation
+ */
+ @Postsubmit
+ @Test
+ open fun pipLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ isVisible(pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the pip app window remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Postsubmit
+ @Test
+ open fun pipWindowRemainInsideVisibleBounds() {
+ testSpec.assertWm {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the pip app layer remains inside the display bounds throughout the whole
+ * animation
+ */
+ @Postsubmit
+ @Test
+ open fun pipLayerRemainInsideVisibleBounds() {
+ testSpec.assertLayers {
+ coversAtMost(displayBounds, pipApp.component)
+ }
+ }
+
+ /**
+ * Checks that the visible region of [pipApp] always moves in the correct direction
+ * during the animation.
+ */
+ @Presubmit
+ @Test
+ open fun pipWindowMoves() {
+ val windowName = pipApp.component.toWindowName()
+ testSpec.assertWm {
+ val pipWindowList = this.windowStates { it.name.contains(windowName) && it.isVisible }
+ pipWindowList.zipWithNext { previous, current ->
+ assertRegionMovement(previous.frame, current.frame)
+ }
+ }
+ }
+
+ /**
+ * Checks that the visible region of [pipApp] always moves up during the animation
+ */
+ @Presubmit
+ @Test
+ open fun pipLayerMoves() {
+ val layerName = pipApp.component.toLayerName()
+ testSpec.assertLayers {
+ val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible }
+ pipLayerList.zipWithNext { previous, current ->
+ assertRegionMovement(previous.visibleRegion, current.visibleRegion)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 1294ac93f647..e507edfda48c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,36 +16,49 @@
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.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.google.common.truth.Truth
+import com.android.server.wm.flicker.traces.RegionSubject
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 movement with Launcher shelf height change.
- * To run this test: `atest WMShellFlickerTests:PipShelfHeightTest`
+ * Test Pip movement with Launcher shelf height change (increase).
+ *
+ * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
+ *
+ * Actions:
+ * Launch [pipApp] in pip mode
+ * Press home
+ * Launch [testApp]
+ * Check if pip window moves up (visually)
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- private val taplInstrumentation = LauncherInstrumentation()
- private val testApp = FixedAppHelper(instrumentation)
-
+class MovePipUpShelfHeightChangeTest(
+ testSpec: FlickerTestParameter
+) : MovePipShelfHeightTransition(testSpec) {
+ /**
+ * Defines the transition used to run the test
+ */
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = false) {
teardown {
@@ -61,33 +74,17 @@ class PipShelfHeightTest(testSpec: FlickerTestParameter) : PipTransition(testSpe
}
}
- @Presubmit
- @Test
- fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
-
- @Presubmit
- @Test
- fun pipLayerInsideDisplay() {
- testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
- }
- }
-
- @Presubmit
- @Test
- fun pipWindowMovesUp() = testSpec.assertWmEnd {
- val initialState = this.trace?.first()?.wmState
- ?: error("Trace should not be empty")
- val startPos = initialState.pinnedWindows.first().frame
- val currPos = this.wmState.pinnedWindows.first().frame
- val subject = Truth.assertWithMessage("Pip should have moved up")
- subject.that(currPos.top).isGreaterThan(startPos.top)
- subject.that(currPos.bottom).isGreaterThan(startPos.bottom)
- subject.that(currPos.left).isEqualTo(startPos.left)
- subject.that(currPos.right).isEqualTo(startPos.right)
+ override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
+ current.isLowerOrEqual(previous.region)
}
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
deleted file mode 100644
index cf84a2c696d0..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
+++ /dev/null
@@ -1,60 +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 androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-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 launch.
- * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
-class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = {
- super.transition(this, it)
- transitions {
- pipApp.closePipWindow(wmHelper)
- }
- }
-
- @FlakyTest
- @Test
- override fun pipLayerBecomesInvisible() {
- super.pipLayerBecomesInvisible()
- }
-
- @FlakyTest
- @Test
- override fun pipWindowBecomesInvisible() {
- super.pipWindowBecomesInvisible()
- }
-} \ No newline at end of file
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 d88f94d5954a..5719413aff25 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
@@ -27,7 +27,7 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -79,7 +79,7 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
fun pipInVisibleBounds() {
testSpec.assertWm {
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -90,7 +90,7 @@ class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
@Test
fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+ isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
}
}
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 6833b96a802b..050beb377978 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
@@ -31,7 +31,10 @@ import com.android.wm.shell.flicker.helpers.ImeAppHelper
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
+import org.junit.Assume.assumeFalse
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,12 +49,17 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 161435597)
@Group3
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
+ @Before
+ open fun setup() {
+ // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+ assumeFalse(isShellTransitionsEnabled())
+ }
+
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
withTestName { testSpec.name }
@@ -80,11 +88,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
}
}
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
fun pipWindowInsideDisplayBounds() {
testSpec.assertWm {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -92,25 +100,17 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(testApp.defaultWindowName)
- isVisible(imeApp.defaultWindowName)
- noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName)
+ isVisible(testApp.component)
+ isVisible(imeApp.component)
+ noWindowsOverlap(testApp.component, imeApp.component)
}
}
- @Presubmit
- @Test
- override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
-
- @Presubmit
- @Test
- override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
fun pipLayerInsideDisplayBounds() {
testSpec.assertLayers {
- coversAtMost(displayBounds, pipApp.defaultWindowName)
+ coversAtMost(displayBounds, pipApp.component)
}
}
@@ -118,18 +118,14 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t
@Test
fun bothAppLayersVisible() {
testSpec.assertLayersEnd {
- visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
- visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
+ visibleRegion(testApp.component).coversAtMost(displayBounds)
+ visibleRegion(imeApp.component).coversAtMost(displayBounds)
}
}
- @Presubmit
- @Test
- override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
-
- @Presubmit
+ @FlakyTest(bugId = 161435597)
@Test
- override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+ override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
const val TEST_REPETITIONS = 2
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 d531af28e2ad..7ea7d5f8a610 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
@@ -26,10 +26,10 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.wm.shell.flicker.helpers.FixedAppHelper
@@ -41,7 +41,22 @@ import org.junit.runners.Parameterized
/**
* Test Pip Stack in bounds after rotations.
+ *
* To run this test: `atest WMShellFlickerTests:PipRotationTest`
+ *
+ * Actions:
+ * Launch a [pipApp] in pip mode
+ * Launch another app [fixedApp] (appears below pip)
+ * Rotate the screen from [testSpec.config.startRotation] to [testSpec.config.endRotation]
+ * (usually, 0->90 and 90->0)
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited from [PipTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -50,8 +65,8 @@ import org.junit.runners.Parameterized
@Group3
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
- private val startingBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- private val endingBounds = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
+ private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.config.endRotation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = buildTransition(eachRun = false) { configuration ->
@@ -66,49 +81,109 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec)
transitions {
setRotation(configuration.endRotation)
}
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
- }
- }
}
- @FlakyTest(bugId = 185400889)
+ /**
+ * Checks that all parts of the screen are covered at the start and end of the transition
+ */
+ @Presubmit
@Test
- override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() =
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
testSpec.config.endRotation)
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
override fun statusBarLayerRotatesScales() =
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @FlakyTest(bugId = 185400889)
+ /**
+ * Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
+ */
+ @Presubmit
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
- visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ visibleRegion(fixedApp.component).coversExactly(screenBoundsStart)
}
}
- @FlakyTest(bugId = 185400889)
+ /**
+ * Checks that [fixedApp] layer is within [screenBoundsEnd] at the end of the transition
+ */
+ @Presubmit
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
- visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
- visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
+ visibleRegion(fixedApp.component).coversExactly(screenBoundsEnd)
+ }
+ }
+
+ /**
+ * Checks that [pipApp] layer is within [screenBoundsStart] at the start of the transition
+ */
+ @Presubmit
+ @Test
+ fun pipLayerRotates_StartingBounds() {
+ testSpec.assertLayersStart {
+ visibleRegion(pipApp.component).coversAtMost(screenBoundsStart)
+ }
+ }
+
+ /**
+ * Checks that [pipApp] layer is within [screenBoundsEnd] at the end of the transition
+ */
+ @Presubmit
+ @Test
+ fun pipLayerRotates_EndingBounds() {
+ testSpec.assertLayersEnd {
+ visibleRegion(pipApp.component).coversAtMost(screenBoundsEnd)
+ }
+ }
+
+ /**
+ * Ensure that the [pipApp] window does not obscure the [fixedApp] at the start of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun pipIsAboveFixedAppWindow_Start() {
+ testSpec.assertWmStart {
+ isAboveWindow(pipApp.component, fixedApp.component)
+ }
+ }
+
+ /**
+ * Ensure that the [pipApp] window does not obscure the [fixedApp] at the end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ fun pipIsAboveFixedAppWindow_End() {
+ testSpec.assertWmEnd {
+ isAboveWindow(pipApp.component, fixedApp.component)
}
}
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b4c75a6d1165..ca80d1837ea8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,25 +20,24 @@ import android.app.Instrumentation
import android.content.Intent
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.wm.shell.flicker.helpers.PipAppHelper
import com.android.wm.shell.flicker.testapp.Components
import org.junit.Test
@@ -162,19 +161,19 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
@@ -188,6 +187,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun noUncoveredRegions() =
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun entireScreenCovered() =
+ testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1f58bb2bf9db..e7b61970cbeb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -83,54 +82,70 @@ class SetRequestedOrientationWhilePinnedTest(
@FlakyTest
@Test
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+ @FlakyTest
+ @Test
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+
+ @FlakyTest
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+
+ @FlakyTest
+ @Test
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+ @FlakyTest
+ @Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@FlakyTest
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
- @Presubmit
+ @FlakyTest
@Test
fun pipWindowInsideDisplay() {
testSpec.assertWmStart {
- frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ frameRegion(pipApp.component).coversAtMost(startingBounds)
}
}
- @Presubmit
+ @FlakyTest
@Test
fun pipAppShowsOnTop() {
testSpec.assertWmEnd {
- showsAppWindowOnTop(pipApp.defaultWindowName)
+ isAppWindowOnTop(pipApp.component)
}
}
- @Presubmit
+ @FlakyTest
@Test
fun pipLayerInsideDisplay() {
testSpec.assertLayersStart {
- visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+ visibleRegion(pipApp.component).coversAtMost(startingBounds)
}
}
- @Presubmit
+ @FlakyTest
@Test
fun pipAlwaysVisible() = testSpec.assertWm {
- this.showsAppWindow(pipApp.windowName)
+ this.isAppWindowVisible(pipApp.component)
}
- @Presubmit
+ @FlakyTest
@Test
fun pipAppLayerCoversFullScreen() {
testSpec.assertLayersEnd {
- visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
+ visibleRegion(pipApp.component).coversExactly(endingBounds)
}
}
@FlakyTest
@Test
- override fun noUncoveredRegions() {
- super.noUncoveredRegions()
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 0110ba3f5b30..061218a015e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -37,14 +37,17 @@ class TvPipMenuTests : TvPipTestBase() {
private val systemUiResources =
packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
- val bounds = getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+ val bounds = getString(getIdentifier("pip_menu_bounds", "string",
+ SYSTEM_UI_PACKAGE_NAME))
Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
}
private val playButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
+ getString(getIdentifier("pip_play", "string",
+ SYSTEM_UI_PACKAGE_NAME))
}
private val pauseButtonDescription = systemUiResources.run {
- getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME))
+ getString(getIdentifier("pip_pause", "string",
+ SYSTEM_UI_PACKAGE_NAME))
}
@Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 1b73920046dc..1c663409b913 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -70,7 +70,8 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
// descendant and then retrieve the element from the menu and return to the caller of this
// method.
val elementSelector = By.desc(desc)
- val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+ val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR)
+ .hasDescendant(elementSelector)
return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
?.findObject(elementSelector)
@@ -94,7 +95,8 @@ fun UiDevice.clickTvPipMenuFullscreenButton() {
}
fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
- focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+ focusOnAndClickTvPipMenuElement(By.desc(desc)
+ .pkg(SYSTEM_UI_PACKAGE_NAME)) ||
error("Could not focus on the Pip menu object with \"$desc\" description")
// So apparently Accessibility framework on TV is not very reliable and sometimes the state of
// the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
index 20ac5bf8fa84..1cbad155ba7b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TaskViewTest.java
@@ -47,6 +47,8 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.HandlerExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.SyncTransactionQueue.TransactionRunnable;
import org.junit.After;
import org.junit.Before;
@@ -71,6 +73,8 @@ public class TaskViewTest extends ShellTestCase {
ShellTaskOrganizer mOrganizer;
@Mock
HandlerExecutor mExecutor;
+ @Mock
+ SyncTransactionQueue mSyncQueue;
SurfaceSession mSession;
SurfaceControl mLeash;
@@ -99,7 +103,14 @@ public class TaskViewTest extends ShellTestCase {
}).when(mExecutor).execute(any());
when(mOrganizer.getExecutor()).thenReturn(mExecutor);
- mTaskView = new TaskView(mContext, mOrganizer);
+
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final TransactionRunnable r = invocationOnMock.getArgument(0);
+ r.runWithTransaction(new SurfaceControl.Transaction());
+ return null;
+ }).when(mSyncQueue).runInSync(any());
+
+ mTaskView = new TaskView(mContext, mOrganizer, mSyncQueue);
mTaskView.setListener(mExecutor, mViewListener);
}
@@ -112,7 +123,7 @@ public class TaskViewTest extends ShellTestCase {
@Test
public void testSetPendingListener_throwsException() {
- TaskView taskView = new TaskView(mContext, mOrganizer);
+ TaskView taskView = new TaskView(mContext, mOrganizer, mSyncQueue);
taskView.setListener(mExecutor, mViewListener);
try {
taskView.setListener(mExecutor, mViewListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 3e3195fe8dc5..b0312e6d6f3c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -869,6 +869,35 @@ public class BubbleDataTest extends ShellTestCase {
assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey()));
}
+ /**
+ * Verifies that after the stack is collapsed with the overflow selected, it will select
+ * the top bubble upon next expansion.
+ */
+ @Test
+ public void test_collapseWithOverflowSelected_nextExpansion() {
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ mBubbleData.setExpanded(true);
+
+ mBubbleData.setListener(mListener);
+
+ // Select the overflow
+ mBubbleData.setShowingOverflow(true);
+ mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
+ verifyUpdateReceived();
+ assertSelectionChangedTo(mBubbleData.getOverflow());
+
+ // Collapse
+ mBubbleData.setExpanded(false);
+ verifyUpdateReceived();
+ assertSelectionNotChanged();
+
+ // Expand (here we should select the new bubble)
+ mBubbleData.setExpanded(true);
+ verifyUpdateReceived();
+ assertSelectionChangedTo(mBubbleA2);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
@@ -902,7 +931,7 @@ public class BubbleDataTest extends ShellTestCase {
assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse();
}
- private void assertSelectionChangedTo(Bubble bubble) {
+ private void assertSelectionChangedTo(BubbleViewProvider bubble) {
BubbleData.Update update = mUpdateCaptor.getValue();
assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble);
@@ -925,7 +954,6 @@ public class BubbleDataTest extends ShellTestCase {
assertThat(update.overflowBubbles).isEqualTo(bubbles);
}
-
private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
NotificationListenerService.Ranking ranking) {
return createBubbleEntry(userId, notifKey, packageName, ranking, 1000);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
index 6644eaf28a62..5c1bcb9753a4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
@@ -63,7 +63,7 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
mFlyoutMessage.senderName = "Josh";
mFlyoutMessage.message = "Hello";
- mFlyout = new BubbleFlyoutView(getContext());
+ mFlyout = new BubbleFlyoutView(getContext(), mPositioner);
mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
mSenderName = mFlyout.findViewById(R.id.bubble_flyout_name);
@@ -75,9 +75,8 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
public void testShowFlyout_isVisible() {
mFlyout.setupFlyoutStartingAsDot(
mFlyoutMessage,
- new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
- false,
- mPositioner);
+ new PointF(100, 100), true, Color.WHITE, null, null, mDotCenter,
+ false);
mFlyout.setVisibility(View.VISIBLE);
assertEquals("Hello", mFlyoutText.getText());
@@ -89,9 +88,8 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
public void testFlyoutHide_runsCallback() {
Runnable after = mock(Runnable.class);
mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
- new PointF(100, 100), 500, true, Color.WHITE, null, after, mDotCenter,
- false,
- mPositioner);
+ new PointF(100, 100), true, Color.WHITE, null, after, mDotCenter,
+ false);
mFlyout.hideFlyout();
verify(after).run();
@@ -100,9 +98,8 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
@Test
public void testSetCollapsePercent() {
mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
- new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
- false,
- mPositioner);
+ new PointF(100, 100), true, Color.WHITE, null, null, mDotCenter,
+ false);
mFlyout.setVisibility(View.VISIBLE);
mFlyout.setCollapsePercent(1f);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 1eba3c266358..9732a8890e0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.animation;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
@@ -41,7 +42,6 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Spy;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -49,26 +49,26 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
private int mDisplayWidth = 500;
private int mDisplayHeight = 1000;
- private int mExpandedViewPadding = 10;
private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
- @Spy
ExpandedAnimationController mExpandedController;
private int mStackOffset;
private PointF mExpansionPoint;
+ private BubblePositioner mPositioner;
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
- BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class));
- positioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
+ mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+ mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
- mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding,
+ mExpandedController = new ExpandedAnimationController(mPositioner,
mOnBubbleAnimatedOutAction);
+ spyOn(mExpandedController);
addOneMoreThanBubbleLimitBubbles();
mLayout.setActiveController(mExpandedController);
@@ -141,13 +141,16 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
/** Check that children are in the correct positions for being expanded. */
private void testBubblesInCorrectExpandedPositions() {
+ boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
// Check all the visible bubbles to see if they're in the right place.
for (int i = 0; i < mLayout.getChildCount(); i++) {
- float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i);
- assertEquals(expectedPosition,
+ PointF expectedPosition = mPositioner.getExpandedBubbleXY(i,
+ mLayout.getChildCount(),
+ onLeft);
+ assertEquals(expectedPosition.x,
mLayout.getChildAt(i).getTranslationX(),
2f);
- assertEquals(expectedPosition,
+ assertEquals(expectedPosition.y,
mLayout.getChildAt(i).getTranslationY(), 2f);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ef046d48e1cf..b88845044263 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -58,7 +58,7 @@ public class DisplayImeControllerTest {
mT = mock(SurfaceControl.Transaction.class);
mMock = mock(IInputMethodManager.class);
mExecutor = spy(Runnable::run);
- mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() {
+ mPerDisplay = new DisplayImeController(null, null, null, mExecutor, new TransactionPool() {
@Override
public SurfaceControl.Transaction acquire() {
return mT;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
new file mode 100644
index 000000000000..b66c2b4aee9b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.common;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+public class DisplayInsetsControllerTest {
+
+ private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 10;
+
+ @Mock
+ private IWindowManager mWm;
+ @Mock
+ private DisplayController mDisplayController;
+ private DisplayInsetsController mController;
+ private SparseArray<IDisplayWindowInsetsController> mInsetsControllersByDisplayId;
+ private TestShellExecutor mExecutor;
+
+ private ArgumentCaptor<Integer> mDisplayIdCaptor;
+ private ArgumentCaptor<IDisplayWindowInsetsController> mInsetsControllerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mExecutor = new TestShellExecutor();
+ mInsetsControllersByDisplayId = new SparseArray<>();
+ mDisplayIdCaptor = ArgumentCaptor.forClass(Integer.class);
+ mInsetsControllerCaptor = ArgumentCaptor.forClass(IDisplayWindowInsetsController.class);
+ mController = new DisplayInsetsController(mWm, mDisplayController, mExecutor);
+ addDisplay(DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testOnDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
+ throws RemoteException {
+ addDisplay(SECOND_DISPLAY);
+
+ verify(mWm).setDisplayWindowInsetsController(eq(SECOND_DISPLAY), notNull());
+ }
+
+ @Test
+ public void testOnDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
+ throws RemoteException {
+ addDisplay(SECOND_DISPLAY);
+ removeDisplay(SECOND_DISPLAY);
+
+ verify(mWm).setDisplayWindowInsetsController(SECOND_DISPLAY, null);
+ }
+
+ @Test
+ public void testPerDisplayListenerCallback() throws RemoteException {
+ TrackedListener defaultListener = new TrackedListener();
+ TrackedListener secondListener = new TrackedListener();
+ addDisplay(SECOND_DISPLAY);
+ mController.addInsetsChangedListener(DEFAULT_DISPLAY, defaultListener);
+ mController.addInsetsChangedListener(SECOND_DISPLAY, secondListener);
+
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).topFocusedWindowChanged(null);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false);
+ mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false);
+ mExecutor.flushAll();
+
+ assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
+ assertTrue(defaultListener.insetsChangedCount == 1);
+ assertTrue(defaultListener.insetsControlChangedCount == 1);
+ assertTrue(defaultListener.showInsetsCount == 1);
+ assertTrue(defaultListener.hideInsetsCount == 1);
+
+ assertTrue(secondListener.topFocusedWindowChangedCount == 0);
+ assertTrue(secondListener.insetsChangedCount == 0);
+ assertTrue(secondListener.insetsControlChangedCount == 0);
+ assertTrue(secondListener.showInsetsCount == 0);
+ assertTrue(secondListener.hideInsetsCount == 0);
+
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).topFocusedWindowChanged(null);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false);
+ mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false);
+ mExecutor.flushAll();
+
+ assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
+ assertTrue(defaultListener.insetsChangedCount == 1);
+ assertTrue(defaultListener.insetsControlChangedCount == 1);
+ assertTrue(defaultListener.showInsetsCount == 1);
+ assertTrue(defaultListener.hideInsetsCount == 1);
+
+ assertTrue(secondListener.topFocusedWindowChangedCount == 1);
+ assertTrue(secondListener.insetsChangedCount == 1);
+ assertTrue(secondListener.insetsControlChangedCount == 1);
+ assertTrue(secondListener.showInsetsCount == 1);
+ assertTrue(secondListener.hideInsetsCount == 1);
+ }
+
+ private void addDisplay(int displayId) throws RemoteException {
+ mController.onDisplayAdded(displayId);
+ verify(mWm, times(mInsetsControllersByDisplayId.size() + 1))
+ .setDisplayWindowInsetsController(mDisplayIdCaptor.capture(),
+ mInsetsControllerCaptor.capture());
+ List<Integer> displayIds = mDisplayIdCaptor.getAllValues();
+ List<IDisplayWindowInsetsController> insetsControllers =
+ mInsetsControllerCaptor.getAllValues();
+ for (int i = 0; i < displayIds.size(); i++) {
+ mInsetsControllersByDisplayId.put(displayIds.get(i), insetsControllers.get(i));
+ }
+ }
+
+ private void removeDisplay(int displayId) {
+ mController.onDisplayRemoved(displayId);
+ mInsetsControllersByDisplayId.remove(displayId);
+ }
+
+ private static class TrackedListener implements
+ DisplayInsetsController.OnInsetsChangedListener {
+ int topFocusedWindowChangedCount = 0;
+ int insetsChangedCount = 0;
+ int insetsControlChangedCount = 0;
+ int showInsetsCount = 0;
+ int hideInsetsCount = 0;
+
+ @Override
+ public void topFocusedWindowChanged(String packageName) {
+ topFocusedWindowChangedCount++;
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ insetsChangedCount++;
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ insetsControlChangedCount++;
+ }
+
+ @Override
+ public void showInsets(int types, boolean fromIme) {
+ showInsetsCount++;
+ }
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) {
+ hideInsetsCount++;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 952dc31cdaee..3557906531b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@ 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.spy;
import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
@@ -42,6 +43,8 @@ import com.android.wm.shell.common.DisplayImeController;
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;
@@ -53,39 +56,53 @@ public class SplitLayoutTests extends ShellTestCase {
@Mock SurfaceControl mRootLeash;
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
+ @Captor ArgumentCaptor<Runnable> mRunnableCaptor;
private SplitLayout mSplitLayout;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSplitLayout = new SplitLayout(
+ mSplitLayout = spy(new SplitLayout(
"TestSplitLayout",
mContext,
- getConfiguration(false),
+ getConfiguration(),
mSplitLayoutHandler,
b -> b.setParent(mRootLeash),
mDisplayImeController,
- mTaskOrganizer);
+ mTaskOrganizer));
}
@Test
@UiThreadTest
public void testUpdateConfiguration() {
- mSplitLayout.init();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(false))).isFalse();
- assertThat(mSplitLayout.updateConfiguration(getConfiguration(true))).isTrue();
+ final Configuration config = getConfiguration();
+
+ // Verify it returns true if new config won't affect split layout.
+ assertThat(mSplitLayout.updateConfiguration(config)).isFalse();
+
+ // Verify updateConfiguration returns true if the orientation changed.
+ config.orientation = ORIENTATION_LANDSCAPE;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if it rotated.
+ config.windowConfiguration.setRotation(1);
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the root bounds changed.
+ config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
}
@Test
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
- verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onLayoutChanging(any(SplitLayout.class));
}
@Test
public void testSetDividePosition() {
mSplitLayout.setDividePosition(anyInt());
- verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onLayoutChanged(any(SplitLayout.class));
}
@Test
@@ -96,24 +113,40 @@ public class SplitLayoutTests extends ShellTestCase {
@Test
@UiThreadTest
- public void testSnapToDismissTarget() {
+ public void testSnapToDismissStart() {
// verify it callbacks properly when the snap target indicates dismissing split.
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
+
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
- snapTarget = getSnapTarget(0 /* position */,
+ }
+
+ @Test
+ @UiThreadTest
+ public void testSnapToDismissEnd() {
+ // verify it callbacks properly when the snap target indicates dismissing split.
+ DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
+
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
}
- private static Configuration getConfiguration(boolean isLandscape) {
+ private void waitDividerFlingFinished() {
+ verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture());
+ mRunnableCaptor.getValue().run();
+ }
+
+ private static Configuration getConfiguration() {
final Configuration configuration = new Configuration();
configuration.unset();
- configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+ configuration.orientation = ORIENTATION_PORTRAIT;
+ configuration.windowConfiguration.setRotation(0);
configuration.windowConfiguration.setBounds(
- new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160));
+ new Rect(0, 0, 1080, 2160));
return configuration;
}
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 ba73d555e334..734b97b69c87 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
@@ -25,6 +25,7 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
@@ -64,6 +65,7 @@ import android.view.DisplayInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.InstanceId;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -95,6 +97,9 @@ public class DragAndDropPolicyTest {
@Mock
private SplitScreenController mSplitScreenStarter;
+ @Mock
+ private InstanceId mLoggerSessionId;
+
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
private Insets mInsets;
@@ -200,7 +205,7 @@ public class DragAndDropPolicyTest {
@Test
public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
setRunningTask(mHomeTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+ mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
@@ -210,15 +215,15 @@ public class DragAndDropPolicyTest {
}
@Test
- public void testDragAppOverFullscreenApp_expectSplitScreenAndFullscreenTargets() {
+ public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
setRunningTask(mFullscreenAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+ mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
@@ -227,15 +232,15 @@ public class DragAndDropPolicyTest {
}
@Test
- public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenAndFullscreenTargets() {
+ public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
setRunningTask(mFullscreenAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
+ mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
@@ -244,71 +249,61 @@ public class DragAndDropPolicyTest {
}
@Test
- public void testDragAppOverFullscreenNonResizeableApp_expectOnlyFullscreenTargets() {
- setRunningTask(mNonResizeableFullscreenAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+ public void testDragAppOverSplitApp_expectSplitTargets_DropLeft() {
+ setInSplitScreen(true);
+ setRunningTask(mSplitPrimaryAppTask);
+ mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
}
@Test
- public void testDragNonResizeableAppOverFullscreenApp_expectOnlyFullscreenTargets() {
- setRunningTask(mFullscreenAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mNonResizeableActivityClipData);
+ public void testDragAppOverSplitApp_expectSplitTargets_DropRight() {
+ setInSplitScreen(true);
+ setRunningTask(mSplitPrimaryAppTask);
+ mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
- public void testDragAppOverSplitApp_expectFullscreenAndSplitTargets() {
+ public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropTop() {
setInSplitScreen(true);
setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+ mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_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);
+ mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_TOP_OR_LEFT), any());
}
@Test
- public void testDragAppOverSplitAppPhone_expectFullscreenAndVerticalSplitTargets() {
+ public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropBottom() {
setInSplitScreen(true);
setRunningTask(mSplitPrimaryAppTask);
- mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
+ mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = assertExactTargetTypes(
- mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
+ mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
- mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startIntent(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_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).startIntent(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
+ eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
public void testTargetHitRects() {
setRunningTask(mFullscreenAppTask);
- mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+ mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
ArrayList<Target> targets = mPolicy.getTargets(mInsets);
for (Target t : targets) {
assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t);
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 9d7c82bb8550..0270093da938 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
@@ -79,6 +79,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState;
+ private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private ComponentName mComponent1;
@@ -90,11 +91,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
mPipBoundsState = new PipBoundsState(mContext);
+ mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
new PipSnapAlgorithm());
mMainExecutor = new TestShellExecutor();
mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext,
- mMockSyncTransactionQueue, mPipBoundsState,
+ mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 56a005642ce2..69ead3ac9cf9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -33,6 +33,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -46,7 +47,7 @@ import org.mockito.Spy;
/** Tests for {@link SideStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class SideStageTests {
+public class SideStageTests extends ShellTestCase {
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -60,8 +61,8 @@ public class SideStageTests {
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
- mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession);
+ mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
+ mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index ab6f76996398..b0a39d67d00c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -65,9 +65,10 @@ public class SplitTestUtils {
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
- SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) {
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+ SplitscreenEventLogger logger) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
- sideStage, imeController, splitLayout, transitions, transactionPool);
+ sideStage, imeController, splitLayout, transitions, transactionPool, logger);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index aca80f3556b9..cb759dc454af 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -82,6 +82,7 @@ public class SplitTransitionTests extends ShellTestCase {
@Mock private TransactionPool mTransactionPool;
@Mock private Transitions mTransitions;
@Mock private SurfaceSession mSurfaceSession;
+ @Mock private SplitscreenEventLogger mLogger;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -102,12 +103,13 @@ public class SplitTransitionTests extends ShellTestCase {
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
- mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool);
+ mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool,
+ mLogger);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
@@ -131,6 +133,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
@@ -168,6 +171,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(newTask, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -188,6 +192,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(newTask);
accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(accepted);
assertTrue(mStageCoordinator.isSplitScreenVisible());
@@ -223,6 +228,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertFalse(mStageCoordinator.isSplitScreenVisible());
}
@@ -244,6 +250,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -274,6 +281,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
assertTrue(accepted);
assertFalse(mStageCoordinator.isSplitScreenVisible());
@@ -298,6 +306,7 @@ public class SplitTransitionTests extends ShellTestCase {
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class));
mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction());
}
@@ -335,10 +344,11 @@ public class SplitTransitionTests extends ShellTestCase {
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback)
throws RemoteException {
mCalled = true;
- finishCallback.onTransitionFinished(mRemoteFinishWCT);
+ finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 06b08686bf4c..a4b76fb943e6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -59,6 +59,7 @@ public class StageCoordinatorTests extends ShellTestCase {
@Mock private DisplayImeController mDisplayImeController;
@Mock private Transitions mTransitions;
@Mock private TransactionPool mTransactionPool;
+ @Mock private SplitscreenEventLogger mLogger;
private StageCoordinator mStageCoordinator;
@Before
@@ -66,7 +67,8 @@ public class StageCoordinatorTests extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool);
+ mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool,
+ mLogger);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 90b5b37694c6..1a30f164f9a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -21,11 +21,13 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -52,6 +54,9 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests {
+ private static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
@@ -93,6 +98,8 @@ public final class StageTaskListenerTests {
@Test
public void testChildTaskAppeared() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
@@ -110,6 +117,8 @@ public final class StageTaskListenerTests {
@Test
public void testTaskVanished() {
+ // With shell transitions, the transition manages status changes, so skip this test.
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
mStageTaskListener.mRootTaskInfo = mRootTask;
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 d536adb9f8ae..160b3673aa8a 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
@@ -42,6 +42,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.UserHandle;
import android.testing.TestableContext;
+import android.view.Display;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
@@ -92,8 +93,8 @@ public class StartingSurfaceDrawerTests {
}
@Override
- protected boolean addWindow(int taskId, IBinder appToken,
- View view, WindowManager wm, WindowManager.LayoutParams params, int suggestType) {
+ protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
+ WindowManager.LayoutParams params, int suggestType) {
// listen for addView
mAddWindowForTask = taskId;
mViewThemeResId = view.getContext().getThemeResId();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 2d2ab2c9f674..54eacee8a9c3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -20,12 +20,20 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -48,6 +56,9 @@ import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.IDisplayWindowListener;
+import android.view.IWindowManager;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IRemoteTransition;
@@ -65,17 +76,23 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
/**
* Tests for the shell transitions.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:ShellTransitionTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -97,8 +114,7 @@ public class ShellTransitionTests {
@Test
public void testBasicTransitionFlow() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
IBinder transitToken = new Binder();
@@ -117,8 +133,7 @@ public class ShellTransitionTests {
@Test
public void testNonDefaultHandler() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
@@ -127,11 +142,13 @@ public class ShellTransitionTests {
TestTransitionHandler testHandler = new TestTransitionHandler() {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
for (TransitionInfo.Change chg : info.getChanges()) {
if (chg.getMode() == TRANSIT_CHANGE) {
- return super.startAnimation(transition, info, t, finishCallback);
+ return super.startAnimation(transition, info, startTransaction,
+ finishTransaction, finishCallback);
}
}
return false;
@@ -199,8 +216,7 @@ public class ShellTransitionTests {
@Test
public void testRequestRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
@@ -211,7 +227,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -273,9 +289,76 @@ public class ShellTransitionTests {
}
@Test
+ public void testTransitionFilterNotRequirement() {
+ // filter that requires one opening and NO translucent apps
+ TransitionFilter filter = new TransitionFilter();
+ filter.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ filter.mRequirements[1].mFlags = FLAG_TRANSLUCENT;
+ filter.mRequirements[1].mNot = true;
+
+ final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ assertTrue(filter.matches(openOnly));
+
+ final TransitionInfo openAndTranslucent = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ openAndTranslucent.getChanges().get(1).setFlags(FLAG_TRANSLUCENT);
+ assertFalse(filter.matches(openAndTranslucent));
+ }
+
+ @Test
+ public void testTransitionFilterChecksTypeSet() {
+ TransitionFilter filter = new TransitionFilter();
+ filter.mTypeSet = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+ final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ assertTrue(filter.matches(openOnly));
+
+ final TransitionInfo toFrontOnly = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
+ .addChange(TRANSIT_TO_FRONT).build();
+ assertTrue(filter.matches(toFrontOnly));
+
+ final TransitionInfo closeOnly = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_CLOSE).build();
+ assertFalse(filter.matches(closeOnly));
+ }
+
+ @Test
+ public void testTransitionFilterChecksFlags() {
+ TransitionFilter filter = new TransitionFilter();
+ filter.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+
+ final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY)
+ .addChange(TRANSIT_TO_BACK).build();
+ assertTrue(filter.matches(withFlag));
+
+ final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ assertFalse(filter.matches(withoutFlag));
+ }
+
+ @Test
+ public void testTransitionFilterChecksNotFlags() {
+ TransitionFilter filter = new TransitionFilter();
+ filter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+
+ final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK,
+ TRANSIT_FLAG_KEYGUARD_GOING_AWAY)
+ .addChange(TRANSIT_TO_BACK).build();
+ assertFalse(filter.matches(withFlag));
+
+ final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ assertTrue(filter.matches(withoutFlag));
+ }
+
+ @Test
public void testRegisteredRemoteTransition() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
@@ -285,7 +368,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
}
@Override
@@ -320,8 +403,7 @@ public class ShellTransitionTests {
@Test
public void testOneShotRemoteHandler() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
final boolean[] remoteCalled = new boolean[]{false};
@@ -332,7 +414,7 @@ public class ShellTransitionTests {
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
remoteCalled[0] = true;
- finishCallback.onTransitionFinished(remoteFinishWCT);
+ finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */);
}
@Override
@@ -358,15 +440,16 @@ public class ShellTransitionTests {
oneShot.setTransition(transitToken);
IBinder anotherToken = new Binder();
assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
- mock(SurfaceControl.Transaction.class), testFinish));
+ mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
+ testFinish));
}
@Test
public void testTransitionQueueing() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
IBinder transitToken1 = new Binder();
@@ -406,8 +489,7 @@ public class ShellTransitionTests {
@Test
public void testTransitionMerging() {
- Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
- mMainExecutor, mAnimExecutor);
+ Transitions transitions = createTestTransitions();
mDefaultHandler.setSimulateMerge(true);
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
@@ -443,11 +525,73 @@ public class ShellTransitionTests {
assertEquals(0, mDefaultHandler.activeCount());
}
+ @Test
+ public void testShouldRotateSeamlessly() throws Exception {
+ final RunningTaskInfo taskInfo =
+ createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final RunningTaskInfo taskInfoPip =
+ createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+ final DisplayController displays = createTestDisplayController();
+ final @Surface.Rotation int upsideDown = displays
+ .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
+
+ final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+ .build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build())
+ .build();
+ assertFalse(DefaultTransitionHandler.isRotationSeamless(normalDispRotate, displays));
+
+ // Seamless if all tasks are seamless
+ final TransitionInfo rotateSeamless = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+ .build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+ .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+ .build();
+ assertTrue(DefaultTransitionHandler.isRotationSeamless(rotateSeamless, displays));
+
+ // Not seamless if there is PiP (or any other non-seamless task)
+ final TransitionInfo pipDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+ .build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+ .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfoPip)
+ .setRotate().build())
+ .build();
+ assertFalse(DefaultTransitionHandler.isRotationSeamless(pipDispRotate, displays));
+
+ // Not seamless if one of rotations is upside-down
+ final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
+ .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+ .setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build())
+ .build();
+ assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessUpsideDown, displays));
+
+ // Not seamless if system alert windows
+ final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE)
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(
+ FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build())
+ .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+ .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+ .build();
+ assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays));
+ }
+
class TransitionInfoBuilder {
final TransitionInfo mInfo;
TransitionInfoBuilder(@WindowManager.TransitionType int type) {
- mInfo = new TransitionInfo(type, 0 /* flags */);
+ this(type, 0 /* flags */);
+ }
+
+ TransitionInfoBuilder(@WindowManager.TransitionType int type,
+ @WindowManager.TransitionFlags int flags) {
+ mInfo = new TransitionInfo(type, flags);
mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0);
}
@@ -465,11 +609,53 @@ public class ShellTransitionTests {
return addChange(mode, null /* taskInfo */);
}
+ TransitionInfoBuilder addChange(TransitionInfo.Change change) {
+ mInfo.addChange(change);
+ return this;
+ }
+
TransitionInfo build() {
return mInfo;
}
}
+ class ChangeBuilder {
+ final TransitionInfo.Change mChange;
+
+ ChangeBuilder(@WindowManager.TransitionType int mode) {
+ mChange = new TransitionInfo.Change(null /* token */, null /* leash */);
+ mChange.setMode(mode);
+ }
+
+ ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) {
+ mChange.setFlags(flags);
+ return this;
+ }
+
+ ChangeBuilder setTask(RunningTaskInfo taskInfo) {
+ mChange.setTaskInfo(taskInfo);
+ return this;
+ }
+
+ ChangeBuilder setRotate(int anim) {
+ return setRotate(Surface.ROTATION_90, anim);
+ }
+
+ ChangeBuilder setRotate() {
+ return setRotate(ROTATION_ANIMATION_UNSPECIFIED);
+ }
+
+ ChangeBuilder setRotate(@Surface.Rotation int target, int anim) {
+ mChange.setRotation(Surface.ROTATION_0, target);
+ mChange.setRotationAnimation(anim);
+ return this;
+ }
+
+ TransitionInfo.Change build() {
+ return mChange;
+ }
+ }
+
class TestTransitionHandler implements Transitions.TransitionHandler {
ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
final ArrayList<IBinder> mMerged = new ArrayList<>();
@@ -477,7 +663,8 @@ public class ShellTransitionTests {
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
mFinishes.add(finishCallback);
return true;
@@ -540,4 +727,46 @@ public class ShellTransitionTests {
return taskInfo;
}
+ private DisplayController createTestDisplayController() {
+ IWindowManager mockWM = mock(IWindowManager.class);
+ final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
+ try {
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ displayListener[0] = invocation.getArgument(0);
+ return null;
+ }
+ }).when(mockWM).registerDisplayWindowListener(any());
+ } catch (RemoteException e) {
+ // No remote stuff happening, so this can't be hit
+ }
+ DisplayController out = new DisplayController(mContext, mockWM, mMainExecutor);
+ out.initialize();
+ try {
+ displayListener[0].onDisplayAdded(DEFAULT_DISPLAY);
+ mMainExecutor.flushAll();
+ } catch (RemoteException e) {
+ // Again, no remote stuff
+ }
+ return out;
+ }
+
+ private Transitions createTestTransitions() {
+ return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
+ mContext, mMainExecutor, mAnimExecutor);
+ }
+//
+// private class TestDisplayController extends DisplayController {
+// private final DisplayLayout mTestDisplayLayout;
+// TestDisplayController() {
+// super(mContext, mock(IWindowManager.class), mMainExecutor);
+// mTestDisplayLayout = new DisplayLayout();
+// mTestDisplayLayout.
+// }
+//
+// @Override
+// DisplayLayout
+// }
+
}
diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index 4ec525a01da5..026912883a73 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -114,10 +114,10 @@ std::unique_ptr<ProcResult> ExecuteBinary(const std::vector<std::string>& argv)
std::unique_ptr<ProcResult> result(new ProcResult());
result->status = status;
const auto out = ReadFile(stdout[0]);
- result->stdout = out ? *out : "";
+ result->stdout_str = out ? *out : "";
close(stdout[0]);
const auto err = ReadFile(stderr[0]);
- result->stderr = err ? *err : "";
+ result->stderr_str = err ? *err : "";
close(stderr[0]);
return result;
}
diff --git a/libs/androidfw/include/androidfw/PosixUtils.h b/libs/androidfw/include/androidfw/PosixUtils.h
index 8fc3ee2733c7..bb2084740a44 100644
--- a/libs/androidfw/include/androidfw/PosixUtils.h
+++ b/libs/androidfw/include/androidfw/PosixUtils.h
@@ -23,8 +23,8 @@ namespace util {
struct ProcResult {
int status;
- std::string stdout;
- std::string stderr;
+ std::string stdout_str;
+ std::string stderr_str;
};
// Fork, exec and wait for an external process. Return nullptr if the process could not be launched,
diff --git a/libs/androidfw/tests/PosixUtils_test.cpp b/libs/androidfw/tests/PosixUtils_test.cpp
index cf97f87a4163..c7b3eba1451f 100644
--- a/libs/androidfw/tests/PosixUtils_test.cpp
+++ b/libs/androidfw/tests/PosixUtils_test.cpp
@@ -30,14 +30,14 @@ TEST(PosixUtilsTest, AbsolutePathToBinary) {
const auto result = ExecuteBinary({"/bin/date", "--help"});
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, 0);
- ASSERT_EQ(result->stdout.find("usage: date "), 0);
+ ASSERT_EQ(result->stdout_str.find("usage: date "), 0);
}
TEST(PosixUtilsTest, RelativePathToBinary) {
const auto result = ExecuteBinary({"date", "--help"});
ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, 0);
- ASSERT_EQ(result->stdout.find("usage: date "), 0);
+ ASSERT_EQ(result->stdout_str.find("usage: date "), 0);
}
TEST(PosixUtilsTest, BadParameters) {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 8d112d1c64bf..0b3b39397ee4 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -149,12 +149,14 @@ void DeferredLayerUpdater::apply() {
android_dataspace dataspace;
int slot;
bool newContent = false;
+ ARect currentCrop;
+ uint32_t outTransform;
// Note: ASurfaceTexture_dequeueBuffer discards all but the last frame. This
// is necessary if the SurfaceTexture queue is in synchronous mode, and we
// cannot tell which mode it is in.
AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer(
- mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &newContent,
- createReleaseFence, fenceWait, this);
+ mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &outTransform,
+ &newContent, createReleaseFence, fenceWait, this, &currentCrop);
if (hardwareBuffer) {
mCurrentSlot = slot;
@@ -165,12 +167,13 @@ void DeferredLayerUpdater::apply() {
// (invoked by createIfNeeded) will add a ref to the AHardwareBuffer.
AHardwareBuffer_release(hardwareBuffer);
if (layerImage.get()) {
- SkMatrix textureTransform;
- mat4(transformMatrix).copyTo(textureTransform);
// force filtration if buffer size != layer size
bool forceFilter =
mWidth != layerImage->width() || mHeight != layerImage->height();
- updateLayer(forceFilter, textureTransform, layerImage);
+ SkRect currentCropRect =
+ SkRect::MakeLTRB(currentCrop.left, currentCrop.top, currentCrop.right,
+ currentCrop.bottom);
+ updateLayer(forceFilter, layerImage, outTransform, currentCropRect);
}
}
}
@@ -182,12 +185,13 @@ void DeferredLayerUpdater::apply() {
}
}
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- const sk_sp<SkImage>& layerImage) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage,
+ const uint32_t transform, SkRect currentCrop) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
- mLayer->getTexTransform() = textureTransform;
+ mLayer->setCurrentCropRect(currentCrop);
+ mLayer->setWindowTransform(transform);
mLayer->setImage(layerImage);
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 8f79c4ec97b8..da8041f0d0fe 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -90,8 +90,8 @@ public:
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
- const sk_sp<SkImage>& layerImage);
+ void updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage, const uint32_t transform,
+ SkRect currentCrop);
void destroyLayer();
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index fb3e21fc1571..4ec782f6fec0 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -27,6 +27,7 @@ X(ClipPath)
X(ClipRect)
X(ClipRRect)
X(ClipRegion)
+X(ResetClip)
X(DrawPaint)
X(DrawBehind)
X(DrawPath)
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b14ade97ca5f..9053c1240957 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include <log/log.h>
+
namespace android {
namespace uirenderer {
@@ -33,7 +35,6 @@ Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alp
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
renderState.registerLayer(this);
- texTransform.setIdentity();
transform.setIdentity();
}
@@ -90,7 +91,7 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons
void Layer::draw(SkCanvas* canvas) {
GrRecordingContext* context = canvas->recordingContext();
if (context == nullptr) {
- SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
return;
}
SkMatrix layerTransform = getTransform();
@@ -99,7 +100,6 @@ void Layer::draw(SkCanvas* canvas) {
const int layerHeight = getHeight();
if (layerImage) {
SkMatrix textureMatrixInv;
- textureMatrixInv = getTexTransform();
// TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
// use bottom left origin and remove flipV and invert transformations.
SkMatrix flipV;
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e99e76299317..0789344f970e 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -74,10 +74,18 @@ public:
void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
- inline SkMatrix& getTexTransform() { return texTransform; }
-
inline SkMatrix& getTransform() { return transform; }
+ inline SkRect getCurrentCropRect() { return mCurrentCropRect; }
+
+ inline void setCurrentCropRect(const SkRect currentCropRect) {
+ mCurrentCropRect = currentCropRect;
+ }
+
+ inline void setWindowTransform(uint32_t windowTransform) { mWindowTransform = windowTransform; }
+
+ inline uint32_t getWindowTransform() { return mWindowTransform; }
+
/**
* Posts a decStrong call to the appropriate thread.
* Thread-safe.
@@ -116,14 +124,19 @@ private:
SkBlendMode mode;
/**
- * Optional texture coordinates transform.
+ * Optional transform.
*/
- SkMatrix texTransform;
+ SkMatrix transform;
/**
- * Optional transform.
+ * Optional crop
*/
- SkMatrix transform;
+ SkRect mCurrentCropRect;
+
+ /**
+ * Optional transform
+ */
+ uint32_t mWindowTransform;
/**
* An image backing the layer.
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 109b5352fe30..9cf300c95e11 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -88,6 +88,8 @@ bool Properties::enableWebViewOverlays = true;
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
+DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
+
bool Properties::load() {
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
@@ -141,6 +143,9 @@ bool Properties::load() {
enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true);
+ // call isDrawingEnabled to force loading of the property
+ isDrawingEnabled();
+
return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
}
@@ -210,5 +215,19 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnit
sRenderPipelineType = type;
}
+void Properties::setDrawingEnabled(bool newDrawingEnabled) {
+ drawingEnabled = newDrawingEnabled ? DrawingEnabled::On : DrawingEnabled::Off;
+ enableRTAnimations = newDrawingEnabled;
+}
+
+bool Properties::isDrawingEnabled() {
+ if (drawingEnabled == DrawingEnabled::NotInitialized) {
+ bool drawingEnabledProp = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true);
+ drawingEnabled = drawingEnabledProp ? DrawingEnabled::On : DrawingEnabled::Off;
+ enableRTAnimations = drawingEnabledProp;
+ }
+ return drawingEnabled == DrawingEnabled::On;
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7df6e2c92247..7f9782bf8d20 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -187,6 +187,12 @@ enum DebugLevel {
*/
#define PROPERTY_WEBVIEW_OVERLAYS_ENABLED "debug.hwui.webview_overlays_enabled"
+/**
+ * Property for globally GL drawing state. Can be overridden per process with
+ * setDrawingEnabled.
+ */
+#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -208,6 +214,8 @@ enum class StretchEffectBehavior {
UniformScale // Uniform scale stretch everywhere
};
+enum class DrawingEnabled { NotInitialized, On, Off };
+
/**
* Renderthread-only singleton which manages several static rendering properties. Most of these
* are driven by system properties which are queried once at initialization, and again if init()
@@ -301,6 +309,11 @@ public:
stretchEffectBehavior = behavior;
}
+ // Represents if drawing is enabled. Should only be Off in headless testing environments
+ static DrawingEnabled drawingEnabled;
+ static bool isDrawingEnabled();
+ static void setDrawingEnabled(bool enable);
+
private:
static StretchEffectBehavior stretchEffectBehavior;
static ProfileType sProfileType;
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index a743d30939d0..4cce87ad1a2f 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -243,18 +243,14 @@ CopyResult Readback::copySurfaceIntoLegacy(ANativeWindow* window, const Rect& sr
static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
sk_sp<SkImage> image =
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
- return copyImageInto(image, texTransform, srcRect, bitmap);
+ return copyImageInto(image, srcRect, bitmap);
}
CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
-
- return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
+ return copyImageInto(hwBitmap->makeImage(), srcRect, bitmap);
}
CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -279,14 +275,11 @@ CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap
CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
- return copyImageInto(image, transform, srcRect, bitmap);
+ return copyImageInto(image, srcRect, bitmap);
}
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap) {
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+ SkBitmap* bitmap) {
ATRACE_CALL();
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
mRenderThread.requireGlContext();
@@ -303,11 +296,6 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran
CopyResult copyResult = CopyResult::UnknownError;
int displayedWidth = imgWidth, displayedHeight = imgHeight;
- // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
- // size.
- if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
- std::swap(displayedWidth, displayedHeight);
- }
SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
SkRect skiaSrcRect = srcRect.toSkRect();
if (skiaSrcRect.isEmpty()) {
@@ -320,7 +308,6 @@ CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTran
Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
layer.setSize(displayedWidth, displayedHeight);
- texTransform.copyTo(layer.getTexTransform());
layer.setImage(image);
// Scaling filter is not explicitly set here, because it is done inside copyLayerInfo
// after checking the necessity based on the src/dest rect size and the transformation.
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index da252695dd3b..d0d748ff5c16 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -56,8 +56,7 @@ public:
private:
CopyResult copySurfaceIntoLegacy(ANativeWindow* window, const Rect& srcRect, SkBitmap* bitmap);
- CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap);
+ CopyResult copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect, SkBitmap* bitmap);
bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
SkBitmap* bitmap);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 442ae0fb2707..a285462eef74 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -15,6 +15,7 @@
*/
#include "RecordingCanvas.h"
+#include <hwui/Paint.h>
#include <GrRecordingContext.h>
@@ -186,6 +187,11 @@ struct ClipRegion final : Op {
SkClipOp op;
void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
};
+struct ResetClip final : Op {
+ static const auto kType = Type::ResetClip;
+ ResetClip() {}
+ void draw(SkCanvas* c, const SkMatrix&) const { SkAndroidFrameworkUtils::ResetClip(c); }
+};
struct DrawPaint final : Op {
static const auto kType = Type::DrawPaint;
@@ -495,7 +501,7 @@ struct DrawVectorDrawable final : Op {
sp<VectorDrawableRoot> mRoot;
SkRect mBounds;
- SkPaint paint;
+ Paint paint;
BitmapPalette palette;
};
@@ -661,6 +667,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) {
this->push<ClipRegion>(0, region, op);
}
+void DisplayListData::resetClip() {
+ this->push<ResetClip>(0);
+}
void DisplayListData::drawPaint(const SkPaint& paint) {
this->push<DrawPaint>(0, paint);
@@ -833,7 +842,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint), op->palette);
};
}
else if
@@ -842,7 +852,8 @@ constexpr color_transform_fn colorTransformForOp() {
// TODO: We should be const. Or not. Or just use a different map
// Unclear, but this is the quick fix
const T* op = reinterpret_cast<const T*>(opRaw);
- transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ const SkPaint* paint = &op->paint;
+ transformPaint(transform, const_cast<SkPaint*>(paint));
};
}
else {
@@ -966,6 +977,14 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
fDL->clipRegion(region, op);
this->INHERITED::onClipRegion(region, op);
}
+void RecordingCanvas::onResetClip() {
+ // This is part of "replace op" emulation, but rely on the following intersection
+ // clip to potentially mark the clip as complex. If we are already complex, we do
+ // not reset the complexity so that we don't break the contract that no higher
+ // save point has a complex clip when "not complex".
+ fDL->resetClip();
+ this->INHERITED::onResetClip();
+}
void RecordingCanvas::onDrawPaint(const SkPaint& paint) {
fDL->drawPaint(paint);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 4fae6a13a25a..212b4e72dcb2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -97,6 +97,7 @@ private:
void clipRect(const SkRect&, SkClipOp, bool aa);
void clipRRect(const SkRRect&, SkClipOp, bool aa);
void clipRegion(const SkRegion&, SkClipOp);
+ void resetClip();
void drawPaint(const SkPaint&);
void drawBehind(const SkPaint&);
@@ -169,6 +170,7 @@ public:
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
void onClipRegion(const SkRegion&, SkClipOp) override;
+ void onResetClip() override;
void onDrawPaint(const SkPaint&) override;
void onDrawBehind(const SkPaint&) override;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d032e2b00649..53c6db0cdf3a 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -182,7 +182,7 @@ int SkiaCanvas::saveUnclippedLayer(int left, int top, int right, int bottom) {
return SkAndroidFrameworkUtils::SaveBehind(mCanvas, &bounds);
}
-void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const SkPaint& paint) {
+void SkiaCanvas::restoreUnclippedLayer(int restoreCount, const Paint& paint) {
while (mCanvas->getSaveCount() > restoreCount + 1) {
this->restore();
@@ -396,6 +396,22 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
return !mCanvas->isClipEmpty();
}
+bool SkiaCanvas::replaceClipRect_deprecated(float left, float top, float right, float bottom) {
+ SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
+
+ // Emulated clip rects are not recorded for partial saves, since
+ // partial saves have been removed from the public API.
+ SkAndroidFrameworkUtils::ResetClip(mCanvas);
+ mCanvas->clipRect(rect, SkClipOp::kIntersect);
+ return !mCanvas->isClipEmpty();
+}
+
+bool SkiaCanvas::replaceClipPath_deprecated(const SkPath* path) {
+ SkAndroidFrameworkUtils::ResetClip(mCanvas);
+ mCanvas->clipPath(*path, SkClipOp::kIntersect, true);
+ return !mCanvas->isClipEmpty();
+}
+
// ----------------------------------------------------------------------------
// Canvas state operations: Filters
// ----------------------------------------------------------------------------
@@ -439,13 +455,13 @@ void SkiaCanvas::drawColor(int color, SkBlendMode mode) {
mCanvas->drawColor(color, mode);
}
-void SkiaCanvas::onFilterPaint(SkPaint& paint) {
+void SkiaCanvas::onFilterPaint(Paint& paint) {
if (mPaintFilter) {
- mPaintFilter->filter(&paint);
+ mPaintFilter->filterFullPaint(&paint);
}
}
-void SkiaCanvas::drawPaint(const SkPaint& paint) {
+void SkiaCanvas::drawPaint(const Paint& paint) {
mCanvas->drawPaint(filterPaint(paint));
}
@@ -552,9 +568,8 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, left, top, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, left, top, p.sampling(), &p);
});
}
@@ -562,9 +577,8 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint*
auto image = bitmap.makeImage();
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImage(image, 0, 0, sampling, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImage(image, 0, 0, p.sampling(), &p);
});
}
@@ -575,18 +589,12 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- applyLooper(paint, [&](const SkPaint& p) {
- auto sampling = SkSamplingOptions(p.getFilterQuality());
- mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint);
});
}
-static SkFilterMode paintToFilter(const Paint* paint) {
- return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const Paint* paint) {
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
@@ -668,13 +676,13 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
if (paint) {
pnt = *paint;
}
- SkSamplingOptions sampling(paintToFilter(&pnt));
+ SkSamplingOptions sampling = pnt.sampling();
pnt.setShader(image->makeShader(sampling));
auto v = builder.detach();
- applyLooper(&pnt, [&](const SkPaint& p) {
+ applyLooper(&pnt, [&](const Paint& p) {
SkPaint copy(p);
- auto s = SkSamplingOptions(p.getFilterQuality());
+ auto s = p.sampling();
if (s != sampling) {
// applyLooper changed the quality?
copy.setShader(image->makeShader(s));
@@ -707,9 +715,8 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
- applyLooper(paint, [&](const SkPaint& p) {
- auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
- mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
+ applyLooper(paint, [&](const Paint& p) {
+ mCanvas->drawImageLattice(image.get(), lattice, dst, p.filterMode(), &p);
});
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 438a40cb4c81..715007cdcd3b 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -23,8 +23,10 @@
#include "VectorDrawable.h"
#include "hwui/Canvas.h"
#include "hwui/Paint.h"
+#include "hwui/BlurDrawLooper.h"
#include <SkCanvas.h>
+#include <SkDeque.h>
#include "pipeline/skia/AnimatedDrawables.h"
#include "src/core/SkArenaAlloc.h"
@@ -73,7 +75,7 @@ public:
virtual int save(SaveFlags::Flags flags) override;
virtual void restore() override;
virtual void restoreToCount(int saveCount) override;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) override;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) override;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) override;
@@ -92,6 +94,9 @@ public:
virtual bool quickRejectPath(const SkPath& path) const override;
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+ virtual bool replaceClipRect_deprecated(float left, float top, float right,
+ float bottom) override;
+ virtual bool replaceClipPath_deprecated(const SkPath* path) override;
virtual PaintFilter* getPaintFilter() override;
virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;
@@ -99,7 +104,7 @@ public:
virtual SkCanvasState* captureCanvasState() const override;
virtual void drawColor(int color, SkBlendMode mode) override;
- virtual void drawPaint(const SkPaint& paint) override;
+ virtual void drawPaint(const Paint& paint) override;
virtual void drawPoint(float x, float y, const Paint& paint) override;
virtual void drawPoints(const float* points, int count, const Paint& paint) override;
@@ -167,10 +172,10 @@ protected:
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
- void onFilterPaint(SkPaint& paint);
+ void onFilterPaint(Paint& paint);
- SkPaint filterPaint(const SkPaint& src) {
- SkPaint dst(src);
+ Paint filterPaint(const Paint& src) {
+ Paint dst(src);
this->onFilterPaint(dst);
return dst;
}
@@ -179,21 +184,20 @@ protected:
template <typename Proc>
void applyLooper(const Paint* paint, Proc proc, void (*preFilter)(SkPaint&) = nullptr) {
BlurDrawLooper* looper = paint ? paint->getLooper() : nullptr;
- const SkPaint* skpPtr = paint;
- SkPaint skp = skpPtr ? *skpPtr : SkPaint();
+ Paint pnt = paint ? *paint : Paint();
if (preFilter) {
- preFilter(skp);
+ preFilter(pnt);
}
- this->onFilterPaint(skp);
+ this->onFilterPaint(pnt);
if (looper) {
- looper->apply(skp, [&](SkPoint offset, const SkPaint& modifiedPaint) {
+ looper->apply(pnt, [&](SkPoint offset, const Paint& modifiedPaint) {
mCanvas->save();
mCanvas->translate(offset.fX, offset.fY);
proc(modifiedPaint);
mCanvas->restore();
});
} else {
- proc(skp);
+ proc(pnt);
}
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 55f434f49bbd..f116641b560f 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -463,10 +463,10 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingCache.dirty = false;
}
- SkPaint skp;
+ Paint skp;
getPaintFor(&skp, mStagingProperties);
Paint paint;
- paint.setFilterQuality(skp.getFilterQuality());
+ paint.setFilterBitmap(skp.isFilterBitmap());
paint.setColorFilter(skp.refColorFilter());
paint.setAlpha(skp.getAlpha());
outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(),
@@ -476,9 +476,9 @@ void Tree::drawStaging(Canvas* outCanvas) {
mStagingProperties.getBounds().bottom(), &paint);
}
-void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties& prop) const {
+void Tree::getPaintFor(Paint* outPaint, const TreeProperties& prop) const {
// HWUI always draws VD with bilinear filtering.
- outPaint->setFilterQuality(kLow_SkFilterQuality);
+ outPaint->setFilterBitmap(true);
if (prop.getColorFilter() != nullptr) {
outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index ac7d41e0d600..30bb04ae8361 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -648,7 +648,7 @@ public:
*/
void draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& paint);
- void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
+ void getPaintFor(Paint* outPaint, const TreeProperties &props) const;
BitmapPalette computePalette();
void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp
index 693b22b62663..04ac3cf0ebc8 100644
--- a/libs/hwui/apex/android_matrix.cpp
+++ b/libs/hwui/apex/android_matrix.cpp
@@ -35,3 +35,10 @@ bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) {
}
return false;
}
+
+jobject AMatrix_newInstance(JNIEnv* env, float values[9]) {
+ jobject matrixObj = android::android_graphics_Matrix_newInstance(env);
+ SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj);
+ m->set9(values);
+ return matrixObj;
+}
diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h
index 987ad13f7635..5705ba485ba3 100644
--- a/libs/hwui/apex/include/android/graphics/matrix.h
+++ b/libs/hwui/apex/include/android/graphics/matrix.h
@@ -34,6 +34,16 @@ __BEGIN_DECLS
*/
ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]);
+/**
+ * Returns a new Matrix jobject that contains the values passed in as initial values.
+ * @param values The 9 values of the 3x3 matrix in the following order.
+ * values[0] = scaleX values[1] = skewX values[2] = transX
+ * values[3] = skewY values[4] = scaleY values[5] = transY
+ * values[6] = persp0 values[7] = persp1 values[8] = persp2
+ * @return The matrix jobject
+ */
+ANDROID_API jobject AMatrix_newInstance(JNIEnv* env, float values[9]);
+
__END_DECLS
#endif // ANDROID_GRAPHICS_MATRIX_H
diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp
index 8f261c83b8d3..dd01a5b15da5 100644
--- a/libs/hwui/canvas/CanvasFrontend.cpp
+++ b/libs/hwui/canvas/CanvasFrontend.cpp
@@ -54,13 +54,10 @@ bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
return false;
}
-// Assert that the cast from SkClipOp to SkRegion::Op is valid
+// Assert that the cast from SkClipOp to SkRegion::Op is valid.
+// SkClipOp is a subset of SkRegion::Op and only supports difference and intersect.
static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
-static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
-static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
-static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
-static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);
void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 876f5c895c60..d08bc5c583c2 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -157,7 +157,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
lazyPaint.emplace();
lazyPaint->setAlpha(mProperties.mAlpha);
lazyPaint->setColorFilter(mProperties.mColorFilter);
- lazyPaint->setFilterQuality(kLow_SkFilterQuality);
}
canvas->concat(matrix);
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index 27a038d4598e..270d24af99fd 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -24,7 +24,7 @@ BlurDrawLooper::BlurDrawLooper(SkColor4f color, float blurSigma, SkPoint offset)
BlurDrawLooper::~BlurDrawLooper() = default;
-SkPoint BlurDrawLooper::apply(SkPaint* paint) const {
+SkPoint BlurDrawLooper::apply(Paint* paint) const {
paint->setColor(mColor);
if (mBlurSigma > 0) {
paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, mBlurSigma, true));
diff --git a/libs/hwui/hwui/BlurDrawLooper.h b/libs/hwui/hwui/BlurDrawLooper.h
index 7e6786f7dfbc..09a4e0f849b0 100644
--- a/libs/hwui/hwui/BlurDrawLooper.h
+++ b/libs/hwui/hwui/BlurDrawLooper.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
#define ANDROID_GRAPHICS_BLURDRAWLOOPER_H_
-#include <SkPaint.h>
+#include <hwui/Paint.h>
#include <SkRefCnt.h>
class SkColorSpace;
@@ -30,10 +30,10 @@ public:
~BlurDrawLooper() override;
- // proc(SkPoint offset, const SkPaint& modifiedPaint)
+ // proc(SkPoint offset, const Paint& modifiedPaint)
template <typename DrawProc>
- void apply(const SkPaint& paint, DrawProc proc) const {
- SkPaint p(paint);
+ void apply(const Paint& paint, DrawProc proc) const {
+ Paint p(paint);
proc(this->apply(&p), p); // draw the shadow
proc({0, 0}, paint); // draw the original (on top)
}
@@ -43,7 +43,7 @@ private:
const float mBlurSigma;
const SkPoint mOffset;
- SkPoint apply(SkPaint* paint) const;
+ SkPoint apply(Paint* paint) const;
BlurDrawLooper(SkColor4f, float, SkPoint);
};
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 9023613478fc..82777646f3a2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -162,7 +162,7 @@ public:
virtual int save(SaveFlags::Flags flags) = 0;
virtual void restore() = 0;
virtual void restoreToCount(int saveCount) = 0;
- virtual void restoreUnclippedLayer(int saveCount, const SkPaint& paint) = 0;
+ virtual void restoreUnclippedLayer(int saveCount, const Paint& paint) = 0;
virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint) = 0;
virtual int saveLayerAlpha(float left, float top, float right, float bottom, int alpha) = 0;
@@ -185,6 +185,12 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0;
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
+ // Resets clip to wide open, used to emulate the now-removed SkClipOp::kReplace on
+ // apps with compatibility < P. Canvases for version P and later are restricted to
+ // intersect and difference at the Java level, matching SkClipOp's definition.
+ // NOTE: These functions are deprecated and will be removed in a future release
+ virtual bool replaceClipRect_deprecated(float left, float top, float right, float bottom) = 0;
+ virtual bool replaceClipPath_deprecated(const SkPath* path) = 0;
// filters
virtual PaintFilter* getPaintFilter() = 0;
@@ -197,7 +203,7 @@ public:
// Canvas draw operations
// ----------------------------------------------------------------------------
virtual void drawColor(int color, SkBlendMode mode) = 0;
- virtual void drawPaint(const SkPaint& paint) = 0;
+ virtual void drawPaint(const Paint& paint) = 0;
// Geometry
virtual void drawPoint(float x, float y, const Paint& paint) = 0;
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 5d9fad5b676e..fc542c81a597 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,7 +24,6 @@
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkEncodedOrigin.h>
-#include <SkFilterQuality.h>
#include <SkPaint.h>
#undef LOG_TAG
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index d9c9eeed03e9..4a8f3e10fc26 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -17,13 +17,13 @@
#ifndef ANDROID_GRAPHICS_PAINT_H_
#define ANDROID_GRAPHICS_PAINT_H_
-#include "BlurDrawLooper.h"
#include "Typeface.h"
#include <cutils/compiler.h>
#include <SkFont.h>
#include <SkPaint.h>
+#include <SkSamplingOptions.h>
#include <string>
#include <minikin/FontFamily.h>
@@ -32,6 +32,8 @@
namespace android {
+class BlurDrawLooper;
+
class Paint : public SkPaint {
public:
// Default values for underlined and strikethrough text,
@@ -60,7 +62,7 @@ public:
const SkFont& getSkFont() const { return mFont; }
BlurDrawLooper* getLooper() const { return mLooper.get(); }
- void setLooper(sk_sp<BlurDrawLooper> looper) { mLooper = std::move(looper); }
+ void setLooper(sk_sp<BlurDrawLooper> looper);
// These shadow the methods on SkPaint, but we need to so we can keep related
// attributes in-sync.
@@ -138,7 +140,15 @@ public:
void setDevKern(bool d) { mDevKern = d; }
// Deprecated -- bitmapshaders will be taking this flag explicitly
- bool isFilterBitmap() const { return this->getFilterQuality() != kNone_SkFilterQuality; }
+ bool isFilterBitmap() const { return mFilterBitmap; }
+ void setFilterBitmap(bool filter) { mFilterBitmap = filter; }
+
+ SkFilterMode filterMode() const {
+ return mFilterBitmap ? SkFilterMode::kLinear : SkFilterMode::kNearest;
+ }
+ SkSamplingOptions sampling() const {
+ return SkSamplingOptions(this->filterMode());
+ }
// The Java flags (Paint.java) no longer fit into the native apis directly.
// These methods handle converting to and from them and the native representations
@@ -169,6 +179,7 @@ private:
// nullptr is valid: it means the default typeface.
const Typeface* mTypeface = nullptr;
Align mAlign = kLeft_Align;
+ bool mFilterBitmap = false;
bool mStrikeThru = false;
bool mUnderline = false;
bool mDevKern = false;
diff --git a/libs/hwui/hwui/PaintFilter.h b/libs/hwui/hwui/PaintFilter.h
index 0e7b61977000..4996aa445316 100644
--- a/libs/hwui/hwui/PaintFilter.h
+++ b/libs/hwui/hwui/PaintFilter.h
@@ -1,17 +1,18 @@
#ifndef ANDROID_GRAPHICS_PAINT_FILTER_H_
#define ANDROID_GRAPHICS_PAINT_FILTER_H_
-class SkPaint;
+#include <SkRefCnt.h>
namespace android {
+class Paint;
+
class PaintFilter : public SkRefCnt {
public:
/**
* Called with the paint that will be used to draw.
* The implementation may modify the paint as they wish.
*/
- virtual void filter(SkPaint*) = 0;
virtual void filterFullPaint(Paint*) = 0;
};
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index fa2674fc2f5e..aac928f85924 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -15,6 +15,7 @@
*/
#include "Paint.h"
+#include "BlurDrawLooper.h"
namespace android {
@@ -43,6 +44,7 @@ Paint::Paint(const Paint& paint)
, mHyphenEdit(paint.mHyphenEdit)
, mTypeface(paint.mTypeface)
, mAlign(paint.mAlign)
+ , mFilterBitmap(paint.mFilterBitmap)
, mStrikeThru(paint.mStrikeThru)
, mUnderline(paint.mUnderline)
, mDevKern(paint.mDevKern) {}
@@ -62,6 +64,7 @@ Paint& Paint::operator=(const Paint& other) {
mHyphenEdit = other.mHyphenEdit;
mTypeface = other.mTypeface;
mAlign = other.mAlign;
+ mFilterBitmap = other.mFilterBitmap;
mStrikeThru = other.mStrikeThru;
mUnderline = other.mUnderline;
mDevKern = other.mDevKern;
@@ -77,6 +80,7 @@ bool operator==(const Paint& a, const Paint& b) {
a.mMinikinLocaleListId == b.mMinikinLocaleListId &&
a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit &&
a.mTypeface == b.mTypeface && a.mAlign == b.mAlign &&
+ a.mFilterBitmap == b.mFilterBitmap &&
a.mStrikeThru == b.mStrikeThru && a.mUnderline == b.mUnderline &&
a.mDevKern == b.mDevKern;
}
@@ -88,11 +92,16 @@ void Paint::reset() {
mFont.setEdging(SkFont::Edging::kAlias);
mLooper.reset();
+ mFilterBitmap = false;
mStrikeThru = false;
mUnderline = false;
mDevKern = false;
}
+void Paint::setLooper(sk_sp<BlurDrawLooper> looper) {
+ mLooper = std::move(looper);
+}
+
void Paint::setAntiAlias(bool aa) {
// Java does not support/understand subpixel(lcd) antialiasing
SkASSERT(mFont.getEdging() != SkFont::Edging::kSubpixelAntiAlias);
@@ -131,9 +140,6 @@ static uint32_t paintToLegacyFlags(const SkPaint& paint) {
uint32_t flags = 0;
flags |= -(int)paint.isAntiAlias() & sAntiAliasFlag;
flags |= -(int)paint.isDither() & sDitherFlag;
- if (paint.getFilterQuality() != kNone_SkFilterQuality) {
- flags |= sFilterBitmapFlag;
- }
return flags;
}
@@ -150,12 +156,6 @@ static uint32_t fontToLegacyFlags(const SkFont& font) {
static void applyLegacyFlagsToPaint(uint32_t flags, SkPaint* paint) {
paint->setAntiAlias((flags & sAntiAliasFlag) != 0);
paint->setDither ((flags & sDitherFlag) != 0);
-
- if (flags & sFilterBitmapFlag) {
- paint->setFilterQuality(kLow_SkFilterQuality);
- } else {
- paint->setFilterQuality(kNone_SkFilterQuality);
- }
}
static void applyLegacyFlagsToFont(uint32_t flags, SkFont* font) {
@@ -182,18 +182,20 @@ void Paint::SetSkPaintJavaFlags(SkPaint* paint, uint32_t flags) {
uint32_t Paint::getJavaFlags() const {
uint32_t flags = paintToLegacyFlags(*this) | fontToLegacyFlags(mFont);
- flags |= -(int)mStrikeThru & sStrikeThruFlag;
- flags |= -(int)mUnderline & sUnderlineFlag;
- flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mStrikeThru & sStrikeThruFlag;
+ flags |= -(int)mUnderline & sUnderlineFlag;
+ flags |= -(int)mDevKern & sDevKernFlag;
+ flags |= -(int)mFilterBitmap & sFilterBitmapFlag;
return flags;
}
void Paint::setJavaFlags(uint32_t flags) {
applyLegacyFlagsToPaint(flags, this);
applyLegacyFlagsToFont(flags, &mFont);
- mStrikeThru = (flags & sStrikeThruFlag) != 0;
- mUnderline = (flags & sUnderlineFlag) != 0;
- mDevKern = (flags & sDevKernFlag) != 0;
+ mStrikeThru = (flags & sStrikeThruFlag) != 0;
+ mUnderline = (flags & sUnderlineFlag) != 0;
+ mDevKern = (flags & sDevKernFlag) != 0;
+ mFilterBitmap = (flags & sFilterBitmapFlag) != 0;
}
} // namespace android
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index c9433ec8a9da..c40b858268be 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -30,7 +30,8 @@
using namespace android;
-static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID;
+static jclass gAnimatedImageDrawableClass;
+static jmethodID gAnimatedImageDrawable_callOnAnimationEndMethodID;
// Note: jpostProcess holds a handle to the ImageDecoder.
static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/,
@@ -178,26 +179,23 @@ class InvokeListener : public MessageHandler {
public:
InvokeListener(JNIEnv* env, jobject javaObject) {
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK);
- // Hold a weak reference to break a cycle that would prevent GC.
- mWeakRef = env->NewWeakGlobalRef(javaObject);
+ mCallbackRef = env->NewGlobalRef(javaObject);
}
~InvokeListener() override {
auto* env = requireEnv(mJvm);
- env->DeleteWeakGlobalRef(mWeakRef);
+ env->DeleteGlobalRef(mCallbackRef);
}
virtual void handleMessage(const Message&) override {
auto* env = get_env_or_die(mJvm);
- jobject localRef = env->NewLocalRef(mWeakRef);
- if (localRef) {
- env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID);
- }
+ env->CallStaticVoidMethod(gAnimatedImageDrawableClass,
+ gAnimatedImageDrawable_callOnAnimationEndMethodID, mCallbackRef);
}
private:
JavaVM* mJvm;
- jweak mWeakRef;
+ jobject mCallbackRef;
};
class JniAnimationEndListener : public OnAnimationEndListener {
@@ -253,26 +251,31 @@ static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlo
}
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
- { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate },
- { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
- { "nDraw", "(JJ)J", (void*) AnimatedImageDrawable_nDraw },
- { "nSetAlpha", "(JI)V", (void*) AnimatedImageDrawable_nSetAlpha },
- { "nGetAlpha", "(J)I", (void*) AnimatedImageDrawable_nGetAlpha },
- { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter },
- { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
- { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
- { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
- { "nGetRepeatCount", "(J)I", (void*) AnimatedImageDrawable_nGetRepeatCount },
- { "nSetRepeatCount", "(JI)V", (void*) AnimatedImageDrawable_nSetRepeatCount },
- { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
- { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
- { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
- { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds },
+ {"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
+ (void*)AnimatedImageDrawable_nCreate},
+ {"nGetNativeFinalizer", "()J", (void*)AnimatedImageDrawable_nGetNativeFinalizer},
+ {"nDraw", "(JJ)J", (void*)AnimatedImageDrawable_nDraw},
+ {"nSetAlpha", "(JI)V", (void*)AnimatedImageDrawable_nSetAlpha},
+ {"nGetAlpha", "(J)I", (void*)AnimatedImageDrawable_nGetAlpha},
+ {"nSetColorFilter", "(JJ)V", (void*)AnimatedImageDrawable_nSetColorFilter},
+ {"nIsRunning", "(J)Z", (void*)AnimatedImageDrawable_nIsRunning},
+ {"nStart", "(J)Z", (void*)AnimatedImageDrawable_nStart},
+ {"nStop", "(J)Z", (void*)AnimatedImageDrawable_nStop},
+ {"nGetRepeatCount", "(J)I", (void*)AnimatedImageDrawable_nGetRepeatCount},
+ {"nSetRepeatCount", "(JI)V", (void*)AnimatedImageDrawable_nSetRepeatCount},
+ {"nSetOnAnimationEndListener", "(JLjava/lang/ref/WeakReference;)V",
+ (void*)AnimatedImageDrawable_nSetOnAnimationEndListener},
+ {"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
+ {"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
+ {"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
- jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable");
- gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V");
+ gAnimatedImageDrawableClass = reinterpret_cast<jclass>(env->NewGlobalRef(
+ FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable")));
+ gAnimatedImageDrawable_callOnAnimationEndMethodID =
+ GetStaticMethodIDOrDie(env, gAnimatedImageDrawableClass, "callOnAnimationEnd",
+ "(Ljava/lang/ref/WeakReference;)V");
return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable",
gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods));
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 05278f24ebbd..6989ac032c36 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -873,7 +873,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
jlong bitmapHandle, jint density, jobject parcel) {
#ifdef __ANDROID__ // Layoutlib does not support parcel
if (parcel == NULL) {
- SkDebugf("------- writeToParcel null parcel\n");
+ ALOGD("------- writeToParcel null parcel\n");
return JNI_FALSE;
}
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 785a5dc995ab..15e529e169fc 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -107,7 +107,7 @@ private:
jint n = env->CallIntMethod(fJavaInputStream,
gInputStream_readMethodID, fJavaByteArray, 0, requested);
if (checkException(env)) {
- SkDebugf("---- read threw an exception\n");
+ ALOGD("---- read threw an exception\n");
return bytesRead;
}
@@ -119,7 +119,7 @@ private:
env->GetByteArrayRegion(fJavaByteArray, 0, n,
reinterpret_cast<jbyte*>(buffer));
if (checkException(env)) {
- SkDebugf("---- read:GetByteArrayRegion threw an exception\n");
+ ALOGD("---- read:GetByteArrayRegion threw an exception\n");
return bytesRead;
}
@@ -136,7 +136,7 @@ private:
jlong skipped = env->CallLongMethod(fJavaInputStream,
gInputStream_skipMethodID, (jlong)size);
if (checkException(env)) {
- SkDebugf("------- skip threw an exception\n");
+ ALOGD("------- skip threw an exception\n");
return 0;
}
if (skipped < 0) {
@@ -236,7 +236,7 @@ public:
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
- SkDebugf("--- write:SetByteArrayElements threw an exception\n");
+ ALOGD("--- write:SetByteArrayElements threw an exception\n");
return false;
}
@@ -245,7 +245,7 @@ public:
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
- SkDebugf("------- write threw an exception\n");
+ ALOGD("------- write threw an exception\n");
return false;
}
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index f84a4bd09073..c5c47116b5a6 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -15,6 +15,8 @@
#include "gif_lib.h"
+#include <log/log.h>
+
#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)
#define DGifCloseFile(a, b) DGifCloseFile(a)
#endif
@@ -244,7 +246,7 @@ static void drawFrame(SkBitmap* bm, const SavedImage* frame, const ColorMapObjec
}
if (cmap == nullptr || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
- SkDEBUGFAIL("bad colortable setup");
+ ALOGD("bad colortable setup");
return;
}
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 6942017d5f27..b7ddd211b0de 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -64,7 +64,7 @@ public:
}
static jlong validateNinePatchChunk(JNIEnv* env, jobject, jbyteArray obj) {
- size_t chunkSize = env->GetArrayLength(obj);
+ size_t chunkSize = obj != NULL ? env->GetArrayLength(obj) : 0;
if (chunkSize < (int) (sizeof(Res_png_9patch))) {
jniThrowRuntimeException(env, "Array too small for chunk.");
return NULL;
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index bcec0fa8a1cc..22a1e1fd94b9 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -663,8 +663,7 @@ namespace PaintGlue {
}
static void setFilterBitmap(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean filterBitmap) {
- reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
- filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+ reinterpret_cast<Paint*>(paintHandle)->setFilterBitmap(filterBitmap);
}
static void setDither(CRITICAL_JNI_PARAMS_COMMA jlong paintHandle, jboolean dither) {
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index ec115b4e141c..c30e29bf247f 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -29,10 +29,6 @@ public:
fClearFlags = static_cast<uint16_t>(clearFlags);
fSetFlags = static_cast<uint16_t>(setFlags);
}
- void filter(SkPaint* paint) override {
- uint32_t flags = Paint::GetSkPaintJavaFlags(*paint);
- Paint::SetSkPaintJavaFlags(paint, (flags & ~fClearFlags) | fSetFlags);
- }
void filterFullPaint(Paint* paint) override {
paint->setJavaFlags((paint->getJavaFlags() & ~fClearFlags) | fSetFlags);
}
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index ac2f5b77d23a..106c6db57e18 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -18,6 +18,7 @@
#include "SkUtils.h"
#include "SkData.h"
+#include <inttypes.h>
#include <log/log.h>
using namespace android;
@@ -30,7 +31,7 @@ AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset)
bool AssetStreamAdaptor::rewind() {
off64_t pos = fAsset->seek(0, SEEK_SET);
if (pos == (off64_t)-1) {
- SkDebugf("----- fAsset->seek(rewind) failed\n");
+ ALOGD("----- fAsset->seek(rewind) failed\n");
return false;
}
return true;
@@ -58,7 +59,7 @@ bool AssetStreamAdaptor::hasPosition() const {
size_t AssetStreamAdaptor::getPosition() const {
const off64_t offset = fAsset->seek(0, SEEK_CUR);
if (offset == -1) {
- SkDebugf("---- fAsset->seek(0, SEEK_CUR) failed\n");
+ ALOGD("---- fAsset->seek(0, SEEK_CUR) failed\n");
return 0;
}
@@ -67,7 +68,7 @@ size_t AssetStreamAdaptor::getPosition() const {
bool AssetStreamAdaptor::seek(size_t position) {
if (fAsset->seek(position, SEEK_SET) == -1) {
- SkDebugf("---- fAsset->seek(0, SEEK_SET) failed\n");
+ ALOGD("---- fAsset->seek(0, SEEK_SET) failed\n");
return false;
}
@@ -76,7 +77,7 @@ bool AssetStreamAdaptor::seek(size_t position) {
bool AssetStreamAdaptor::move(long offset) {
if (fAsset->seek(offset, SEEK_CUR) == -1) {
- SkDebugf("---- fAsset->seek(%i, SEEK_CUR) failed\n", offset);
+ ALOGD("---- fAsset->seek(%li, SEEK_CUR) failed\n", offset);
return false;
}
@@ -95,12 +96,12 @@ size_t AssetStreamAdaptor::read(void* buffer, size_t size) {
off64_t oldOffset = fAsset->seek(0, SEEK_CUR);
if (-1 == oldOffset) {
- SkDebugf("---- fAsset->seek(oldOffset) failed\n");
+ ALOGD("---- fAsset->seek(oldOffset) failed\n");
return 0;
}
off64_t newOffset = fAsset->seek(size, SEEK_CUR);
if (-1 == newOffset) {
- SkDebugf("---- fAsset->seek(%d) failed\n", size);
+ ALOGD("---- fAsset->seek(%zu) failed\n", size);
return 0;
}
amount = newOffset - oldOffset;
@@ -121,20 +122,20 @@ sk_sp<SkData> android::CopyAssetToData(Asset* asset) {
const off64_t seekReturnVal = asset->seek(0, SEEK_SET);
if ((off64_t)-1 == seekReturnVal) {
- SkDebugf("---- copyAsset: asset rewind failed\n");
+ ALOGD("---- copyAsset: asset rewind failed\n");
return NULL;
}
const off64_t size = asset->getLength();
if (size <= 0) {
- SkDebugf("---- copyAsset: asset->getLength() returned %d\n", size);
+ ALOGD("---- copyAsset: asset->getLength() returned %" PRId64 "\n", size);
return NULL;
}
sk_sp<SkData> data(SkData::MakeUninitialized(size));
const off64_t len = asset->read(data->writable_data(), size);
if (len != size) {
- SkDebugf("---- copyAsset: asset->read(%d) returned %d\n", size, len);
+ ALOGD("---- copyAsset: asset->read(%" PRId64 ") returned %" PRId64 "\n", size, len);
return NULL;
}
@@ -143,7 +144,7 @@ sk_sp<SkData> android::CopyAssetToData(Asset* asset) {
jobject android::nullObjectReturn(const char msg[]) {
if (msg) {
- SkDebugf("--- %s\n", msg);
+ ALOGD("--- %s\n", msg);
}
return NULL;
}
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 689cf0bea741..77f42ae70268 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -85,7 +85,7 @@ Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
uint8_t* yuv, int* offsets) {
- SkDebugf("onFlyCompress");
+ ALOGD("onFlyCompress");
JSAMPROW y[16];
JSAMPROW cb[8];
JSAMPROW cr[8];
@@ -161,7 +161,7 @@ Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
uint8_t* yuv, int* offsets) {
- SkDebugf("onFlyCompress_422");
+ ALOGD("onFlyCompress_422");
JSAMPROW y[16];
JSAMPROW cb[16];
JSAMPROW cr[16];
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index a611f7ce2d14..0ef80ee10708 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -188,39 +188,57 @@ static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jl
return result ? JNI_TRUE : JNI_FALSE;
}
-// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
-// from one to the other (though SkClipOp is destined to become a strict subset)
+// SkClipOp is a strict subset of SkRegion::Op and is castable back and forth for their
+// shared operations (intersect and difference).
static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
-static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
-static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
-static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
-static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
-
-static SkClipOp opHandleToClipOp(jint opHandle) {
- // The opHandle is defined in Canvas.java to be Region::Op
- SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
-
- // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
- // this function can perform a range check and throw an unsupported-exception.
- // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
-
- // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
- // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
- return static_cast<SkClipOp>(rgnOp);
-}
static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jint opHandle) {
- bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
- opHandleToClipOp(opHandle));
+ // The opHandle is defined in Canvas.java to be Region::Op
+ SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+ bool nonEmptyClip;
+ switch (rgnOp) {
+ case SkRegion::Op::kIntersect_Op:
+ case SkRegion::Op::kDifference_Op:
+ // Intersect and difference are supported clip operations
+ nonEmptyClip =
+ get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
+ break;
+ case SkRegion::Op::kReplace_Op:
+ // Replace is emulated to support legacy apps older than P
+ nonEmptyClip = get_canvas(canvasHandle)->replaceClipRect_deprecated(l, t, r, b);
+ break;
+ default:
+ // All other operations would expand the clip and are no longer supported,
+ // so log and skip (to avoid breaking legacy apps).
+ ALOGW("Ignoring unsupported clip operation %d", opHandle);
+ SkRect clipBounds; // ignored
+ nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+ break;
+ }
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
jint opHandle) {
+ SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
- bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
+ bool nonEmptyClip;
+ switch (rgnOp) {
+ case SkRegion::Op::kIntersect_Op:
+ case SkRegion::Op::kDifference_Op:
+ nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, static_cast<SkClipOp>(rgnOp));
+ break;
+ case SkRegion::Op::kReplace_Op:
+ nonEmptyClip = get_canvas(canvasHandle)->replaceClipPath_deprecated(path);
+ break;
+ default:
+ ALOGW("Ignoring unsupported clip operation %d", opHandle);
+ SkRect clipBounds; // ignored
+ nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+ break;
+ }
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
@@ -233,7 +251,7 @@ static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorS
jlong colorLong, jint modeHandle) {
SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
- SkPaint p;
+ Paint p;
p.setColor4f(color, cs.get());
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
@@ -421,7 +439,7 @@ static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmap
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
&filteredPaint);
@@ -443,7 +461,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, left, top, &filteredPaint);
} else {
canvas->drawBitmap(bitmap, left, top, paint);
@@ -458,7 +476,7 @@ static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHan
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
canvas->restore();
@@ -486,7 +504,7 @@ static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitma
if (paint) {
filteredPaint = *paint;
}
- filteredPaint.setFilterQuality(kLow_SkFilterQuality);
+ filteredPaint.setFilterBitmap(true);
canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
} else {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 54367b8334cb..1ca4ce9dc3d8 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -423,28 +423,6 @@ private:
jobject mObject;
};
-class JWeakGlobalRefHolder {
-public:
- JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) {
- mWeakRef = getenv(vm)->NewWeakGlobalRef(object);
- }
-
- virtual ~JWeakGlobalRefHolder() {
- if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- }
-
- jobject ref() { return mWeakRef; }
- JavaVM* vm() { return mVm; }
-
-private:
- JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete;
- void operator=(const JWeakGlobalRefHolder&) = delete;
-
- JavaVM* mVm;
- jobject mWeakRef;
-};
-
using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;
struct PictureCaptureState {
@@ -578,20 +556,16 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback(
} else {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto globalCallbackRef =
- std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback);
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
+ vm, env->NewGlobalRef(aSurfaceTransactionCallback));
proxy->setASurfaceTransactionCallback(
[globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool {
JNIEnv* env = getenv(globalCallbackRef->vm());
- jobject localref = env->NewLocalRef(globalCallbackRef->ref());
- if (CC_UNLIKELY(!localref)) {
- return false;
- }
jboolean ret = env->CallBooleanMethod(
- localref, gASurfaceTransactionCallback.onMergeTransaction,
+ globalCallbackRef->object(),
+ gASurfaceTransactionCallback.onMergeTransaction,
static_cast<jlong>(transObj), static_cast<jlong>(scObj),
static_cast<jlong>(frameNr));
- env->DeleteLocalRef(localref);
return ret;
});
}
@@ -606,15 +580,11 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef =
- std::make_shared<JWeakGlobalRefHolder>(vm, callback);
+ std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
JNIEnv* env = getenv(globalCallbackRef->vm());
- jobject localref = env->NewLocalRef(globalCallbackRef->ref());
- if (CC_UNLIKELY(!localref)) {
- return;
- }
- env->CallVoidMethod(localref, gPrepareSurfaceControlForWebviewCallback.prepare);
- env->DeleteLocalRef(localref);
+ env->CallVoidMethod(globalCallbackRef->object(),
+ gPrepareSurfaceControlForWebviewCallback.prepare);
});
}
}
@@ -817,6 +787,14 @@ static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint
DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos);
}
+static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) {
+ Properties::setDrawingEnabled(enabled);
+}
+
+static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) {
+ return Properties::isDrawingEnabled();
+}
+
// ----------------------------------------------------------------------------
// HardwareRendererObserver
// ----------------------------------------------------------------------------
@@ -953,6 +931,8 @@ static const JNINativeMethod gMethods[] = {
{"preload", "()V", (void*)android_view_ThreadedRenderer_preload},
{"isWebViewOverlaysEnabled", "()Z",
(void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled},
+ {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled},
+ {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled},
};
static JavaVM* mJvm = nullptr;
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
index e5d5e75d0f3b..6cae5ffa397f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.cpp
@@ -24,6 +24,7 @@
namespace android {
struct {
+ jclass clazz;
jmethodID callback;
} gHardwareRendererObserverClassInfo;
@@ -38,14 +39,13 @@ static JNIEnv* getenv(JavaVM* vm) {
HardwareRendererObserver::HardwareRendererObserver(JavaVM* vm, jobject observer,
bool waitForPresentTime)
: uirenderer::FrameMetricsObserver(waitForPresentTime), mVm(vm) {
- mObserverWeak = getenv(mVm)->NewWeakGlobalRef(observer);
- LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
- "unable to create frame stats observer reference");
+ mObserver = getenv(mVm)->NewGlobalRef(observer);
+ LOG_ALWAYS_FATAL_IF(mObserver == nullptr, "unable to create frame stats observer reference");
}
HardwareRendererObserver::~HardwareRendererObserver() {
JNIEnv* env = getenv(mVm);
- env->DeleteWeakGlobalRef(mObserverWeak);
+ env->DeleteGlobalRef(mObserver);
}
bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, int* dropCount) {
@@ -66,6 +66,8 @@ bool HardwareRendererObserver::getNextBuffer(JNIEnv* env, jlongArray metrics, in
}
void HardwareRendererObserver::notify(const int64_t* stats) {
+ if (!mKeepListening) return;
+
FrameMetricsNotification& elem = mRingBuffer[mNextFree];
if (!elem.hasData.load()) {
@@ -77,18 +79,17 @@ void HardwareRendererObserver::notify(const int64_t* stats) {
elem.hasData = true;
JNIEnv* env = getenv(mVm);
- jobject target = env->NewLocalRef(mObserverWeak);
- if (target != nullptr) {
- env->CallVoidMethod(target, gHardwareRendererObserverClassInfo.callback);
- env->DeleteLocalRef(target);
- }
+ mKeepListening = env->CallStaticBooleanMethod(gHardwareRendererObserverClassInfo.clazz,
+ gHardwareRendererObserverClassInfo.callback,
+ mObserver);
} else {
mDroppedReports++;
}
}
static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* env,
- jobject observerObj,
+ jobject /*clazz*/,
+ jobject weakRefThis,
jboolean waitForPresentTime) {
JavaVM* vm = nullptr;
if (env->GetJavaVM(&vm) != JNI_OK) {
@@ -97,7 +98,7 @@ static jlong android_graphics_HardwareRendererObserver_createObserver(JNIEnv* en
}
HardwareRendererObserver* observer =
- new HardwareRendererObserver(vm, observerObj, waitForPresentTime);
+ new HardwareRendererObserver(vm, weakRefThis, waitForPresentTime);
return reinterpret_cast<jlong>(observer);
}
@@ -114,7 +115,7 @@ static jint android_graphics_HardwareRendererObserver_getNextBuffer(JNIEnv* env,
}
static const std::array gMethods = {
- MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Z)J",
+ MAKE_JNI_NATIVE_METHOD("nCreateObserver", "(Ljava/lang/ref/WeakReference;Z)J",
android_graphics_HardwareRendererObserver_createObserver),
MAKE_JNI_NATIVE_METHOD("nGetNextBuffer", "(J[J)I",
android_graphics_HardwareRendererObserver_getNextBuffer),
@@ -123,8 +124,10 @@ static const std::array gMethods = {
int register_android_graphics_HardwareRendererObserver(JNIEnv* env) {
jclass observerClass = FindClassOrDie(env, "android/graphics/HardwareRendererObserver");
- gHardwareRendererObserverClassInfo.callback = GetMethodIDOrDie(env, observerClass,
- "notifyDataAvailable", "()V");
+ gHardwareRendererObserverClassInfo.clazz =
+ reinterpret_cast<jclass>(env->NewGlobalRef(observerClass));
+ gHardwareRendererObserverClassInfo.callback = GetStaticMethodIDOrDie(
+ env, observerClass, "invokeDataAvailable", "(Ljava/lang/ref/WeakReference;)Z");
return RegisterMethodsOrDie(env, "android/graphics/HardwareRendererObserver",
gMethods.data(), gMethods.size());
diff --git a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
index d3076140541b..5ee3e1669502 100644
--- a/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
+++ b/libs/hwui/jni/android_graphics_HardwareRendererObserver.h
@@ -63,7 +63,8 @@ private:
};
JavaVM* const mVm;
- jweak mObserverWeak;
+ jobject mObserver;
+ bool mKeepListening = true;
int mNextFree = 0;
int mNextInQueue = 0;
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index 7338ef24cb58..cf6702e45fff 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -378,13 +378,17 @@ static const JNINativeMethod methods[] = {
{"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
};
+static jclass sClazz;
static jfieldID sNativeInstanceField;
+static jmethodID sCtor;
int register_android_graphics_Matrix(JNIEnv* env) {
int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods));
jclass clazz = FindClassOrDie(env, "android/graphics/Matrix");
+ sClazz = MakeGlobalRefOrDie(env, clazz);
sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J");
+ sCtor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
return result;
}
@@ -393,4 +397,7 @@ SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj) {
return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField));
}
+jobject android_graphics_Matrix_newInstance(JNIEnv* env) {
+ return env->NewObject(sClazz, sCtor);
+}
}
diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h
index fe90d2ef945d..79de48b46954 100644
--- a/libs/hwui/jni/android_graphics_Matrix.h
+++ b/libs/hwui/jni/android_graphics_Matrix.h
@@ -25,6 +25,9 @@ namespace android {
/* Gets the underlying SkMatrix from a Matrix object. */
SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+/* Creates a new Matrix java object. */
+jobject android_graphics_Matrix_newInstance(JNIEnv* env);
+
} // namespace android
#endif // _ANDROID_GRAPHICS_MATRIX_H_
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index e1da1690518a..944393c63ad6 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -547,9 +547,12 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
// SurfaceView position callback
// ----------------------------------------------------------------------------
-jmethodID gPositionListener_PositionChangedMethod;
-jmethodID gPositionListener_ApplyStretchMethod;
-jmethodID gPositionListener_PositionLostMethod;
+struct {
+ jclass clazz;
+ jmethodID callPositionChanged;
+ jmethodID callApplyStretch;
+ jmethodID callPositionLost;
+} gPositionListener;
static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
jlong renderNodePtr, jobject listener) {
@@ -557,16 +560,16 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
public:
PositionListenerTrampoline(JNIEnv* env, jobject listener) {
env->GetJavaVM(&mVm);
- mWeakRef = env->NewWeakGlobalRef(listener);
+ mListener = env->NewGlobalRef(listener);
}
virtual ~PositionListenerTrampoline() {
- jnienv()->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
+ jnienv()->DeleteGlobalRef(mListener);
+ mListener = nullptr;
}
virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override {
- if (CC_UNLIKELY(!mWeakRef || !info.updateWindowPositions)) return;
+ if (CC_UNLIKELY(!mListener || !info.updateWindowPositions)) return;
Matrix4 transform;
info.damageAccumulator->computeCurrentTransform(&transform);
@@ -609,7 +612,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
}
virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
- if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
+ if (CC_UNLIKELY(!mListener || (info && !info->updateWindowPositions))) return;
if (mPreviousPosition.isEmpty()) {
return;
@@ -618,18 +621,16 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
ATRACE_NAME("SurfaceView position lost");
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
// TODO: Remember why this is synchronous and then make a comment
- env->CallVoidMethod(localref, gPositionListener_PositionLostMethod,
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionLost, mListener,
info ? info->canvasContext.getFrameNumber() : 0);
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
+ }
#endif
- env->DeleteLocalRef(localref);
}
private:
@@ -684,28 +685,20 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- return;
- }
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
SkVector stretchDirection = effect->getStretchDirection();
- env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
- info.canvasContext.getFrameNumber(),
- result.width,
- result.height,
- stretchDirection.fX,
- stretchDirection.fY,
- effect->maxStretchAmountX,
- effect->maxStretchAmountY,
- childRelativeBounds.left(),
- childRelativeBounds.top(),
- childRelativeBounds.right(),
- childRelativeBounds.bottom());
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callApplyStretch, mListener,
+ info.canvasContext.getFrameNumber(), result.width, result.height,
+ stretchDirection.fX, stretchDirection.fY, effect->maxStretchAmountX,
+ effect->maxStretchAmountY, childRelativeBounds.left(),
+ childRelativeBounds.top(), childRelativeBounds.right(),
+ childRelativeBounds.bottom());
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
+ }
#endif
- env->DeleteLocalRef(localref);
}
}
@@ -714,14 +707,12 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
ATRACE_NAME("Update SurfaceView position");
JNIEnv* env = jnienv();
- jobject localref = env->NewLocalRef(mWeakRef);
- if (CC_UNLIKELY(!localref)) {
- env->DeleteWeakGlobalRef(mWeakRef);
- mWeakRef = nullptr;
- } else {
- env->CallVoidMethod(localref, gPositionListener_PositionChangedMethod,
- frameNumber, left, top, right, bottom);
- env->DeleteLocalRef(localref);
+ jboolean keepListening = env->CallStaticBooleanMethod(
+ gPositionListener.clazz, gPositionListener.callPositionChanged, mListener,
+ frameNumber, left, top, right, bottom);
+ if (!keepListening) {
+ env->DeleteGlobalRef(mListener);
+ mListener = nullptr;
}
// We need to release ourselves here
@@ -729,7 +720,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
}
JavaVM* mVm;
- jobject mWeakRef;
+ jobject mListener;
uirenderer::Rect mPreviousPosition;
};
@@ -754,7 +745,7 @@ static const JNINativeMethod gMethods[] = {
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
- {"nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V",
+ {"nRequestPositionUpdates", "(JLjava/lang/ref/WeakReference;)V",
(void*)android_view_RenderNode_requestPositionUpdates},
// ----------------------------------------------------------------------------
@@ -852,12 +843,13 @@ static const JNINativeMethod gMethods[] = {
int register_android_view_RenderNode(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/graphics/RenderNode$PositionUpdateListener");
- gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
- "positionChanged", "(JIIII)V");
- gPositionListener_ApplyStretchMethod =
- GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFFFFF)V");
- gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
- "positionLost", "(J)V");
+ gPositionListener.clazz = MakeGlobalRefOrDie(env, clazz);
+ gPositionListener.callPositionChanged = GetStaticMethodIDOrDie(
+ env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z");
+ gPositionListener.callApplyStretch = GetStaticMethodIDOrDie(
+ env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z");
+ gPositionListener.callPositionLost = GetStaticMethodIDOrDie(
+ env, clazz, "callPositionLost", "(Ljava/lang/ref/WeakReference;J)Z");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index a6fb95832c03..8e4dd53069f4 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -160,7 +160,6 @@ static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
}
static const JNINativeMethod gMethods[] = {
- // Fast Natives
{"nativeShapeTextRun", "("
"[C" // text
"I" // start
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 73de0d12a60b..77b8a44d85a1 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -28,6 +28,7 @@ LIBHWUI {
register_android_graphics_GraphicsStatsService;
zygote_preload_graphics;
AMatrix_getContents;
+ AMatrix_newInstance;
APaint_createPaint;
APaint_destroyPaint;
APaint_setBlendMode;
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 3580bed45a1f..3f89c0712407 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -52,6 +52,8 @@ protected:
mOutput << mIdent << "clipRegion" << std::endl;
}
+ void onResetClip() override { mOutput << mIdent << "resetClip" << std::endl; }
+
void onDrawPaint(const SkPaint&) override { mOutput << mIdent << "drawPaint" << std::endl; }
void onDrawPath(const SkPath&, const SkPaint&) override {
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 471a7f7af3b1..6db717096da7 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -21,6 +21,7 @@
#include "SkColorFilter.h"
#include "SkSurface.h"
#include "gl/GrGLTypes.h"
+#include "system/window.h"
namespace android {
namespace uirenderer {
@@ -29,7 +30,8 @@ namespace skiapipeline {
void LayerDrawable::onDraw(SkCanvas* canvas) {
Layer* layer = mLayerUpdater->backingLayer();
if (layer) {
- DrawLayer(canvas->recordingContext(), canvas, layer, nullptr, nullptr, true);
+ SkRect srcRect = layer->getCurrentCropRect();
+ DrawLayer(canvas->recordingContext(), canvas, layer, &srcRect, nullptr, true);
}
}
@@ -75,66 +77,67 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
const SkRect* dstRect,
bool useLayerTransform) {
if (context == nullptr) {
- SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
+ ALOGD("Attempting to draw LayerDrawable into an unsupported surface");
return false;
}
// transform the matrix based on the layer
- SkMatrix layerTransform = layer->getTransform();
+ // SkMatrix layerTransform = layer->getTransform();
+ const uint32_t windowTransform = layer->getWindowTransform();
sk_sp<SkImage> layerImage = layer->getImage();
const int layerWidth = layer->getWidth();
const int layerHeight = layer->getHeight();
if (layerImage) {
- SkMatrix textureMatrixInv;
- textureMatrixInv = layer->getTexTransform();
- // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
- // use bottom left origin and remove flipV and invert transformations.
- SkMatrix flipV;
- flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
- textureMatrixInv.preConcat(flipV);
- textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
- textureMatrixInv.postScale(layerImage->width(), layerImage->height());
- SkMatrix textureMatrix;
- if (!textureMatrixInv.invert(&textureMatrix)) {
- textureMatrix = textureMatrixInv;
- }
+ const int imageWidth = layerImage->width();
+ const int imageHeight = layerImage->height();
- SkMatrix matrix;
if (useLayerTransform) {
- matrix = SkMatrix::Concat(layerTransform, textureMatrix);
- } else {
- matrix = textureMatrix;
+ canvas->save();
+ canvas->concat(layer->getTransform());
}
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(layer->getColorFilter());
- const bool nonIdentityMatrix = !matrix.isIdentity();
- if (nonIdentityMatrix) {
- canvas->save();
- canvas->concat(matrix);
- }
const SkMatrix& totalMatrix = canvas->getTotalMatrix();
- if (dstRect || srcRect) {
- SkMatrix matrixInv;
- if (!matrix.invert(&matrixInv)) {
- matrixInv = matrix;
- }
+ if (srcRect || dstRect) {
SkRect skiaSrcRect;
- if (srcRect) {
+ if (srcRect && !srcRect->isEmpty()) {
skiaSrcRect = *srcRect;
} else {
- skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ skiaSrcRect = SkRect::MakeIWH(imageWidth, imageHeight);
}
- matrixInv.mapRect(&skiaSrcRect);
SkRect skiaDestRect;
- if (dstRect) {
+ if (dstRect && !dstRect->isEmpty()) {
skiaDestRect = *dstRect;
} else {
- skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ skiaDestRect = (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90)
+ ? SkRect::MakeIWH(layerHeight, layerWidth)
+ : SkRect::MakeIWH(layerWidth, layerHeight);
+ }
+
+ const float px = skiaDestRect.centerX();
+ const float py = skiaDestRect.centerY();
+ SkMatrix m;
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ m.postScale(-1.f, 1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ m.postScale(1.f, -1.f, px, py);
+ }
+ if (windowTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ m.postRotate(90, 0, 0);
+ m.postTranslate(skiaDestRect.height(), 0);
+ }
+ auto constraint = SkCanvas::kFast_SrcRectConstraint;
+ if (srcRect && !srcRect->isEmpty()) {
+ constraint = SkCanvas::kStrict_SrcRectConstraint;
}
- matrixInv.mapRect(&skiaDestRect);
+
+ canvas->save();
+ canvas->concat(m);
+
// If (matrix is a rect-to-rect transform)
// and (src/dst buffers size match in screen coordinates)
// and (src/dst corners align fractionally),
@@ -146,18 +149,13 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
+
canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- SkCanvas::kFast_SrcRectConstraint);
- } else {
- SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height());
- SkSamplingOptions sampling(SkFilterMode::kNearest);
- if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) {
- sampling = SkSamplingOptions(SkFilterMode::kLinear);
- }
- canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint);
+ constraint);
+ canvas->restore();
}
// restore the original matrix
- if (nonIdentityMatrix) {
+ if (useLayerTransform) {
canvas->restore();
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 76c4a03d3a91..9c51e628e04a 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -187,28 +187,18 @@ void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
void SkiaRecordingCanvas::FilterForImage(SkPaint& paint) {
// kClear blend mode is drawn as kDstOut on HW for compatibility with Android O and
// older.
- if (sApiLevel <= 27 && paint.getBlendMode() == SkBlendMode::kClear) {
+ if (sApiLevel <= 27 && paint.asBlendMode() == SkBlendMode::kClear) {
paint.setBlendMode(SkBlendMode::kDstOut);
}
}
-static SkFilterMode Paint_to_filter(const SkPaint& paint) {
- return paint.getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-}
-
-static SkSamplingOptions Paint_to_sampling(const SkPaint& paint) {
- // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
- return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
-}
-
void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
sk_sp<SkImage> image = bitmap.makeImage();
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, left, top, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, left, top, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -228,8 +218,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImage(image, 0, 0, Paint_to_sampling(p), &p, bitmap.palette());
+ [&](const Paint& p) {
+ mRecorder.drawImage(image, 0, 0, p.sampling(), &p, bitmap.palette());
},
FilterForImage);
@@ -248,8 +238,8 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop
applyLooper(
paint,
- [&](const SkPaint& p) {
- mRecorder.drawImageRect(image, srcRect, dstRect, Paint_to_sampling(p), &p,
+ [&](const Paint& p) {
+ mRecorder.drawImageRect(image, srcRect, dstRect, p.sampling(), &p,
SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
},
FilterForImage);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index ddfb66f84f28..3c7617d35c7c 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -68,7 +68,7 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
ATRACE_CALL();
if (canvas->recordingContext() == nullptr) {
- SkDEBUGF(("Attempting to draw VkInteropFunctor into an unsupported surface"));
+ ALOGD("Attempting to draw VkInteropFunctor into an unsupported surface");
return;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2f3a509831d1..bb0b1352c360 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -256,7 +256,7 @@ void CanvasContext::setStopped(bool stopped) {
}
void CanvasContext::allocateBuffers() {
- if (mNativeSurface) {
+ if (mNativeSurface && Properties::isDrawingEnabled()) {
ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
}
}
@@ -480,7 +480,8 @@ nsecs_t CanvasContext::draw() {
SkRect dirty;
mDamageAccumulator.finish(&dirty);
- if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
+ if (!Properties::isDrawingEnabled() ||
+ (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
if (auto grContext = getGrContext()) {
// Submit to ensure that any texture uploads complete and Skia can
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6dbfcc349d50..2fed4686f16e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -90,9 +90,17 @@ public:
* and false otherwise (e.g. cache limits have been exceeded).
*/
bool pinImages(std::vector<SkImage*>& mutableImages) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
return mRenderPipeline->pinImages(mutableImages);
}
- bool pinImages(LsaVector<sk_sp<Bitmap>>& images) { return mRenderPipeline->pinImages(images); }
+ bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+ if (!Properties::isDrawingEnabled()) {
+ return true;
+ }
+ return mRenderPipeline->pinImages(images);
+ }
/**
* Unpin any image that had be previously pinned to the GPU cache
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index e8ba15fe92af..491af4336f97 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -74,7 +74,7 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
+ layerUpdater->updateLayer(true, nullptr, 0, SkRect::MakeEmpty());
return layerUpdater;
}
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 10ba07905c45..31a8ae1d38cd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -49,7 +49,7 @@ public:
paint.setAntiAlias(true);
paint.setColor(Color::Green_700);
canvas.drawCircle(200, 200, 200, paint);
- SkPaint alphaPaint;
+ Paint alphaPaint;
alphaPaint.setAlpha(128);
canvas.restoreUnclippedLayer(unclippedSaveLayer, alphaPaint);
canvas.restore();
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 955a5e7d8b3a..0c389bfe8b71 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -36,19 +36,16 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight());
EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter());
EXPECT_FALSE(layerUpdater->backingLayer()->isBlend());
- EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform());
// push the deferred updates to the layer
- SkMatrix scaledMatrix = SkMatrix::Scale(0.5, 0.5);
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
- layerUpdater->updateLayer(true, scaledMatrix, layerImage);
+ layerUpdater->updateLayer(true, layerImage, 0, SkRect::MakeEmpty());
// the backing layer should now have all the properties applied.
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight());
EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter());
EXPECT_TRUE(layerUpdater->backingLayer()->isBlend());
- EXPECT_EQ(scaledMatrix, layerUpdater->backingLayer()->getTexTransform());
}
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index a1ba70a22581..dc1b2e668dd0 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -61,11 +61,11 @@ TEST(SkiaBehavior, lightingColorFilter_simplify) {
TEST(SkiaBehavior, porterDuffCreateIsCached) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kOverlay);
- auto expected = paint.getBlendMode();
+ auto expected = paint.asBlendMode();
paint.setBlendMode(SkBlendMode::kClear);
- ASSERT_NE(expected, paint.getBlendMode());
+ ASSERT_NE(expected, paint.asBlendMode());
paint.setBlendMode(SkBlendMode::kOverlay);
- ASSERT_EQ(expected, paint.getBlendMode());
+ ASSERT_EQ(expected, paint.asBlendMode());
}
TEST(SkiaBehavior, pathIntersection) {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 8c999c41bf7b..57a7fe3fd466 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -382,7 +382,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
std::vector<sp<RenderNode>> nodes;
nodes.push_back(TestUtils::createSkiaNode(
20, 20, 30, 30, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
- canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
+ canvas.replaceClipRect_deprecated(0, -20, 10, 30);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
}));
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index a8f2d9a28d67..94bcb1110e05 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -32,13 +32,6 @@ namespace uirenderer {
*/
class PaintUtils {
public:
- static inline GLenum getFilter(const SkPaint* paint) {
- if (!paint || paint->getFilterQuality() != kNone_SkFilterQuality) {
- return GL_LINEAR;
- }
- return GL_NEAREST;
- }
-
static bool isOpaquePaint(const SkPaint* paint) {
if (!paint) return true; // default (paintless) behavior is SrcOver, black
@@ -48,7 +41,7 @@ public:
}
// Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
- SkBlendMode mode = paint->getBlendMode();
+ const auto mode = paint->asBlendMode();
return mode == SkBlendMode::kSrcOver || mode == SkBlendMode::kSrc;
}
@@ -59,7 +52,7 @@ public:
}
static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) {
- return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver;
+ return paint ? paint->getBlendMode_or(SkBlendMode::kSrcOver) : SkBlendMode::kSrcOver;
}
static inline int getAlphaDirect(const SkPaint* paint) {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index acd8bced0612..0c6dac02453a 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -153,8 +153,7 @@ void SpriteController::doUpdateSprites() {
|| update.state.surfaceHeight < desiredHeight) {
needApplyTransaction = true;
- t.setSize(update.state.surfaceControl,
- desiredWidth, desiredHeight);
+ update.state.surfaceControl->updateDefaultBufferSize(desiredWidth, desiredHeight);
update.state.surfaceWidth = desiredWidth;
update.state.surfaceHeight = desiredHeight;
update.state.surfaceDrawn = false;
@@ -169,7 +168,8 @@ void SpriteController::doUpdateSprites() {
// If surface is a new one, we have to set right layer stack.
if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
- t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ t.setLayerStack(update.state.surfaceControl,
+ ui::LayerStack::fromValue(update.state.displayId));
needApplyTransaction = true;
}
}
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 0970c1c76a36..73c5c826584f 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -53,7 +53,9 @@ public final class LastLocationRequest implements Parcelable {
*
* @return true if this request should be ignored while updating app ops with location usage
*
+ * @hide
*/
+ @SystemApi
public boolean isHiddenFromAppOps() {
return mHiddenFromAppOps;
}
@@ -65,9 +67,10 @@ public final class LastLocationRequest implements Parcelable {
* Driving Assistance Systems) application.
*
* @return true if all limiting factors will be ignored to satisfy GNSS request
+ *
* @hide
*/
- // TODO: make this system api
+ @SystemApi
public boolean isAdasGnssBypass() {
return mAdasGnssBypass;
}
@@ -78,7 +81,10 @@ public final class LastLocationRequest implements Parcelable {
* possible limiting factors will be ignored in order to satisfy this last location request.
*
* @return true if all limiting factors will be ignored to satisfy this request
+ *
+ * @hide
*/
+ @SystemApi
public boolean isLocationSettingsIgnored() {
return mLocationSettingsIgnored;
}
@@ -192,7 +198,10 @@ public final class LastLocationRequest implements Parcelable {
*
* <p>Permissions enforcement occurs when resulting last location request is actually used,
* not when this method is invoked.
+ *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) {
mHiddenFromAppOps = hiddenFromAppOps;
@@ -211,7 +220,7 @@ public final class LastLocationRequest implements Parcelable {
*
* @hide
*/
- // TODO: make this system api
+ @SystemApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
mAdasGnssBypass = adasGnssBypass;
@@ -226,7 +235,10 @@ public final class LastLocationRequest implements Parcelable {
*
* <p>Permissions enforcement occurs when resulting last location request is actually used,
* not when this method is invoked.
+ *
+ * @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
mLocationSettingsIgnored = locationSettingsIgnored;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 526b84e85e38..42bbc7232944 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -325,7 +325,7 @@ public class LocationManager {
*
* @hide
*/
- // TODO: @SystemApi
+ @SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
"android.location.action.ADAS_GNSS_ENABLED_CHANGED";
@@ -338,7 +338,7 @@ public class LocationManager {
*
* @hide
*/
- // TODO: @SystemApi
+ @SystemApi
public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
/**
@@ -656,7 +656,7 @@ public class LocationManager {
*
* @hide
*/
- //TODO: @SystemApi
+ @SystemApi
public boolean isAdasGnssLocationEnabled() {
try {
return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
@@ -673,7 +673,7 @@ public class LocationManager {
*
* @hide
*/
- // TODO: @SystemApi
+ @SystemApi
@RequiresPermission(WRITE_SECURE_SETTINGS)
public void setAdasGnssLocationEnabled(boolean enabled) {
try {
@@ -1569,6 +1569,7 @@ public class LocationManager {
*
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
public boolean injectLocation(@NonNull Location location) {
Preconditions.checkArgument(location != null, "invalid null location");
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index b48e59676ac1..d6e203c39870 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -660,7 +660,7 @@ public final class LocationRequest implements Parcelable {
*
* @hide
*/
- // TODO: @SystemApi
+ @SystemApi
public boolean isAdasGnssBypass() {
return mAdasGnssBypass;
}
@@ -1139,7 +1139,7 @@ public final class LocationRequest implements Parcelable {
*
* @hide
*/
- // TODO: @SystemApi
+ @SystemApi
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
mAdasGnssBypass = adasGnssBypass;
diff --git a/media/Android.bp b/media/Android.bp
index a66236e6f4ea..cf4a0b1005d7 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -8,23 +8,6 @@ package {
}
aidl_interface {
- name: "audio_common-aidl",
- unstable: true,
- host_supported: true,
- vendor_available: true,
- local_include_dir: "aidl",
- double_loadable: true,
- srcs: [
- "aidl/android/media/audio/common/AudioChannelMask.aidl",
- "aidl/android/media/audio/common/AudioConfig.aidl",
- "aidl/android/media/audio/common/AudioFormat.aidl",
- "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
- "aidl/android/media/audio/common/AudioStreamType.aidl",
- "aidl/android/media/audio/common/AudioUsage.aidl",
- ],
-}
-
-aidl_interface {
name: "media_permission-aidl",
unstable: true,
host_supported: true,
@@ -40,30 +23,92 @@ aidl_interface {
name: "soundtrigger_middleware-aidl",
unstable: true,
local_include_dir: "aidl",
+ backend: {
+ java: {
+ sdk_version: "module_current",
+ },
+ },
srcs: [
- "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
- "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
"aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
- "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
- "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
- "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
- "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
"aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
- "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
- "aidl/android/media/soundtrigger_middleware/Status.aidl",
],
imports: [
- "audio_common-aidl",
+ "android.media.soundtrigger.types",
"media_permission-aidl",
],
}
+
+aidl_interface {
+ name: "android.media.audio.common.types",
+ vendor_available: true,
+ host_supported: true,
+ double_loadable: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/audio/common/AudioChannelMask.aidl",
+ "aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioFormat.aidl",
+ "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioStreamType.aidl",
+ "aidl/android/media/audio/common/AudioUsage.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+}
+
+aidl_interface {
+ name: "android.media.soundtrigger.types",
+ vendor_available: true,
+ flags: ["-Werror", "-Weverything", ],
+ local_include_dir: "aidl",
+ srcs: [
+ "aidl/android/media/soundtrigger/AudioCapabilities.aidl",
+ "aidl/android/media/soundtrigger/ConfidenceLevel.aidl",
+ "aidl/android/media/soundtrigger/ModelParameter.aidl",
+ "aidl/android/media/soundtrigger/ModelParameterRange.aidl",
+ "aidl/android/media/soundtrigger/Phrase.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl",
+ "aidl/android/media/soundtrigger/PhraseSoundModel.aidl",
+ "aidl/android/media/soundtrigger/Properties.aidl",
+ "aidl/android/media/soundtrigger/RecognitionConfig.aidl",
+ "aidl/android/media/soundtrigger/RecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger/RecognitionMode.aidl",
+ "aidl/android/media/soundtrigger/RecognitionStatus.aidl",
+ "aidl/android/media/soundtrigger/SoundModel.aidl",
+ "aidl/android/media/soundtrigger/SoundModelType.aidl",
+ "aidl/android/media/soundtrigger/Status.aidl",
+ ],
+ stability: "vintf",
+ backend: {
+ cpp: {
+ enabled: true,
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ ndk: {
+ vndk: {
+ enabled: true,
+ },
+ },
+ },
+ imports: [
+ "android.media.audio.common.types",
+ ],
+}
diff --git a/media/aidl/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
index b9b08e6921bc..17be8dd5461f 100644
--- a/media/aidl/android/media/audio/common/AudioChannelMask.aidl
+++ b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
@@ -57,8 +57,11 @@ package android.media.audio.common;
* checking the channel mask, the implementer should look for ways to fix it
* with additional information outside of the mask.
*
+ * TODO: this should be replaced with strings or other mechanisms that are easy to extend, in line
+ * with the audio HAL conventions.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioChannelMask {
/**
@@ -113,42 +116,43 @@ enum AudioChannelMask {
*/
OUT_HAPTIC_A = 0x20000000,
OUT_HAPTIC_B = 0x10000000,
-// TODO(ytai): Aliases not currently supported in AIDL - can inline the values.
-// OUT_MONO = OUT_FRONT_LEFT,
-// OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
-// OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
-// OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_QUAD_BACK = OUT_QUAD,
-// /**
-// * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
-// OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
-// OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
-// OUT_5POINT1_BACK = OUT_5POINT1,
-// /**
-// * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
-// */
-// OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
-// /**
-// * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
-// */
-// OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
-// OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
-// OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
-// OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
-// OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
-// OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
-// OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
+ OUT_MONO = OUT_FRONT_LEFT,
+ OUT_STEREO = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT),
+ OUT_2POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_2POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_2POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_3POINT0POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_3POINT1POINT2 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT | OUT_LOW_FREQUENCY),
+ OUT_QUAD = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_QUAD_BACK = OUT_QUAD,
+ /**
+ * like OUT_QUAD_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_QUAD_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_SURROUND = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_BACK_CENTER),
+ OUT_PENTA = (OUT_QUAD | OUT_FRONT_CENTER),
+ OUT_5POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT),
+ OUT_5POINT1_BACK = OUT_5POINT1,
+ /**
+ * like OUT_5POINT1_BACK with *_SIDE_* instead of *_BACK_*
+ */
+ OUT_5POINT1_SIDE = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_5POINT1POINT2 = (OUT_5POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_5POINT1POINT4 = (OUT_5POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_6POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_BACK_CENTER),
+ /**
+ * matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
+ */
+ OUT_7POINT1 = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_FRONT_CENTER | OUT_LOW_FREQUENCY | OUT_BACK_LEFT | OUT_BACK_RIGHT | OUT_SIDE_LEFT | OUT_SIDE_RIGHT),
+ OUT_7POINT1POINT2 = (OUT_7POINT1 | OUT_TOP_SIDE_LEFT | OUT_TOP_SIDE_RIGHT),
+ OUT_7POINT1POINT4 = (OUT_7POINT1 | OUT_TOP_FRONT_LEFT | OUT_TOP_FRONT_RIGHT | OUT_TOP_BACK_LEFT | OUT_TOP_BACK_RIGHT),
+ OUT_MONO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_HAPTIC_A),
+ OUT_STEREO_HAPTIC_A = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A),
+ OUT_HAPTIC_AB = (OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_MONO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+ OUT_STEREO_HAPTIC_AB = (OUT_FRONT_LEFT | OUT_FRONT_RIGHT | OUT_HAPTIC_A | OUT_HAPTIC_B),
+
/**
* These are bits only, not complete values
*
diff --git a/media/aidl/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
index 50dd796e1fa0..d1285611f44e 100644
--- a/media/aidl/android/media/audio/common/AudioConfig.aidl
+++ b/media/aidl/android/media/audio/common/AudioConfig.aidl
@@ -27,10 +27,12 @@ import android.media.audio.common.AudioOffloadInfo;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioConfig {
int sampleRateHz;
int channelMask;
- AudioFormat format;
+ AudioFormat format = AudioFormat.INVALID;
AudioOffloadInfo offloadInfo;
long frameCount;
}
diff --git a/media/aidl/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
index aadc8e26cce3..73fbca25a578 100644
--- a/media/aidl/android/media/audio/common/AudioFormat.aidl
+++ b/media/aidl/android/media/audio/common/AudioFormat.aidl
@@ -32,6 +32,7 @@ package android.media.audio.common;
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioFormat {
INVALID = 0xFFFFFFFF,
diff --git a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
index ec10d71135ae..7be5e6aadcb3 100644
--- a/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
+++ b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
@@ -28,17 +28,19 @@ import android.media.audio.common.AudioUsage;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable AudioOffloadInfo {
int sampleRateHz;
int channelMask;
- AudioFormat format;
- AudioStreamType streamType;
+ AudioFormat format = AudioFormat.INVALID;
+ AudioStreamType streamType = AudioStreamType.INVALID;
int bitRatePerSecond;
long durationMicroseconds;
boolean hasVideo;
boolean isStreaming;
int bitWidth;
int bufferSize;
- AudioUsage usage;
+ AudioUsage usage = AudioUsage.INVALID;
}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index c54566726350..8b7036705ac5 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -26,8 +26,14 @@ package android.media.audio.common;
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioStreamType {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -2,
DEFAULT = -1,
MIN = 0,
VOICE_CALL = 0,
diff --git a/media/aidl/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
index ef348165b22c..028eefee180b 100644
--- a/media/aidl/android/media/audio/common/AudioUsage.aidl
+++ b/media/aidl/android/media/audio/common/AudioUsage.aidl
@@ -22,8 +22,14 @@ package android.media.audio.common;
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum AudioUsage {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
UNKNOWN = 0,
MEDIA = 1,
VOICE_COMMUNICATION = 2,
diff --git a/media/aidl/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
index 36389047cee8..524978618ff5 100644
--- a/media/aidl/android/media/permission/Identity.aidl
+++ b/media/aidl/android/media/permission/Identity.aidl
@@ -20,6 +20,7 @@ package android.media.permission;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable Identity {
/** Linux user ID. */
int uid = -1;
diff --git a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
index 97a8849c7b07..7b0825b1e6cb 100644
--- a/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
+++ b/media/aidl/android/media/soundtrigger/AudioCapabilities.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* AudioCapabilities supported by the implemented HAL driver.
* @hide
*/
+@VintfStability
@Backing(type="int")
enum AudioCapabilities {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
index 3dbc70556bd3..3fcba404e21c 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
+++ b/media/aidl/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A recognition confidence level.
@@ -21,6 +21,8 @@ package android.media.soundtrigger_middleware;
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ConfidenceLevel {
/** user ID. */
int userId;
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
index 09936278e93a..948400883736 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameter.aidl
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Model specific parameters to be used with parameter set and get APIs.
*
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum ModelParameter {
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
index d6948a87dc6d..e7c616bae9bb 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
+++ b/media/aidl/android/media/soundtrigger/ModelParameterRange.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Value range for a model parameter.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable ModelParameterRange {
/** Minimum (inclusive) */
int minInclusive;
diff --git a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger/Phrase.aidl
index 98a489f8a6a9..077db21586c0 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
+++ b/media/aidl/android/media/soundtrigger/Phrase.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Key phrase descriptor.
*
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable Phrase {
/** Unique keyphrase ID assigned at enrollment time. */
int id;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
index 6a3ec61d1ebf..654f7c259d2f 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -13,16 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.RecognitionEvent;
/**
* An event that gets sent to indicate a phrase recognition (or aborting of the recognition
process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionEvent {
/** Common recognition event. */
RecognitionEvent common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
index cb96bf37a95d..eb523eb0d396 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ConfidenceLevel;
/**
* Specialized recognition event for key phrase detection.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseRecognitionExtra {
- // TODO(ytai): Constants / enums.
-
- /** keyphrase ID */
+ /** Keyphrase ID */
int id;
- /** recognition modes used for this keyphrase */
+ /** Bitfield, indexed by RecognitionMode. */
int recognitionModes;
- /** confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER */
+ /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER. Value is between 0-100. */
int confidenceLevel;
- /** number of user confidence levels */
+ /** Number of user confidence levels */
ConfidenceLevel[] levels;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
index 81028c1608ea..e0ffdeecd99c 100644
--- a/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.Phrase;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Phrase;
/**
* Specialized sound model for key phrase detection.
@@ -24,6 +24,8 @@ import android.media.soundtrigger_middleware.Phrase;
* information indicated by phrases field.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable PhraseSoundModel {
/** Common part of sound model descriptor */
SoundModel common;
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger/Properties.aidl
index 9c56e7b98b3f..efa1b6aac68d 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
+++ b/media/aidl/android/media/soundtrigger/Properties.aidl
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Capabilities of a sound trigger module.
* {@hide}
*/
-parcelable SoundTriggerModuleProperties {
+@JavaDerive(equals = true, toString = true)
+@VintfStability
+parcelable Properties {
/** Implementor name */
String implementor;
/** Implementation description */
@@ -44,7 +46,7 @@ parcelable SoundTriggerModuleProperties {
int maxKeyPhrases;
/** Maximum number of concurrent users detected */
int maxUsers;
- /** All supported modes. e.g RecognitionMode.VOICE_TRIGGER */
+ /** All supported modes. Bitfield, indexed by RecognitionMode. */
int recognitionModes;
/** Supports seamless transition from detection to capture */
boolean captureTransition;
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
index 5c0eeb1e32b1..a00f0e58e293 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionConfig.aidl
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseRecognitionExtra;
/**
* Configuration for tuning behavior of an active recognition process.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionConfig {
/* Capture and buffer audio for this recognition instance. */
boolean captureRequested;
@@ -34,6 +36,6 @@ parcelable RecognitionConfig {
*/
int audioCapabilities;
- /** Opaque capture configuration data. */
+ /** Capture configuration data. Content is implementation-defined. */
byte[] data;
}
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
index a237ec1aa3b3..94668a30606d 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionEvent.aidl
@@ -13,25 +13,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
import android.media.audio.common.AudioConfig;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModelType;
/**
* An event that gets sent to indicate a recognition (or aborting of the recognition process).
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable RecognitionEvent {
/** Recognition status. */
- RecognitionStatus status;
+ RecognitionStatus status = RecognitionStatus.INVALID;
/** Event type, same as sound model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Is it possible to capture audio from this utterance buffered by the implementation. */
boolean captureAvailable;
- /* Audio session ID. framework use. */
- int captureSession;
/**
* Delay in ms between end of model detection and start of audio available for capture.
* A negative value is possible (e.g. if key phrase is also available for Capture.
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
index d8bfff4bec6f..ce2cffedda32 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionMode.aidl
@@ -13,12 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Recognition mode.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionMode {
/** Simple voice trigger. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
index d563edca547d..cccf0f3b6d49 100644
--- a/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
+++ b/media/aidl/android/media/soundtrigger/RecognitionStatus.aidl
@@ -13,14 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* A status for indicating the type of a recognition event.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum RecognitionStatus {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Recognition success. */
SUCCESS = 0,
/** Recognition aborted (e.g. capture preempted by another use-case. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger/SoundModel.aidl
index 8186fb741b59..94244d0ba0ff 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModel.aidl
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
-import android.media.soundtrigger_middleware.SoundModelType;
+import android.media.soundtrigger.SoundModelType;
import android.os.ParcelFileDescriptor;
/**
@@ -23,9 +23,11 @@ import android.os.ParcelFileDescriptor;
* aggregation.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
+@VintfStability
parcelable SoundModel {
/** Model type. */
- SoundModelType type;
+ SoundModelType type = SoundModelType.INVALID;
/** Unique sound model ID. */
String uuid;
/**
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
index f2abc9af7780..34a9376a26b9 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
+++ b/media/aidl/android/media/soundtrigger/SoundModelType.aidl
@@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* Sound model type.
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum SoundModelType {
- /** Unspecified sound model type */
- UNKNOWN = -1,
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Key phrase sound models */
KEYPHRASE = 0,
/** All models other than keyphrase */
diff --git a/media/aidl/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger/Status.aidl
index c7623f5bf491..ca1487fb119f 100644
--- a/media/aidl/android/media/soundtrigger_middleware/Status.aidl
+++ b/media/aidl/android/media/soundtrigger/Status.aidl
@@ -13,13 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.media.soundtrigger_middleware;
+package android.media.soundtrigger;
/**
* {@hide}
*/
+@VintfStability
@Backing(type="int")
enum Status {
+ /**
+ * Used as default value in parcelables to indicate that a value was not set.
+ * Should never be considered a valid setting, except for backward compatibility scenarios.
+ */
+ INVALID = -1,
/** Success. */
SUCCESS = 0,
/** Failure due to resource contention. This is typically a temporary condition. */
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
index 726af7681979..6092ac53ec39 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
@@ -15,8 +15,8 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionEvent;
/**
* Main interface for a client to get notifications of events coming from this module.
@@ -28,22 +28,29 @@ oneway interface ISoundTriggerCallback {
* Invoked whenever a recognition event is triggered (typically, on recognition, but also in
* case of external aborting of a recognition or a forced recognition event - see the status
* code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onRecognition(int modelHandle, in RecognitionEvent event);
+ void onRecognition(int modelHandle, in RecognitionEvent event, int captureSession);
/**
* Invoked whenever a phrase recognition event is triggered (typically, on recognition, but
* also in case of external aborting of a recognition or a forced recognition event - see the
* status code in the event for determining).
+ * In case of abortion, the caller may retry after the next onRecognitionAvailabilityChange()
+ * callback.
*/
- void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event);
+ void onPhraseRecognition(int modelHandle, in PhraseRecognitionEvent event, int captureSession);
/**
- * Notifies the client the recognition has become available after previously having been
- * unavailable, or vice versa. This method will always be invoked once immediately after
- * attachment, and then every time there is a change in availability.
- * When availability changes from available to unavailable, all active recognitions are aborted,
- * and this event will be sent in addition to the abort event.
+ * Notifies the client that some start/load operations that have previously failed for resource
+ * reasons (threw a ServiceSpecificException(RESOURCE_CONTENTION) or have been preempted) may
+ * now succeed. This is not a guarantee, but a hint for the client to retry.
*/
- void onRecognitionAvailabilityChange(boolean available);
+ void onResourcesAvailable();
+ /**
+ * Notifies the client that a model had been preemptively unloaded by the service.
+ * The caller may retry after the next onRecognitionAvailabilityChange() callback.
+ */
+ void onModelUnloaded(int modelHandle);
/**
* Notifies the client that the associated module has crashed and restarted. The module instance
* is no longer usable and will throw a ServiceSpecificException with a Status.DEAD_OBJECT code
diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
index c4a57857dd3d..0b46fd4ef5f0 100644
--- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
@@ -15,11 +15,11 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
/**
* A sound-trigger module.
@@ -75,6 +75,9 @@ interface ISoundTriggerModule {
* Once a recognition event is passed to the client, the recognition automatically become
* inactive, unless the event is of the RecognitionStatus.FORCED kind. Client can also shut down
* the recognition explicitly, via stopRecognition.
+ *
+ * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that
+ * resources required for starting the model are currently consumed by other clients.
*/
void startRecognition(int modelHandle, in RecognitionConfig config);
diff --git a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
index 667135ff61b9..6c210bf7ccd4 100644
--- a/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
@@ -15,17 +15,18 @@
*/
package android.media.soundtrigger_middleware;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.media.soundtrigger.Properties;
/**
* A descriptor of an available sound trigger module, containing the handle used to reference the
* module, as well its capabilities.
* {@hide}
*/
+@JavaDerive(equals = true, toString = true)
parcelable SoundTriggerModuleDescriptor {
/** Module handle to be used for attaching to it. */
int handle;
/** Module capabilities. */
- SoundTriggerModuleProperties properties;
+ Properties properties;
}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
new file mode 100644
index 000000000000..c3af3bfdfde7
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioChannelMask.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioChannelMask {
+ REPRESENTATION_POSITION = 0,
+ REPRESENTATION_INDEX = 2,
+ NONE = 0,
+ INVALID = -1073741824,
+ OUT_FRONT_LEFT = 1,
+ OUT_FRONT_RIGHT = 2,
+ OUT_FRONT_CENTER = 4,
+ OUT_LOW_FREQUENCY = 8,
+ OUT_BACK_LEFT = 16,
+ OUT_BACK_RIGHT = 32,
+ OUT_FRONT_LEFT_OF_CENTER = 64,
+ OUT_FRONT_RIGHT_OF_CENTER = 128,
+ OUT_BACK_CENTER = 256,
+ OUT_SIDE_LEFT = 512,
+ OUT_SIDE_RIGHT = 1024,
+ OUT_TOP_CENTER = 2048,
+ OUT_TOP_FRONT_LEFT = 4096,
+ OUT_TOP_FRONT_CENTER = 8192,
+ OUT_TOP_FRONT_RIGHT = 16384,
+ OUT_TOP_BACK_LEFT = 32768,
+ OUT_TOP_BACK_CENTER = 65536,
+ OUT_TOP_BACK_RIGHT = 131072,
+ OUT_TOP_SIDE_LEFT = 262144,
+ OUT_TOP_SIDE_RIGHT = 524288,
+ OUT_HAPTIC_A = 536870912,
+ OUT_HAPTIC_B = 268435456,
+ OUT_MONO = 1,
+ OUT_STEREO = 3,
+ OUT_2POINT1 = 11,
+ OUT_2POINT0POINT2 = 786435,
+ OUT_2POINT1POINT2 = 786443,
+ OUT_3POINT0POINT2 = 786439,
+ OUT_3POINT1POINT2 = 786447,
+ OUT_QUAD = 51,
+ OUT_QUAD_BACK = 51,
+ OUT_QUAD_SIDE = 1539,
+ OUT_SURROUND = 263,
+ OUT_PENTA = 55,
+ OUT_5POINT1 = 63,
+ OUT_5POINT1_BACK = 63,
+ OUT_5POINT1_SIDE = 1551,
+ OUT_5POINT1POINT2 = 786495,
+ OUT_5POINT1POINT4 = 184383,
+ OUT_6POINT1 = 319,
+ OUT_7POINT1 = 1599,
+ OUT_7POINT1POINT2 = 788031,
+ OUT_7POINT1POINT4 = 185919,
+ OUT_MONO_HAPTIC_A = 536870913,
+ OUT_STEREO_HAPTIC_A = 536870915,
+ OUT_HAPTIC_AB = 805306368,
+ OUT_MONO_HAPTIC_AB = 805306369,
+ OUT_STEREO_HAPTIC_AB = 805306371,
+ IN_LEFT = 4,
+ IN_RIGHT = 8,
+ IN_FRONT = 16,
+ IN_BACK = 32,
+ IN_LEFT_PROCESSED = 64,
+ IN_RIGHT_PROCESSED = 128,
+ IN_FRONT_PROCESSED = 256,
+ IN_BACK_PROCESSED = 512,
+ IN_PRESSURE = 1024,
+ IN_X_AXIS = 2048,
+ IN_Y_AXIS = 4096,
+ IN_Z_AXIS = 8192,
+ IN_BACK_LEFT = 65536,
+ IN_BACK_RIGHT = 131072,
+ IN_CENTER = 262144,
+ IN_LOW_FREQUENCY = 1048576,
+ IN_TOP_LEFT = 2097152,
+ IN_TOP_RIGHT = 4194304,
+ IN_VOICE_UPLINK = 16384,
+ IN_VOICE_DNLINK = 32768,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
new file mode 100644
index 000000000000..c11eb76857d2
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioConfig {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioOffloadInfo offloadInfo;
+ long frameCount;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
new file mode 100644
index 000000000000..b7c86599a2c4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioFormat.aidl
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioFormat {
+ INVALID = -1,
+ DEFAULT = 0,
+ PCM = 0,
+ MP3 = 16777216,
+ AMR_NB = 33554432,
+ AMR_WB = 50331648,
+ AAC = 67108864,
+ HE_AAC_V1 = 83886080,
+ HE_AAC_V2 = 100663296,
+ VORBIS = 117440512,
+ OPUS = 134217728,
+ AC3 = 150994944,
+ E_AC3 = 167772160,
+ DTS = 184549376,
+ DTS_HD = 201326592,
+ IEC61937 = 218103808,
+ DOLBY_TRUEHD = 234881024,
+ EVRC = 268435456,
+ EVRCB = 285212672,
+ EVRCWB = 301989888,
+ EVRCNW = 318767104,
+ AAC_ADIF = 335544320,
+ WMA = 352321536,
+ WMA_PRO = 369098752,
+ AMR_WB_PLUS = 385875968,
+ MP2 = 402653184,
+ QCELP = 419430400,
+ DSD = 436207616,
+ FLAC = 452984832,
+ ALAC = 469762048,
+ APE = 486539264,
+ AAC_ADTS = 503316480,
+ SBC = 520093696,
+ APTX = 536870912,
+ APTX_HD = 553648128,
+ AC4 = 570425344,
+ LDAC = 587202560,
+ MAT = 603979776,
+ AAC_LATM = 620756992,
+ CELT = 637534208,
+ APTX_ADAPTIVE = 654311424,
+ LHDC = 671088640,
+ LHDC_LL = 687865856,
+ APTX_TWSP = 704643072,
+ MAIN_MASK = -16777216,
+ SUB_MASK = 16777215,
+ PCM_SUB_16_BIT = 1,
+ PCM_SUB_8_BIT = 2,
+ PCM_SUB_32_BIT = 3,
+ PCM_SUB_8_24_BIT = 4,
+ PCM_SUB_FLOAT = 5,
+ PCM_SUB_24_BIT_PACKED = 6,
+ MP3_SUB_NONE = 0,
+ AMR_SUB_NONE = 0,
+ AAC_SUB_MAIN = 1,
+ AAC_SUB_LC = 2,
+ AAC_SUB_SSR = 4,
+ AAC_SUB_LTP = 8,
+ AAC_SUB_HE_V1 = 16,
+ AAC_SUB_SCALABLE = 32,
+ AAC_SUB_ERLC = 64,
+ AAC_SUB_LD = 128,
+ AAC_SUB_HE_V2 = 256,
+ AAC_SUB_ELD = 512,
+ AAC_SUB_XHE = 768,
+ VORBIS_SUB_NONE = 0,
+ E_AC3_SUB_JOC = 1,
+ MAT_SUB_1_0 = 1,
+ MAT_SUB_2_0 = 2,
+ MAT_SUB_2_1 = 3,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
new file mode 100644
index 000000000000..b5d889ea5d0b
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioOffloadInfo.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioOffloadInfo {
+ int sampleRateHz;
+ int channelMask;
+ android.media.audio.common.AudioFormat format = android.media.audio.common.AudioFormat.INVALID;
+ android.media.audio.common.AudioStreamType streamType = android.media.audio.common.AudioStreamType.INVALID;
+ int bitRatePerSecond;
+ long durationMicroseconds;
+ boolean hasVideo;
+ boolean isStreaming;
+ int bitWidth;
+ int bufferSize;
+ android.media.audio.common.AudioUsage usage = android.media.audio.common.AudioUsage.INVALID;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
new file mode 100644
index 000000000000..915c66814aa8
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioStreamType {
+ INVALID = -2,
+ DEFAULT = -1,
+ MIN = 0,
+ VOICE_CALL = 0,
+ SYSTEM = 1,
+ RING = 2,
+ MUSIC = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
+ ENFORCED_AUDIBLE = 7,
+ DTMF = 8,
+ TTS = 9,
+ ACCESSIBILITY = 10,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
new file mode 100644
index 000000000000..f5130a4d0231
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUsage.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */// This file has been semi-automatically generated using hidl2aidl from its counterpart in
+// hardware/interfaces/audio/common/5.0/types.hal
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioUsage {
+ INVALID = -1,
+ UNKNOWN = 0,
+ MEDIA = 1,
+ VOICE_COMMUNICATION = 2,
+ VOICE_COMMUNICATION_SIGNALLING = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ NOTIFICATION_TELEPHONY_RINGTONE = 6,
+ ASSISTANCE_ACCESSIBILITY = 11,
+ ASSISTANCE_NAVIGATION_GUIDANCE = 12,
+ ASSISTANCE_SONIFICATION = 13,
+ GAME = 14,
+ VIRTUAL_SOURCE = 15,
+ ASSISTANT = 16,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
new file mode 100644
index 000000000000..5d88305484d0
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/AudioCapabilities.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioCapabilities {
+ ECHO_CANCELLATION = 1,
+ NOISE_SUPPRESSION = 2,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
new file mode 100644
index 000000000000..5127a110efc5
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ConfidenceLevel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ConfidenceLevel {
+ int userId;
+ int levelPercent;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
new file mode 100644
index 000000000000..aadbf80b4f96
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameter.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum ModelParameter {
+ INVALID = -1,
+ THRESHOLD_FACTOR = 0,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
new file mode 100644
index 000000000000..f29b7284a275
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/ModelParameterRange.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ModelParameterRange {
+ int minInclusive;
+ int maxInclusive;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
new file mode 100644
index 000000000000..11029ba9d25f
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Phrase.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Phrase {
+ int id;
+ int recognitionModes;
+ int[] users;
+ String locale;
+ String text;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
new file mode 100644
index 000000000000..b75d1b83c72a
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionEvent.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionEvent {
+ android.media.soundtrigger.RecognitionEvent common;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseExtras;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
new file mode 100644
index 000000000000..e417c69454a5
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseRecognitionExtra.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseRecognitionExtra {
+ int id;
+ int recognitionModes;
+ int confidenceLevel;
+ android.media.soundtrigger.ConfidenceLevel[] levels;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
new file mode 100644
index 000000000000..b4b3854d4926
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/PhraseSoundModel.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable PhraseSoundModel {
+ android.media.soundtrigger.SoundModel common;
+ android.media.soundtrigger.Phrase[] phrases;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
new file mode 100644
index 000000000000..068db52a2a6b
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Properties.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Properties {
+ String implementor;
+ String description;
+ int version;
+ String uuid;
+ String supportedModelArch;
+ int maxSoundModels;
+ int maxKeyPhrases;
+ int maxUsers;
+ int recognitionModes;
+ boolean captureTransition;
+ int maxBufferMs;
+ boolean concurrentCapture;
+ boolean triggerInEvent;
+ int powerConsumptionMw;
+ int audioCapabilities;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
new file mode 100644
index 000000000000..63cd2cbbb797
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionConfig.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionConfig {
+ boolean captureRequested;
+ android.media.soundtrigger.PhraseRecognitionExtra[] phraseRecognitionExtras;
+ int audioCapabilities;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
new file mode 100644
index 000000000000..e6cfb6bfc3f6
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionEvent.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable RecognitionEvent {
+ android.media.soundtrigger.RecognitionStatus status = android.media.soundtrigger.RecognitionStatus.INVALID;
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ boolean captureAvailable;
+ int captureDelayMs;
+ int capturePreambleMs;
+ boolean triggerInData;
+ @nullable android.media.audio.common.AudioConfig audioConfig;
+ byte[] data;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
new file mode 100644
index 000000000000..588291084f4a
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionMode {
+ VOICE_TRIGGER = 1,
+ USER_IDENTIFICATION = 2,
+ USER_AUTHENTICATION = 4,
+ GENERIC_TRIGGER = 8,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
new file mode 100644
index 000000000000..7881c28c7ecf
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/RecognitionStatus.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum RecognitionStatus {
+ INVALID = -1,
+ SUCCESS = 0,
+ ABORTED = 1,
+ FAILURE = 2,
+ FORCED = 3,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
new file mode 100644
index 000000000000..fe382643f3d2
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModel.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable SoundModel {
+ android.media.soundtrigger.SoundModelType type = android.media.soundtrigger.SoundModelType.INVALID;
+ String uuid;
+ String vendorUuid;
+ @nullable ParcelFileDescriptor data;
+ int dataSize;
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
new file mode 100644
index 000000000000..ac7864170535
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/SoundModelType.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum SoundModelType {
+ INVALID = -1,
+ KEYPHRASE = 0,
+ GENERIC = 1,
+}
diff --git a/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
new file mode 100644
index 000000000000..29f3167ac196
--- /dev/null
+++ b/media/aidl_api/android.media.soundtrigger.types/current/android/media/soundtrigger/Status.aidl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.soundtrigger;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Status {
+ INVALID = -1,
+ SUCCESS = 0,
+ RESOURCE_CONTENTION = 1,
+ OPERATION_NOT_SUPPORTED = 2,
+ TEMPORARY_PERMISSION_DENIED = 3,
+ DEAD_OBJECT = 4,
+ INTERNAL_ERROR = 5,
+}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index a031b4cfc911..347a9b161ae7 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -334,7 +334,19 @@ public final class AudioAttributes implements Parcelable {
};
/**
+ * @hide
+ */
+ @TestApi
+ public static int[] getSdkUsages() {
+ return SDK_USAGES;
+ }
+
+ /**
* Flag defining a behavior where the audibility of the sound will be ensured by the system.
+ * To ensure sound audibility, the system only uses built-in speakers or wired headphones
+ * and specifically excludes wireless audio devices.
+ * <p>Note this flag should only be used for sounds subject to regulatory behaviors in some
+ * countries, such as for camera shutter sound, and not for routing behaviors.
*/
public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
/**
@@ -447,13 +459,26 @@ public final class AudioAttributes implements Parcelable {
*/
public static final int FLAG_CAPTURE_PRIVATE = 0x1 << 13;
+ /**
+ * @hide
+ * Flag indicating the audio content has been processed to provide a virtual multichannel
+ * audio experience
+ */
+ public static final int FLAG_CONTENT_SPATIALIZED = 0x1 << 14;
+
+ /**
+ * @hide
+ * Flag indicating the audio content is to never be spatialized
+ */
+ public static final int FLAG_NEVER_SPATIALIZE = 0x1 << 15;
// Note that even though FLAG_MUTE_HAPTIC is stored as a flag bit, it is not here since
// it is known as a boolean value outside of AudioAttributes.
private static final int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO
| FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY
| FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER | FLAG_NO_MEDIA_PROJECTION
- | FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE;
+ | FLAG_NO_SYSTEM_CAPTURE | FLAG_CAPTURE_PRIVATE | FLAG_CONTENT_SPATIALIZED
+ | FLAG_NEVER_SPATIALIZE;
private final static int FLAG_ALL_PUBLIC = FLAG_AUDIBILITY_ENFORCED |
FLAG_HW_AV_SYNC | FLAG_LOW_LATENCY;
/* mask of flags that can be set by SDK and System APIs through the Builder */
@@ -615,6 +640,49 @@ public final class AudioAttributes implements Parcelable {
}
/**
+ * Return true if the audio content associated with these attributes has already been
+ * spatialized, that is it has already been processed to offer a binaural or transaural
+ * immersive audio experience.
+ * @return {@code true} if the content has been processed
+ */
+ public boolean isContentSpatialized() {
+ return (mFlags & FLAG_CONTENT_SPATIALIZED) != 0;
+ }
+
+ /** @hide */
+ @IntDef(flag = false, value = {
+ SPATIALIZATION_BEHAVIOR_AUTO,
+ SPATIALIZATION_BEHAVIOR_NEVER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SpatializationBehavior {};
+
+ /**
+ * Constant indicating the audio content associated with these attributes will follow the
+ * default platform behavior with regards to which content will be spatialized or not.
+ * @see #getSpatializationBehavior()
+ * @see Spatializer
+ */
+ public static final int SPATIALIZATION_BEHAVIOR_AUTO = 0;
+
+ /**
+ * Constant indicating the audio content associated with these attributes should never
+ * be virtualized.
+ * @see #getSpatializationBehavior()
+ * @see Spatializer
+ */
+ public static final int SPATIALIZATION_BEHAVIOR_NEVER = 1;
+
+ /**
+ * Return the behavior affecting whether spatialization will be used.
+ * @return the spatialization behavior
+ */
+ public @SpatializationBehavior int getSpatializationBehavior() {
+ return ((mFlags & FLAG_NEVER_SPATIALIZE) != 0)
+ ? SPATIALIZATION_BEHAVIOR_NEVER : SPATIALIZATION_BEHAVIOR_AUTO;
+ }
+
+ /**
* Return the capture policy.
* @return the capture policy set by {@link Builder#setAllowedCapturePolicy(int)} or
* the default if it was not called.
@@ -657,6 +725,8 @@ public final class AudioAttributes implements Parcelable {
private int mSource = MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID;
private int mFlags = 0x0;
private boolean mMuteHapticChannels = true;
+ private boolean mIsContentSpatialized = false;
+ private int mSpatializationBehavior = SPATIALIZATION_BEHAVIOR_AUTO;
private HashSet<String> mTags = new HashSet<String>();
private Bundle mBundle;
private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
@@ -687,6 +757,8 @@ public final class AudioAttributes implements Parcelable {
mFlags = aa.getAllFlags();
mTags = (HashSet<String>) aa.mTags.clone();
mMuteHapticChannels = aa.areHapticChannelsMuted();
+ mIsContentSpatialized = aa.isContentSpatialized();
+ mSpatializationBehavior = aa.getSpatializationBehavior();
}
/**
@@ -719,6 +791,12 @@ public final class AudioAttributes implements Parcelable {
if (mMuteHapticChannels) {
aa.mFlags |= FLAG_MUTE_HAPTIC;
}
+ if (mIsContentSpatialized) {
+ aa.mFlags |= FLAG_CONTENT_SPATIALIZED;
+ }
+ if (mSpatializationBehavior == SPATIALIZATION_BEHAVIOR_NEVER) {
+ aa.mFlags |= FLAG_NEVER_SPATIALIZE;
+ }
if (mPrivacySensitive == PRIVACY_SENSITIVE_DEFAULT) {
// capturing for camcorder or communication is private by default to
@@ -906,6 +984,35 @@ public final class AudioAttributes implements Parcelable {
}
/**
+ * Specifies whether the content has already been processed for spatialization.
+ * If it has, setting this to true will prevent issues such as double-processing.
+ * @param isSpatialized
+ * @return the same Builder instance
+ */
+ public @NonNull Builder setIsContentSpatialized(boolean isSpatialized) {
+ mIsContentSpatialized = isSpatialized;
+ return this;
+ }
+
+ /**
+ * Sets the behavior affecting whether spatialization will be used.
+ * @param sb the spatialization behavior
+ * @return the same Builder instance
+ *
+ */
+ public @NonNull Builder setSpatializationBehavior(@SpatializationBehavior int sb) {
+ switch (sb) {
+ case SPATIALIZATION_BEHAVIOR_NEVER:
+ case SPATIALIZATION_BEHAVIOR_AUTO:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid spatialization behavior " + sb);
+ }
+ mSpatializationBehavior = sb;
+ return this;
+ }
+
+ /**
* @hide
* Replaces flags.
* @param flags any combination of {@link AudioAttributes#FLAG_ALL}.
@@ -990,6 +1097,8 @@ public final class AudioAttributes implements Parcelable {
mContentType = attributes.mContentType;
mFlags = attributes.getAllFlags();
mMuteHapticChannels = attributes.areHapticChannelsMuted();
+ mIsContentSpatialized = attributes.isContentSpatialized();
+ mSpatializationBehavior = attributes.getSpatializationBehavior();
mTags = attributes.mTags;
mBundle = attributes.mBundle;
mSource = attributes.mSource;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 1644ec892c7e..a8199c4d028e 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -175,6 +175,28 @@ import java.util.Objects;
* <br>These masks are an ORed composite of individual channel masks. For example
* {@link #CHANNEL_OUT_STEREO} is composed of {@link #CHANNEL_OUT_FRONT_LEFT} and
* {@link #CHANNEL_OUT_FRONT_RIGHT}.
+ * <p>
+ * The following diagram represents the layout of the output channels, as seen from above
+ * the listener (in the center at the "lis" position, facing the front-center channel).
+ * <pre>
+ * TFL ----- TFC ----- TFR T is Top
+ * | \ | / |
+ * | FL --- FC --- FR | F is Front
+ * | |\ | /| |
+ * | | BFL-BFC-BFR | | BF is Bottom Front
+ * | | | |
+ * | FWL lis FWR | W is Wide
+ * | | | |
+ * TSL SL TC SR TSR S is Side
+ * | | | |
+ * | BL --- BC -- BR | B is Back
+ * | / \ |
+ * TBL ----- TBC ----- TBR C is Center, L/R is Left/Right
+ * </pre>
+ * All "T" (top) channels are above the listener, all "BF" (bottom-front) channels are below the
+ * listener, all others are in the listener's horizontal plane. When used in conjunction, LFE1 and
+ * LFE2 are below the listener, when used alone, LFE plane is undefined.
+ * See the channel definitions for the abbreviations
*
* <h5 id="channelIndexMask">Channel index masks</h5>
* Channel index masks are introduced in API {@link android.os.Build.VERSION_CODES#M}. They allow
@@ -417,43 +439,62 @@ public final class AudioFormat implements Parcelable {
// Output channel mask definitions below are translated to the native values defined in
// in /system/media/audio/include/system/audio.h in the JNI code of AudioTrack
+ /** Front left output channel (see FL in channel diagram) */
public static final int CHANNEL_OUT_FRONT_LEFT = 0x4;
+ /** Front right output channel (see FR in channel diagram) */
public static final int CHANNEL_OUT_FRONT_RIGHT = 0x8;
+ /** Front center output channel (see FC in channel diagram) */
public static final int CHANNEL_OUT_FRONT_CENTER = 0x10;
+ /** LFE "low frequency effect" channel
+ * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY_2}, it is intended
+ * to contain the left low-frequency effect signal, also referred to as "LFE1"
+ * in ITU-R BS.2159-8 */
public static final int CHANNEL_OUT_LOW_FREQUENCY = 0x20;
+ /** Back left output channel (see BL in channel diagram) */
public static final int CHANNEL_OUT_BACK_LEFT = 0x40;
+ /** Back right output channel (see BR in channel diagram) */
public static final int CHANNEL_OUT_BACK_RIGHT = 0x80;
public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
+ /** Back center output channel (see BC in channel diagram) */
public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
+ /** Side left output channel (see SL in channel diagram) */
public static final int CHANNEL_OUT_SIDE_LEFT = 0x800;
+ /** Side right output channel (see SR in channel diagram) */
public static final int CHANNEL_OUT_SIDE_RIGHT = 0x1000;
- /** @hide */
+ /** Top center (above listener) output channel (see TC in channel diagram) */
public static final int CHANNEL_OUT_TOP_CENTER = 0x2000;
- /** @hide */
+ /** Top front left output channel (see TFL in channel diagram above FL) */
public static final int CHANNEL_OUT_TOP_FRONT_LEFT = 0x4000;
- /** @hide */
+ /** Top front center output channel (see TFC in channel diagram above FC) */
public static final int CHANNEL_OUT_TOP_FRONT_CENTER = 0x8000;
- /** @hide */
+ /** Top front right output channel (see TFR in channel diagram above FR) */
public static final int CHANNEL_OUT_TOP_FRONT_RIGHT = 0x10000;
- /** @hide */
+ /** Top back left output channel (see TBL in channel diagram above BL) */
public static final int CHANNEL_OUT_TOP_BACK_LEFT = 0x20000;
- /** @hide */
+ /** Top back center output channel (see TBC in channel diagram above BC) */
public static final int CHANNEL_OUT_TOP_BACK_CENTER = 0x40000;
- /** @hide */
+ /** Top back right output channel (see TBR in channel diagram above BR) */
public static final int CHANNEL_OUT_TOP_BACK_RIGHT = 0x80000;
- /** @hide */
+ /** Top side left output channel (see TSL in channel diagram above SL) */
public static final int CHANNEL_OUT_TOP_SIDE_LEFT = 0x100000;
- /** @hide */
+ /** Top side right output channel (see TSR in channel diagram above SR) */
public static final int CHANNEL_OUT_TOP_SIDE_RIGHT = 0x200000;
- /** @hide */
+ /** Bottom front left output channel (see BFL in channel diagram below FL) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_LEFT = 0x400000;
- /** @hide */
+ /** Bottom front center output channel (see BFC in channel diagram below FC) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_CENTER = 0x800000;
- /** @hide */
+ /** Bottom front right output channel (see BFR in channel diagram below FR) */
public static final int CHANNEL_OUT_BOTTOM_FRONT_RIGHT = 0x1000000;
- /** @hide */
+ /** The second LFE channel
+ * When used in conjunction with {@link #CHANNEL_OUT_LOW_FREQUENCY}, it is intended
+ * to contain the right low-frequency effect signal, also referred to as "LFE2"
+ * in ITU-R BS.2159-8 */
public static final int CHANNEL_OUT_LOW_FREQUENCY_2 = 0x2000000;
+ /** Front wide left output channel (see FWL in channel diagram) */
+ public static final int CHANNEL_OUT_FRONT_WIDE_LEFT = 0x4000000;
+ /** Front wide right output channel (see FWR in channel diagram) */
+ public static final int CHANNEL_OUT_FRONT_WIDE_RIGHT = 0x8000000;
public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
@@ -466,6 +507,7 @@ public final class AudioFormat implements Parcelable {
public static final int CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER);
// aka 5POINT1_BACK
+ /** Output channel mask for 5.1 */
public static final int CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
/** @hide */
@@ -477,26 +519,39 @@ public final class AudioFormat implements Parcelable {
@Deprecated public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
+ /** Output channel mask for 7.1 */
// matches AUDIO_CHANNEL_OUT_7POINT1
public static final int CHANNEL_OUT_7POINT1_SURROUND = (
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_SIDE_LEFT | CHANNEL_OUT_SIDE_RIGHT |
CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
CHANNEL_OUT_LOW_FREQUENCY);
- /** @hide */
+ /** Output channel mask for 5.1.2
+ * Same as 5.1 with the addition of left and right top channels */
public static final int CHANNEL_OUT_5POINT1POINT2 = (CHANNEL_OUT_5POINT1 |
CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
- /** @hide */
+ /** Output channel mask for 5.1.4
+ * Same as 5.1 with the addition of four top channels */
public static final int CHANNEL_OUT_5POINT1POINT4 = (CHANNEL_OUT_5POINT1 |
CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
- /** @hide */
+ /** Output channel mask for 7.1.2
+ * Same as 7.1 with the addition of left and right top channels*/
public static final int CHANNEL_OUT_7POINT1POINT2 = (CHANNEL_OUT_7POINT1_SURROUND |
CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
- /** @hide */
+ /** Output channel mask for 7.1.4
+ * Same as 7.1 with the addition of four top channels */
public static final int CHANNEL_OUT_7POINT1POINT4 = (CHANNEL_OUT_7POINT1_SURROUND |
CHANNEL_OUT_TOP_FRONT_LEFT | CHANNEL_OUT_TOP_FRONT_RIGHT |
CHANNEL_OUT_TOP_BACK_LEFT | CHANNEL_OUT_TOP_BACK_RIGHT);
+ /** Output channel mask for 9.1.4
+ * Same as 7.1.4 with the addition of left and right front wide channels */
+ public static final int CHANNEL_OUT_9POINT1POINT4 = (CHANNEL_OUT_7POINT1POINT4
+ | CHANNEL_OUT_FRONT_WIDE_LEFT | CHANNEL_OUT_FRONT_WIDE_RIGHT);
+ /** Output channel mask for 9.1.6
+ * Same as 9.1.4 with the addition of left and right top side channels */
+ public static final int CHANNEL_OUT_9POINT1POINT6 = (CHANNEL_OUT_9POINT1POINT4
+ | CHANNEL_OUT_TOP_SIDE_LEFT | CHANNEL_OUT_TOP_SIDE_RIGHT);
/** @hide */
public static final int CHANNEL_OUT_13POINT_360RA = (
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_FRONT_RIGHT |
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3b9c05bbe64f..a73b436f19e2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -390,6 +390,18 @@ public class AudioManager {
*/
@Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
+ /** @hide */
+ private static final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
+
+ /** @hide */
+ @TestApi
+ public static final int[] getPublicStreamTypes() {
+ return PUBLIC_STREAM_TYPES;
+ }
+
/**
* Increase the ringer volume.
*
@@ -805,7 +817,7 @@ public class AudioManager {
}
@UnsupportedAppUsage
- private static IAudioService getService()
+ static IAudioService getService()
{
if (sService != null) {
return sService;
@@ -922,8 +934,8 @@ public class AudioManager {
public void adjustStreamVolume(int streamType, int direction, int flags) {
final IAudioService service = getService();
try {
- service.adjustStreamVolume(streamType, direction, flags,
- getContext().getOpPackageName());
+ service.adjustStreamVolumeWithAttribution(streamType, direction, flags,
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -993,7 +1005,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setMasterMute(mute, flags, getContext().getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1075,6 +1087,7 @@ public class AudioManager {
* @return The minimum valid volume index for the stream.
* @see #getStreamVolume(int)
*/
+ @TestApi
public int getStreamMinVolumeInt(int streamType) {
final IAudioService service = getService();
try {
@@ -1245,7 +1258,8 @@ public class AudioManager {
public void setStreamVolume(int streamType, int index, int flags) {
final IAudioService service = getService();
try {
- service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName());
+ service.setStreamVolumeWithAttribution(streamType, index, flags,
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1270,7 +1284,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setVolumeIndexForAttributes(attr, index, flags,
- getContext().getOpPackageName());
+ getContext().getOpPackageName(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2439,6 +2453,149 @@ public class AudioManager {
}
//====================================================================
+ // Immersive audio
+
+ /**
+ * @hide
+ * Returns the level of support for immersive audio from the {@link Spatializer} if
+ * available.
+ * @return the level of immersive audio support through spatialization
+ * @see Spatializer#getImmersiveAudioLevel()
+ */
+ @Spatializer.ImmersiveAudioLevel int getSpatializerImmersiveAudioLevel() {
+ final IAudioService service = getService();
+ try {
+ return service.getSpatializerImmersiveAudioLevel();
+ } catch (RemoteException e) {
+ return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+ }
+
+ /**
+ * Return a handle to the optional platform's {@link Spatializer}
+ * @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
+ * otherwise.
+ */
+ public @Nullable Spatializer getSpatializer() {
+ if (getSpatializerImmersiveAudioLevel() == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
+ return null;
+ }
+ return new Spatializer(this);
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#isEnabled()
+ * @return {@code true} if spatialization is enabled
+ */
+ boolean isSpatializerEnabled() {
+ final IAudioService service = getService();
+ try {
+ return service.isSpatializerEnabled();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying isSpatializerEnabled, returning false", e);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#setEnabled(boolean)
+ * Enable/disable the spatialization wherever supported.
+ * @param enabled {@code true} to enable
+ */
+ void setSpatializerFeatureEnabled(boolean enabled) {
+ final IAudioService service = getService();
+ try {
+ service.setSpatializerFeatureEnabled(enabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setSpatializerFeatureEnabled", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
+ * @see Spatializer#setEnabled(boolean)
+ * @param enabled enable/disable for a specific device.
+ * @param device the device concerned with spatializer functionality.
+ */
+ void setSpatializerEnabledForDevice(boolean enabled,
+ @NonNull AudioDeviceAttributes device) {
+ final IAudioService service = getService();
+ try {
+ service.setSpatializerEnabledForDevice(enabled, device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setSpatializerEnabledForDevice", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#canBeSpatialized(AudioAttributes, AudioFormat)
+ * @param attributes the {@code AudioAttributes} of the content as used for playback
+ * @param format the {@code AudioFormat} of the content as used for playback
+ * @return true if the device is capable of spatializing the combination of audio
+ * format and attributes.
+ */
+ boolean canBeSpatialized(
+ @NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
+ final IAudioService service = getService();
+ try {
+ return service.canBeSpatialized(attributes, format);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying canBeSpatialized for attr:" + attributes
+ + " format:" + format + " returning false", e);
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#getCompatibleAudioDevices()
+ * @return a non-null list of the spatialization-compatible audio devices
+ */
+ @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
+ final IAudioService service = getService();
+ try {
+ return service.getSpatializerCompatibleAudioDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error querying getSpatializerCompatibleAudioDevices(), "
+ + " returning empty list", e);
+ return new ArrayList<AudioDeviceAttributes>(0);
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#addCompatibleAudioDevice(AudioDeviceAttributes)
+ * @param ada the audio device compatible with spatialization
+ */
+ void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ final IAudioService service = getService();
+ try {
+ service.addSpatializerCompatibleAudioDevice(ada);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling addSpatializerCompatibleAudioDevice()", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @see Spatializer#removeCompatibleAudioDevice(AudioDeviceAttributes)
+ * @param ada the audio device incompatible with spatialization
+ */
+ void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ final IAudioService service = getService();
+ try {
+ service.removeSpatializerCompatibleAudioDevice(ada);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling removeSpatializerCompatibleAudioDevice()", e);
+ }
+ }
+
+
+ //====================================================================
// Bluetooth SCO control
/**
* Sticky broadcast intent action indicating that the Bluetooth SCO audio
@@ -2730,7 +2887,7 @@ public class AudioManager {
final IAudioService service = getService();
try {
service.setMicrophoneMute(on, getContext().getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), getContext().getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4257,7 +4414,9 @@ public class AudioManager {
afr.getFocusGain(), mICallBack,
mAudioFocusDispatcher,
clientId,
- getContext().getOpPackageName() /* package name */, afr.getFlags(),
+ getContext().getOpPackageName() /* package name */,
+ getContext().getAttributionTag(),
+ afr.getFlags(),
ap != null ? ap.cb() : null,
sdk);
} catch (RemoteException e) {
@@ -4362,6 +4521,7 @@ public class AudioManager {
durationHint, mICallBack, null,
AudioSystem.IN_VOICE_COMM_FOCUS_ID,
getContext().getOpPackageName(),
+ getContext().getAttributionTag(),
AUDIOFOCUS_FLAG_LOCK,
null /* policy token */, 0 /* sdk n/a here*/);
} catch (RemoteException e) {
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 69d1889d5762..5bd6891cb035 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2058,7 +2058,8 @@ public class AudioSystem
};
/** @hide */
- public static String streamToString(int stream) {
+ @TestApi
+ public static @NonNull String streamToString(int stream) {
if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
return "UNKNOWN_STREAM_" + stream;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 23d9532e11a0..8822aeac80e4 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2082,8 +2082,9 @@ public class AudioTrack extends PlayerBase
* It may also be adjusted slightly for internal reasons.
* If bufferSizeInFrames is less than zero then {@link #ERROR_BAD_VALUE}
* will be returned.
- * <p>This method is only supported for PCM audio.
- * It is not supported for compressed audio tracks.
+ * <p>This method is supported for PCM audio at all API levels.
+ * Compressed audio is supported in API levels 33 and above.
+ * For compressed streams the size of a frame is considered to be exactly one byte.
*
* @param bufferSizeInFrames requested buffer size in frames
* @return the actual buffer size in frames or an error code,
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 37054b8383b1..115fb741a543 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -82,7 +82,7 @@ import java.util.zip.CRC32;
* Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF,
* AVIF.
* <p>
- * Supported for writing: JPEG, PNG, WebP.
+ * Supported for writing: JPEG, PNG, WebP, DNG.
* <p>
* Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
* it. This class will search both locations for XMP data, but if XMP data exist both inside and
@@ -1294,6 +1294,7 @@ public class ExifInterface {
new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
+ new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
@@ -1361,12 +1362,6 @@ public class ExifInterface {
new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
};
- // Tags for indicating the thumbnail offset and length
- private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
- new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
- private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
- new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
-
// Mappings from tag number to tag name and each item represents one IFD tag group.
private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
// Mappings from tag name to tag number and each item represents one IFD tag group.
@@ -2081,7 +2076,7 @@ public class ExifInterface {
* {@link #setAttribute(String,String)} to set all attributes to write and
* make a single call rather than multiple calls for each attribute.
* <p>
- * This method is supported for JPEG, PNG and WebP files.
+ * This method is supported for JPEG, PNG, WebP, and DNG files.
* <p class="note">
* Note: after calling this method, any attempts to obtain range information
* from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
@@ -2094,13 +2089,17 @@ public class ExifInterface {
*/
public void saveAttributes() throws IOException {
if (!isSupportedFormatForSavingAttributes()) {
- throw new IOException("ExifInterface only supports saving attributes on JPEG, PNG, "
- + "or WebP formats.");
+ throw new IOException("ExifInterface only supports saving attributes for JPEG, PNG, "
+ + "WebP, and DNG formats.");
}
if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
throw new IOException(
"ExifInterface does not support saving attributes for the current input.");
}
+ if (mHasThumbnail && mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
+ throw new IOException("ExifInterface does not support saving attributes when the image "
+ + "file has non-consecutive thumbnail strips");
+ }
// Remember the fact that we've changed the file on disk from what was
// originally parsed, meaning we can't answer range questions
@@ -2149,6 +2148,10 @@ public class ExifInterface {
savePngAttributes(bufferedIn, bufferedOut);
} else if (mMimeType == IMAGE_TYPE_WEBP) {
saveWebpAttributes(bufferedIn, bufferedOut);
+ } else if (mMimeType == IMAGE_TYPE_DNG || mMimeType == IMAGE_TYPE_UNKNOWN) {
+ ByteOrderedDataOutputStream dataOutputStream =
+ new ByteOrderedDataOutputStream(bufferedOut, ByteOrder.BIG_ENDIAN);
+ writeExifSegment(dataOutputStream);
}
}
} catch (Exception e) {
@@ -2288,9 +2291,9 @@ public class ExifInterface {
}
ExifAttribute imageLengthAttribute =
- (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
+ (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_LENGTH);
ExifAttribute imageWidthAttribute =
- (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
+ (ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_WIDTH);
if (imageLengthAttribute != null && imageWidthAttribute != null) {
int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
@@ -2934,10 +2937,12 @@ public class ExifInterface {
if (in.skipBytes(1) != 1) {
throw new IOException("Invalid SOFx");
}
- mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
- in.readUnsignedShort(), mExifByteOrder));
- mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
- in.readUnsignedShort(), mExifByteOrder));
+ mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
+ ? TAG_IMAGE_LENGTH : TAG_THUMBNAIL_IMAGE_LENGTH,
+ ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
+ mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
+ ? TAG_IMAGE_WIDTH : TAG_THUMBNAIL_IMAGE_WIDTH,
+ ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
length -= 5;
break;
}
@@ -4508,6 +4513,17 @@ public class ExifInterface {
if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
}
+
+ // TAG_THUMBNAIL_* tags should be replaced with TAG_* equivalents and vice versa if needed.
+ replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
+ replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
+ replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
+ replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
+ replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
+ replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
+ replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_ORIENTATION, TAG_THUMBNAIL_ORIENTATION);
+ replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_LENGTH, TAG_THUMBNAIL_IMAGE_LENGTH);
+ replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_WIDTH, TAG_THUMBNAIL_IMAGE_WIDTH);
}
/**
@@ -4588,8 +4604,15 @@ public class ExifInterface {
removeAttribute(tag.name);
}
// Remove old thumbnail data
- removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
- removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
+ if (mHasThumbnail) {
+ if (mHasThumbnailStrips) {
+ removeAttribute(TAG_STRIP_OFFSETS);
+ removeAttribute(TAG_STRIP_BYTE_COUNTS);
+ } else {
+ removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT);
+ removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
+ }
+ }
// Remove null value tags.
for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
@@ -4616,10 +4639,17 @@ public class ExifInterface {
ExifAttribute.createULong(0, mExifByteOrder));
}
if (mHasThumbnail) {
- mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
- ExifAttribute.createULong(0, mExifByteOrder));
- mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
- ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
+ if (mHasThumbnailStrips) {
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
+ ExifAttribute.createUShort(0, mExifByteOrder));
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_BYTE_COUNTS,
+ ExifAttribute.createUShort(mThumbnailLength, mExifByteOrder));
+ } else {
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
+ ExifAttribute.createULong(0, mExifByteOrder));
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+ ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
+ }
}
// Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
@@ -4648,8 +4678,13 @@ public class ExifInterface {
}
if (mHasThumbnail) {
int thumbnailOffset = position;
- mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
- ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
+ if (mHasThumbnailStrips) {
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
+ ExifAttribute.createUShort(thumbnailOffset, mExifByteOrder));
+ } else {
+ mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
+ ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
+ }
// Need to add mExifOffset, which is the offset to the EXIF data segment
mThumbnailOffset = thumbnailOffset + mExifOffset;
position += mThumbnailLength;
@@ -5213,9 +5248,20 @@ public class ExifInterface {
}
}
+ private void replaceInvalidTags(@IfdType int ifdType, String invalidTag, String validTag) {
+ if (!mAttributes[ifdType].isEmpty()) {
+ if (mAttributes[ifdType].get(invalidTag) != null) {
+ mAttributes[ifdType].put(validTag,
+ mAttributes[ifdType].get(invalidTag));
+ mAttributes[ifdType].remove(invalidTag);
+ }
+ }
+ }
+
private boolean isSupportedFormatForSavingAttributes() {
if (mIsSupportedFile && (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_PNG
- || mMimeType == IMAGE_TYPE_WEBP)) {
+ || mMimeType == IMAGE_TYPE_WEBP || mMimeType == IMAGE_TYPE_DNG
+ || mMimeType == IMAGE_TYPE_UNKNOWN)) {
return true;
}
return false;
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 0b35ebed966a..28b4143b8718 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
+import android.media.AudioFormat;
import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
@@ -34,6 +35,7 @@ import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IStrategyPreferredDevicesDispatcher;
+import android.media.ISpatializerCallback;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.PlayerBase;
@@ -77,15 +79,17 @@ interface IAudioService {
oneway void playerSessionId(in int piid, in int sessionId);
// Java-only methods below.
-
- oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
- String callingPackage, String caller);
-
void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
+ void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+ in String callingPackage, in String attributionTag);
+
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void setStreamVolume(int streamType, int index, int flags, String callingPackage);
+ void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+ in String callingPackage, in String attributionTag);
+
oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
String callingPackage, String caller);
@@ -95,7 +99,8 @@ interface IAudioService {
boolean isMasterMute();
- void setMasterMute(boolean mute, int flags, String callingPackage, int userId);
+ void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+ in String attributionTag);
@UnsupportedAppUsage
int getStreamVolume(int streamType);
@@ -107,7 +112,8 @@ interface IAudioService {
List<AudioVolumeGroup> getAudioVolumeGroups();
- void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags, String callingPackage);
+ void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags,
+ String callingPackage, in String attributionTag);
int getVolumeIndexForAttributes(in AudioAttributes aa);
@@ -125,7 +131,7 @@ interface IAudioService {
boolean isMicrophoneMuted();
- void setMicrophoneMute(boolean on, String callingPackage, int userId);
+ void setMicrophoneMute(boolean on, String callingPackage, int userId, in String attributionTag);
oneway void setMicrophoneMuteFromSwitch(boolean on);
@@ -186,8 +192,8 @@ interface IAudioService {
boolean isBluetoothA2dpOn();
int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
- IAudioPolicyCallback pcb, int sdk);
+ IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
+ in String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk);
int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa,
in String callingPackageName);
@@ -392,4 +398,24 @@ interface IAudioService {
void registerModeDispatcher(IAudioModeDispatcher dispatcher);
oneway void unregisterModeDispatcher(IAudioModeDispatcher dispatcher);
+
+ int getSpatializerImmersiveAudioLevel();
+
+ boolean isSpatializerEnabled();
+
+ void setSpatializerFeatureEnabled(boolean enabled);
+
+ void setSpatializerEnabledForDevice(boolean enabled, in AudioDeviceAttributes device);
+
+ boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
+
+ void registerSpatializerCallback(in ISpatializerCallback callback);
+
+ void unregisterSpatializerCallback(in ISpatializerCallback callback);
+
+ List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices();
+
+ void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
+
+ void removeSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
}
diff --git a/media/java/android/media/ISpatializerCallback.aidl b/media/java/android/media/ISpatializerCallback.aidl
new file mode 100644
index 000000000000..50c5a6b8338f
--- /dev/null
+++ b/media/java/android/media/ISpatializerCallback.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.media;
+
+/**
+ * AIDL for the AudioService to signal Spatializer state changes.
+ *
+ * {@hide}
+ */
+oneway interface ISpatializerCallback {
+
+ void dispatchSpatializerStateChanged(boolean enabled);
+
+}
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 1616c03112a8..bac44adbe101 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -399,7 +399,7 @@ public abstract class Image implements AutoCloseable {
* <p>The number and meaning of the planes in an Image are determined by the
* format of the Image.</p>
*
- * <p>Once the Image has been closed, any access to the the plane's
+ * <p>Once the Image has been closed, any access to the plane's
* ByteBuffer will fail.</p>
*
* @see #getFormat
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8b9153621165..85baa3d73a32 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1726,7 +1726,6 @@ final public class MediaCodec {
private static final int CB_ERROR = 3;
private static final int CB_OUTPUT_FORMAT_CHANGE = 4;
-
private class EventHandler extends Handler {
private MediaCodec mCodec;
diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java
index a46095484fe7..5d7a8b312b9b 100644
--- a/media/java/android/media/MediaCodecList.java
+++ b/media/java/android/media/MediaCodecList.java
@@ -18,7 +18,6 @@ package android.media;
import android.util.Log;
-import android.media.MediaCodecInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
@@ -166,7 +165,8 @@ final public class MediaCodecList {
/**
* Create a list of media-codecs of a specific kind.
- * @param kind Either {@code REGULAR_CODECS} or {@code ALL_CODECS}.
+ * @param kind Either {@link MediaCodecList#REGULAR_CODECS} or
+ * {@link MediaCodecList#ALL_CODECS}.
*/
public MediaCodecList(int kind) {
initCodecList();
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 3a5216e1c4e7..6eb1af8ac980 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -53,6 +53,7 @@ public class MediaMetrics {
public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume";
public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event";
public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode";
+ public static final String METRICS_MANAGER = "metrics" + SEPARATOR + "manager";
}
/**
@@ -120,10 +121,11 @@ public class MediaMetrics {
createKey("gainDb", Double.class);
public static final Key<String> GROUP =
createKey("group", String.class);
- // For volume
- public static final Key<Integer> INDEX = createKey("index", Integer.class);
- public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class);
- public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class);
+
+ public static final Key<Integer> INDEX = createKey("index", Integer.class); // volume
+ public static final Key<String> LOG_SESSION_ID = createKey("logSessionId", String.class);
+ public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class); // vol
+ public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class); // vol
public static final Key<String> MODE =
createKey("mode", String.class); // audio_mode
public static final Key<String> MUTE =
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 26eb2a9dc109..83bc38b21717 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,7 +19,6 @@ package android.media;
import static android.Manifest.permission.BIND_IMS_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -4311,7 +4310,7 @@ public class MediaPlayer extends PlayerBase
@RequiresPermission(BIND_IMS_SERVICE)
public void setOnRtpRxNoticeListener(
@NonNull Context context,
- @NonNull @CallbackExecutor Executor executor,
+ @NonNull Executor executor,
@NonNull OnRtpRxNoticeListener listener) {
Objects.requireNonNull(context);
Preconditions.checkArgument(
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 7d80e931c943..77c1e55b08cb 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -171,8 +171,12 @@ public class MediaRecorder implements AudioRouting,
}
/**
+ *
* Sets the {@link LogSessionId} for MediaRecorder.
*
+ * <p>The log session ID is a random 32-byte hexadecimal string that is used for monitoring the
+ * MediaRecorder performance.</p>
+ *
* @param id the global ID for monitoring the MediaRecorder performance
*/
public void setLogSessionId(@NonNull LogSessionId id) {
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 345d9b27c8a8..4de63f980ca5 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2034,8 +2034,8 @@ public class MediaRouter {
public void requestSetVolume(int volume) {
if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
try {
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
- ActivityThread.currentPackageName());
+ sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName(), null);
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
@@ -2053,8 +2053,8 @@ public class MediaRouter {
try {
final int volume =
Math.max(0, Math.min(getVolume() + direction, getVolumeMax()));
- sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0,
- ActivityThread.currentPackageName());
+ sStatic.mAudioService.setStreamVolumeWithAttribution(mPlaybackStream, volume, 0,
+ ActivityThread.currentPackageName(), null);
} catch (RemoteException e) {
Log.e(TAG, "Error setting local stream volume", e);
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 4ec79b7e085a..3e7b8860c818 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -895,7 +895,7 @@ public class RingtoneManager {
throw new IOException("External storage is not mounted. Unable to install ringtones.");
}
- // Sanity-check: are we actually being asked to install an audio file?
+ // Consistency-check: are we actually being asked to install an audio file?
final String mimeType = mContext.getContentResolver().getType(fileUri);
if(mimeType == null ||
!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) {
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
new file mode 100644
index 000000000000..a2267cc51c83
--- /dev/null
+++ b/media/java/android/media/Spatializer.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.SafeCloseable;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Spatializer provides access to querying capabilities and behavior of sound spatialization
+ * on the device.
+ * Sound spatialization simulates sounds originating around the listener as if they were coming
+ * from virtual speakers placed around the listener.<br>
+ * Support for spatialization is optional, use {@link AudioManager#getSpatializer()} to obtain an
+ * instance of this class if the feature is supported.
+ *
+ */
+public class Spatializer {
+
+ private final @NonNull AudioManager mAm;
+
+ private final Object mStateListenerLock = new Object();
+
+ /**
+ * List of listeners for state listener and their associated Executor.
+ * List is lazy-initialized on first registration
+ */
+ @GuardedBy("mStateListenerLock")
+ private @Nullable ArrayList<StateListenerInfo> mStateListeners;
+
+ @GuardedBy("mStateListenerLock")
+ private SpatializerInfoDispatcherStub mInfoDispatcherStub;
+
+ /**
+ * @hide
+ * Constructor with AudioManager acting as proxy to AudioService
+ * @param am a non-null AudioManager
+ */
+ protected Spatializer(@NonNull AudioManager am) {
+ mAm = Objects.requireNonNull(am);
+ }
+
+ /**
+ * Returns whether spatialization is enabled or not.
+ * A false value can originate from a number of sources, examples are the user electing to
+ * disable the feature, or the use of an audio device that is not compatible with multichannel
+ * audio spatialization (for instance playing audio over a monophonic speaker).
+ * @return {@code true} if spatialization is enabled
+ */
+ public boolean isEnabled() {
+ return mAm.isSpatializerEnabled();
+ }
+
+ /** @hide */
+ @IntDef(flag = false, value = {
+ SPATIALIZER_IMMERSIVE_LEVEL_NONE,
+ SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImmersiveAudioLevel {};
+
+ /**
+ * @hide
+ * Constant indicating there are no spatialization capabilities supported on this device.
+ * @see AudioManager#getImmersiveAudioLevel()
+ */
+ public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0;
+
+ /**
+ * @hide
+ * Constant indicating the {@link Spatializer} on this device supports multichannel
+ * spatialization.
+ * @see AudioManager#getImmersiveAudioLevel()
+ */
+ public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
+
+
+ /**
+ * @hide
+ * @param enabled
+ * @param device
+ */
+ //TODO make as API if needed for UX, remove otherwise
+ //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setEnabledForDevice(boolean enabled,
+ @NonNull AudioDeviceAttributes device) {
+ Objects.requireNonNull(device);
+ mAm.setSpatializerEnabledForDevice(enabled, device);
+ }
+
+ /**
+ * @hide
+ * Enables / disables the spatializer effect
+ * @param enabled {@code true} for enabling the effect
+ */
+ //TODO make as API if needed for UX, remove otherwise
+ //@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setEnabled(boolean enabled) {
+ mAm.setSpatializerFeatureEnabled(enabled);
+ }
+
+ /**
+ * An interface to be notified of changes to the state of the spatializer.
+ */
+ public interface OnSpatializerEnabledChangedListener {
+ /**
+ * Called when the enabled state of the Spatializer changes
+ * @param enabled {@code true} if the Spatializer effect is enabled on the device,
+ * {@code false} otherwise
+ */
+ void onSpatializerEnabledChanged(boolean enabled);
+ }
+
+ /**
+ * Returns whether audio of the given {@link AudioFormat}, played with the given
+ * {@link AudioAttributes} can be spatialized.
+ * Note that the result reflects the capabilities of the device and may change when
+ * audio accessories are connected/disconnected (e.g. wired headphones plugged in or not).
+ * The result is independent from whether spatialization processing is enabled or not.
+ * @param attributes the {@code AudioAttributes} of the content as used for playback
+ * @param format the {@code AudioFormat} of the content as used for playback
+ * @return true if the device is capable of spatializing the combination of audio format and
+ * attributes.
+ */
+ public boolean canBeSpatialized(
+ @NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
+ return mAm.canBeSpatialized(
+ Objects.requireNonNull(attributes), Objects.requireNonNull(format));
+ }
+
+ /**
+ * Adds a listener to be notified of changes to the enabled state of the
+ * {@code Spatializer}.
+ * @see #isEnabled()
+ * @param executor the {@code Executor} handling the callback
+ * @param listener the listener to receive enabled state updates
+ */
+ public void addOnSpatializerEnabledChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnSpatializerEnabledChangedListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mStateListenerLock) {
+ if (hasSpatializerStateListener(listener)) {
+ throw new IllegalArgumentException(
+ "Called addOnSpatializerEnabledChangedListener() "
+ + "on a previously registered listener");
+ }
+ // lazy initialization of the list of strategy-preferred device listener
+ if (mStateListeners == null) {
+ mStateListeners = new ArrayList<>();
+ }
+ mStateListeners.add(new StateListenerInfo(listener, executor));
+ if (mStateListeners.size() == 1) {
+ // register binder for callbacks
+ if (mInfoDispatcherStub == null) {
+ mInfoDispatcherStub =
+ new SpatializerInfoDispatcherStub();
+ }
+ try {
+ mAm.getService().registerSpatializerCallback(
+ mInfoDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes a previously added listener for changes to the enabled state of the
+ * {@code Spatializer}.
+ * @see #isEnabled()
+ * @param listener the listener to receive enabled state updates
+ */
+ public void removeOnSpatializerEnabledChangedListener(
+ @NonNull OnSpatializerEnabledChangedListener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mStateListenerLock) {
+ if (!removeStateListener(listener)) {
+ throw new IllegalArgumentException(
+ "Called removeOnSpatializerEnabledChangedListener() "
+ + "on an unregistered listener");
+ }
+ if (mStateListeners.size() == 0) {
+ // unregister binder for callbacks
+ try {
+ mAm.getService().unregisterSpatializerCallback(mInfoDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ mInfoDispatcherStub = null;
+ mStateListeners = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the list of playback devices that are compatible with the playback of multichannel
+ * audio through virtualization
+ * @return a list of devices. An empty list indicates virtualization is not supported.
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
+ return mAm.getSpatializerCompatibleAudioDevices();
+ }
+
+ /**
+ * @hide
+ * Adds a playback device to the list of devices compatible with the playback of multichannel
+ * audio through spatialization.
+ * @see #getCompatibleAudioDevices()
+ * @param ada the audio device compatible with spatialization
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ mAm.addSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ }
+
+ /**
+ * @hide
+ * Remove a playback device from the list of devices compatible with the playback of
+ * multichannel audio through spatialization.
+ * @see #getCompatibleAudioDevices()
+ * @param ada the audio device incompatible with spatialization
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ mAm.removeSpatializerCompatibleAudioDevice(Objects.requireNonNull(ada));
+ }
+
+ private final class SpatializerInfoDispatcherStub
+ extends ISpatializerCallback.Stub {
+ @Override
+ public void dispatchSpatializerStateChanged(boolean enabled) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<StateListenerInfo> stateListeners;
+ synchronized (mStateListenerLock) {
+ if (mStateListeners == null || mStateListeners.size() == 0) {
+ return;
+ }
+ stateListeners = (ArrayList<StateListenerInfo>) mStateListeners.clone();
+ }
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ for (StateListenerInfo info : stateListeners) {
+ info.mExecutor.execute(() ->
+ info.mListener.onSpatializerEnabledChanged(enabled));
+ }
+ }
+ }
+ }
+
+ private static class StateListenerInfo {
+ final @NonNull OnSpatializerEnabledChangedListener mListener;
+ final @NonNull Executor mExecutor;
+
+ StateListenerInfo(@NonNull OnSpatializerEnabledChangedListener listener,
+ @NonNull Executor exe) {
+ mListener = listener;
+ mExecutor = exe;
+ }
+ }
+
+ @GuardedBy("mStateListenerLock")
+ private boolean hasSpatializerStateListener(OnSpatializerEnabledChangedListener listener) {
+ return getStateListenerInfo(listener) != null;
+ }
+
+ @GuardedBy("mStateListenerLock")
+ private @Nullable StateListenerInfo getStateListenerInfo(
+ OnSpatializerEnabledChangedListener listener) {
+ if (mStateListeners == null) {
+ return null;
+ }
+ for (StateListenerInfo info : mStateListeners) {
+ if (info.mListener == listener) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mStateListenerLock")
+ /**
+ * @return true if the listener was removed from the list
+ */
+ private boolean removeStateListener(OnSpatializerEnabledChangedListener listener) {
+ final StateListenerInfo infoToRemove = getStateListenerInfo(listener);
+ if (infoToRemove != null) {
+ mStateListeners.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index 0c733482b2b5..8080aad6dcc2 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -422,6 +422,20 @@ public class AudioMix {
rate = 44100;
}
mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
+ } else {
+ // Ensure that 'mFormat' uses an output channel mask. Using an input channel
+ // mask was not made 'illegal' initially, however the framework code
+ // assumes usage in AudioMixes of output channel masks only (b/194910301).
+ if ((mFormat.getPropertySetMask()
+ & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {
+ if (mFormat.getChannelCount() == 1
+ && mFormat.getChannelMask() == AudioFormat.CHANNEL_IN_MONO) {
+ mFormat = new AudioFormat.Builder(mFormat).setChannelMask(
+ AudioFormat.CHANNEL_OUT_MONO).build();
+ }
+ // CHANNEL_IN_STEREO == CHANNEL_OUT_STEREO so no need to correct.
+ // CHANNEL_IN_FRONT_BACK is hidden, should not appear.
+ }
}
if ((mDeviceSystemType != AudioSystem.DEVICE_NONE)
&& (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index abbcc669504d..fa3dc7211dea 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -16,6 +16,9 @@
package android.media.audiopolicy;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -24,6 +27,7 @@ import android.os.Build;
import android.os.Parcel;
import android.util.Log;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
@@ -199,7 +203,36 @@ public class AudioMixingRule {
}
private final int mTargetMixType;
- int getTargetMixType() { return mTargetMixType; }
+ int getTargetMixType() {
+ return mTargetMixType;
+ }
+
+ /**
+ * Captures an audio signal from one or more playback streams.
+ */
+ public static final int MIX_ROLE_PLAYERS = AudioMix.MIX_TYPE_PLAYERS;
+ /**
+ * Injects an audio signal into the framework to replace a recording source.
+ */
+ public static final int MIX_ROLE_INJECTOR = AudioMix.MIX_TYPE_RECORDERS;
+
+ /** @hide */
+ @IntDef({MIX_ROLE_PLAYERS, MIX_ROLE_INJECTOR})
+ @Retention(SOURCE)
+ public @interface MixRole {}
+
+ /**
+ * Gets target mix role of this mixing rule.
+ *
+ * <p>The mix role indicates playback streams will be captured or recording source will be
+ * injected.
+ *
+ * @return integer value of {@link #MIX_ROLE_PLAYERS} or {@link #MIX_ROLE_INJECTOR}
+ */
+ public @MixRole int getTargetMixRole() {
+ return mTargetMixType == AudioMix.MIX_TYPE_RECORDERS ? MIX_ROLE_INJECTOR : MIX_ROLE_PLAYERS;
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private final ArrayList<AudioMixMatchCriterion> mCriteria;
/** @hide */
@@ -469,19 +502,23 @@ public class AudioMixingRule {
}
/**
- * Set target mix type of the mixing rule.
+ * Sets target mix role of the mixing rule.
*
- * <p>Note: If the mix type was not specified, it will be decided automatically by mixing
- * rule. For {@link #RULE_MATCH_UID}, the default type is {@link AudioMix#MIX_TYPE_PLAYERS}.
+ * <p>The mix role indicates playback streams will be captured or recording source will be
+ * injected. If not specified, the mix role will be decided automatically when
+ * {@link #addRule(AudioAttributes, int)} or {@link #addMixRule(int, Object)} be called.
*
- * @param mixType {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS}
+ * @param mixRole integer value of {@link #MIX_ROLE_PLAYERS} or {@link #MIX_ROLE_INJECTOR}
* @return the same Builder instance.
- *
- * @hide
*/
- public @NonNull Builder setTargetMixType(int mixType) {
- mTargetMixType = mixType;
- Log.i("AudioMixingRule", "Builder setTargetMixType " + mixType);
+ public @NonNull Builder setTargetMixRole(@MixRole int mixRole) {
+ if (mixRole != MIX_ROLE_PLAYERS && mixRole != MIX_ROLE_INJECTOR) {
+ throw new IllegalArgumentException("Illegal argument for mix role");
+ }
+
+ Log.i("AudioMixingRule", "Builder setTargetMixRole " + mixRole);
+ mTargetMixType = mixRole == MIX_ROLE_INJECTOR
+ ? AudioMix.MIX_TYPE_RECORDERS : AudioMix.MIX_TYPE_PLAYERS;
return this;
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index 346edc369246..f3731b68f787 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -101,7 +101,7 @@ public class AudioPolicyConfig implements Parcelable {
// write voice communication capture allowed flag
dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());
// write specified mix type
- dest.writeInt(mix.getRule().getTargetMixType());
+ dest.writeInt(mix.getRule().getTargetMixRole());
// write mix rules
final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
dest.writeInt(criteria.size());
@@ -137,7 +137,7 @@ public class AudioPolicyConfig implements Parcelable {
// read voice capture allowed flag
ruleBuilder.voiceCommunicationCaptureAllowed(in.readBoolean());
// read specified mix type
- ruleBuilder.setTargetMixType(in.readInt());
+ ruleBuilder.setTargetMixRole(in.readInt());
// read mix rules
int nbRules = in.readInt();
for (int j = 0 ; j < nbRules ; j++) {
@@ -181,7 +181,7 @@ public class AudioPolicyConfig implements Parcelable {
+ mix.getRule().voiceCommunicationCaptureAllowed() + "\n";
// write mix rules
textDump += " specified mix type="
- + mix.getRule().getTargetMixType() + "\n";
+ + mix.getRule().getTargetMixRole() + "\n";
final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
for (AudioMixMatchCriterion criterion : criteria) {
switch(criterion.mRule) {
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index fca3498838ad..31d596765bcc 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -19,6 +19,7 @@ package android.media.audiopolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
import android.media.MediaRecorder;
@@ -240,6 +241,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the legacy stream type relevant for the given {@link AudioAttributes}.
* If none is found, it return DEFAULT stream type.
*/
+ @TestApi
public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -273,6 +275,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the volume group id relevant for the given streamType.
* If none is found, {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} is returned.
*/
+ @TestApi
public int getVolumeGroupIdForLegacyStreamType(int streamType) {
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
if (aag.supportsStreamType(streamType)) {
@@ -288,6 +291,7 @@ public final class AudioProductStrategy implements Parcelable {
* @return the volume group id associated with the given audio attributes if found,
* {@link AudioVolumeGroup#DEFAULT_VOLUME_GROUP} otherwise.
*/
+ @TestApi
public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) {
Preconditions.checkNotNull(aa, "AudioAttributes must not be null");
for (final AudioAttributesGroup aag : mAudioAttributesGroups) {
@@ -352,11 +356,19 @@ public final class AudioProductStrategy implements Parcelable {
* @hide
* Default attributes, with default source to be aligned with native.
*/
- public static final @NonNull AudioAttributes sDefaultAttributes =
+ private static final @NonNull AudioAttributes DEFAULT_ATTRIBUTES =
new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
.build();
/**
+ * @hide
+ */
+ @TestApi
+ public static @NonNull AudioAttributes getDefaultAttributes() {
+ return DEFAULT_ATTRIBUTES;
+ }
+
+ /**
* To avoid duplicating the logic in java and native, we shall make use of
* native API native_get_product_strategies_from_audio_attributes
* Keep in sync with frameworks/av/media/libaudioclient/AudioProductStrategy::attributesMatches
@@ -369,7 +381,7 @@ public final class AudioProductStrategy implements Parcelable {
Preconditions.checkNotNull(attr, "attr must not be null");
String refFormattedTags = TextUtils.join(";", refAttr.getTags());
String cliFormattedTags = TextUtils.join(";", attr.getTags());
- if (refAttr.equals(sDefaultAttributes)) {
+ if (refAttr.equals(DEFAULT_ATTRIBUTES)) {
return false;
}
return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 37e141537c79..72cddc91f436 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -16,14 +16,14 @@
package android.media.projection;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
-import android.media.projection.IMediaProjection;
-import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -106,7 +106,7 @@ public final class MediaProjection {
if (isSecure) {
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
}
- final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
height, dpi);
builder.setFlags(flags);
if (surface != null) {
@@ -141,7 +141,7 @@ public final class MediaProjection {
public VirtualDisplay createVirtualDisplay(@NonNull String name,
int width, int height, int dpi, int flags, @Nullable Surface surface,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
- final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
height, dpi);
builder.setFlags(flags);
if (surface != null) {
@@ -151,6 +151,26 @@ public final class MediaProjection {
}
/**
+ * Constructs a {@link VirtualDisplayConfig.Builder}, which will mirror the contents of a
+ * DisplayArea. The DisplayArea to mirror is from the DisplayArea the caller is launched on.
+ *
+ * @param name The name of the virtual display, must be non-empty.
+ * @param width The width of the virtual display in pixels. Must be greater than 0.
+ * @param height The height of the virtual display in pixels. Must be greater than 0.
+ * @param dpi The density of the virtual display in dpi. Must be greater than 0.
+ * @return a config representing a VirtualDisplay
+ */
+ private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
+ int width, int height, int dpi) {
+ Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+ TYPE_APPLICATION, null /* options */);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, dpi);
+ builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
+ return builder;
+ }
+
+ /**
* Creates a {@link android.hardware.display.VirtualDisplay} to capture the
* contents of the screen.
*
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 3b0f577d84cb..207ccbee0b50 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -39,8 +39,8 @@ interface ISessionManager {
ISession createSession(String packageName, in ISessionCallback sessionCb, String tag,
in Bundle sessionInfo, int userId);
List<MediaSession.Token> getSessions(in ComponentName compName, int userId);
- MediaSession.Token getMediaKeyEventSession();
- String getMediaKeyEventSessionPackageName();
+ MediaSession.Token getMediaKeyEventSession(String packageName);
+ String getMediaKeyEventSessionPackageName(String packageName);
void dispatchMediaKeyEvent(String packageName, boolean asSystemService, in KeyEvent keyEvent,
boolean needWakeLock);
boolean dispatchMediaKeyEventToSessionAsSystemService(String packageName,
@@ -66,7 +66,8 @@ interface ISessionManager {
void addOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener);
void removeOnMediaKeyEventDispatchedListener(in IOnMediaKeyEventDispatchedListener listener);
void addOnMediaKeyEventSessionChangedListener(
- in IOnMediaKeyEventSessionChangedListener listener);
+ in IOnMediaKeyEventSessionChangedListener listener,
+ String packageName);
void removeOnMediaKeyEventSessionChangedListener(
in IOnMediaKeyEventSessionChangedListener listener);
void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 269b70b667a0..6a50a98018d0 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -196,16 +196,19 @@ public final class MediaSessionManager {
/**
* Gets the media key event session, which would receive a media key event unless specified.
+ * <p>
+ * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL}
+ * permission be held by the calling app, or the app has an enabled notification listener
+ * using the {@link NotificationListenerService} APIs. If none of them applies, it will throw
+ * a {@link SecurityException}.
+ *
* @return The media key event session, which would receive key events by default, unless
* the caller has specified the target. Can be {@code null}.
- * @hide
*/
- @SystemApi
- @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
@Nullable
public MediaSession.Token getMediaKeyEventSession() {
try {
- return mService.getMediaKeyEventSession();
+ return mService.getMediaKeyEventSession(mContext.getPackageName());
} catch (RemoteException ex) {
Log.e(TAG, "Failed to get media key event session", ex);
}
@@ -214,20 +217,25 @@ public final class MediaSessionManager {
/**
* Gets the package name of the media key event session.
+ * <p>
+ * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL}
+ * permission be held by the calling app, or the app has an enabled notification listener
+ * using the {@link NotificationListenerService} APIs. If none of them applies, it will throw
+ * a {@link SecurityException}.
+ *
* @return The package name of the media key event session or the last session's media button
- * receiver if the media key event session is {@code null}.
+ * receiver if the media key event session is {@code null}. Returns an empty string
+ * if neither of them exists.
* @see #getMediaKeyEventSession()
- * @hide
*/
- @SystemApi
- @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
@NonNull
public String getMediaKeyEventSessionPackageName() {
try {
- String packageName = mService.getMediaKeyEventSessionPackageName();
+ String packageName = mService.getMediaKeyEventSessionPackageName(
+ mContext.getPackageName());
return (packageName != null) ? packageName : "";
} catch (RemoteException ex) {
- Log.e(TAG, "Failed to get media key event session", ex);
+ Log.e(TAG, "Failed to get media key event session package name", ex);
}
return "";
}
@@ -894,14 +902,16 @@ public final class MediaSessionManager {
}
/**
- * Add a {@link OnMediaKeyEventSessionChangedListener}.
+ * Add a listener to be notified when the media key session is changed.
+ * <p>
+ * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL}
+ * permission be held by the calling app, or the app has an enabled notification listener
+ * using the {@link NotificationListenerService} APIs. If none of them applies, it will throw
+ * a {@link SecurityException}.
*
- * @param executor The executor on which the listener should be invoked
+ * @param executor The executor on which the listener should be invoked.
* @param listener A {@link OnMediaKeyEventSessionChangedListener}.
- * @hide
*/
- @SystemApi
- @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void addOnMediaKeyEventSessionChangedListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnMediaKeyEventSessionChangedListener listener) {
@@ -909,40 +919,37 @@ public final class MediaSessionManager {
Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
+ if (mMediaKeyEventSessionChangedCallbacks.isEmpty()) {
+ mService.addOnMediaKeyEventSessionChangedListener(
+ mOnMediaKeyEventSessionChangedListenerStub, mContext.getPackageName());
+ }
mMediaKeyEventSessionChangedCallbacks.put(listener, executor);
executor.execute(
() -> listener.onMediaKeyEventSessionChanged(
mCurMediaKeyEventSessionPackage, mCurMediaKeyEventSession));
- if (mMediaKeyEventSessionChangedCallbacks.size() == 1) {
- mService.addOnMediaKeyEventSessionChangedListener(
- mOnMediaKeyEventSessionChangedListenerStub);
- }
} catch (RemoteException e) {
- Log.e(TAG, "Failed to set media key listener", e);
+ Log.e(TAG, "Failed to add MediaKeyEventSessionChangedListener", e);
}
}
}
/**
- * Remove a {@link OnMediaKeyEventSessionChangedListener}.
+ * Stop receiving updates on media key event session change on the specified listener.
*
* @param listener A {@link OnMediaKeyEventSessionChangedListener}.
- * @hide
*/
- @SystemApi
- @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL)
public void removeOnMediaKeyEventSessionChangedListener(
@NonNull OnMediaKeyEventSessionChangedListener listener) {
Objects.requireNonNull(listener, "listener shouldn't be null");
synchronized (mLock) {
try {
- mMediaKeyEventSessionChangedCallbacks.remove(listener);
- if (mMediaKeyEventSessionChangedCallbacks.size() == 0) {
+ if (mMediaKeyEventSessionChangedCallbacks.remove(listener) != null
+ && mMediaKeyEventSessionChangedCallbacks.isEmpty()) {
mService.removeOnMediaKeyEventSessionChangedListener(
mOnMediaKeyEventSessionChangedListenerStub);
}
} catch (RemoteException e) {
- Log.e(TAG, "Failed to set media key listener", e);
+ Log.e(TAG, "Failed to remove MediaKeyEventSessionChangedListener", e);
}
}
}
@@ -1124,16 +1131,14 @@ public final class MediaSessionManager {
/**
* Listener to receive changes in the media key event session, which would receive a media key
* event unless specified.
- * @hide
*/
- @SystemApi
public interface OnMediaKeyEventSessionChangedListener {
/**
* Called when the media key session is changed to the given media session. The key event
* session is the media session which would receive key event by default, unless the caller
* has specified the target.
* <p>
- * The session token can be {@link null} if the media button session is unset. In that case,
+ * The session token can be {@code null} if the media button session is unset. In that case,
* packageName will return the package name of the last session's media button receiver, or
* an empty string if the last session didn't set a media button receiver.
*
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6b329f8ec694..b1baf94f844b 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -23,6 +23,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -39,6 +41,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.util.Xml;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -99,6 +102,7 @@ public class TvView extends ViewGroup {
private int mSurfaceWidth;
private int mSurfaceHeight;
private final AttributeSet mAttrs;
+ private final XmlResourceParser mParser;
private final int mDefStyleAttr;
private int mWindowZOrder;
private boolean mUseRequestedSurfaceLayout;
@@ -168,7 +172,16 @@ public class TvView extends ViewGroup {
public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mAttrs = attrs;
+ int sourceResId = Resources.getAttributeSetSourceResId(attrs);
+ if (sourceResId != Resources.ID_NULL) {
+ Log.d(TAG, "Build local AttributeSet");
+ mParser = context.getResources().getXml(sourceResId);
+ mAttrs = Xml.asAttributeSet(mParser);
+ } else {
+ Log.d(TAG, "Use passed in AttributeSet");
+ mParser = null;
+ mAttrs = attrs;
+ }
mDefStyleAttr = defStyleAttr;
resetSurfaceView();
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 8b69d33722c5..6a6a22c1fc0e 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -20,7 +20,10 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.LnbEventType;
+import android.hardware.tv.tuner.LnbPosition;
+import android.hardware.tv.tuner.LnbTone;
+import android.hardware.tv.tuner.LnbVoltage;
import android.media.tv.tuner.Tuner.Result;
import java.lang.annotation.Retention;
@@ -48,39 +51,39 @@ public class Lnb implements AutoCloseable {
/**
* LNB power voltage not set.
*/
- public static final int VOLTAGE_NONE = Constants.LnbVoltage.NONE;
+ public static final int VOLTAGE_NONE = LnbVoltage.NONE;
/**
* LNB power voltage 5V.
*/
- public static final int VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
+ public static final int VOLTAGE_5V = LnbVoltage.VOLTAGE_5V;
/**
* LNB power voltage 11V.
*/
- public static final int VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
+ public static final int VOLTAGE_11V = LnbVoltage.VOLTAGE_11V;
/**
* LNB power voltage 12V.
*/
- public static final int VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
+ public static final int VOLTAGE_12V = LnbVoltage.VOLTAGE_12V;
/**
* LNB power voltage 13V.
*/
- public static final int VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
+ public static final int VOLTAGE_13V = LnbVoltage.VOLTAGE_13V;
/**
* LNB power voltage 14V.
*/
- public static final int VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
+ public static final int VOLTAGE_14V = LnbVoltage.VOLTAGE_14V;
/**
* LNB power voltage 15V.
*/
- public static final int VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
+ public static final int VOLTAGE_15V = LnbVoltage.VOLTAGE_15V;
/**
* LNB power voltage 18V.
*/
- public static final int VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
+ public static final int VOLTAGE_18V = LnbVoltage.VOLTAGE_18V;
/**
* LNB power voltage 19V.
*/
- public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
+ public static final int VOLTAGE_19V = LnbVoltage.VOLTAGE_19V;
/** @hide */
@IntDef(prefix = "TONE_",
@@ -91,11 +94,11 @@ public class Lnb implements AutoCloseable {
/**
* LNB tone mode not set.
*/
- public static final int TONE_NONE = Constants.LnbTone.NONE;
+ public static final int TONE_NONE = LnbTone.NONE;
/**
* LNB continuous tone mode.
*/
- public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
+ public static final int TONE_CONTINUOUS = LnbTone.CONTINUOUS;
/** @hide */
@IntDef(prefix = "POSITION_",
@@ -106,15 +109,15 @@ public class Lnb implements AutoCloseable {
/**
* LNB position is not defined.
*/
- public static final int POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED;
+ public static final int POSITION_UNDEFINED = LnbPosition.UNDEFINED;
/**
* Position A of two-band LNBs
*/
- public static final int POSITION_A = Constants.LnbPosition.POSITION_A;
+ public static final int POSITION_A = LnbPosition.POSITION_A;
/**
* Position B of two-band LNBs
*/
- public static final int POSITION_B = Constants.LnbPosition.POSITION_B;
+ public static final int POSITION_B = LnbPosition.POSITION_B;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -126,22 +129,19 @@ public class Lnb implements AutoCloseable {
/**
* Outgoing Diseqc message overflow.
*/
- public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW =
- Constants.LnbEventType.DISEQC_RX_OVERFLOW;
+ public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW = LnbEventType.DISEQC_RX_OVERFLOW;
/**
* Outgoing Diseqc message isn't delivered on time.
*/
- public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT =
- Constants.LnbEventType.DISEQC_RX_TIMEOUT;
+ public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT = LnbEventType.DISEQC_RX_TIMEOUT;
/**
* Incoming Diseqc message has parity error.
*/
- public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR =
- Constants.LnbEventType.DISEQC_RX_PARITY_ERROR;
+ public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR = LnbEventType.DISEQC_RX_PARITY_ERROR;
/**
* LNB is overload.
*/
- public static final int EVENT_TYPE_LNB_OVERLOAD = Constants.LnbEventType.LNB_OVERLOAD;
+ public static final int EVENT_TYPE_LNB_OVERLOAD = LnbEventType.LNB_OVERLOAD;
private static final String TAG = "Lnb";
@@ -175,7 +175,13 @@ public class Lnb implements AutoCloseable {
private void onEvent(int eventType) {
synchronized (mCallbackLock) {
if (mExecutor != null && mCallback != null) {
- mExecutor.execute(() -> mCallback.onEvent(eventType));
+ mExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onEvent(eventType);
+ }
+ }
+ });
}
}
}
@@ -183,7 +189,13 @@ public class Lnb implements AutoCloseable {
private void onDiseqcMessage(byte[] diseqcMessage) {
synchronized (mCallbackLock) {
if (mExecutor != null && mCallback != null) {
- mExecutor.execute(() -> mCallback.onDiseqcMessage(diseqcMessage));
+ mExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onDiseqcMessage(diseqcMessage);
+ }
+ }
+ });
}
}
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 325436648e63..ed5e6eeca34f 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -25,7 +25,9 @@ import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.Constant;
+import android.hardware.tv.tuner.Constant64Bit;
+import android.hardware.tv.tuner.FrontendScanType;
import android.media.tv.TvInputService;
import android.media.tv.tuner.dvr.DvrPlayback;
import android.media.tv.tuner.dvr.DvrRecorder;
@@ -85,19 +87,19 @@ public class Tuner implements AutoCloseable {
/**
* Invalid TS packet ID.
*/
- public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+ public static final int INVALID_TS_PID = Constant.INVALID_TS_PID;
/**
* Invalid stream ID.
*/
- public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+ public static final int INVALID_STREAM_ID = Constant.INVALID_STREAM_ID;
/**
* Invalid filter ID.
*/
- public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+ public static final int INVALID_FILTER_ID = Constant.INVALID_FILTER_ID;
/**
* Invalid AV Sync ID.
*/
- public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
+ public static final int INVALID_AV_SYNC_ID = Constant.INVALID_AV_SYNC_ID;
/**
* Invalid timestamp.
*
@@ -113,7 +115,7 @@ public class Tuner implements AutoCloseable {
* @see android.media.tv.tuner.filter.MmtpRecordEvent#getPts()
*/
public static final long INVALID_TIMESTAMP =
- android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_PRESENTATION_TIME_STAMP;
+ Constant64Bit.INVALID_PRESENTATION_TIME_STAMP;
/**
* Invalid mpu sequence number in MmtpRecordEvent.
*
@@ -123,8 +125,7 @@ public class Tuner implements AutoCloseable {
* @see android.media.tv.tuner.filter.MmtpRecordEvent#getMpuSequenceNumber()
*/
public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM =
- android.hardware.tv.tuner.V1_1.Constants.Constant
- .INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
+ Constant.INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM;
/**
* Invalid first macroblock address in MmtpRecordEvent and TsRecordEvent.
*
@@ -135,7 +136,7 @@ public class Tuner implements AutoCloseable {
* @see android.media.tv.tuner.filter.TsRecordEvent#getMbInSlice()
*/
public static final int INVALID_FIRST_MACROBLOCK_IN_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FIRST_MACROBLOCK_IN_SLICE;
+ Constant.INVALID_FIRST_MACROBLOCK_IN_SLICE;
/**
* Invalid local transport stream id.
*
@@ -144,30 +145,26 @@ public class Tuner implements AutoCloseable {
*
* @see #linkFrontendToCiCam(int)
*/
- public static final int INVALID_LTS_ID =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LTS_ID;
+ public static final int INVALID_LTS_ID = Constant.INVALID_LTS_ID;
/**
* Invalid 64-bit filter ID.
*/
- public static final long INVALID_FILTER_ID_LONG =
- android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT;
+ public static final long INVALID_FILTER_ID_LONG = Constant64Bit.INVALID_FILTER_ID_64BIT;
/**
* Invalid frequency that is used as the default frontend frequency setting.
*/
public static final int INVALID_FRONTEND_SETTING_FREQUENCY =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_SETTING_FREQUENCY;
+ Constant.INVALID_FRONTEND_SETTING_FREQUENCY;
/**
* Invalid frontend id.
*/
- public static final int INVALID_FRONTEND_ID =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_ID;
+ public static final int INVALID_FRONTEND_ID = Constant.INVALID_FRONTEND_ID;
/**
* Invalid LNB id.
*
* @hide
*/
- public static final int INVALID_LNB_ID =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LNB_ID;
+ public static final int INVALID_LNB_ID = Constant.INVALID_LNB_ID;
/**
* A void key token. It is used to remove the current key from descrambler.
*
@@ -175,8 +172,7 @@ public class Tuner implements AutoCloseable {
* to use this constant to remove current key before closing MediaCas session.
*/
@NonNull
- public static final byte[] VOID_KEYTOKEN =
- {android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_KEYTOKEN};
+ public static final byte[] VOID_KEYTOKEN = {Constant.INVALID_KEYTOKEN};
/** @hide */
@IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
@@ -185,20 +181,20 @@ public class Tuner implements AutoCloseable {
/**
* Scan type undefined.
*/
- public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ public static final int SCAN_TYPE_UNDEFINED = FrontendScanType.SCAN_UNDEFINED;
/**
* Scan type auto.
*
* <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
*/
- public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ public static final int SCAN_TYPE_AUTO = FrontendScanType.SCAN_AUTO;
/**
* Blind scan.
*
* <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
* implementation specific range.
*/
- public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+ public static final int SCAN_TYPE_BLIND = FrontendScanType.SCAN_BLIND;
/** @hide */
@@ -210,31 +206,33 @@ public class Tuner implements AutoCloseable {
/**
* Operation succeeded.
*/
- public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+ public static final int RESULT_SUCCESS = android.hardware.tv.tuner.Result.SUCCESS;
/**
* Operation failed because the corresponding resources are not available.
*/
- public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+ public static final int RESULT_UNAVAILABLE = android.hardware.tv.tuner.Result.UNAVAILABLE;
/**
* Operation failed because the corresponding resources are not initialized.
*/
- public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+ public static final int RESULT_NOT_INITIALIZED =
+ android.hardware.tv.tuner.Result.NOT_INITIALIZED;
/**
* Operation failed because it's not in a valid state.
*/
- public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+ public static final int RESULT_INVALID_STATE = android.hardware.tv.tuner.Result.INVALID_STATE;
/**
* Operation failed because there are invalid arguments.
*/
- public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+ public static final int RESULT_INVALID_ARGUMENT =
+ android.hardware.tv.tuner.Result.INVALID_ARGUMENT;
/**
* Memory allocation failed.
*/
- public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+ public static final int RESULT_OUT_OF_MEMORY = android.hardware.tv.tuner.Result.OUT_OF_MEMORY;
/**
* Operation failed due to unknown errors.
*/
- public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
+ public static final int RESULT_UNKNOWN_ERROR = android.hardware.tv.tuner.Result.UNKNOWN_ERROR;
@@ -257,12 +255,12 @@ public class Tuner implements AutoCloseable {
* DVR for recording.
* @hide
*/
- public static final int DVR_TYPE_RECORD = Constants.DvrType.RECORD;
+ public static final int DVR_TYPE_RECORD = android.hardware.tv.tuner.DvrType.RECORD;
/**
* DVR for playback of recorded programs.
* @hide
*/
- public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
+ public static final int DVR_TYPE_PLAYBACK = android.hardware.tv.tuner.DvrType.PLAYBACK;
static {
try {
@@ -432,6 +430,7 @@ public class Tuner implements AutoCloseable {
mFrontend = tuner.mFrontend;
mIsSharedFrontend = true;
}
+ nativeShareFrontend(mFrontend.mId);
}
/**
@@ -551,6 +550,7 @@ public class Tuner implements AutoCloseable {
* Native method to open frontend of the given ID.
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
+ private native int nativeShareFrontend(int id);
@Result
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
@@ -629,8 +629,13 @@ public class Tuner implements AutoCloseable {
synchronized (mOnResourceLostListenerLock) {
if (mOnResourceLostListener != null
&& mOnResourceLostListenerExecutor != null) {
- mOnResourceLostListenerExecutor.execute(
- () -> mOnResourceLostListener.onResourceLost(Tuner.this));
+ mOnResourceLostListenerExecutor.execute(() -> {
+ synchronized (mOnResourceLostListenerLock) {
+ if (mOnResourceLostListener != null) {
+ mOnResourceLostListener.onResourceLost(Tuner.this);
+ }
+ }
+ });
}
}
break;
@@ -1068,7 +1073,13 @@ public class Tuner implements AutoCloseable {
Log.d(TAG, "Got event from tuning. Event type: " + eventType);
synchronized (mOnTuneEventLock) {
if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) {
- mOnTuneEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
+ mOnTuneEventExecutor.execute(() -> {
+ synchronized (mOnTuneEventLock) {
+ if (mOnTuneEventListener != null) {
+ mOnTuneEventListener.onTuneEvent(eventType);
+ }
+ }
+ });
}
}
@@ -1096,7 +1107,13 @@ public class Tuner implements AutoCloseable {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onLocked());
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onLocked();
+ }
+ }
+ });
}
}
}
@@ -1104,7 +1121,13 @@ public class Tuner implements AutoCloseable {
private void onScanStopped() {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onScanStopped());
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onScanStopped();
+ }
+ }
+ });
}
}
}
@@ -1112,7 +1135,13 @@ public class Tuner implements AutoCloseable {
private void onProgress(int percent) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onProgress(percent));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onProgress(percent);
+ }
+ }
+ });
}
}
}
@@ -1120,7 +1149,13 @@ public class Tuner implements AutoCloseable {
private void onFrequenciesReport(int[] frequency) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onFrequenciesReported(frequency));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onFrequenciesReported(frequency);
+ }
+ }
+ });
}
}
}
@@ -1128,7 +1163,13 @@ public class Tuner implements AutoCloseable {
private void onSymbolRates(int[] rate) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onSymbolRatesReported(rate));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onSymbolRatesReported(rate);
+ }
+ }
+ });
}
}
}
@@ -1136,7 +1177,13 @@ public class Tuner implements AutoCloseable {
private void onHierarchy(int hierarchy) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onHierarchyReported(hierarchy));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onHierarchyReported(hierarchy);
+ }
+ }
+ });
}
}
}
@@ -1144,7 +1191,13 @@ public class Tuner implements AutoCloseable {
private void onSignalType(int signalType) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onSignalTypeReported(signalType));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onSignalTypeReported(signalType);
+ }
+ }
+ });
}
}
}
@@ -1152,7 +1205,13 @@ public class Tuner implements AutoCloseable {
private void onPlpIds(int[] plpIds) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onPlpIdsReported(plpIds));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onPlpIdsReported(plpIds);
+ }
+ }
+ });
}
}
}
@@ -1160,7 +1219,13 @@ public class Tuner implements AutoCloseable {
private void onGroupIds(int[] groupIds) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onGroupIdsReported(groupIds));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onGroupIdsReported(groupIds);
+ }
+ }
+ });
}
}
}
@@ -1168,8 +1233,13 @@ public class Tuner implements AutoCloseable {
private void onInputStreamIds(int[] inputStreamIds) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onInputStreamIdsReported(inputStreamIds));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onInputStreamIdsReported(inputStreamIds);
+ }
+ }
+ });
}
}
}
@@ -1177,8 +1247,13 @@ public class Tuner implements AutoCloseable {
private void onDvbsStandard(int dvbsStandandard) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onDvbsStandardReported(dvbsStandandard));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onDvbsStandardReported(dvbsStandandard);
+ }
+ }
+ });
}
}
}
@@ -1186,8 +1261,13 @@ public class Tuner implements AutoCloseable {
private void onDvbtStandard(int dvbtStandard) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onDvbtStandardReported(dvbtStandard));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onDvbtStandardReported(dvbtStandard);
+ }
+ }
+ });
}
}
}
@@ -1195,7 +1275,13 @@ public class Tuner implements AutoCloseable {
private void onAnalogSifStandard(int sif) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(() -> mScanCallback.onAnalogSifStandardReported(sif));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onAnalogSifStandardReported(sif);
+ }
+ }
+ });
}
}
}
@@ -1203,8 +1289,13 @@ public class Tuner implements AutoCloseable {
private void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onAtsc3PlpInfosReported(atsc3PlpInfos));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onAtsc3PlpInfosReported(atsc3PlpInfos);
+ }
+ }
+ });
}
}
}
@@ -1212,8 +1303,13 @@ public class Tuner implements AutoCloseable {
private void onModulationReported(int modulation) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onModulationReported(modulation));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onModulationReported(modulation);
+ }
+ }
+ });
}
}
}
@@ -1221,8 +1317,13 @@ public class Tuner implements AutoCloseable {
private void onPriorityReported(boolean isHighPriority) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onPriorityReported(isHighPriority));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onPriorityReported(isHighPriority);
+ }
+ }
+ });
}
}
}
@@ -1230,8 +1331,13 @@ public class Tuner implements AutoCloseable {
private void onDvbcAnnexReported(int dvbcAnnex) {
synchronized (mScanCallbackLock) {
if (mScanCallbackExecutor != null && mScanCallback != null) {
- mScanCallbackExecutor.execute(
- () -> mScanCallback.onDvbcAnnexReported(dvbcAnnex));
+ mScanCallbackExecutor.execute(() -> {
+ synchronized (mScanCallbackLock) {
+ if (mScanCallback != null) {
+ mScanCallback.onDvbcAnnexReported(dvbcAnnex);
+ }
+ }
+ });
}
}
}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index a13077c77ced..3a15daf74232 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -17,7 +17,11 @@
package android.media.tv.tuner;
import android.annotation.Nullable;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.DemuxAlpFilterType;
+import android.hardware.tv.tuner.DemuxIpFilterType;
+import android.hardware.tv.tuner.DemuxMmtpFilterType;
+import android.hardware.tv.tuner.DemuxTlvFilterType;
+import android.hardware.tv.tuner.DemuxTsFilterType;
import android.media.tv.tuner.filter.Filter;
/**
@@ -37,44 +41,44 @@ public final class TunerUtils {
if (mainType == Filter.TYPE_TS) {
switch (subtype) {
case Filter.SUBTYPE_UNDEFINED:
- return Constants.DemuxTsFilterType.UNDEFINED;
+ return DemuxTsFilterType.UNDEFINED;
case Filter.SUBTYPE_SECTION:
- return Constants.DemuxTsFilterType.SECTION;
+ return DemuxTsFilterType.SECTION;
case Filter.SUBTYPE_PES:
- return Constants.DemuxTsFilterType.PES;
+ return DemuxTsFilterType.PES;
case Filter.SUBTYPE_TS:
- return Constants.DemuxTsFilterType.TS;
+ return DemuxTsFilterType.TS;
case Filter.SUBTYPE_AUDIO:
- return Constants.DemuxTsFilterType.AUDIO;
+ return DemuxTsFilterType.AUDIO;
case Filter.SUBTYPE_VIDEO:
- return Constants.DemuxTsFilterType.VIDEO;
+ return DemuxTsFilterType.VIDEO;
case Filter.SUBTYPE_PCR:
- return Constants.DemuxTsFilterType.PCR;
+ return DemuxTsFilterType.PCR;
case Filter.SUBTYPE_RECORD:
- return Constants.DemuxTsFilterType.RECORD;
+ return DemuxTsFilterType.RECORD;
case Filter.SUBTYPE_TEMI:
- return Constants.DemuxTsFilterType.TEMI;
+ return DemuxTsFilterType.TEMI;
default:
break;
}
} else if (mainType == Filter.TYPE_MMTP) {
switch (subtype) {
case Filter.SUBTYPE_UNDEFINED:
- return Constants.DemuxMmtpFilterType.UNDEFINED;
+ return DemuxMmtpFilterType.UNDEFINED;
case Filter.SUBTYPE_SECTION:
- return Constants.DemuxMmtpFilterType.SECTION;
+ return DemuxMmtpFilterType.SECTION;
case Filter.SUBTYPE_PES:
- return Constants.DemuxMmtpFilterType.PES;
+ return DemuxMmtpFilterType.PES;
case Filter.SUBTYPE_MMTP:
- return Constants.DemuxMmtpFilterType.MMTP;
+ return DemuxMmtpFilterType.MMTP;
case Filter.SUBTYPE_AUDIO:
- return Constants.DemuxMmtpFilterType.AUDIO;
+ return DemuxMmtpFilterType.AUDIO;
case Filter.SUBTYPE_VIDEO:
- return Constants.DemuxMmtpFilterType.VIDEO;
+ return DemuxMmtpFilterType.VIDEO;
case Filter.SUBTYPE_RECORD:
- return Constants.DemuxMmtpFilterType.RECORD;
+ return DemuxMmtpFilterType.RECORD;
case Filter.SUBTYPE_DOWNLOAD:
- return Constants.DemuxMmtpFilterType.DOWNLOAD;
+ return DemuxMmtpFilterType.DOWNLOAD;
default:
break;
}
@@ -82,43 +86,43 @@ public final class TunerUtils {
} else if (mainType == Filter.TYPE_IP) {
switch (subtype) {
case Filter.SUBTYPE_UNDEFINED:
- return Constants.DemuxIpFilterType.UNDEFINED;
+ return DemuxIpFilterType.UNDEFINED;
case Filter.SUBTYPE_SECTION:
- return Constants.DemuxIpFilterType.SECTION;
+ return DemuxIpFilterType.SECTION;
case Filter.SUBTYPE_NTP:
- return Constants.DemuxIpFilterType.NTP;
+ return DemuxIpFilterType.NTP;
case Filter.SUBTYPE_IP_PAYLOAD:
- return Constants.DemuxIpFilterType.IP_PAYLOAD;
+ return DemuxIpFilterType.IP_PAYLOAD;
case Filter.SUBTYPE_IP:
- return Constants.DemuxIpFilterType.IP;
+ return DemuxIpFilterType.IP;
case Filter.SUBTYPE_PAYLOAD_THROUGH:
- return Constants.DemuxIpFilterType.PAYLOAD_THROUGH;
+ return DemuxIpFilterType.PAYLOAD_THROUGH;
default:
break;
}
} else if (mainType == Filter.TYPE_TLV) {
switch (subtype) {
case Filter.SUBTYPE_UNDEFINED:
- return Constants.DemuxTlvFilterType.UNDEFINED;
+ return DemuxTlvFilterType.UNDEFINED;
case Filter.SUBTYPE_SECTION:
- return Constants.DemuxTlvFilterType.SECTION;
+ return DemuxTlvFilterType.SECTION;
case Filter.SUBTYPE_TLV:
- return Constants.DemuxTlvFilterType.TLV;
+ return DemuxTlvFilterType.TLV;
case Filter.SUBTYPE_PAYLOAD_THROUGH:
- return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH;
+ return DemuxTlvFilterType.PAYLOAD_THROUGH;
default:
break;
}
} else if (mainType == Filter.TYPE_ALP) {
switch (subtype) {
case Filter.SUBTYPE_UNDEFINED:
- return Constants.DemuxAlpFilterType.UNDEFINED;
+ return DemuxAlpFilterType.UNDEFINED;
case Filter.SUBTYPE_SECTION:
- return Constants.DemuxAlpFilterType.SECTION;
+ return DemuxAlpFilterType.SECTION;
case Filter.SUBTYPE_PTP:
- return Constants.DemuxAlpFilterType.PTP;
+ return DemuxAlpFilterType.PTP;
case Filter.SUBTYPE_PAYLOAD_THROUGH:
- return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH;
+ return DemuxAlpFilterType.PAYLOAD_THROUGH;
default:
break;
}
diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java
index b40ba1e763c1..3e13bed0afbd 100644
--- a/media/java/android/media/tv/tuner/TunerVersionChecker.java
+++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java
@@ -38,8 +38,9 @@ public final class TunerVersionChecker {
private TunerVersionChecker() {}
/** @hide */
- @IntDef(prefix = "TUNER_VERSION_", value = {TUNER_VERSION_UNKNOWN, TUNER_VERSION_1_0,
- TUNER_VERSION_1_1})
+ @IntDef(prefix = "TUNER_VERSION_",
+ value = {TUNER_VERSION_UNKNOWN, TUNER_VERSION_1_0, TUNER_VERSION_1_1,
+ TUNER_VERSION_2_0})
@Retention(RetentionPolicy.SOURCE)
public @interface TunerVersion {}
/**
@@ -54,6 +55,10 @@ public final class TunerVersionChecker {
* Tuner version 1.1.
*/
public static final int TUNER_VERSION_1_1 = ((1 << 16) | 1);
+ /**
+ * Tuner version 2.0.
+ */
+ public static final int TUNER_VERSION_2_0 = (2 << 16);
/**
* Get the current running Tuner version.
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 1f805d761d49..cfd85834048c 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -20,7 +20,6 @@ import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
@@ -56,25 +55,27 @@ public class DvrPlayback implements AutoCloseable {
/**
* The space of the playback is empty.
*/
- public static final int PLAYBACK_STATUS_EMPTY = Constants.PlaybackStatus.SPACE_EMPTY;
+ public static final int PLAYBACK_STATUS_EMPTY =
+ android.hardware.tv.tuner.PlaybackStatus.SPACE_EMPTY;
/**
* The space of the playback is almost empty.
*
* <p> the threshold is set in {@link DvrSettings}.
*/
public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
- Constants.PlaybackStatus.SPACE_ALMOST_EMPTY;
+ android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_EMPTY;
/**
* The space of the playback is almost full.
*
* <p> the threshold is set in {@link DvrSettings}.
*/
public static final int PLAYBACK_STATUS_ALMOST_FULL =
- Constants.PlaybackStatus.SPACE_ALMOST_FULL;
+ android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_FULL;
/**
* The space of the playback is full.
*/
- public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ public static final int PLAYBACK_STATUS_FULL =
+ android.hardware.tv.tuner.PlaybackStatus.SPACE_FULL;
private static final String TAG = "TvTunerPlayback";
@@ -119,7 +120,13 @@ public class DvrPlayback implements AutoCloseable {
}
synchronized (mListenerLock) {
if (mExecutor != null && mListener != null) {
- mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status));
+ mExecutor.execute(() -> {
+ synchronized (mListenerLock) {
+ if (mListener != null) {
+ mListener.onPlaybackStatusChanged(status);
+ }
+ }
+ });
}
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 2b694668eb03..212a71343a49 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -82,7 +82,13 @@ public class DvrRecorder implements AutoCloseable {
}
synchronized (mListenerLock) {
if (mExecutor != null && mListener != null) {
- mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
+ mExecutor.execute(() -> {
+ synchronized (mListenerLock) {
+ if (mListener != null) {
+ mListener.onRecordStatusChanged(status);
+ }
+ }
+ });
}
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index 60f0d1686ea7..0c4af4b6da07 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -20,7 +20,7 @@ import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.DataFormat;
import android.media.tv.tuner.filter.Filter;
import java.lang.annotation.Retention;
@@ -43,19 +43,19 @@ public class DvrSettings {
/**
* Transport Stream.
*/
- public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
+ public static final int DATA_FORMAT_TS = android.hardware.tv.tuner.DataFormat.TS;
/**
* Packetized Elementary Stream.
*/
- public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
+ public static final int DATA_FORMAT_PES = android.hardware.tv.tuner.DataFormat.PES;
/**
* Elementary Stream.
*/
- public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
+ public static final int DATA_FORMAT_ES = android.hardware.tv.tuner.DataFormat.ES;
/**
* TLV (type-length-value) Stream for SHV
*/
- public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
+ public static final int DATA_FORMAT_SHV_TLV = android.hardware.tv.tuner.DataFormat.SHV_TLV;
@Filter.Status
diff --git a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
index 29bc2c9bd9c1..bac3b5674eab 100644
--- a/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/AlpFilterConfiguration.java
@@ -20,7 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.DemuxAlpLengthType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -63,17 +63,17 @@ public final class AlpFilterConfiguration extends FilterConfiguration {
/**
* Length type not defined.
*/
- public static final int LENGTH_TYPE_UNDEFINED = Constants.DemuxAlpLengthType.UNDEFINED;
+ public static final int LENGTH_TYPE_UNDEFINED = DemuxAlpLengthType.UNDEFINED;
/**
* Length does NOT include additional header.
*/
public static final int LENGTH_TYPE_WITHOUT_ADDITIONAL_HEADER =
- Constants.DemuxAlpLengthType.WITHOUT_ADDITIONAL_HEADER;
+ DemuxAlpLengthType.WITHOUT_ADDITIONAL_HEADER;
/**
* Length includes additional header.
*/
public static final int LENGTH_TYPE_WITH_ADDITIONAL_HEADER =
- Constants.DemuxAlpLengthType.WITH_ADDITIONAL_HEADER;
+ DemuxAlpLengthType.WITH_ADDITIONAL_HEADER;
private final int mPacketType;
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 25457a7bd437..8bcf3d268c3f 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -19,7 +19,6 @@ package android.media.tv.tuner.filter;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_1.Constants;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.TunerVersionChecker;
@@ -46,55 +45,60 @@ public class AvSettings extends Settings {
/*
* Undefined Video stream type
*/
- public static final int VIDEO_STREAM_TYPE_UNDEFINED = Constants.VideoStreamType.UNDEFINED;
+ public static final int VIDEO_STREAM_TYPE_UNDEFINED =
+ android.hardware.tv.tuner.VideoStreamType.UNDEFINED;
/*
* ITU-T | ISO/IEC Reserved
*/
- public static final int VIDEO_STREAM_TYPE_RESERVED = Constants.VideoStreamType.RESERVED;
+ public static final int VIDEO_STREAM_TYPE_RESERVED =
+ android.hardware.tv.tuner.VideoStreamType.RESERVED;
/*
* ISO/IEC 11172
*/
- public static final int VIDEO_STREAM_TYPE_MPEG1 = Constants.VideoStreamType.MPEG1;
+ public static final int VIDEO_STREAM_TYPE_MPEG1 =
+ android.hardware.tv.tuner.VideoStreamType.MPEG1;
/*
* ITU-T Rec.H.262 and ISO/IEC 13818-2
*/
- public static final int VIDEO_STREAM_TYPE_MPEG2 = Constants.VideoStreamType.MPEG2;
+ public static final int VIDEO_STREAM_TYPE_MPEG2 =
+ android.hardware.tv.tuner.VideoStreamType.MPEG2;
/*
* ISO/IEC 14496-2 (MPEG-4 H.263 based video)
*/
- public static final int VIDEO_STREAM_TYPE_MPEG4P2 = Constants.VideoStreamType.MPEG4P2;
+ public static final int VIDEO_STREAM_TYPE_MPEG4P2 =
+ android.hardware.tv.tuner.VideoStreamType.MPEG4P2;
/*
* ITU-T Rec.H.264 and ISO/IEC 14496-10
*/
- public static final int VIDEO_STREAM_TYPE_AVC = Constants.VideoStreamType.AVC;
+ public static final int VIDEO_STREAM_TYPE_AVC = android.hardware.tv.tuner.VideoStreamType.AVC;
/*
* ITU-T Rec. H.265 and ISO/IEC 23008-2
*/
- public static final int VIDEO_STREAM_TYPE_HEVC = Constants.VideoStreamType.HEVC;
+ public static final int VIDEO_STREAM_TYPE_HEVC = android.hardware.tv.tuner.VideoStreamType.HEVC;
/*
* Microsoft VC.1
*/
- public static final int VIDEO_STREAM_TYPE_VC1 = Constants.VideoStreamType.VC1;
+ public static final int VIDEO_STREAM_TYPE_VC1 = android.hardware.tv.tuner.VideoStreamType.VC1;
/*
* Google VP8
*/
- public static final int VIDEO_STREAM_TYPE_VP8 = Constants.VideoStreamType.VP8;
+ public static final int VIDEO_STREAM_TYPE_VP8 = android.hardware.tv.tuner.VideoStreamType.VP8;
/*
* Google VP9
*/
- public static final int VIDEO_STREAM_TYPE_VP9 = Constants.VideoStreamType.VP9;
+ public static final int VIDEO_STREAM_TYPE_VP9 = android.hardware.tv.tuner.VideoStreamType.VP9;
/*
* AOMedia Video 1
*/
- public static final int VIDEO_STREAM_TYPE_AV1 = Constants.VideoStreamType.AV1;
+ public static final int VIDEO_STREAM_TYPE_AV1 = android.hardware.tv.tuner.VideoStreamType.AV1;
/*
* Chinese Standard
*/
- public static final int VIDEO_STREAM_TYPE_AVS = Constants.VideoStreamType.AVS;
+ public static final int VIDEO_STREAM_TYPE_AVS = android.hardware.tv.tuner.VideoStreamType.AVS;
/*
* New Chinese Standard
*/
- public static final int VIDEO_STREAM_TYPE_AVS2 = Constants.VideoStreamType.AVS2;
+ public static final int VIDEO_STREAM_TYPE_AVS2 = android.hardware.tv.tuner.VideoStreamType.AVS2;
/** @hide */
@IntDef(prefix = "AUDIO_STREAM_TYPE_",
@@ -110,67 +114,73 @@ public class AvSettings extends Settings {
/*
* Undefined Audio stream type
*/
- public static final int AUDIO_STREAM_TYPE_UNDEFINED = Constants.AudioStreamType.UNDEFINED;
+ public static final int AUDIO_STREAM_TYPE_UNDEFINED =
+ android.hardware.tv.tuner.AudioStreamType.UNDEFINED;
/*
* Uncompressed Audio
*/
- public static final int AUDIO_STREAM_TYPE_PCM = Constants.AudioStreamType.PCM;
+ public static final int AUDIO_STREAM_TYPE_PCM = android.hardware.tv.tuner.AudioStreamType.PCM;
/*
* MPEG Audio Layer III versions
*/
- public static final int AUDIO_STREAM_TYPE_MP3 = Constants.AudioStreamType.MP3;
+ public static final int AUDIO_STREAM_TYPE_MP3 = android.hardware.tv.tuner.AudioStreamType.MP3;
/*
* ISO/IEC 11172 Audio
*/
- public static final int AUDIO_STREAM_TYPE_MPEG1 = Constants.AudioStreamType.MPEG1;
+ public static final int AUDIO_STREAM_TYPE_MPEG1 =
+ android.hardware.tv.tuner.AudioStreamType.MPEG1;
/*
* ISO/IEC 13818-3
*/
- public static final int AUDIO_STREAM_TYPE_MPEG2 = Constants.AudioStreamType.MPEG2;
+ public static final int AUDIO_STREAM_TYPE_MPEG2 =
+ android.hardware.tv.tuner.AudioStreamType.MPEG2;
/*
* ISO/IEC 23008-3 (MPEG-H Part 3)
*/
- public static final int AUDIO_STREAM_TYPE_MPEGH = Constants.AudioStreamType.MPEGH;
+ public static final int AUDIO_STREAM_TYPE_MPEGH =
+ android.hardware.tv.tuner.AudioStreamType.MPEGH;
/*
* ISO/IEC 14496-3
*/
- public static final int AUDIO_STREAM_TYPE_AAC = Constants.AudioStreamType.AAC;
+ public static final int AUDIO_STREAM_TYPE_AAC = android.hardware.tv.tuner.AudioStreamType.AAC;
/*
* Dolby Digital
*/
- public static final int AUDIO_STREAM_TYPE_AC3 = Constants.AudioStreamType.AC3;
+ public static final int AUDIO_STREAM_TYPE_AC3 = android.hardware.tv.tuner.AudioStreamType.AC3;
/*
* Dolby Digital Plus
*/
- public static final int AUDIO_STREAM_TYPE_EAC3 = Constants.AudioStreamType.EAC3;
+ public static final int AUDIO_STREAM_TYPE_EAC3 = android.hardware.tv.tuner.AudioStreamType.EAC3;
/*
* Dolby AC-4
*/
- public static final int AUDIO_STREAM_TYPE_AC4 = Constants.AudioStreamType.AC4;
+ public static final int AUDIO_STREAM_TYPE_AC4 = android.hardware.tv.tuner.AudioStreamType.AC4;
/*
* Basic DTS
*/
- public static final int AUDIO_STREAM_TYPE_DTS = Constants.AudioStreamType.DTS;
+ public static final int AUDIO_STREAM_TYPE_DTS = android.hardware.tv.tuner.AudioStreamType.DTS;
/*
* High Resolution DTS
*/
- public static final int AUDIO_STREAM_TYPE_DTS_HD = Constants.AudioStreamType.DTS_HD;
+ public static final int AUDIO_STREAM_TYPE_DTS_HD =
+ android.hardware.tv.tuner.AudioStreamType.DTS_HD;
/*
* Windows Media Audio
*/
- public static final int AUDIO_STREAM_TYPE_WMA = Constants.AudioStreamType.WMA;
+ public static final int AUDIO_STREAM_TYPE_WMA = android.hardware.tv.tuner.AudioStreamType.WMA;
/*
* Opus Interactive Audio Codec
*/
- public static final int AUDIO_STREAM_TYPE_OPUS = Constants.AudioStreamType.OPUS;
+ public static final int AUDIO_STREAM_TYPE_OPUS = android.hardware.tv.tuner.AudioStreamType.OPUS;
/*
* VORBIS Interactive Audio Codec
*/
- public static final int AUDIO_STREAM_TYPE_VORBIS = Constants.AudioStreamType.VORBIS;
+ public static final int AUDIO_STREAM_TYPE_VORBIS =
+ android.hardware.tv.tuner.AudioStreamType.VORBIS;
/*
* SJ/T 11368-2006
*/
- public static final int AUDIO_STREAM_TYPE_DRA = Constants.AudioStreamType.DRA;
+ public static final int AUDIO_STREAM_TYPE_DRA = android.hardware.tv.tuner.AudioStreamType.DRA;
private final boolean mIsPassthrough;
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 33742ffd99bf..e7612bc08e50 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -21,7 +21,9 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.DemuxFilterMainType;
+import android.hardware.tv.tuner.DemuxFilterMonitorEventType;
+import android.hardware.tv.tuner.DemuxFilterStatus;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
@@ -53,23 +55,23 @@ public class Filter implements AutoCloseable {
/**
* TS filter type.
*/
- public static final int TYPE_TS = Constants.DemuxFilterMainType.TS;
+ public static final int TYPE_TS = DemuxFilterMainType.TS;
/**
* MMTP filter type.
*/
- public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+ public static final int TYPE_MMTP = DemuxFilterMainType.MMTP;
/**
* IP filter type.
*/
- public static final int TYPE_IP = Constants.DemuxFilterMainType.IP;
+ public static final int TYPE_IP = DemuxFilterMainType.IP;
/**
* TLV filter type.
*/
- public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+ public static final int TYPE_TLV = DemuxFilterMainType.TLV;
/**
* ALP filter type.
*/
- public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+ public static final int TYPE_ALP = DemuxFilterMainType.ALP;
/** @hide */
@IntDef(prefix = "SUBTYPE_",
@@ -158,7 +160,7 @@ public class Filter implements AutoCloseable {
/**
* The status of a filter that the data in the filter buffer is ready to be read.
*/
- public static final int STATUS_DATA_READY = Constants.DemuxFilterStatus.DATA_READY;
+ public static final int STATUS_DATA_READY = DemuxFilterStatus.DATA_READY;
/**
* The status of a filter that the amount of available data in the filter buffer is at low
* level.
@@ -166,19 +168,19 @@ public class Filter implements AutoCloseable {
* The value is set to 25 percent of the buffer size by default. It can be changed when
* configuring the filter.
*/
- public static final int STATUS_LOW_WATER = Constants.DemuxFilterStatus.LOW_WATER;
+ public static final int STATUS_LOW_WATER = DemuxFilterStatus.LOW_WATER;
/**
* The status of a filter that the amount of available data in the filter buffer is at high
* level.
* The value is set to 75 percent of the buffer size by default. It can be changed when
* configuring the filter.
*/
- public static final int STATUS_HIGH_WATER = Constants.DemuxFilterStatus.HIGH_WATER;
+ public static final int STATUS_HIGH_WATER = DemuxFilterStatus.HIGH_WATER;
/**
* The status of a filter that the filter buffer is full and newly filtered data is being
* discarded.
*/
- public static final int STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
+ public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW;
/** @hide */
@IntDef(flag = true,
@@ -192,17 +194,17 @@ public class Filter implements AutoCloseable {
* Content’s scrambling status is unknown
*/
public static final int SCRAMBLING_STATUS_UNKNOWN =
- android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.UNKNOWN;
+ android.hardware.tv.tuner.ScramblingStatus.UNKNOWN;
/**
* Content is not scrambled.
*/
public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED =
- android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.NOT_SCRAMBLED;
+ android.hardware.tv.tuner.ScramblingStatus.NOT_SCRAMBLED;
/**
* Content is scrambled.
*/
public static final int SCRAMBLING_STATUS_SCRAMBLED =
- android.hardware.tv.tuner.V1_1.Constants.ScramblingStatus.SCRAMBLED;
+ android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED;
/** @hide */
@IntDef(flag = true,
@@ -215,12 +217,11 @@ public class Filter implements AutoCloseable {
* Monitor scrambling status change.
*/
public static final int MONITOR_EVENT_SCRAMBLING_STATUS =
- android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.SCRAMBLING_STATUS;
+ DemuxFilterMonitorEventType.SCRAMBLING_STATUS;
/**
* Monitor ip cid change.
*/
- public static final int MONITOR_EVENT_IP_CID_CHANGE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxFilterMonitorEventType.IP_CID_CHANGE;
+ public static final int MONITOR_EVENT_IP_CID_CHANGE = DemuxFilterMonitorEventType.IP_CID_CHANGE;
private static final String TAG = "Filter";
@@ -256,7 +257,13 @@ public class Filter implements AutoCloseable {
private void onFilterStatus(int status) {
synchronized (mCallbackLock) {
if (mCallback != null && mExecutor != null) {
- mExecutor.execute(() -> mCallback.onFilterStatusChanged(this, status));
+ mExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onFilterStatusChanged(this, status);
+ }
+ }
+ });
}
}
}
@@ -264,7 +271,13 @@ public class Filter implements AutoCloseable {
private void onFilterEvent(FilterEvent[] events) {
synchronized (mCallbackLock) {
if (mCallback != null && mExecutor != null) {
- mExecutor.execute(() -> mCallback.onFilterEvent(this, events));
+ mExecutor.execute(() -> {
+ synchronized (mCallbackLock) {
+ if (mCallback != null) {
+ mCallback.onFilterEvent(this, events);
+ }
+ }
+ });
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
index 4b69807e5078..657846486158 100644
--- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.SystemApi;
+import android.hardware.tv.tuner.Constant;
import android.media.tv.tuner.TunerVersionChecker;
/**
@@ -33,8 +34,7 @@ public final class IpFilterConfiguration extends FilterConfiguration {
/**
* Undefined filter type.
*/
- public static final int INVALID_IP_FILTER_CONTEXT_ID =
- android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_IP_FILTER_CONTEXT_ID;
+ public static final int INVALID_IP_FILTER_CONTEXT_ID = Constant.INVALID_IP_FILTER_CONTEXT_ID;
private final byte[] mSrcIpAddress;
private final byte[] mDstIpAddress;
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index 91992afadb3e..cd703651f2fa 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -19,7 +19,10 @@ package android.media.tv.tuner.filter;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.DemuxRecordScIndexType;
+import android.hardware.tv.tuner.DemuxScHevcIndex;
+import android.hardware.tv.tuner.DemuxScIndex;
+import android.hardware.tv.tuner.DemuxTsIndex;
import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
@@ -57,88 +60,80 @@ public class RecordSettings extends Settings {
/**
* TS index FIRST_PACKET.
*/
- public static final int TS_INDEX_FIRST_PACKET = Constants.DemuxTsIndex.FIRST_PACKET;
+ public static final int TS_INDEX_FIRST_PACKET = DemuxTsIndex.FIRST_PACKET;
/**
* TS index PAYLOAD_UNIT_START_INDICATOR.
*/
public static final int TS_INDEX_PAYLOAD_UNIT_START_INDICATOR =
- Constants.DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
+ DemuxTsIndex.PAYLOAD_UNIT_START_INDICATOR;
/**
* TS index CHANGE_TO_NOT_SCRAMBLED.
*/
- public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
+ public static final int TS_INDEX_CHANGE_TO_NOT_SCRAMBLED = DemuxTsIndex.CHANGE_TO_NOT_SCRAMBLED;
/**
* TS index CHANGE_TO_EVEN_SCRAMBLED.
*/
public static final int TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
+ DemuxTsIndex.CHANGE_TO_EVEN_SCRAMBLED;
/**
* TS index CHANGE_TO_ODD_SCRAMBLED.
*/
- public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED =
- Constants.DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
+ public static final int TS_INDEX_CHANGE_TO_ODD_SCRAMBLED = DemuxTsIndex.CHANGE_TO_ODD_SCRAMBLED;
/**
* TS index DISCONTINUITY_INDICATOR.
*/
- public static final int TS_INDEX_DISCONTINUITY_INDICATOR =
- Constants.DemuxTsIndex.DISCONTINUITY_INDICATOR;
+ public static final int TS_INDEX_DISCONTINUITY_INDICATOR = DemuxTsIndex.DISCONTINUITY_INDICATOR;
/**
* TS index RANDOM_ACCESS_INDICATOR.
*/
- public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR =
- Constants.DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
+ public static final int TS_INDEX_RANDOM_ACCESS_INDICATOR = DemuxTsIndex.RANDOM_ACCESS_INDICATOR;
/**
* TS index PRIORITY_INDICATOR.
*/
- public static final int TS_INDEX_PRIORITY_INDICATOR = Constants.DemuxTsIndex.PRIORITY_INDICATOR;
+ public static final int TS_INDEX_PRIORITY_INDICATOR = DemuxTsIndex.PRIORITY_INDICATOR;
/**
* TS index PCR_FLAG.
*/
- public static final int TS_INDEX_PCR_FLAG = Constants.DemuxTsIndex.PCR_FLAG;
+ public static final int TS_INDEX_PCR_FLAG = DemuxTsIndex.PCR_FLAG;
/**
* TS index OPCR_FLAG.
*/
- public static final int TS_INDEX_OPCR_FLAG = Constants.DemuxTsIndex.OPCR_FLAG;
+ public static final int TS_INDEX_OPCR_FLAG = DemuxTsIndex.OPCR_FLAG;
/**
* TS index SPLICING_POINT_FLAG.
*/
- public static final int TS_INDEX_SPLICING_POINT_FLAG =
- Constants.DemuxTsIndex.SPLICING_POINT_FLAG;
+ public static final int TS_INDEX_SPLICING_POINT_FLAG = DemuxTsIndex.SPLICING_POINT_FLAG;
/**
* TS index PRIVATE_DATA.
*/
- public static final int TS_INDEX_PRIVATE_DATA = Constants.DemuxTsIndex.PRIVATE_DATA;
+ public static final int TS_INDEX_PRIVATE_DATA = DemuxTsIndex.PRIVATE_DATA;
/**
* TS index ADAPTATION_EXTENSION_FLAG.
*/
public static final int TS_INDEX_ADAPTATION_EXTENSION_FLAG =
- Constants.DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
+ DemuxTsIndex.ADAPTATION_EXTENSION_FLAG;
/**
* Index the address of MPEG Media Transport Packet Table(MPT).
*/
- public static final int MPT_INDEX_MPT =
- android.hardware.tv.tuner.V1_1.Constants.DemuxTsIndex.MPT_INDEX_MPT;
+ public static final int MPT_INDEX_MPT = DemuxTsIndex.MPT_INDEX_MPT;
/**
* Index the address of Video.
*/
- public static final int MPT_INDEX_VIDEO =
- android.hardware.tv.tuner.V1_1.Constants.DemuxTsIndex.MPT_INDEX_VIDEO;
+ public static final int MPT_INDEX_VIDEO = DemuxTsIndex.MPT_INDEX_VIDEO;
/**
* Index the address of Audio.
*/
- public static final int MPT_INDEX_AUDIO =
- android.hardware.tv.tuner.V1_1.Constants.DemuxTsIndex.MPT_INDEX_AUDIO;
+ public static final int MPT_INDEX_AUDIO = DemuxTsIndex.MPT_INDEX_AUDIO;
/**
* Index to indicate this is a target of timestamp extraction for video.
*/
public static final int MPT_INDEX_TIMESTAMP_TARGET_VIDEO =
- android.hardware.tv.tuner.V1_1.Constants.DemuxTsIndex.MPT_INDEX_TIMESTAMP_TARGET_VIDEO;
+ DemuxTsIndex.MPT_INDEX_TIMESTAMP_TARGET_VIDEO;
/**
* Index to indicate this is a target of timestamp extraction for audio.
*/
public static final int MPT_INDEX_TIMESTAMP_TARGET_AUDIO =
- android.hardware.tv.tuner.V1_1.Constants.DemuxTsIndex.MPT_INDEX_TIMESTAMP_TARGET_AUDIO;
+ DemuxTsIndex.MPT_INDEX_TIMESTAMP_TARGET_AUDIO;
/** @hide */
@@ -150,15 +145,15 @@ public class RecordSettings extends Settings {
/**
* Start Code Index is not used.
*/
- public static final int INDEX_TYPE_NONE = Constants.DemuxRecordScIndexType.NONE;
+ public static final int INDEX_TYPE_NONE = DemuxRecordScIndexType.NONE;
/**
* Start Code index.
*/
- public static final int INDEX_TYPE_SC = Constants.DemuxRecordScIndexType.SC;
+ public static final int INDEX_TYPE_SC = DemuxRecordScIndexType.SC;
/**
* Start Code index for HEVC.
*/
- public static final int INDEX_TYPE_SC_HEVC = Constants.DemuxRecordScIndexType.SC_HEVC;
+ public static final int INDEX_TYPE_SC_HEVC = DemuxRecordScIndexType.SC_HEVC;
/**
* Indexes can be tagged by Start Code in PES (Packetized Elementary Stream)
@@ -176,44 +171,39 @@ public class RecordSettings extends Settings {
/**
* SC index for a new I-frame.
*/
- public static final int SC_INDEX_I_FRAME = Constants.DemuxScIndex.I_FRAME;
+ public static final int SC_INDEX_I_FRAME = DemuxScIndex.I_FRAME;
/**
* SC index for a new P-frame.
*/
- public static final int SC_INDEX_P_FRAME = Constants.DemuxScIndex.P_FRAME;
+ public static final int SC_INDEX_P_FRAME = DemuxScIndex.P_FRAME;
/**
* SC index for a new B-frame.
*/
- public static final int SC_INDEX_B_FRAME = Constants.DemuxScIndex.B_FRAME;
+ public static final int SC_INDEX_B_FRAME = DemuxScIndex.B_FRAME;
/**
* SC index for a new sequence.
*/
- public static final int SC_INDEX_SEQUENCE = Constants.DemuxScIndex.SEQUENCE;
+ public static final int SC_INDEX_SEQUENCE = DemuxScIndex.SEQUENCE;
/**
* All blocks are coded as I blocks.
*/
- public static final int SC_INDEX_I_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.I_SLICE;
+ public static final int SC_INDEX_I_SLICE = DemuxScIndex.I_SLICE;
/**
* Blocks are coded as I or P blocks.
*/
- public static final int SC_INDEX_P_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.P_SLICE;
+ public static final int SC_INDEX_P_SLICE = DemuxScIndex.P_SLICE;
/**
* Blocks are coded as I, P or B blocks.
*/
- public static final int SC_INDEX_B_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.B_SLICE;
+ public static final int SC_INDEX_B_SLICE = DemuxScIndex.B_SLICE;
/**
* A so-called switching I slice that is coded.
*/
- public static final int SC_INDEX_SI_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.SI_SLICE;
+ public static final int SC_INDEX_SI_SLICE = DemuxScIndex.SI_SLICE;
/**
* A so-called switching P slice that is coded.
*/
- public static final int SC_INDEX_SP_SLICE =
- android.hardware.tv.tuner.V1_1.Constants.DemuxScIndex.SP_SLICE;
+ public static final int SC_INDEX_SP_SLICE = DemuxScIndex.SP_SLICE;
/**
* Indexes can be tagged by NAL unit group in HEVC according to ISO/IEC 23008-2.
@@ -231,41 +221,35 @@ public class RecordSettings extends Settings {
/**
* SC HEVC index SPS.
*/
- public static final int SC_HEVC_INDEX_SPS = Constants.DemuxScHevcIndex.SPS;
+ public static final int SC_HEVC_INDEX_SPS = DemuxScHevcIndex.SPS;
/**
* SC HEVC index AUD.
*/
- public static final int SC_HEVC_INDEX_AUD = Constants.DemuxScHevcIndex.AUD;
+ public static final int SC_HEVC_INDEX_AUD = DemuxScHevcIndex.AUD;
/**
* SC HEVC index SLICE_CE_BLA_W_LP.
*/
- public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP =
- Constants.DemuxScHevcIndex.SLICE_CE_BLA_W_LP;
+ public static final int SC_HEVC_INDEX_SLICE_CE_BLA_W_LP = DemuxScHevcIndex.SLICE_CE_BLA_W_LP;
/**
* SC HEVC index SLICE_BLA_W_RADL.
*/
- public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL =
- Constants.DemuxScHevcIndex.SLICE_BLA_W_RADL;
+ public static final int SC_HEVC_INDEX_SLICE_BLA_W_RADL = DemuxScHevcIndex.SLICE_BLA_W_RADL;
/**
* SC HEVC index SLICE_BLA_N_LP.
*/
- public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP =
- Constants.DemuxScHevcIndex.SLICE_BLA_N_LP;
+ public static final int SC_HEVC_INDEX_SLICE_BLA_N_LP = DemuxScHevcIndex.SLICE_BLA_N_LP;
/**
* SC HEVC index SLICE_IDR_W_RADL.
*/
- public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL =
- Constants.DemuxScHevcIndex.SLICE_IDR_W_RADL;
+ public static final int SC_HEVC_INDEX_SLICE_IDR_W_RADL = DemuxScHevcIndex.SLICE_IDR_W_RADL;
/**
* SC HEVC index SLICE_IDR_N_LP.
*/
- public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP =
- Constants.DemuxScHevcIndex.SLICE_IDR_N_LP;
+ public static final int SC_HEVC_INDEX_SLICE_IDR_N_LP = DemuxScHevcIndex.SLICE_IDR_N_LP;
/**
* SC HEVC index SLICE_TRAIL_CRA.
*/
- public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA =
- Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA;
+ public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA = DemuxScHevcIndex.SLICE_TRAIL_CRA;
/**
* @hide
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index b2c3fd243de0..768f1d389a32 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -20,7 +20,9 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendAnalogAftFlag;
+import android.hardware.tv.tuner.FrontendAnalogSifStandard;
+import android.hardware.tv.tuner.FrontendAnalogType;
import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
@@ -45,39 +47,39 @@ public class AnalogFrontendSettings extends FrontendSettings {
/**
* Undefined analog signal type.
*/
- public static final int SIGNAL_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
+ public static final int SIGNAL_TYPE_UNDEFINED = FrontendAnalogType.UNDEFINED;
/**
* AUTO analog signal type.
*/
- public static final int SIGNAL_TYPE_AUTO = Constants.FrontendAnalogType.AUTO;
+ public static final int SIGNAL_TYPE_AUTO = FrontendAnalogType.AUTO;
/**
* PAL analog signal type.
*/
- public static final int SIGNAL_TYPE_PAL = Constants.FrontendAnalogType.PAL;
+ public static final int SIGNAL_TYPE_PAL = FrontendAnalogType.PAL;
/**
* PAL M analog signal type.
*/
- public static final int SIGNAL_TYPE_PAL_M = Constants.FrontendAnalogType.PAL_M;
+ public static final int SIGNAL_TYPE_PAL_M = FrontendAnalogType.PAL_M;
/**
* PAL N analog signal type.
*/
- public static final int SIGNAL_TYPE_PAL_N = Constants.FrontendAnalogType.PAL_N;
+ public static final int SIGNAL_TYPE_PAL_N = FrontendAnalogType.PAL_N;
/**
* PAL 60 analog signal type.
*/
- public static final int SIGNAL_TYPE_PAL_60 = Constants.FrontendAnalogType.PAL_60;
+ public static final int SIGNAL_TYPE_PAL_60 = FrontendAnalogType.PAL_60;
/**
* NTSC analog signal type.
*/
- public static final int SIGNAL_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
+ public static final int SIGNAL_TYPE_NTSC = FrontendAnalogType.NTSC;
/**
* NTSC 443 analog signal type.
*/
- public static final int SIGNAL_TYPE_NTSC_443 = Constants.FrontendAnalogType.NTSC_443;
+ public static final int SIGNAL_TYPE_NTSC_443 = FrontendAnalogType.NTSC_443;
/**
* SECM analog signal type.
*/
- public static final int SIGNAL_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
+ public static final int SIGNAL_TYPE_SECAM = FrontendAnalogType.SECAM;
/** @hide */
@IntDef(flag = true,
@@ -91,79 +93,79 @@ public class AnalogFrontendSettings extends FrontendSettings {
/**
* Undefined Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_UNDEFINED = Constants.FrontendAnalogSifStandard.UNDEFINED;
+ public static final int SIF_UNDEFINED = FrontendAnalogSifStandard.UNDEFINED;
/**
* Audo Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_AUTO = Constants.FrontendAnalogSifStandard.AUTO;
+ public static final int SIF_AUTO = FrontendAnalogSifStandard.AUTO;
/**
* BG Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_BG = Constants.FrontendAnalogSifStandard.BG;
+ public static final int SIF_BG = FrontendAnalogSifStandard.BG;
/**
* BG-A2 Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
+ public static final int SIF_BG_A2 = FrontendAnalogSifStandard.BG_A2;
/**
* BG-NICAM Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_BG_NICAM = Constants.FrontendAnalogSifStandard.BG_NICAM;
+ public static final int SIF_BG_NICAM = FrontendAnalogSifStandard.BG_NICAM;
/**
* I Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_I = Constants.FrontendAnalogSifStandard.I;
+ public static final int SIF_I = FrontendAnalogSifStandard.I;
/**
* DK Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_DK = Constants.FrontendAnalogSifStandard.DK;
+ public static final int SIF_DK = FrontendAnalogSifStandard.DK;
/**
* DK1 A2 Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_DK1_A2 = Constants.FrontendAnalogSifStandard.DK1_A2;
+ public static final int SIF_DK1_A2 = FrontendAnalogSifStandard.DK1_A2;
/**
* DK2 A2 Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_DK2_A2 = Constants.FrontendAnalogSifStandard.DK2_A2;
+ public static final int SIF_DK2_A2 = FrontendAnalogSifStandard.DK2_A2;
/**
* DK3 A2 Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_DK3_A2 = Constants.FrontendAnalogSifStandard.DK3_A2;
+ public static final int SIF_DK3_A2 = FrontendAnalogSifStandard.DK3_A2;
/**
* DK-NICAM Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_DK_NICAM = Constants.FrontendAnalogSifStandard.DK_NICAM;
+ public static final int SIF_DK_NICAM = FrontendAnalogSifStandard.DK_NICAM;
/**
* L Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_L = Constants.FrontendAnalogSifStandard.L;
+ public static final int SIF_L = FrontendAnalogSifStandard.L;
/**
* M Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_M = Constants.FrontendAnalogSifStandard.M;
+ public static final int SIF_M = FrontendAnalogSifStandard.M;
/**
* M-BTSC Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
+ public static final int SIF_M_BTSC = FrontendAnalogSifStandard.M_BTSC;
/**
* M-A2 Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
+ public static final int SIF_M_A2 = FrontendAnalogSifStandard.M_A2;
/**
* M-EIAJ Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_M_EIAJ = Constants.FrontendAnalogSifStandard.M_EIAJ;
+ public static final int SIF_M_EIAJ = FrontendAnalogSifStandard.M_EIAJ;
/**
* I-NICAM Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_I_NICAM = Constants.FrontendAnalogSifStandard.I_NICAM;
+ public static final int SIF_I_NICAM = FrontendAnalogSifStandard.I_NICAM;
/**
* L-NICAM Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_L_NICAM = Constants.FrontendAnalogSifStandard.L_NICAM;
+ public static final int SIF_L_NICAM = FrontendAnalogSifStandard.L_NICAM;
/**
* L-PRIME Analog Standard Interchange Format (SIF).
*/
- public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME;
+ public static final int SIF_L_PRIME = FrontendAnalogSifStandard.L_PRIME;
/** @hide */
@IntDef(prefix = "AFT_FLAG_",
@@ -174,18 +176,15 @@ public class AnalogFrontendSettings extends FrontendSettings {
/**
* Aft flag is not defined.
*/
- public static final int AFT_FLAG_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.UNDEFINED;
+ public static final int AFT_FLAG_UNDEFINED = FrontendAnalogAftFlag.UNDEFINED;
/**
* Aft flag is set true.
*/
- public static final int AFT_FLAG_TRUE =
- android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_TRUE;
+ public static final int AFT_FLAG_TRUE = FrontendAnalogAftFlag.AFT_TRUE;
/**
* Aft flag is not set.
*/
- public static final int AFT_FLAG_FALSE =
- android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_FALSE;
+ public static final int AFT_FLAG_FALSE = FrontendAnalogAftFlag.AFT_FALSE;
private final int mSignalType;
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index ed1ce2d6a566..52a20cb8b815 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -20,7 +20,12 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendAtsc3Bandwidth;
+import android.hardware.tv.tuner.FrontendAtsc3CodeRate;
+import android.hardware.tv.tuner.FrontendAtsc3DemodOutputFormat;
+import android.hardware.tv.tuner.FrontendAtsc3Fec;
+import android.hardware.tv.tuner.FrontendAtsc3Modulation;
+import android.hardware.tv.tuner.FrontendAtsc3TimeInterleaveMode;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -44,27 +49,23 @@ public class Atsc3FrontendSettings extends FrontendSettings {
/**
* Bandwidth not defined.
*/
- public static final int BANDWIDTH_UNDEFINED =
- Constants.FrontendAtsc3Bandwidth.UNDEFINED;
+ public static final int BANDWIDTH_UNDEFINED = FrontendAtsc3Bandwidth.UNDEFINED;
/**
* Hardware is able to detect and set bandwidth automatically
*/
- public static final int BANDWIDTH_AUTO = Constants.FrontendAtsc3Bandwidth.AUTO;
+ public static final int BANDWIDTH_AUTO = FrontendAtsc3Bandwidth.AUTO;
/**
* 6 MHz bandwidth.
*/
- public static final int BANDWIDTH_BANDWIDTH_6MHZ =
- Constants.FrontendAtsc3Bandwidth.BANDWIDTH_6MHZ;
+ public static final int BANDWIDTH_BANDWIDTH_6MHZ = FrontendAtsc3Bandwidth.BANDWIDTH_6MHZ;
/**
* 7 MHz bandwidth.
*/
- public static final int BANDWIDTH_BANDWIDTH_7MHZ =
- Constants.FrontendAtsc3Bandwidth.BANDWIDTH_7MHZ;
+ public static final int BANDWIDTH_BANDWIDTH_7MHZ = FrontendAtsc3Bandwidth.BANDWIDTH_7MHZ;
/**
* 8 MHz bandwidth.
*/
- public static final int BANDWIDTH_BANDWIDTH_8MHZ =
- Constants.FrontendAtsc3Bandwidth.BANDWIDTH_8MHZ;
+ public static final int BANDWIDTH_BANDWIDTH_8MHZ = FrontendAtsc3Bandwidth.BANDWIDTH_8MHZ;
/** @hide */
@@ -80,35 +81,35 @@ public class Atsc3FrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendAtsc3Modulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendAtsc3Modulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically.
*/
- public static final int MODULATION_AUTO = Constants.FrontendAtsc3Modulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendAtsc3Modulation.AUTO;
/**
* QPSK modulation.
*/
- public static final int MODULATION_MOD_QPSK = Constants.FrontendAtsc3Modulation.MOD_QPSK;
+ public static final int MODULATION_MOD_QPSK = FrontendAtsc3Modulation.MOD_QPSK;
/**
* 16QAM modulation.
*/
- public static final int MODULATION_MOD_16QAM = Constants.FrontendAtsc3Modulation.MOD_16QAM;
+ public static final int MODULATION_MOD_16QAM = FrontendAtsc3Modulation.MOD_16QAM;
/**
* 64QAM modulation.
*/
- public static final int MODULATION_MOD_64QAM = Constants.FrontendAtsc3Modulation.MOD_64QAM;
+ public static final int MODULATION_MOD_64QAM = FrontendAtsc3Modulation.MOD_64QAM;
/**
* 256QAM modulation.
*/
- public static final int MODULATION_MOD_256QAM = Constants.FrontendAtsc3Modulation.MOD_256QAM;
+ public static final int MODULATION_MOD_256QAM = FrontendAtsc3Modulation.MOD_256QAM;
/**
* 1024QAM modulation.
*/
- public static final int MODULATION_MOD_1024QAM = Constants.FrontendAtsc3Modulation.MOD_1024QAM;
+ public static final int MODULATION_MOD_1024QAM = FrontendAtsc3Modulation.MOD_1024QAM;
/**
* 4096QAM modulation.
*/
- public static final int MODULATION_MOD_4096QAM = Constants.FrontendAtsc3Modulation.MOD_4096QAM;
+ public static final int MODULATION_MOD_4096QAM = FrontendAtsc3Modulation.MOD_4096QAM;
/** @hide */
@@ -123,22 +124,19 @@ public class Atsc3FrontendSettings extends FrontendSettings {
* Time interleave mode undefined.
*/
public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
- Constants.FrontendAtsc3TimeInterleaveMode.UNDEFINED;
+ FrontendAtsc3TimeInterleaveMode.UNDEFINED;
/**
* Hardware is able to detect and set Time Interleave Mode automatically.
*/
- public static final int TIME_INTERLEAVE_MODE_AUTO =
- Constants.FrontendAtsc3TimeInterleaveMode.AUTO;
+ public static final int TIME_INTERLEAVE_MODE_AUTO = FrontendAtsc3TimeInterleaveMode.AUTO;
/**
* CTI Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_CTI =
- Constants.FrontendAtsc3TimeInterleaveMode.CTI;
+ public static final int TIME_INTERLEAVE_MODE_CTI = FrontendAtsc3TimeInterleaveMode.CTI;
/**
* HTI Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_HTI =
- Constants.FrontendAtsc3TimeInterleaveMode.HTI;
+ public static final int TIME_INTERLEAVE_MODE_HTI = FrontendAtsc3TimeInterleaveMode.HTI;
/** @hide */
@@ -153,59 +151,59 @@ public class Atsc3FrontendSettings extends FrontendSettings {
/**
* Code rate undefined.
*/
- public static final int CODERATE_UNDEFINED = Constants.FrontendAtsc3CodeRate.UNDEFINED;
+ public static final int CODERATE_UNDEFINED = FrontendAtsc3CodeRate.UNDEFINED;
/**
* Hardware is able to detect and set code rate automatically
*/
- public static final int CODERATE_AUTO = Constants.FrontendAtsc3CodeRate.AUTO;
+ public static final int CODERATE_AUTO = FrontendAtsc3CodeRate.AUTO;
/**
* 2/15 code rate.
*/
- public static final int CODERATE_2_15 = Constants.FrontendAtsc3CodeRate.CODERATE_2_15;
+ public static final int CODERATE_2_15 = FrontendAtsc3CodeRate.CODERATE_2_15;
/**
* 3/15 code rate.
*/
- public static final int CODERATE_3_15 = Constants.FrontendAtsc3CodeRate.CODERATE_3_15;
+ public static final int CODERATE_3_15 = FrontendAtsc3CodeRate.CODERATE_3_15;
/**
* 4/15 code rate.
*/
- public static final int CODERATE_4_15 = Constants.FrontendAtsc3CodeRate.CODERATE_4_15;
+ public static final int CODERATE_4_15 = FrontendAtsc3CodeRate.CODERATE_4_15;
/**
* 5/15 code rate.
*/
- public static final int CODERATE_5_15 = Constants.FrontendAtsc3CodeRate.CODERATE_5_15;
+ public static final int CODERATE_5_15 = FrontendAtsc3CodeRate.CODERATE_5_15;
/**
* 6/15 code rate.
*/
- public static final int CODERATE_6_15 = Constants.FrontendAtsc3CodeRate.CODERATE_6_15;
+ public static final int CODERATE_6_15 = FrontendAtsc3CodeRate.CODERATE_6_15;
/**
* 7/15 code rate.
*/
- public static final int CODERATE_7_15 = Constants.FrontendAtsc3CodeRate.CODERATE_7_15;
+ public static final int CODERATE_7_15 = FrontendAtsc3CodeRate.CODERATE_7_15;
/**
* 8/15 code rate.
*/
- public static final int CODERATE_8_15 = Constants.FrontendAtsc3CodeRate.CODERATE_8_15;
+ public static final int CODERATE_8_15 = FrontendAtsc3CodeRate.CODERATE_8_15;
/**
* 9/15 code rate.
*/
- public static final int CODERATE_9_15 = Constants.FrontendAtsc3CodeRate.CODERATE_9_15;
+ public static final int CODERATE_9_15 = FrontendAtsc3CodeRate.CODERATE_9_15;
/**
* 10/15 code rate.
*/
- public static final int CODERATE_10_15 = Constants.FrontendAtsc3CodeRate.CODERATE_10_15;
+ public static final int CODERATE_10_15 = FrontendAtsc3CodeRate.CODERATE_10_15;
/**
* 11/15 code rate.
*/
- public static final int CODERATE_11_15 = Constants.FrontendAtsc3CodeRate.CODERATE_11_15;
+ public static final int CODERATE_11_15 = FrontendAtsc3CodeRate.CODERATE_11_15;
/**
* 12/15 code rate.
*/
- public static final int CODERATE_12_15 = Constants.FrontendAtsc3CodeRate.CODERATE_12_15;
+ public static final int CODERATE_12_15 = FrontendAtsc3CodeRate.CODERATE_12_15;
/**
* 13/15 code rate.
*/
- public static final int CODERATE_13_15 = Constants.FrontendAtsc3CodeRate.CODERATE_13_15;
+ public static final int CODERATE_13_15 = FrontendAtsc3CodeRate.CODERATE_13_15;
/** @hide */
@@ -219,35 +217,35 @@ public class Atsc3FrontendSettings extends FrontendSettings {
/**
* Forward Error Correction undefined.
*/
- public static final int FEC_UNDEFINED = Constants.FrontendAtsc3Fec.UNDEFINED;
+ public static final int FEC_UNDEFINED = FrontendAtsc3Fec.UNDEFINED;
/**
* Hardware is able to detect and set FEC automatically
*/
- public static final int FEC_AUTO = Constants.FrontendAtsc3Fec.AUTO;
+ public static final int FEC_AUTO = FrontendAtsc3Fec.AUTO;
/**
* BCH LDPC 16K Forward Error Correction
*/
- public static final int FEC_BCH_LDPC_16K = Constants.FrontendAtsc3Fec.BCH_LDPC_16K;
+ public static final int FEC_BCH_LDPC_16K = FrontendAtsc3Fec.BCH_LDPC_16K;
/**
* BCH LDPC 64K Forward Error Correction
*/
- public static final int FEC_BCH_LDPC_64K = Constants.FrontendAtsc3Fec.BCH_LDPC_64K;
+ public static final int FEC_BCH_LDPC_64K = FrontendAtsc3Fec.BCH_LDPC_64K;
/**
* CRC LDPC 16K Forward Error Correction
*/
- public static final int FEC_CRC_LDPC_16K = Constants.FrontendAtsc3Fec.CRC_LDPC_16K;
+ public static final int FEC_CRC_LDPC_16K = FrontendAtsc3Fec.CRC_LDPC_16K;
/**
* CRC LDPC 64K Forward Error Correction
*/
- public static final int FEC_CRC_LDPC_64K = Constants.FrontendAtsc3Fec.CRC_LDPC_64K;
+ public static final int FEC_CRC_LDPC_64K = FrontendAtsc3Fec.CRC_LDPC_64K;
/**
* LDPC 16K Forward Error Correction
*/
- public static final int FEC_LDPC_16K = Constants.FrontendAtsc3Fec.LDPC_16K;
+ public static final int FEC_LDPC_16K = FrontendAtsc3Fec.LDPC_16K;
/**
* LDPC 64K Forward Error Correction
*/
- public static final int FEC_LDPC_64K = Constants.FrontendAtsc3Fec.LDPC_64K;
+ public static final int FEC_LDPC_64K = FrontendAtsc3Fec.LDPC_64K;
/** @hide */
@@ -262,17 +260,17 @@ public class Atsc3FrontendSettings extends FrontendSettings {
* Demod output format undefined.
*/
public static final int DEMOD_OUTPUT_FORMAT_UNDEFINED =
- Constants.FrontendAtsc3DemodOutputFormat.UNDEFINED;
+ FrontendAtsc3DemodOutputFormat.UNDEFINED;
/**
* ALP format. Typically used in US region.
*/
public static final int DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET =
- Constants.FrontendAtsc3DemodOutputFormat.ATSC3_LINKLAYER_PACKET;
+ FrontendAtsc3DemodOutputFormat.ATSC3_LINKLAYER_PACKET;
/**
* BaseBand packet format. Typically used in Korea region.
*/
public static final int DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET =
- Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
+ FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
private final int mBandwidth;
private final int mDemodOutputFormat;
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index f7244bbdf951..042bba890d22 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -20,7 +20,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendAtscModulation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -44,19 +44,19 @@ public class AtscFrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendAtscModulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendAtscModulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically
*/
- public static final int MODULATION_AUTO = Constants.FrontendAtscModulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendAtscModulation.AUTO;
/**
* 8VSB Modulation.
*/
- public static final int MODULATION_MOD_8VSB = Constants.FrontendAtscModulation.MOD_8VSB;
+ public static final int MODULATION_MOD_8VSB = FrontendAtscModulation.MOD_8VSB;
/**
* 16VSB Modulation.
*/
- public static final int MODULATION_MOD_16VSB = Constants.FrontendAtscModulation.MOD_16VSB;
+ public static final int MODULATION_MOD_16VSB = FrontendAtscModulation.MOD_16VSB;
private final int mModulation;
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index c1d08339c690..9ba41d5e1851 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -21,6 +21,12 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.hardware.tv.tuner.FrontendDtmbBandwidth;
+import android.hardware.tv.tuner.FrontendDtmbCodeRate;
+import android.hardware.tv.tuner.FrontendDtmbGuardInterval;
+import android.hardware.tv.tuner.FrontendDtmbModulation;
+import android.hardware.tv.tuner.FrontendDtmbTimeInterleaveMode;
+import android.hardware.tv.tuner.FrontendDtmbTransmissionMode;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -46,23 +52,19 @@ public final class DtmbFrontendSettings extends FrontendSettings {
/**
* Bandwidth not defined.
*/
- public static final int BANDWIDTH_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.UNDEFINED;
+ public static final int BANDWIDTH_UNDEFINED = FrontendDtmbBandwidth.UNDEFINED;
/**
* Hardware is able to detect and set bandwidth automatically
*/
- public static final int BANDWIDTH_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.AUTO;
+ public static final int BANDWIDTH_AUTO = FrontendDtmbBandwidth.AUTO;
/**
* 6 MHz bandwidth.
*/
- public static final int BANDWIDTH_6MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_6MHZ;
+ public static final int BANDWIDTH_6MHZ = FrontendDtmbBandwidth.BANDWIDTH_6MHZ;
/**
* 8 MHz bandwidth.
*/
- public static final int BANDWIDTH_8MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_8MHZ;
+ public static final int BANDWIDTH_8MHZ = FrontendDtmbBandwidth.BANDWIDTH_8MHZ;
/** @hide */
@@ -77,22 +79,21 @@ public final class DtmbFrontendSettings extends FrontendSettings {
* Time Interleave Mode undefined.
*/
public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.UNDEFINED;
+ FrontendDtmbTimeInterleaveMode.UNDEFINED;
/**
* Hardware is able to detect and set time interleave mode automatically
*/
- public static final int TIME_INTERLEAVE_MODE_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.AUTO;
+ public static final int TIME_INTERLEAVE_MODE_AUTO = FrontendDtmbTimeInterleaveMode.AUTO;
/**
* Time Interleave Mode timer int 240.
*/
public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_240;
+ FrontendDtmbTimeInterleaveMode.TIMER_INT_240;
/**
* Time Interleave Mode timer int 720.
*/
public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_720;
+ FrontendDtmbTimeInterleaveMode.TIMER_INT_720;
/** @hide */
@@ -108,43 +109,37 @@ public final class DtmbFrontendSettings extends FrontendSettings {
/**
* Guard Interval undefined.
*/
- public static final int GUARD_INTERVAL_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.UNDEFINED;
+ public static final int GUARD_INTERVAL_UNDEFINED = FrontendDtmbGuardInterval.UNDEFINED;
/**
* Hardware is able to detect and set Guard Interval automatically.
*/
- public static final int GUARD_INTERVAL_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.AUTO;
+ public static final int GUARD_INTERVAL_AUTO = FrontendDtmbGuardInterval.AUTO;
/**
* PN_420_VARIOUS Guard Interval.
*/
public static final int GUARD_INTERVAL_PN_420_VARIOUS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_VARIOUS;
+ FrontendDtmbGuardInterval.PN_420_VARIOUS;
/**
* PN_595_CONST Guard Interval.
*/
- public static final int GUARD_INTERVAL_PN_595_CONST =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_595_CONST;
+ public static final int GUARD_INTERVAL_PN_595_CONST = FrontendDtmbGuardInterval.PN_595_CONST;
/**
* PN_945_VARIOUS Guard Interval.
*/
public static final int GUARD_INTERVAL_PN_945_VARIOUS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_VARIOUS;
+ FrontendDtmbGuardInterval.PN_945_VARIOUS;
/**
* PN_420_CONST Guard Interval.
*/
- public static final int GUARD_INTERVAL_PN_420_CONST =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_CONST;
+ public static final int GUARD_INTERVAL_PN_420_CONST = FrontendDtmbGuardInterval.PN_420_CONST;
/**
* PN_945_CONST Guard Interval.
*/
- public static final int GUARD_INTERVAL_PN_945_CONST =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_CONST;
+ public static final int GUARD_INTERVAL_PN_945_CONST = FrontendDtmbGuardInterval.PN_945_CONST;
/**
* PN_RESERVED Guard Interval.
*/
- public static final int GUARD_INTERVAL_PN_RESERVED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_RESERVED;
+ public static final int GUARD_INTERVAL_PN_RESERVED = FrontendDtmbGuardInterval.PN_RESERVED;
/** @hide */
@@ -160,38 +155,36 @@ public final class DtmbFrontendSettings extends FrontendSettings {
/**
* Constellation not defined.
*/
- public static final int MODULATION_CONSTELLATION_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.UNDEFINED;
+ public static final int MODULATION_CONSTELLATION_UNDEFINED = FrontendDtmbModulation.UNDEFINED;
/**
* Hardware is able to detect and set Constellation automatically.
*/
- public static final int MODULATION_CONSTELLATION_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.AUTO;
+ public static final int MODULATION_CONSTELLATION_AUTO = FrontendDtmbModulation.AUTO;
/**
* 4QAM Constellation.
*/
public static final int MODULATION_CONSTELLATION_4QAM =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM;
+ FrontendDtmbModulation.CONSTELLATION_4QAM;
/**
* 4QAM_NR Constellation.
*/
public static final int MODULATION_CONSTELLATION_4QAM_NR =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM_NR;
+ FrontendDtmbModulation.CONSTELLATION_4QAM_NR;
/**
* 16QAM Constellation.
*/
public static final int MODULATION_CONSTELLATION_16QAM =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_16QAM;
+ FrontendDtmbModulation.CONSTELLATION_16QAM;
/**
* 32QAM Constellation.
*/
public static final int MODULATION_CONSTELLATION_32QAM =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_32QAM;
+ FrontendDtmbModulation.CONSTELLATION_32QAM;
/**
* 64QAM Constellation.
*/
public static final int MODULATION_CONSTELLATION_64QAM =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_64QAM;
+ FrontendDtmbModulation.CONSTELLATION_64QAM;
/** @hide */
@IntDef(flag = true,
@@ -203,28 +196,23 @@ public final class DtmbFrontendSettings extends FrontendSettings {
/**
* Code rate undefined.
*/
- public static final int CODERATE_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.UNDEFINED;
+ public static final int CODERATE_UNDEFINED = FrontendDtmbCodeRate.UNDEFINED;
/**
* Hardware is able to detect and set code rate automatically.
*/
- public static final int CODERATE_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.AUTO;
+ public static final int CODERATE_AUTO = FrontendDtmbCodeRate.AUTO;
/**
* 2/5 code rate.
*/
- public static final int CODERATE_2_5 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_2_5;
+ public static final int CODERATE_2_5 = FrontendDtmbCodeRate.CODERATE_2_5;
/**
* 3/5 code rate.
*/
- public static final int CODERATE_3_5 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_3_5;
+ public static final int CODERATE_3_5 = FrontendDtmbCodeRate.CODERATE_3_5;
/**
* 4/5 code rate.
*/
- public static final int CODERATE_4_5 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_4_5;
+ public static final int CODERATE_4_5 = FrontendDtmbCodeRate.CODERATE_4_5;
/** @hide */
@IntDef(flag = true,
@@ -237,23 +225,19 @@ public final class DtmbFrontendSettings extends FrontendSettings {
/**
* Transmission Mode undefined.
*/
- public static final int TRANSMISSION_MODE_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.UNDEFINED;
+ public static final int TRANSMISSION_MODE_UNDEFINED = FrontendDtmbTransmissionMode.UNDEFINED;
/**
* Hardware is able to detect and set Transmission Mode automatically
*/
- public static final int TRANSMISSION_MODE_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.AUTO;
+ public static final int TRANSMISSION_MODE_AUTO = FrontendDtmbTransmissionMode.AUTO;
/**
* C1 Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_C1 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C1;
+ public static final int TRANSMISSION_MODE_C1 = FrontendDtmbTransmissionMode.C1;
/**
* C3780 Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_C3780 =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C3780;
+ public static final int TRANSMISSION_MODE_C3780 = FrontendDtmbTransmissionMode.C3780;
private final int mModulation;
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index db28631fe06c..b209d97feee5 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -20,7 +20,11 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendCableTimeInterleaveMode;
+import android.hardware.tv.tuner.FrontendDvbcAnnex;
+import android.hardware.tv.tuner.FrontendDvbcBandwidth;
+import android.hardware.tv.tuner.FrontendDvbcModulation;
+import android.hardware.tv.tuner.FrontendDvbcOuterFec;
import android.media.tv.tuner.TunerVersionChecker;
import android.media.tv.tuner.frontend.FrontendSettings.FrontendSpectralInversion;
@@ -47,31 +51,31 @@ public class DvbcFrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendDvbcModulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendDvbcModulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically
*/
- public static final int MODULATION_AUTO = Constants.FrontendDvbcModulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendDvbcModulation.AUTO;
/**
* 16QAM Modulation.
*/
- public static final int MODULATION_MOD_16QAM = Constants.FrontendDvbcModulation.MOD_16QAM;
+ public static final int MODULATION_MOD_16QAM = FrontendDvbcModulation.MOD_16QAM;
/**
* 32QAM Modulation.
*/
- public static final int MODULATION_MOD_32QAM = Constants.FrontendDvbcModulation.MOD_32QAM;
+ public static final int MODULATION_MOD_32QAM = FrontendDvbcModulation.MOD_32QAM;
/**
* 64QAM Modulation.
*/
- public static final int MODULATION_MOD_64QAM = Constants.FrontendDvbcModulation.MOD_64QAM;
+ public static final int MODULATION_MOD_64QAM = FrontendDvbcModulation.MOD_64QAM;
/**
* 128QAM Modulation.
*/
- public static final int MODULATION_MOD_128QAM = Constants.FrontendDvbcModulation.MOD_128QAM;
+ public static final int MODULATION_MOD_128QAM = FrontendDvbcModulation.MOD_128QAM;
/**
* 256QAM Modulation.
*/
- public static final int MODULATION_MOD_256QAM = Constants.FrontendDvbcModulation.MOD_256QAM;
+ public static final int MODULATION_MOD_256QAM = FrontendDvbcModulation.MOD_256QAM;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -82,16 +86,15 @@ public class DvbcFrontendSettings extends FrontendSettings {
/**
* Outer Forward Error Correction (FEC) Type undefined.
*/
- public static final int OUTER_FEC_UNDEFINED = Constants.FrontendDvbcOuterFec.UNDEFINED;
+ public static final int OUTER_FEC_UNDEFINED = FrontendDvbcOuterFec.UNDEFINED;
/**
* None Outer Forward Error Correction (FEC) Type.
*/
- public static final int OUTER_FEC_OUTER_FEC_NONE =
- Constants.FrontendDvbcOuterFec.OUTER_FEC_NONE;
+ public static final int OUTER_FEC_OUTER_FEC_NONE = FrontendDvbcOuterFec.OUTER_FEC_NONE;
/**
* RS Outer Forward Error Correction (FEC) Type.
*/
- public static final int OUTER_FEC_OUTER_FEC_RS = Constants.FrontendDvbcOuterFec.OUTER_FEC_RS;
+ public static final int OUTER_FEC_OUTER_FEC_RS = FrontendDvbcOuterFec.OUTER_FEC_RS;
/** @hide */
@@ -104,19 +107,19 @@ public class DvbcFrontendSettings extends FrontendSettings {
/**
* Annex Type undefined.
*/
- public static final int ANNEX_UNDEFINED = Constants.FrontendDvbcAnnex.UNDEFINED;
+ public static final int ANNEX_UNDEFINED = FrontendDvbcAnnex.UNDEFINED;
/**
* Annex Type A.
*/
- public static final int ANNEX_A = Constants.FrontendDvbcAnnex.A;
+ public static final int ANNEX_A = FrontendDvbcAnnex.A;
/**
* Annex Type B.
*/
- public static final int ANNEX_B = Constants.FrontendDvbcAnnex.B;
+ public static final int ANNEX_B = FrontendDvbcAnnex.B;
/**
* Annex Type C.
*/
- public static final int ANNEX_C = Constants.FrontendDvbcAnnex.C;
+ public static final int ANNEX_C = FrontendDvbcAnnex.C;
/**
@@ -137,7 +140,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
*/
@Deprecated
public static final int SPECTRAL_INVERSION_UNDEFINED =
- Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+ android.hardware.tv.tuner.FrontendSpectralInversion.UNDEFINED;
/**
* Normal Spectral Inversion.
*
@@ -145,7 +148,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
*/
@Deprecated
public static final int SPECTRAL_INVERSION_NORMAL =
- Constants.FrontendDvbcSpectralInversion.NORMAL;
+ android.hardware.tv.tuner.FrontendSpectralInversion.NORMAL;
/**
* Inverted Spectral Inversion.
*
@@ -153,7 +156,7 @@ public class DvbcFrontendSettings extends FrontendSettings {
*/
@Deprecated
public static final int SPECTRAL_INVERSION_INVERTED =
- Constants.FrontendDvbcSpectralInversion.INVERTED;
+ android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED;
/** @hide */
@IntDef(flag = true,
@@ -171,57 +174,56 @@ public class DvbcFrontendSettings extends FrontendSettings {
* Time interleave mode undefined.
*/
public static final int TIME_INTERLEAVE_MODE_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.UNDEFINED;
+ FrontendCableTimeInterleaveMode.UNDEFINED;
/**
* Hardware is able to detect and set Time Interleave Mode automatically.
*/
- public static final int TIME_INTERLEAVE_MODE_AUTO =
- android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.AUTO;
+ public static final int TIME_INTERLEAVE_MODE_AUTO = FrontendCableTimeInterleaveMode.AUTO;
/**
* 128/1/0 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_128_1_0 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_0;
+ public static final int TIME_INTERLEAVE_MODE_128_1_0 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_0;
/**
* 128/1/1 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_128_1_1 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_1;
+ public static final int TIME_INTERLEAVE_MODE_128_1_1 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_1;
/**
* 64/2 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_64_2 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_64_2;
+ public static final int TIME_INTERLEAVE_MODE_64_2 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_64_2;
/**
* 32/4 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_32_4 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_32_4;
+ public static final int TIME_INTERLEAVE_MODE_32_4 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_32_4;
/**
* 16/8 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_16_8 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_16_8;
+ public static final int TIME_INTERLEAVE_MODE_16_8 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_16_8;
/**
* 8/16 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_8_16 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_8_16;
+ public static final int TIME_INTERLEAVE_MODE_8_16 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_8_16;
/**
* 128/2 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_128_2 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_128_2;
+ public static final int TIME_INTERLEAVE_MODE_128_2 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_128_2;
/**
* 128/3 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_128_3 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_128_3;
+ public static final int TIME_INTERLEAVE_MODE_128_3 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_128_3;
/**
* 128/4 Time Interleave Mode.
*/
- public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants
- .FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
+ public static final int TIME_INTERLEAVE_MODE_128_4 =
+ FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
/** @hide */
@IntDef(flag = true,
@@ -234,28 +236,23 @@ public class DvbcFrontendSettings extends FrontendSettings {
/**
* Bandwidth undefined.
*/
- public static final int BANDWIDTH_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.UNDEFINED;
+ public static final int BANDWIDTH_UNDEFINED = FrontendDvbcBandwidth.UNDEFINED;
/**
* 5 MHz bandwidth.
*/
- public static final int BANDWIDTH_5MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_5MHZ;
+ public static final int BANDWIDTH_5MHZ = FrontendDvbcBandwidth.BANDWIDTH_5MHZ;
/**
* 6 MHz bandwidth.
*/
- public static final int BANDWIDTH_6MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_6MHZ;
+ public static final int BANDWIDTH_6MHZ = FrontendDvbcBandwidth.BANDWIDTH_6MHZ;
/**
* 7 MHz bandwidth.
*/
- public static final int BANDWIDTH_7MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_7MHZ;
+ public static final int BANDWIDTH_7MHZ = FrontendDvbcBandwidth.BANDWIDTH_7MHZ;
/**
* 8 MHz bandwidth.
*/
- public static final int BANDWIDTH_8MHZ =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_8MHZ;
+ public static final int BANDWIDTH_8MHZ = FrontendDvbcBandwidth.BANDWIDTH_8MHZ;
private final int mModulation;
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index f68d554c50c2..6e3d98a11a2b 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -21,7 +21,12 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendDvbsModulation;
+import android.hardware.tv.tuner.FrontendDvbsPilot;
+import android.hardware.tv.tuner.FrontendDvbsRolloff;
+import android.hardware.tv.tuner.FrontendDvbsScanType;
+import android.hardware.tv.tuner.FrontendDvbsStandard;
+import android.hardware.tv.tuner.FrontendDvbsVcmMode;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerVersionChecker;
@@ -47,32 +52,27 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Dvbs scan type undefined.
*/
- public static final int SCAN_TYPE_UNDEFINED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNDEFINED;
+ public static final int SCAN_TYPE_UNDEFINED = FrontendDvbsScanType.UNDEFINED;
/**
* Dvbs scan type DIRECT.
*/
- public static final int SCAN_TYPE_DIRECT =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DIRECT;
+ public static final int SCAN_TYPE_DIRECT = FrontendDvbsScanType.DIRECT;
/**
* Dvbs scan type DISEQC.
*/
- public static final int SCAN_TYPE_DISEQC =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DISEQC;
+ public static final int SCAN_TYPE_DISEQC = FrontendDvbsScanType.DISEQC;
/**
* Dvbs scan type UNICABLE.
*/
- public static final int SCAN_TYPE_UNICABLE =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNICABLE;
+ public static final int SCAN_TYPE_UNICABLE = FrontendDvbsScanType.UNICABLE;
/**
* Dvbs scan type JESS.
*/
- public static final int SCAN_TYPE_JESS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.JESS;
+ public static final int SCAN_TYPE_JESS = FrontendDvbsScanType.JESS;
/** @hide */
@IntDef(flag = true,
@@ -88,63 +88,63 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendDvbsModulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendDvbsModulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically
*/
- public static final int MODULATION_AUTO = Constants.FrontendDvbsModulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendDvbsModulation.AUTO;
/**
* QPSK Modulation.
*/
- public static final int MODULATION_MOD_QPSK = Constants.FrontendDvbsModulation.MOD_QPSK;
+ public static final int MODULATION_MOD_QPSK = FrontendDvbsModulation.MOD_QPSK;
/**
* 8PSK Modulation.
*/
- public static final int MODULATION_MOD_8PSK = Constants.FrontendDvbsModulation.MOD_8PSK;
+ public static final int MODULATION_MOD_8PSK = FrontendDvbsModulation.MOD_8PSK;
/**
* 16QAM Modulation.
*/
- public static final int MODULATION_MOD_16QAM = Constants.FrontendDvbsModulation.MOD_16QAM;
+ public static final int MODULATION_MOD_16QAM = FrontendDvbsModulation.MOD_16QAM;
/**
* 16PSK Modulation.
*/
- public static final int MODULATION_MOD_16PSK = Constants.FrontendDvbsModulation.MOD_16PSK;
+ public static final int MODULATION_MOD_16PSK = FrontendDvbsModulation.MOD_16PSK;
/**
* 32PSK Modulation.
*/
- public static final int MODULATION_MOD_32PSK = Constants.FrontendDvbsModulation.MOD_32PSK;
+ public static final int MODULATION_MOD_32PSK = FrontendDvbsModulation.MOD_32PSK;
/**
* ACM Modulation.
*/
- public static final int MODULATION_MOD_ACM = Constants.FrontendDvbsModulation.MOD_ACM;
+ public static final int MODULATION_MOD_ACM = FrontendDvbsModulation.MOD_ACM;
/**
* 8APSK Modulation.
*/
- public static final int MODULATION_MOD_8APSK = Constants.FrontendDvbsModulation.MOD_8APSK;
+ public static final int MODULATION_MOD_8APSK = FrontendDvbsModulation.MOD_8APSK;
/**
* 16APSK Modulation.
*/
- public static final int MODULATION_MOD_16APSK = Constants.FrontendDvbsModulation.MOD_16APSK;
+ public static final int MODULATION_MOD_16APSK = FrontendDvbsModulation.MOD_16APSK;
/**
* 32APSK Modulation.
*/
- public static final int MODULATION_MOD_32APSK = Constants.FrontendDvbsModulation.MOD_32APSK;
+ public static final int MODULATION_MOD_32APSK = FrontendDvbsModulation.MOD_32APSK;
/**
* 64APSK Modulation.
*/
- public static final int MODULATION_MOD_64APSK = Constants.FrontendDvbsModulation.MOD_64APSK;
+ public static final int MODULATION_MOD_64APSK = FrontendDvbsModulation.MOD_64APSK;
/**
* 128APSK Modulation.
*/
- public static final int MODULATION_MOD_128APSK = Constants.FrontendDvbsModulation.MOD_128APSK;
+ public static final int MODULATION_MOD_128APSK = FrontendDvbsModulation.MOD_128APSK;
/**
* 256APSK Modulation.
*/
- public static final int MODULATION_MOD_256APSK = Constants.FrontendDvbsModulation.MOD_256APSK;
+ public static final int MODULATION_MOD_256APSK = FrontendDvbsModulation.MOD_256APSK;
/**
* Reversed Modulation.
*/
- public static final int MODULATION_MOD_RESERVED = Constants.FrontendDvbsModulation.MOD_RESERVED;
+ public static final int MODULATION_MOD_RESERVED = FrontendDvbsModulation.MOD_RESERVED;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -156,31 +156,31 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Rolloff range undefined.
*/
- public static final int ROLLOFF_UNDEFINED = Constants.FrontendDvbsRolloff.UNDEFINED;
+ public static final int ROLLOFF_UNDEFINED = FrontendDvbsRolloff.UNDEFINED;
/**
* Rolloff range 0,35.
*/
- public static final int ROLLOFF_0_35 = Constants.FrontendDvbsRolloff.ROLLOFF_0_35;
+ public static final int ROLLOFF_0_35 = FrontendDvbsRolloff.ROLLOFF_0_35;
/**
* Rolloff range 0,25.
*/
- public static final int ROLLOFF_0_25 = Constants.FrontendDvbsRolloff.ROLLOFF_0_25;
+ public static final int ROLLOFF_0_25 = FrontendDvbsRolloff.ROLLOFF_0_25;
/**
* Rolloff range 0,20.
*/
- public static final int ROLLOFF_0_20 = Constants.FrontendDvbsRolloff.ROLLOFF_0_20;
+ public static final int ROLLOFF_0_20 = FrontendDvbsRolloff.ROLLOFF_0_20;
/**
* Rolloff range 0,15.
*/
- public static final int ROLLOFF_0_15 = Constants.FrontendDvbsRolloff.ROLLOFF_0_15;
+ public static final int ROLLOFF_0_15 = FrontendDvbsRolloff.ROLLOFF_0_15;
/**
* Rolloff range 0,10.
*/
- public static final int ROLLOFF_0_10 = Constants.FrontendDvbsRolloff.ROLLOFF_0_10;
+ public static final int ROLLOFF_0_10 = FrontendDvbsRolloff.ROLLOFF_0_10;
/**
* Rolloff range 0,5.
*/
- public static final int ROLLOFF_0_5 = Constants.FrontendDvbsRolloff.ROLLOFF_0_5;
+ public static final int ROLLOFF_0_5 = FrontendDvbsRolloff.ROLLOFF_0_5;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -191,19 +191,19 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Pilot mode undefined.
*/
- public static final int PILOT_UNDEFINED = Constants.FrontendDvbsPilot.UNDEFINED;
+ public static final int PILOT_UNDEFINED = FrontendDvbsPilot.UNDEFINED;
/**
* Pilot mode on.
*/
- public static final int PILOT_ON = Constants.FrontendDvbsPilot.ON;
+ public static final int PILOT_ON = FrontendDvbsPilot.ON;
/**
* Pilot mode off.
*/
- public static final int PILOT_OFF = Constants.FrontendDvbsPilot.OFF;
+ public static final int PILOT_OFF = FrontendDvbsPilot.OFF;
/**
* Pilot mode auto.
*/
- public static final int PILOT_AUTO = Constants.FrontendDvbsPilot.AUTO;
+ public static final int PILOT_AUTO = FrontendDvbsPilot.AUTO;
/** @hide */
@@ -216,19 +216,19 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* Standard undefined.
*/
- public static final int STANDARD_AUTO = Constants.FrontendDvbsStandard.AUTO;
+ public static final int STANDARD_AUTO = FrontendDvbsStandard.AUTO;
/**
* Standard S.
*/
- public static final int STANDARD_S = Constants.FrontendDvbsStandard.S;
+ public static final int STANDARD_S = FrontendDvbsStandard.S;
/**
* Standard S2.
*/
- public static final int STANDARD_S2 = Constants.FrontendDvbsStandard.S2;
+ public static final int STANDARD_S2 = FrontendDvbsStandard.S2;
/**
* Standard S2X.
*/
- public static final int STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+ public static final int STANDARD_S2X = FrontendDvbsStandard.S2X;
/** @hide */
@IntDef(prefix = "VCM_MODE_",
@@ -239,15 +239,15 @@ public class DvbsFrontendSettings extends FrontendSettings {
/**
* VCM mode undefined.
*/
- public static final int VCM_MODE_UNDEFINED = Constants.FrontendDvbsVcmMode.UNDEFINED;
+ public static final int VCM_MODE_UNDEFINED = FrontendDvbsVcmMode.UNDEFINED;
/**
* Auto VCM mode.
*/
- public static final int VCM_MODE_AUTO = Constants.FrontendDvbsVcmMode.AUTO;
+ public static final int VCM_MODE_AUTO = FrontendDvbsVcmMode.AUTO;
/**
* Manual VCM mode.
*/
- public static final int VCM_MODE_MANUAL = Constants.FrontendDvbsVcmMode.MANUAL;
+ public static final int VCM_MODE_MANUAL = FrontendDvbsVcmMode.MANUAL;
private final int mModulation;
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 536c7b82d0af..5735b39b0a56 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -20,7 +20,14 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendDvbtBandwidth;
+import android.hardware.tv.tuner.FrontendDvbtCoderate;
+import android.hardware.tv.tuner.FrontendDvbtConstellation;
+import android.hardware.tv.tuner.FrontendDvbtGuardInterval;
+import android.hardware.tv.tuner.FrontendDvbtHierarchy;
+import android.hardware.tv.tuner.FrontendDvbtPlpMode;
+import android.hardware.tv.tuner.FrontendDvbtStandard;
+import android.hardware.tv.tuner.FrontendDvbtTransmissionMode;
import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
@@ -46,51 +53,49 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Transmission Mode undefined.
*/
- public static final int TRANSMISSION_MODE_UNDEFINED =
- Constants.FrontendDvbtTransmissionMode.UNDEFINED;
+ public static final int TRANSMISSION_MODE_UNDEFINED = FrontendDvbtTransmissionMode.UNDEFINED;
/**
* Hardware is able to detect and set Transmission Mode automatically
*/
- public static final int TRANSMISSION_MODE_AUTO = Constants.FrontendDvbtTransmissionMode.AUTO;
+ public static final int TRANSMISSION_MODE_AUTO = FrontendDvbtTransmissionMode.AUTO;
/**
* 2K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_2K = Constants.FrontendDvbtTransmissionMode.MODE_2K;
+ public static final int TRANSMISSION_MODE_2K = FrontendDvbtTransmissionMode.MODE_2K;
/**
* 8K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_8K = Constants.FrontendDvbtTransmissionMode.MODE_8K;
+ public static final int TRANSMISSION_MODE_8K = FrontendDvbtTransmissionMode.MODE_8K;
/**
* 4K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_4K = Constants.FrontendDvbtTransmissionMode.MODE_4K;
+ public static final int TRANSMISSION_MODE_4K = FrontendDvbtTransmissionMode.MODE_4K;
/**
* 1K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_1K = Constants.FrontendDvbtTransmissionMode.MODE_1K;
+ public static final int TRANSMISSION_MODE_1K = FrontendDvbtTransmissionMode.MODE_1K;
/**
* 16K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_16K = Constants.FrontendDvbtTransmissionMode.MODE_16K;
+ public static final int TRANSMISSION_MODE_16K = FrontendDvbtTransmissionMode.MODE_16K;
/**
* 32K Transmission Mode.
*/
- public static final int TRANSMISSION_MODE_32K = Constants.FrontendDvbtTransmissionMode.MODE_32K;
+ public static final int TRANSMISSION_MODE_32K = FrontendDvbtTransmissionMode.MODE_32K;
/**
* 8K Transmission Extended Mode.
*/
- public static final int TRANSMISSION_MODE_EXTENDED_8K =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_8K_E;
+ public static final int TRANSMISSION_MODE_EXTENDED_8K = FrontendDvbtTransmissionMode.MODE_8K_E;
/**
* 16K Transmission Extended Mode.
*/
public static final int TRANSMISSION_MODE_EXTENDED_16K =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_16K_E;
+ FrontendDvbtTransmissionMode.MODE_16K_E;
/**
* 32K Transmission Extended Mode.
*/
public static final int TRANSMISSION_MODE_EXTENDED_32K =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_32K_E;
+ FrontendDvbtTransmissionMode.MODE_32K_E;
/** @hide */
@IntDef(flag = true,
@@ -103,35 +108,35 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Bandwidth undefined.
*/
- public static final int BANDWIDTH_UNDEFINED = Constants.FrontendDvbtBandwidth.UNDEFINED;
+ public static final int BANDWIDTH_UNDEFINED = FrontendDvbtBandwidth.UNDEFINED;
/**
* Hardware is able to detect and set Bandwidth automatically.
*/
- public static final int BANDWIDTH_AUTO = Constants.FrontendDvbtBandwidth.AUTO;
+ public static final int BANDWIDTH_AUTO = FrontendDvbtBandwidth.AUTO;
/**
* 8 MHz bandwidth.
*/
- public static final int BANDWIDTH_8MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_8MHZ;
+ public static final int BANDWIDTH_8MHZ = FrontendDvbtBandwidth.BANDWIDTH_8MHZ;
/**
* 7 MHz bandwidth.
*/
- public static final int BANDWIDTH_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_7MHZ;
+ public static final int BANDWIDTH_7MHZ = FrontendDvbtBandwidth.BANDWIDTH_7MHZ;
/**
* 6 MHz bandwidth.
*/
- public static final int BANDWIDTH_6MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_6MHZ;
+ public static final int BANDWIDTH_6MHZ = FrontendDvbtBandwidth.BANDWIDTH_6MHZ;
/**
* 5 MHz bandwidth.
*/
- public static final int BANDWIDTH_5MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
+ public static final int BANDWIDTH_5MHZ = FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
/**
* 1,7 MHz bandwidth.
*/
- public static final int BANDWIDTH_1_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
+ public static final int BANDWIDTH_1_7MHZ = FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
/**
* 10 MHz bandwidth.
*/
- public static final int BANDWIDTH_10MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_10MHZ;
+ public static final int BANDWIDTH_10MHZ = FrontendDvbtBandwidth.BANDWIDTH_10MHZ;
/** @hide */
@@ -147,55 +152,44 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Constellation not defined.
*/
- public static final int CONSTELLATION_UNDEFINED = Constants.FrontendDvbtConstellation.UNDEFINED;
+ public static final int CONSTELLATION_UNDEFINED = FrontendDvbtConstellation.UNDEFINED;
/**
* Hardware is able to detect and set Constellation automatically.
*/
- public static final int CONSTELLATION_AUTO = Constants.FrontendDvbtConstellation.AUTO;
+ public static final int CONSTELLATION_AUTO = FrontendDvbtConstellation.AUTO;
/**
* QPSK Constellation.
*/
- public static final int CONSTELLATION_QPSK =
- Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
+ public static final int CONSTELLATION_QPSK = FrontendDvbtConstellation.CONSTELLATION_QPSK;
/**
* 16QAM Constellation.
*/
- public static final int CONSTELLATION_16QAM =
- Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
+ public static final int CONSTELLATION_16QAM = FrontendDvbtConstellation.CONSTELLATION_16QAM;
/**
* 64QAM Constellation.
*/
- public static final int CONSTELLATION_64QAM =
- Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
+ public static final int CONSTELLATION_64QAM = FrontendDvbtConstellation.CONSTELLATION_64QAM;
/**
* 256QAM Constellation.
*/
- public static final int CONSTELLATION_256QAM =
- Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
+ public static final int CONSTELLATION_256QAM = FrontendDvbtConstellation.CONSTELLATION_256QAM;
/**
* QPSK Rotated Constellation.
*/
- public static final int CONSTELLATION_QPSK_R =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
- .CONSTELLATION_QPSK_R;
+ public static final int CONSTELLATION_QPSK_R = FrontendDvbtConstellation.CONSTELLATION_QPSK_R;
/**
* 16QAM Rotated Constellation.
*/
- public static final int CONSTELLATION_16QAM_R =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
- .CONSTELLATION_16QAM_R;
+ public static final int CONSTELLATION_16QAM_R = FrontendDvbtConstellation.CONSTELLATION_16QAM_R;
/**
* 64QAM Rotated Constellation.
*/
- public static final int CONSTELLATION_64QAM_R =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
- .CONSTELLATION_64QAM_R;
+ public static final int CONSTELLATION_64QAM_R = FrontendDvbtConstellation.CONSTELLATION_64QAM_R;
/**
* 256QAM Rotated Constellation.
*/
public static final int CONSTELLATION_256QAM_R =
- android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation
- .CONSTELLATION_256QAM_R;
+ FrontendDvbtConstellation.CONSTELLATION_256QAM_R;
/** @hide */
@IntDef(flag = true,
@@ -209,48 +203,43 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Hierarchy undefined.
*/
- public static final int HIERARCHY_UNDEFINED = Constants.FrontendDvbtHierarchy.UNDEFINED;
+ public static final int HIERARCHY_UNDEFINED = FrontendDvbtHierarchy.UNDEFINED;
/**
* Hardware is able to detect and set Hierarchy automatically.
*/
- public static final int HIERARCHY_AUTO = Constants.FrontendDvbtHierarchy.AUTO;
+ public static final int HIERARCHY_AUTO = FrontendDvbtHierarchy.AUTO;
/**
* Non-native Hierarchy
*/
- public static final int HIERARCHY_NON_NATIVE =
- Constants.FrontendDvbtHierarchy.HIERARCHY_NON_NATIVE;
+ public static final int HIERARCHY_NON_NATIVE = FrontendDvbtHierarchy.HIERARCHY_NON_NATIVE;
/**
* 1-native Hierarchy
*/
- public static final int HIERARCHY_1_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_1_NATIVE;
+ public static final int HIERARCHY_1_NATIVE = FrontendDvbtHierarchy.HIERARCHY_1_NATIVE;
/**
* 2-native Hierarchy
*/
- public static final int HIERARCHY_2_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_2_NATIVE;
+ public static final int HIERARCHY_2_NATIVE = FrontendDvbtHierarchy.HIERARCHY_2_NATIVE;
/**
* 4-native Hierarchy
*/
- public static final int HIERARCHY_4_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_4_NATIVE;
+ public static final int HIERARCHY_4_NATIVE = FrontendDvbtHierarchy.HIERARCHY_4_NATIVE;
/**
* Non-indepth Hierarchy
*/
- public static final int HIERARCHY_NON_INDEPTH =
- Constants.FrontendDvbtHierarchy.HIERARCHY_NON_INDEPTH;
+ public static final int HIERARCHY_NON_INDEPTH = FrontendDvbtHierarchy.HIERARCHY_NON_INDEPTH;
/**
* 1-indepth Hierarchy
*/
- public static final int HIERARCHY_1_INDEPTH =
- Constants.FrontendDvbtHierarchy.HIERARCHY_1_INDEPTH;
+ public static final int HIERARCHY_1_INDEPTH = FrontendDvbtHierarchy.HIERARCHY_1_INDEPTH;
/**
* 2-indepth Hierarchy
*/
- public static final int HIERARCHY_2_INDEPTH =
- Constants.FrontendDvbtHierarchy.HIERARCHY_2_INDEPTH;
+ public static final int HIERARCHY_2_INDEPTH = FrontendDvbtHierarchy.HIERARCHY_2_INDEPTH;
/**
* 4-indepth Hierarchy
*/
- public static final int HIERARCHY_4_INDEPTH =
- Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
+ public static final int HIERARCHY_4_INDEPTH = FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
/** @hide */
@@ -264,48 +253,47 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Code rate undefined.
*/
- public static final int CODERATE_UNDEFINED =
- Constants.FrontendDvbtCoderate.UNDEFINED;
+ public static final int CODERATE_UNDEFINED = FrontendDvbtCoderate.UNDEFINED;
/**
* Hardware is able to detect and set code rate automatically.
*/
- public static final int CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
+ public static final int CODERATE_AUTO = FrontendDvbtCoderate.AUTO;
/**
* 1/2 code rate.
*/
- public static final int CODERATE_1_2 = Constants.FrontendDvbtCoderate.CODERATE_1_2;
+ public static final int CODERATE_1_2 = FrontendDvbtCoderate.CODERATE_1_2;
/**
* 2/3 code rate.
*/
- public static final int CODERATE_2_3 = Constants.FrontendDvbtCoderate.CODERATE_2_3;
+ public static final int CODERATE_2_3 = FrontendDvbtCoderate.CODERATE_2_3;
/**
* 3/4 code rate.
*/
- public static final int CODERATE_3_4 = Constants.FrontendDvbtCoderate.CODERATE_3_4;
+ public static final int CODERATE_3_4 = FrontendDvbtCoderate.CODERATE_3_4;
/**
* 5/6 code rate.
*/
- public static final int CODERATE_5_6 = Constants.FrontendDvbtCoderate.CODERATE_5_6;
+ public static final int CODERATE_5_6 = FrontendDvbtCoderate.CODERATE_5_6;
/**
* 7/8 code rate.
*/
- public static final int CODERATE_7_8 = Constants.FrontendDvbtCoderate.CODERATE_7_8;
+ public static final int CODERATE_7_8 = FrontendDvbtCoderate.CODERATE_7_8;
/**
* 4/5 code rate.
*/
- public static final int CODERATE_3_5 = Constants.FrontendDvbtCoderate.CODERATE_3_5;
+ public static final int CODERATE_3_5 = FrontendDvbtCoderate.CODERATE_3_5;
/**
* 4/5 code rate.
*/
- public static final int CODERATE_4_5 = Constants.FrontendDvbtCoderate.CODERATE_4_5;
+ public static final int CODERATE_4_5 = FrontendDvbtCoderate.CODERATE_4_5;
/**
* 6/7 code rate.
*/
- public static final int CODERATE_6_7 = Constants.FrontendDvbtCoderate.CODERATE_6_7;
+ public static final int CODERATE_6_7 = FrontendDvbtCoderate.CODERATE_6_7;
/**
* 8/9 code rate.
*/
- public static final int CODERATE_8_9 = Constants.FrontendDvbtCoderate.CODERATE_8_9;
+ public static final int CODERATE_8_9 = FrontendDvbtCoderate.CODERATE_8_9;
/** @hide */
@IntDef(flag = true,
@@ -323,46 +311,39 @@ public class DvbtFrontendSettings extends FrontendSettings {
* Guard Interval undefined.
*/
public static final int GUARD_INTERVAL_UNDEFINED =
- Constants.FrontendDvbtGuardInterval.UNDEFINED;
+ FrontendDvbtGuardInterval.UNDEFINED;
/**
* Hardware is able to detect and set Guard Interval automatically.
*/
- public static final int GUARD_INTERVAL_AUTO = Constants.FrontendDvbtGuardInterval.AUTO;
+ public static final int GUARD_INTERVAL_AUTO = FrontendDvbtGuardInterval.AUTO;
/**
* 1/32 Guard Interval.
*/
- public static final int GUARD_INTERVAL_1_32 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
+ public static final int GUARD_INTERVAL_1_32 = FrontendDvbtGuardInterval.INTERVAL_1_32;
/**
* 1/16 Guard Interval.
*/
- public static final int GUARD_INTERVAL_1_16 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
+ public static final int GUARD_INTERVAL_1_16 = FrontendDvbtGuardInterval.INTERVAL_1_16;
/**
* 1/8 Guard Interval.
*/
- public static final int GUARD_INTERVAL_1_8 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
+ public static final int GUARD_INTERVAL_1_8 = FrontendDvbtGuardInterval.INTERVAL_1_8;
/**
* 1/4 Guard Interval.
*/
- public static final int GUARD_INTERVAL_1_4 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
+ public static final int GUARD_INTERVAL_1_4 = FrontendDvbtGuardInterval.INTERVAL_1_4;
/**
* 1/128 Guard Interval.
*/
- public static final int GUARD_INTERVAL_1_128 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
+ public static final int GUARD_INTERVAL_1_128 = FrontendDvbtGuardInterval.INTERVAL_1_128;
/**
* 19/128 Guard Interval.
*/
- public static final int GUARD_INTERVAL_19_128 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
+ public static final int GUARD_INTERVAL_19_128 = FrontendDvbtGuardInterval.INTERVAL_19_128;
/**
* 19/256 Guard Interval.
*/
- public static final int GUARD_INTERVAL_19_256 =
- Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
+ public static final int GUARD_INTERVAL_19_256 = FrontendDvbtGuardInterval.INTERVAL_19_256;
/** @hide */
@IntDef(flag = true,
@@ -375,15 +356,15 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Hardware is able to detect and set Standard automatically.
*/
- public static final int STANDARD_AUTO = Constants.FrontendDvbtStandard.AUTO;
+ public static final int STANDARD_AUTO = FrontendDvbtStandard.AUTO;
/**
* T standard.
*/
- public static final int STANDARD_T = Constants.FrontendDvbtStandard.T;
+ public static final int STANDARD_T = FrontendDvbtStandard.T;
/**
* T2 standard.
*/
- public static final int STANDARD_T2 = Constants.FrontendDvbtStandard.T2;
+ public static final int STANDARD_T2 = FrontendDvbtStandard.T2;
/** @hide */
@IntDef(prefix = "PLP_MODE_",
@@ -394,15 +375,15 @@ public class DvbtFrontendSettings extends FrontendSettings {
/**
* Physical Layer Pipe (PLP) Mode undefined.
*/
- public static final int PLP_MODE_UNDEFINED = Constants.FrontendDvbtPlpMode.UNDEFINED;
+ public static final int PLP_MODE_UNDEFINED = FrontendDvbtPlpMode.UNDEFINED;
/**
* Hardware is able to detect and set Physical Layer Pipe (PLP) Mode automatically.
*/
- public static final int PLP_MODE_AUTO = Constants.FrontendDvbtPlpMode.AUTO;
+ public static final int PLP_MODE_AUTO = FrontendDvbtPlpMode.AUTO;
/**
* Physical Layer Pipe (PLP) manual Mode.
*/
- public static final int PLP_MODE_MANUAL = Constants.FrontendDvbtPlpMode.MANUAL;
+ public static final int PLP_MODE_MANUAL = FrontendDvbtPlpMode.MANUAL;
private int mTransmissionMode;
private final int mBandwidth;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 4bfe8078be35..4a31686e0895 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -20,7 +20,8 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.LongDef;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendInnerFec;
+import android.hardware.tv.tuner.FrontendType;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.TunerVersionChecker;
@@ -44,47 +45,47 @@ public abstract class FrontendSettings {
/**
* Undefined frontend type.
*/
- public static final int TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+ public static final int TYPE_UNDEFINED = FrontendType.UNDEFINED;
/**
* Analog frontend type.
*/
- public static final int TYPE_ANALOG = Constants.FrontendType.ANALOG;
+ public static final int TYPE_ANALOG = FrontendType.ANALOG;
/**
* Advanced Television Systems Committee (ATSC) frontend type.
*/
- public static final int TYPE_ATSC = Constants.FrontendType.ATSC;
+ public static final int TYPE_ATSC = FrontendType.ATSC;
/**
* Advanced Television Systems Committee 3.0 (ATSC-3) frontend type.
*/
- public static final int TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+ public static final int TYPE_ATSC3 = FrontendType.ATSC3;
/**
* Digital Video Broadcasting-Cable (DVB-C) frontend type.
*/
- public static final int TYPE_DVBC = Constants.FrontendType.DVBC;
+ public static final int TYPE_DVBC = FrontendType.DVBC;
/**
* Digital Video Broadcasting-Satellite (DVB-S) frontend type.
*/
- public static final int TYPE_DVBS = Constants.FrontendType.DVBS;
+ public static final int TYPE_DVBS = FrontendType.DVBS;
/**
* Digital Video Broadcasting-Terrestrial (DVB-T) frontend type.
*/
- public static final int TYPE_DVBT = Constants.FrontendType.DVBT;
+ public static final int TYPE_DVBT = FrontendType.DVBT;
/**
* Integrated Services Digital Broadcasting-Satellite (ISDB-S) frontend type.
*/
- public static final int TYPE_ISDBS = Constants.FrontendType.ISDBS;
+ public static final int TYPE_ISDBS = FrontendType.ISDBS;
/**
* Integrated Services Digital Broadcasting-Satellite 3 (ISDB-S3) frontend type.
*/
- public static final int TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+ public static final int TYPE_ISDBS3 = FrontendType.ISDBS3;
/**
* Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type.
*/
- public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT;
+ public static final int TYPE_ISDBT = FrontendType.ISDBT;
/**
* Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type.
*/
- public static final int TYPE_DTMB = android.hardware.tv.tuner.V1_1.Constants.FrontendType.DTMB;
+ public static final int TYPE_DTMB = FrontendType.DTMB;
/** @hide */
@@ -101,151 +102,151 @@ public abstract class FrontendSettings {
/**
* FEC not defined.
*/
- public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED;
+ public static final long FEC_UNDEFINED = FrontendInnerFec.FEC_UNDEFINED;
/**
* hardware is able to detect and set FEC automatically.
*/
- public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO;
+ public static final long FEC_AUTO = FrontendInnerFec.AUTO;
/**
* 1/2 conv. code rate.
*/
- public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2;
+ public static final long FEC_1_2 = FrontendInnerFec.FEC_1_2;
/**
* 1/3 conv. code rate.
*/
- public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3;
+ public static final long FEC_1_3 = FrontendInnerFec.FEC_1_3;
/**
* 1/4 conv. code rate.
*/
- public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4;
+ public static final long FEC_1_4 = FrontendInnerFec.FEC_1_4;
/**
* 1/5 conv. code rate.
*/
- public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5;
+ public static final long FEC_1_5 = FrontendInnerFec.FEC_1_5;
/**
* 2/3 conv. code rate.
*/
- public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3;
+ public static final long FEC_2_3 = FrontendInnerFec.FEC_2_3;
/**
* 2/5 conv. code rate.
*/
- public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5;
+ public static final long FEC_2_5 = FrontendInnerFec.FEC_2_5;
/**
* 2/9 conv. code rate.
*/
- public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9;
+ public static final long FEC_2_9 = FrontendInnerFec.FEC_2_9;
/**
* 3/4 conv. code rate.
*/
- public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4;
+ public static final long FEC_3_4 = FrontendInnerFec.FEC_3_4;
/**
* 3/5 conv. code rate.
*/
- public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5;
+ public static final long FEC_3_5 = FrontendInnerFec.FEC_3_5;
/**
* 4/5 conv. code rate.
*/
- public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5;
+ public static final long FEC_4_5 = FrontendInnerFec.FEC_4_5;
/**
* 4/15 conv. code rate.
*/
- public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15;
+ public static final long FEC_4_15 = FrontendInnerFec.FEC_4_15;
/**
* 5/6 conv. code rate.
*/
- public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6;
+ public static final long FEC_5_6 = FrontendInnerFec.FEC_5_6;
/**
* 5/9 conv. code rate.
*/
- public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9;
+ public static final long FEC_5_9 = FrontendInnerFec.FEC_5_9;
/**
* 6/7 conv. code rate.
*/
- public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7;
+ public static final long FEC_6_7 = FrontendInnerFec.FEC_6_7;
/**
* 7/8 conv. code rate.
*/
- public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8;
+ public static final long FEC_7_8 = FrontendInnerFec.FEC_7_8;
/**
* 7/9 conv. code rate.
*/
- public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9;
+ public static final long FEC_7_9 = FrontendInnerFec.FEC_7_9;
/**
* 7/15 conv. code rate.
*/
- public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15;
+ public static final long FEC_7_15 = FrontendInnerFec.FEC_7_15;
/**
* 8/9 conv. code rate.
*/
- public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9;
+ public static final long FEC_8_9 = FrontendInnerFec.FEC_8_9;
/**
* 8/15 conv. code rate.
*/
- public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15;
+ public static final long FEC_8_15 = FrontendInnerFec.FEC_8_15;
/**
* 9/10 conv. code rate.
*/
- public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10;
+ public static final long FEC_9_10 = FrontendInnerFec.FEC_9_10;
/**
* 9/20 conv. code rate.
*/
- public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20;
+ public static final long FEC_9_20 = FrontendInnerFec.FEC_9_20;
/**
* 11/15 conv. code rate.
*/
- public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15;
+ public static final long FEC_11_15 = FrontendInnerFec.FEC_11_15;
/**
* 11/20 conv. code rate.
*/
- public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20;
+ public static final long FEC_11_20 = FrontendInnerFec.FEC_11_20;
/**
* 11/45 conv. code rate.
*/
- public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45;
+ public static final long FEC_11_45 = FrontendInnerFec.FEC_11_45;
/**
* 13/18 conv. code rate.
*/
- public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18;
+ public static final long FEC_13_18 = FrontendInnerFec.FEC_13_18;
/**
* 13/45 conv. code rate.
*/
- public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45;
+ public static final long FEC_13_45 = FrontendInnerFec.FEC_13_45;
/**
* 14/45 conv. code rate.
*/
- public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45;
+ public static final long FEC_14_45 = FrontendInnerFec.FEC_14_45;
/**
* 23/36 conv. code rate.
*/
- public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36;
+ public static final long FEC_23_36 = FrontendInnerFec.FEC_23_36;
/**
* 25/36 conv. code rate.
*/
- public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36;
+ public static final long FEC_25_36 = FrontendInnerFec.FEC_25_36;
/**
* 26/45 conv. code rate.
*/
- public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45;
+ public static final long FEC_26_45 = FrontendInnerFec.FEC_26_45;
/**
* 28/45 conv. code rate.
*/
- public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45;
+ public static final long FEC_28_45 = FrontendInnerFec.FEC_28_45;
/**
* 29/45 conv. code rate.
*/
- public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45;
+ public static final long FEC_29_45 = FrontendInnerFec.FEC_29_45;
/**
* 31/45 conv. code rate.
*/
- public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45;
+ public static final long FEC_31_45 = FrontendInnerFec.FEC_31_45;
/**
* 32/45 conv. code rate.
*/
- public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45;
+ public static final long FEC_32_45 = FrontendInnerFec.FEC_32_45;
/**
* 77/90 conv. code rate.
*/
- public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
+ public static final long FEC_77_90 = FrontendInnerFec.FEC_77_90;
/** @hide */
@IntDef(prefix = "FRONTEND_SPECTRAL_INVERSION_",
@@ -258,17 +259,17 @@ public abstract class FrontendSettings {
* Spectral Inversion Type undefined.
*/
public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED =
- Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+ android.hardware.tv.tuner.FrontendSpectralInversion.UNDEFINED;
/**
* Normal Spectral Inversion.
*/
public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL =
- Constants.FrontendDvbcSpectralInversion.NORMAL;
+ android.hardware.tv.tuner.FrontendSpectralInversion.NORMAL;
/**
* Inverted Spectral Inversion.
*/
public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED =
- Constants.FrontendDvbcSpectralInversion.INVERTED;
+ android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED;
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index b82a5a914da8..36fd94234e09 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -20,7 +20,6 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
@@ -61,175 +60,188 @@ public class FrontendStatus {
* Lock status for Demod.
*/
public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
- Constants.FrontendStatusType.DEMOD_LOCK;
+ android.hardware.tv.tuner.FrontendStatusType.DEMOD_LOCK;
/**
* Signal to Noise Ratio.
*/
- public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
+ public static final int FRONTEND_STATUS_TYPE_SNR =
+ android.hardware.tv.tuner.FrontendStatusType.SNR;
/**
* Bit Error Ratio.
*/
- public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
+ public static final int FRONTEND_STATUS_TYPE_BER =
+ android.hardware.tv.tuner.FrontendStatusType.BER;
/**
* Packages Error Ratio.
*/
- public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
+ public static final int FRONTEND_STATUS_TYPE_PER =
+ android.hardware.tv.tuner.FrontendStatusType.PER;
/**
* Bit Error Ratio before FEC.
*/
- public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
+ public static final int FRONTEND_STATUS_TYPE_PRE_BER =
+ android.hardware.tv.tuner.FrontendStatusType.PRE_BER;
/**
* Signal Quality (0..100). Good data over total data in percent can be
* used as a way to present Signal Quality.
*/
public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
- Constants.FrontendStatusType.SIGNAL_QUALITY;
+ android.hardware.tv.tuner.FrontendStatusType.SIGNAL_QUALITY;
/**
* Signal Strength.
*/
public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
- Constants.FrontendStatusType.SIGNAL_STRENGTH;
+ android.hardware.tv.tuner.FrontendStatusType.SIGNAL_STRENGTH;
/**
* Symbol Rate in symbols per second.
*/
public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
- Constants.FrontendStatusType.SYMBOL_RATE;
+ android.hardware.tv.tuner.FrontendStatusType.SYMBOL_RATE;
/**
* Forward Error Correction Type.
*/
- public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
+ public static final int FRONTEND_STATUS_TYPE_FEC =
+ android.hardware.tv.tuner.FrontendStatusType.FEC;
/**
* Modulation Type.
*/
public static final int FRONTEND_STATUS_TYPE_MODULATION =
- Constants.FrontendStatusType.MODULATION;
+ android.hardware.tv.tuner.FrontendStatusType.MODULATION;
/**
* Spectral Inversion Type.
*/
- public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
+ public static final int FRONTEND_STATUS_TYPE_SPECTRAL =
+ android.hardware.tv.tuner.FrontendStatusType.SPECTRAL;
/**
* LNB Voltage.
*/
public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
- Constants.FrontendStatusType.LNB_VOLTAGE;
+ android.hardware.tv.tuner.FrontendStatusType.LNB_VOLTAGE;
/**
* Physical Layer Pipe ID.
*/
- public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
+ public static final int FRONTEND_STATUS_TYPE_PLP_ID =
+ android.hardware.tv.tuner.FrontendStatusType.PLP_ID;
/**
* Status for Emergency Warning Broadcasting System.
*/
- public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
+ public static final int FRONTEND_STATUS_TYPE_EWBS =
+ android.hardware.tv.tuner.FrontendStatusType.EWBS;
/**
* Automatic Gain Control.
*/
- public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
+ public static final int FRONTEND_STATUS_TYPE_AGC =
+ android.hardware.tv.tuner.FrontendStatusType.AGC;
/**
* Low Noise Amplifier.
*/
- public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
+ public static final int FRONTEND_STATUS_TYPE_LNA =
+ android.hardware.tv.tuner.FrontendStatusType.LNA;
/**
* Error status by layer.
*/
public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
- Constants.FrontendStatusType.LAYER_ERROR;
+ android.hardware.tv.tuner.FrontendStatusType.LAYER_ERROR;
/**
* Modulation Error Ratio.
*/
- public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
+ public static final int FRONTEND_STATUS_TYPE_MER =
+ android.hardware.tv.tuner.FrontendStatusType.MER;
/**
* Difference between tuning frequency and actual locked frequency.
*/
public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
- Constants.FrontendStatusType.FREQ_OFFSET;
+ android.hardware.tv.tuner.FrontendStatusType.FREQ_OFFSET;
/**
* Hierarchy for DVBT.
*/
- public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
+ public static final int FRONTEND_STATUS_TYPE_HIERARCHY =
+ android.hardware.tv.tuner.FrontendStatusType.HIERARCHY;
/**
* Lock status for RF.
*/
- public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
+ public static final int FRONTEND_STATUS_TYPE_RF_LOCK =
+ android.hardware.tv.tuner.FrontendStatusType.RF_LOCK;
/**
* PLP information in a frequency band for ATSC-3.0 frontend.
*/
public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
- Constants.FrontendStatusType.ATSC3_PLP_INFO;
+ android.hardware.tv.tuner.FrontendStatusType.ATSC3_PLP_INFO;
/**
* BERS Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_BERS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BERS;
+ android.hardware.tv.tuner.FrontendStatusType.BERS;
/**
* Coderate Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_CODERATES =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.CODERATES;
+ android.hardware.tv.tuner.FrontendStatusType.CODERATES;
/**
* Bandwidth Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_BANDWIDTH =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BANDWIDTH;
+ android.hardware.tv.tuner.FrontendStatusType.BANDWIDTH;
/**
* Guard Interval Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.GUARD_INTERVAL;
+ android.hardware.tv.tuner.FrontendStatusType.GUARD_INTERVAL;
/**
* Transmission Mode Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TRANSMISSION_MODE;
+ android.hardware.tv.tuner.FrontendStatusType.TRANSMISSION_MODE;
/**
* UEC Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_UEC =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.UEC;
+ android.hardware.tv.tuner.FrontendStatusType.UEC;
/**
* T2 System Id Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.T2_SYSTEM_ID;
+ android.hardware.tv.tuner.FrontendStatusType.T2_SYSTEM_ID;
/**
* Interleavings Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.INTERLEAVINGS;
+ android.hardware.tv.tuner.FrontendStatusType.INTERLEAVINGS;
/**
* ISDBT Segments Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.ISDBT_SEGMENTS;
+ android.hardware.tv.tuner.FrontendStatusType.ISDBT_SEGMENTS;
/**
* TS Data Rates Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TS_DATA_RATES;
+ android.hardware.tv.tuner.FrontendStatusType.TS_DATA_RATES;
/**
* Extended Modulations Type. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.MODULATIONS;
+ android.hardware.tv.tuner.FrontendStatusType.MODULATIONS;
/**
* Roll Off Type status of the frontend. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_ROLL_OFF =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.ROLL_OFF;
+ android.hardware.tv.tuner.FrontendStatusType.ROLL_OFF;
/**
* If the frontend currently supports MISO or not. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_IS_MISO_ENABLED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.IS_MISO;
+ android.hardware.tv.tuner.FrontendStatusType.IS_MISO;
/**
* If the frontend code rate is linear or not. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_IS_LINEAR =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.IS_LINEAR;
+ android.hardware.tv.tuner.FrontendStatusType.IS_LINEAR;
/**
* If short frames is enabled or not. Only supported in Tuner HAL 1.1 or higher.
*/
public static final int FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED =
- android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.IS_SHORT_FRAMES;
+ android.hardware.tv.tuner.FrontendStatusType.IS_SHORT_FRAMES;
/** @hide */
@IntDef(value = {
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 02cdb96285e3..14b0b029ab37 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -20,7 +20,9 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendIsdbs3Coderate;
+import android.hardware.tv.tuner.FrontendIsdbs3Modulation;
+import android.hardware.tv.tuner.FrontendIsdbs3Rolloff;
import android.media.tv.tuner.Tuner;
import java.lang.annotation.Retention;
@@ -45,31 +47,31 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendIsdbs3Modulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendIsdbs3Modulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically.
*/
- public static final int MODULATION_AUTO = Constants.FrontendIsdbs3Modulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendIsdbs3Modulation.AUTO;
/**
* BPSK Modulation.
*/
- public static final int MODULATION_MOD_BPSK = Constants.FrontendIsdbs3Modulation.MOD_BPSK;
+ public static final int MODULATION_MOD_BPSK = FrontendIsdbs3Modulation.MOD_BPSK;
/**
* QPSK Modulation.
*/
- public static final int MODULATION_MOD_QPSK = Constants.FrontendIsdbs3Modulation.MOD_QPSK;
+ public static final int MODULATION_MOD_QPSK = FrontendIsdbs3Modulation.MOD_QPSK;
/**
* 8PSK Modulation.
*/
- public static final int MODULATION_MOD_8PSK = Constants.FrontendIsdbs3Modulation.MOD_8PSK;
+ public static final int MODULATION_MOD_8PSK = FrontendIsdbs3Modulation.MOD_8PSK;
/**
* 16APSK Modulation.
*/
- public static final int MODULATION_MOD_16APSK = Constants.FrontendIsdbs3Modulation.MOD_16APSK;
+ public static final int MODULATION_MOD_16APSK = FrontendIsdbs3Modulation.MOD_16APSK;
/**
* 32APSK Modulation.
*/
- public static final int MODULATION_MOD_32APSK = Constants.FrontendIsdbs3Modulation.MOD_32APSK;
+ public static final int MODULATION_MOD_32APSK = FrontendIsdbs3Modulation.MOD_32APSK;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -83,55 +85,55 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
/**
* Code rate undefined.
*/
- public static final int CODERATE_UNDEFINED = Constants.FrontendIsdbs3Coderate.UNDEFINED;
+ public static final int CODERATE_UNDEFINED = FrontendIsdbs3Coderate.UNDEFINED;
/**
* Hardware is able to detect and set code rate automatically.
*/
- public static final int CODERATE_AUTO = Constants.FrontendIsdbs3Coderate.AUTO;
+ public static final int CODERATE_AUTO = FrontendIsdbs3Coderate.AUTO;
/**
* 1/3 code rate.
*/
- public static final int CODERATE_1_3 = Constants.FrontendIsdbs3Coderate.CODERATE_1_3;
+ public static final int CODERATE_1_3 = FrontendIsdbs3Coderate.CODERATE_1_3;
/**
* 2/5 code rate.
*/
- public static final int CODERATE_2_5 = Constants.FrontendIsdbs3Coderate.CODERATE_2_5;
+ public static final int CODERATE_2_5 = FrontendIsdbs3Coderate.CODERATE_2_5;
/**
* 1/2 code rate.
*/
- public static final int CODERATE_1_2 = Constants.FrontendIsdbs3Coderate.CODERATE_1_2;
+ public static final int CODERATE_1_2 = FrontendIsdbs3Coderate.CODERATE_1_2;
/**
* 3/5 code rate.
*/
- public static final int CODERATE_3_5 = Constants.FrontendIsdbs3Coderate.CODERATE_3_5;
+ public static final int CODERATE_3_5 = FrontendIsdbs3Coderate.CODERATE_3_5;
/**
* 2/3 code rate.
*/
- public static final int CODERATE_2_3 = Constants.FrontendIsdbs3Coderate.CODERATE_2_3;
+ public static final int CODERATE_2_3 = FrontendIsdbs3Coderate.CODERATE_2_3;
/**
* 3/4 code rate.
*/
- public static final int CODERATE_3_4 = Constants.FrontendIsdbs3Coderate.CODERATE_3_4;
+ public static final int CODERATE_3_4 = FrontendIsdbs3Coderate.CODERATE_3_4;
/**
* 7/9 code rate.
*/
- public static final int CODERATE_7_9 = Constants.FrontendIsdbs3Coderate.CODERATE_7_9;
+ public static final int CODERATE_7_9 = FrontendIsdbs3Coderate.CODERATE_7_9;
/**
* 4/5 code rate.
*/
- public static final int CODERATE_4_5 = Constants.FrontendIsdbs3Coderate.CODERATE_4_5;
+ public static final int CODERATE_4_5 = FrontendIsdbs3Coderate.CODERATE_4_5;
/**
* 5/6 code rate.
*/
- public static final int CODERATE_5_6 = Constants.FrontendIsdbs3Coderate.CODERATE_5_6;
+ public static final int CODERATE_5_6 = FrontendIsdbs3Coderate.CODERATE_5_6;
/**
* 7/8 code rate.
*/
- public static final int CODERATE_7_8 = Constants.FrontendIsdbs3Coderate.CODERATE_7_8;
+ public static final int CODERATE_7_8 = FrontendIsdbs3Coderate.CODERATE_7_8;
/**
* 9/10 code rate.
*/
- public static final int CODERATE_9_10 = Constants.FrontendIsdbs3Coderate.CODERATE_9_10;
+ public static final int CODERATE_9_10 = FrontendIsdbs3Coderate.CODERATE_9_10;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -142,11 +144,11 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
/**
* Rolloff type undefined.
*/
- public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
+ public static final int ROLLOFF_UNDEFINED = FrontendIsdbs3Rolloff.UNDEFINED;
/**
* 0,03 Rolloff.
*/
- public static final int ROLLOFF_0_03 = Constants.FrontendIsdbs3Rolloff.ROLLOFF_0_03;
+ public static final int ROLLOFF_0_03 = FrontendIsdbs3Rolloff.ROLLOFF_0_03;
private final int mStreamId;
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 3cc1aab6a9a6..aab6408e4f42 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -20,7 +20,10 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendIsdbsCoderate;
+import android.hardware.tv.tuner.FrontendIsdbsModulation;
+import android.hardware.tv.tuner.FrontendIsdbsRolloff;
+import android.hardware.tv.tuner.FrontendIsdbsStreamIdType;
import android.media.tv.tuner.Tuner;
import java.lang.annotation.Retention;
@@ -42,12 +45,12 @@ public class IsdbsFrontendSettings extends FrontendSettings {
/**
* Uses stream ID.
*/
- public static final int STREAM_ID_TYPE_ID = Constants.FrontendIsdbsStreamIdType.STREAM_ID;
+ public static final int STREAM_ID_TYPE_ID = FrontendIsdbsStreamIdType.STREAM_ID;
/**
* Uses relative number.
*/
public static final int STREAM_ID_TYPE_RELATIVE_NUMBER =
- Constants.FrontendIsdbsStreamIdType.RELATIVE_STREAM_NUMBER;
+ FrontendIsdbsStreamIdType.RELATIVE_STREAM_NUMBER;
/** @hide */
@@ -61,23 +64,23 @@ public class IsdbsFrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendIsdbsModulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendIsdbsModulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically
*/
- public static final int MODULATION_AUTO = Constants.FrontendIsdbsModulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendIsdbsModulation.AUTO;
/**
* BPSK Modulation.
*/
- public static final int MODULATION_MOD_BPSK = Constants.FrontendIsdbsModulation.MOD_BPSK;
+ public static final int MODULATION_MOD_BPSK = FrontendIsdbsModulation.MOD_BPSK;
/**
* QPSK Modulation.
*/
- public static final int MODULATION_MOD_QPSK = Constants.FrontendIsdbsModulation.MOD_QPSK;
+ public static final int MODULATION_MOD_QPSK = FrontendIsdbsModulation.MOD_QPSK;
/**
* TC8PSK Modulation.
*/
- public static final int MODULATION_MOD_TC8PSK = Constants.FrontendIsdbsModulation.MOD_TC8PSK;
+ public static final int MODULATION_MOD_TC8PSK = FrontendIsdbsModulation.MOD_TC8PSK;
/** @hide */
@@ -91,31 +94,31 @@ public class IsdbsFrontendSettings extends FrontendSettings {
/**
* Code rate undefined.
*/
- public static final int CODERATE_UNDEFINED = Constants.FrontendIsdbsCoderate.UNDEFINED;
+ public static final int CODERATE_UNDEFINED = FrontendIsdbsCoderate.UNDEFINED;
/**
* Hardware is able to detect and set code rate automatically.
*/
- public static final int CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
+ public static final int CODERATE_AUTO = FrontendIsdbsCoderate.AUTO;
/**
* 1/2 code rate.
*/
- public static final int CODERATE_1_2 = Constants.FrontendIsdbsCoderate.CODERATE_1_2;
+ public static final int CODERATE_1_2 = FrontendIsdbsCoderate.CODERATE_1_2;
/**
* 2/3 code rate.
*/
- public static final int CODERATE_2_3 = Constants.FrontendIsdbsCoderate.CODERATE_2_3;
+ public static final int CODERATE_2_3 = FrontendIsdbsCoderate.CODERATE_2_3;
/**
* 3/4 code rate.
*/
- public static final int CODERATE_3_4 = Constants.FrontendIsdbsCoderate.CODERATE_3_4;
+ public static final int CODERATE_3_4 = FrontendIsdbsCoderate.CODERATE_3_4;
/**
* 5/6 code rate.
*/
- public static final int CODERATE_5_6 = Constants.FrontendIsdbsCoderate.CODERATE_5_6;
+ public static final int CODERATE_5_6 = FrontendIsdbsCoderate.CODERATE_5_6;
/**
* 7/8 code rate.
*/
- public static final int CODERATE_7_8 = Constants.FrontendIsdbsCoderate.CODERATE_7_8;
+ public static final int CODERATE_7_8 = FrontendIsdbsCoderate.CODERATE_7_8;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -126,11 +129,11 @@ public class IsdbsFrontendSettings extends FrontendSettings {
/**
* Rolloff type undefined.
*/
- public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbsRolloff.UNDEFINED;
+ public static final int ROLLOFF_UNDEFINED = FrontendIsdbsRolloff.UNDEFINED;
/**
* 0,35 rolloff.
*/
- public static final int ROLLOFF_0_35 = Constants.FrontendIsdbsRolloff.ROLLOFF_0_35;
+ public static final int ROLLOFF_0_35 = FrontendIsdbsRolloff.ROLLOFF_0_35;
private final int mStreamId;
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index 6a14d08a4248..de2476a6ba20 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -20,7 +20,9 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendIsdbtBandwidth;
+import android.hardware.tv.tuner.FrontendIsdbtMode;
+import android.hardware.tv.tuner.FrontendIsdbtModulation;
import android.media.tv.tuner.frontend.DvbtFrontendSettings.CodeRate;
import java.lang.annotation.Retention;
@@ -44,27 +46,27 @@ public class IsdbtFrontendSettings extends FrontendSettings {
/**
* Modulation undefined.
*/
- public static final int MODULATION_UNDEFINED = Constants.FrontendIsdbtModulation.UNDEFINED;
+ public static final int MODULATION_UNDEFINED = FrontendIsdbtModulation.UNDEFINED;
/**
* Hardware is able to detect and set modulation automatically
*/
- public static final int MODULATION_AUTO = Constants.FrontendIsdbtModulation.AUTO;
+ public static final int MODULATION_AUTO = FrontendIsdbtModulation.AUTO;
/**
* DQPSK Modulation.
*/
- public static final int MODULATION_MOD_DQPSK = Constants.FrontendIsdbtModulation.MOD_DQPSK;
+ public static final int MODULATION_MOD_DQPSK = FrontendIsdbtModulation.MOD_DQPSK;
/**
* QPSK Modulation.
*/
- public static final int MODULATION_MOD_QPSK = Constants.FrontendIsdbtModulation.MOD_QPSK;
+ public static final int MODULATION_MOD_QPSK = FrontendIsdbtModulation.MOD_QPSK;
/**
* 16QAM Modulation.
*/
- public static final int MODULATION_MOD_16QAM = Constants.FrontendIsdbtModulation.MOD_16QAM;
+ public static final int MODULATION_MOD_16QAM = FrontendIsdbtModulation.MOD_16QAM;
/**
* 64QAM Modulation.
*/
- public static final int MODULATION_MOD_64QAM = Constants.FrontendIsdbtModulation.MOD_64QAM;
+ public static final int MODULATION_MOD_64QAM = FrontendIsdbtModulation.MOD_64QAM;
/** @hide */
@@ -77,23 +79,23 @@ public class IsdbtFrontendSettings extends FrontendSettings {
/**
* Mode undefined.
*/
- public static final int MODE_UNDEFINED = Constants.FrontendIsdbtMode.UNDEFINED;
+ public static final int MODE_UNDEFINED = FrontendIsdbtMode.UNDEFINED;
/**
* Hardware is able to detect and set Mode automatically.
*/
- public static final int MODE_AUTO = Constants.FrontendIsdbtMode.AUTO;
+ public static final int MODE_AUTO = FrontendIsdbtMode.AUTO;
/**
* Mode 1
*/
- public static final int MODE_1 = Constants.FrontendIsdbtMode.MODE_1;
+ public static final int MODE_1 = FrontendIsdbtMode.MODE_1;
/**
* Mode 2
*/
- public static final int MODE_2 = Constants.FrontendIsdbtMode.MODE_2;
+ public static final int MODE_2 = FrontendIsdbtMode.MODE_2;
/**
* Mode 3
*/
- public static final int MODE_3 = Constants.FrontendIsdbtMode.MODE_3;
+ public static final int MODE_3 = FrontendIsdbtMode.MODE_3;
/** @hide */
@@ -107,23 +109,23 @@ public class IsdbtFrontendSettings extends FrontendSettings {
/**
* Bandwidth undefined.
*/
- public static final int BANDWIDTH_UNDEFINED = Constants.FrontendIsdbtBandwidth.UNDEFINED;
+ public static final int BANDWIDTH_UNDEFINED = FrontendIsdbtBandwidth.UNDEFINED;
/**
* Hardware is able to detect and set Bandwidth automatically.
*/
- public static final int BANDWIDTH_AUTO = Constants.FrontendIsdbtBandwidth.AUTO;
+ public static final int BANDWIDTH_AUTO = FrontendIsdbtBandwidth.AUTO;
/**
* 8 MHz bandwidth.
*/
- public static final int BANDWIDTH_8MHZ = Constants.FrontendIsdbtBandwidth.BANDWIDTH_8MHZ;
+ public static final int BANDWIDTH_8MHZ = FrontendIsdbtBandwidth.BANDWIDTH_8MHZ;
/**
* 7 MHz bandwidth.
*/
- public static final int BANDWIDTH_7MHZ = Constants.FrontendIsdbtBandwidth.BANDWIDTH_7MHZ;
+ public static final int BANDWIDTH_7MHZ = FrontendIsdbtBandwidth.BANDWIDTH_7MHZ;
/**
* 6 MHz bandwidth.
*/
- public static final int BANDWIDTH_6MHZ = Constants.FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
+ public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
private final int mModulation;
private final int mBandwidth;
diff --git a/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
index 5cf0d319c7c9..3b5419e74769 100644
--- a/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
+++ b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
@@ -18,7 +18,7 @@ package android.media.tv.tuner.frontend;
import android.annotation.IntDef;
import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
+import android.hardware.tv.tuner.FrontendEventType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -37,11 +37,11 @@ public interface OnTuneEventListener {
@interface TuneEvent {}
/** The frontend has locked to the signal specified by the tune method. */
- int SIGNAL_LOCKED = Constants.FrontendEventType.LOCKED;
+ int SIGNAL_LOCKED = FrontendEventType.LOCKED;
/** The frontend is unable to lock to the signal specified by the tune method. */
- int SIGNAL_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+ int SIGNAL_NO_SIGNAL = FrontendEventType.NO_SIGNAL;
/** The frontend has lost the lock to the signal specified by the tune method. */
- int SIGNAL_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
+ int SIGNAL_LOST_LOCK = FrontendEventType.LOST_LOCK;
/** Tune Event from the frontend */
void onTuneEvent(@TuneEvent int tuneEvent);
diff --git a/media/java/android/media/tv/tunerresourcemanager/Android.bp b/media/java/android/media/tv/tunerresourcemanager/Android.bp
index c904ca2be00c..65e7e10738ca 100644
--- a/media/java/android/media/tv/tunerresourcemanager/Android.bp
+++ b/media/java/android/media/tv/tunerresourcemanager/Android.bp
@@ -24,7 +24,7 @@ aidl_interface {
enabled: true,
},
cpp: {
- enabled: true,
+ enabled: false,
},
ndk: {
enabled: true,
@@ -33,5 +33,4 @@ aidl_interface {
srcs: [
":framework-media-tv-tunerresourcemanager-sources-aidl",
],
- imports: ["tv_tuner_frontend_info_aidl_interface"],
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index e399fbdfabcf..6f7adbc65318 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -24,7 +24,6 @@ import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.media.tv.tuner.TunerFrontendInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 483d9720b430..a1f6687a1b81 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -16,13 +16,13 @@
package android.media.tv.tunerresourcemanager;
-import android.media.tv.tuner.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ResourceClientProfile;
import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
new file mode 100644
index 000000000000..8981ce00d509
--- /dev/null
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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.media.tv.tunerresourcemanager;
+
+/**
+ * FrontendInfo interface that carries tuner frontend information.
+ *
+ * This is used to update the TunerResourceManager fronted resources.
+ * @hide
+ */
+parcelable TunerFrontendInfo {
+ /**
+ * Frontend Handle
+ */
+ int handle;
+
+ /**
+ * Frontend Type
+ */
+ int type;
+
+ /**
+ * Frontends are assigned with the same exclusiveGroupId if they can't
+ * function at same time. For instance, they share same hardware module.
+ */
+ int exclusiveGroupId;
+}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index ee70714ce763..e8ef46499dc8 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -46,6 +46,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.Collections;
import java.util.HashMap;
@@ -112,21 +113,34 @@ public abstract class MediaBrowserService extends Service {
/**
* All the info about a connection.
*/
- private class ConnectionRecord implements IBinder.DeathRecipient {
- String pkg;
- int uid;
- int pid;
- Bundle rootHints;
- IMediaBrowserServiceCallbacks callbacks;
- BrowserRoot root;
- HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+ private static class ConnectionRecord implements IBinder.DeathRecipient {
+ public final MediaBrowserService service;
+ public final String pkg;
+ public final int pid;
+ public final int uid;
+ public final Bundle rootHints;
+ public final IMediaBrowserServiceCallbacks callbacks;
+ public final BrowserRoot root;
+ public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
+
+ ConnectionRecord(
+ MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
+ this.service = service;
+ this.pkg = pkg;
+ this.pid = pid;
+ this.uid = uid;
+ this.rootHints = rootHints;
+ this.callbacks = callbacks;
+ this.root = root;
+ }
@Override
public void binderDied() {
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
- mConnections.remove(callbacks.asBinder());
+ service.mConnections.remove(callbacks.asBinder());
}
});
}
@@ -199,39 +213,46 @@ public abstract class MediaBrowserService extends Service {
}
}
- private class ServiceBinder extends IMediaBrowserService.Stub {
+ private static class ServiceBinder extends IMediaBrowserService.Stub {
+ private WeakReference<MediaBrowserService> mService;
+
+ private ServiceBinder(MediaBrowserService service) {
+ mService = new WeakReference(service);
+ }
+
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- if (!isValidPackage(pkg, uid)) {
+ if (!service.isValidPackage(pkg, uid)) {
throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+ " package=" + pkg);
}
- mHandler.post(new Runnable() {
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- mConnections.remove(b);
-
- final ConnectionRecord connection = new ConnectionRecord();
- connection.pkg = pkg;
- connection.pid = pid;
- connection.uid = uid;
- connection.rootHints = rootHints;
- connection.callbacks = callbacks;
+ service.mConnections.remove(b);
- mCurConnection = connection;
- connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
- mCurConnection = null;
+ // Temporarily sets a placeholder ConnectionRecord to make
+ // getCurrentBrowserInfo() work in onGetRoot().
+ service.mCurConnection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, null);
+ BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
+ service.mCurConnection = null;
// If they didn't return something, don't allow this client.
- if (connection.root == null) {
+ if (root == null) {
Log.i(TAG, "No root for client " + pkg + " from service "
+ getClass().getName());
try {
@@ -242,16 +263,19 @@ public abstract class MediaBrowserService extends Service {
}
} else {
try {
- mConnections.put(b, connection);
+ ConnectionRecord connection =
+ new ConnectionRecord(
+ service, pkg, pid, uid, rootHints, callbacks, root);
+ service.mConnections.put(b, connection);
b.linkToDeath(connection, 0);
- if (mSession != null) {
+ if (service.mSession != null) {
callbacks.onConnect(connection.root.getRootId(),
- mSession, connection.root.getExtras());
+ service.mSession, connection.root.getExtras());
}
} catch (RemoteException ex) {
Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ "pkg=" + pkg);
- mConnections.remove(b);
+ service.mConnections.remove(b);
}
}
}
@@ -260,13 +284,18 @@ public abstract class MediaBrowserService extends Service {
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Clear out the old subscriptions. We are getting new ones.
- final ConnectionRecord old = mConnections.remove(b);
+ final ConnectionRecord old = service.mConnections.remove(b);
if (old != null) {
// TODO
old.callbacks.asBinder().unlinkToDeath(old, 0);
@@ -283,20 +312,25 @@ public abstract class MediaBrowserService extends Service {
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
// Get the record for the connection
- final ConnectionRecord connection = mConnections.get(b);
+ final ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "addSubscription for callback that isn't registered id="
+ id);
return;
}
- MediaBrowserService.this.addSubscription(id, connection, token, options);
+ service.addSubscription(id, connection, token, options);
}
});
}
@@ -310,18 +344,23 @@ public abstract class MediaBrowserService extends Service {
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "removeSubscription for callback that isn't registered id="
+ id);
return;
}
- if (!MediaBrowserService.this.removeSubscription(id, connection, token)) {
+ if (!service.removeSubscription(id, connection, token)) {
Log.w(TAG, "removeSubscription called for " + id
+ " which is not subscribed");
}
@@ -332,16 +371,21 @@ public abstract class MediaBrowserService extends Service {
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
+ MediaBrowserService service = mService.get();
+ if (service == null) {
+ return;
+ }
+
+ service.mHandler.post(new Runnable() {
@Override
public void run() {
final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
+ ConnectionRecord connection = service.mConnections.get(b);
if (connection == null) {
Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
return;
}
- performLoadItem(mediaId, connection, receiver);
+ service.performLoadItem(mediaId, connection, receiver);
}
});
}
@@ -350,7 +394,7 @@ public abstract class MediaBrowserService extends Service {
@Override
public void onCreate() {
super.onCreate();
- mBinder = new ServiceBinder();
+ mBinder = new ServiceBinder(this);
}
@Override
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index bc73f6ad1ad2..e817f2dc9e1d 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -172,8 +172,7 @@ cc_library_shared {
shared_libs: [
"android.hardware.graphics.bufferqueue@2.0",
- "android.hardware.tv.tuner@1.0",
- "android.hardware.tv.tuner@1.1",
+ "android.hardware.tv.tuner-V1-ndk",
"libbinder_ndk",
"libandroid_runtime",
"libcutils",
@@ -183,8 +182,7 @@ cc_library_shared {
"libmedia",
"libnativehelper",
"libutils",
- "tv_tuner_aidl_interface-ndk_platform",
- "tv_tuner_resource_manager_aidl_interface-ndk_platform",
+ "tv_tuner_aidl_interface-ndk",
],
static_libs: [
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index f1b0237d9008..445672b7ca13 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -2,4 +2,4 @@
per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
# extra for TV related files
-per-file android_media_tv_*=nchalko@google.com,quxiangfang@google.com
+per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 7c5f58e31108..116237f6b998 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -282,7 +282,6 @@ status_t JMediaExtractor::getMetrics(Parcel *reply) const {
return status;
}
-
status_t JMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) {
return mImpl->getSampleMeta(sampleMeta);
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 3cf9b0370823..f1c72f86e800 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -14,154 +14,280 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
#define LOG_TAG "TvTuner-JNI"
-#include <utils/Log.h>
-#include "android_media_MediaCodecLinearBlock.h"
#include "android_media_tv_Tuner.h"
-#include "android_runtime/AndroidRuntime.h"
+#include <aidl/android/hardware/tv/tuner/AudioExtraMetaData.h>
+#include <aidl/android/hardware/tv/tuner/AudioStreamType.h>
+#include <aidl/android/hardware/tv/tuner/AvStreamType.h>
+#include <aidl/android/hardware/tv/tuner/Constant.h>
+#include <aidl/android/hardware/tv/tuner/Constant64Bit.h>
+#include <aidl/android/hardware/tv/tuner/DataFormat.h>
+#include <aidl/android/hardware/tv/tuner/DemuxAlpFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxAlpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxAlpLengthType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxCapabilities.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterAvSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterDownloadEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterDownloadSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterIpPayloadEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMainType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMediaEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterPesDataSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterPesEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterRecordSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterScIndexMask.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSectionBits.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSectionEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSectionSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSectionSettingsCondition.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSubType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterTemiEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterTsRecordEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpAddress.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxIpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxMmtpFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxMmtpFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
+#include <aidl/android/hardware/tv/tuner/DemuxRecordScIndexType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxScHevcIndex.h>
+#include <aidl/android/hardware/tv/tuner/DemuxScIndex.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTlvFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTlvFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTsFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTsFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxTsIndex.h>
+#include <aidl/android/hardware/tv/tuner/DvrSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAnalogAftFlag.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAnalogSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAnalogSifStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAnalogType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3Bandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3CodeRate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3DemodOutputFormat.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3Fec.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3Modulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3PlpSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3Settings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtsc3TimeInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtscModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendAtscSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendCableTimeInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbCapabilities.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbCodeRate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbTimeInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDtmbTransmissionMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbcAnnex.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbcBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbcModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbcOuterFec.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbcSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsCodeRate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsPilot.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsRolloff.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsScanType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbsVcmMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtCoderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtConstellation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtHierarchy.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtPlpMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendDvbtTransmissionMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendInnerFec.h>
+#include <aidl/android/hardware/tv/tuner/FrontendInterleaveMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbs3Coderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbs3Modulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbs3Rolloff.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbs3Settings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbsCoderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbsModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbsRolloff.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbsSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbsStreamIdType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtBandwidth.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtCoderate.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtGuardInterval.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendIsdbtSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendModulation.h>
+#include <aidl/android/hardware/tv/tuner/FrontendModulationStatus.h>
+#include <aidl/android/hardware/tv/tuner/FrontendRollOff.h>
+#include <aidl/android/hardware/tv/tuner/FrontendScanAtsc3PlpInfo.h>
+#include <aidl/android/hardware/tv/tuner/FrontendScanMessageStandard.h>
+#include <aidl/android/hardware/tv/tuner/FrontendSpectralInversion.h>
+#include <aidl/android/hardware/tv/tuner/FrontendStatus.h>
+#include <aidl/android/hardware/tv/tuner/FrontendStatusAtsc3PlpInfo.h>
+#include <aidl/android/hardware/tv/tuner/FrontendStatusType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendTransmissionMode.h>
+#include <aidl/android/hardware/tv/tuner/FrontendType.h>
+#include <aidl/android/hardware/tv/tuner/LnbPosition.h>
+#include <aidl/android/hardware/tv/tuner/LnbTone.h>
+#include <aidl/android/hardware/tv/tuner/LnbVoltage.h>
+#include <aidl/android/hardware/tv/tuner/PlaybackSettings.h>
+#include <aidl/android/hardware/tv/tuner/RecordSettings.h>
+#include <aidl/android/hardware/tv/tuner/VideoStreamType.h>
+#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
-#include <utils/NativeHandle.h>
+#include <utils/Log.h>
+
+#include "android_media_MediaCodecLinearBlock.h"
+#include "android_runtime/AndroidRuntime.h"
#pragma GCC diagnostic ignored "-Wunused-function"
-using ::android::hardware::Void;
-using ::android::hardware::hidl_bitfield;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::tv::tuner::V1_0::AudioExtraMetaData;
-using ::android::hardware::tv::tuner::V1_0::DataFormat;
-using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
-using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterIpPayloadEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterMediaEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterMmtpRecordEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterTemiEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterTsRecordEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
-using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid;
-using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
-using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
-using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex;
-using ::android::hardware::tv::tuner::V1_0::DemuxScIndex;
-using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
-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_0::DemuxTsIndex;
-using ::android::hardware::tv::tuner::V1_0::DvrSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3CodeRate;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3DemodOutputFormat;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Fec;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3PlpSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Settings;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtscSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcOuterFec;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsCodeRate;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsPilot;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsVcmMode;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtPlpMode;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
-using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Coderate;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Settings;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsCoderate;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsStreamIdType;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtCoderate;
-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::FrontendIsdbtSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendModulationStatus;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendStatus;
-using ::android::hardware::tv::tuner::V1_0::FrontendStatusAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
-using ::android::hardware::tv::tuner::V1_0::LnbPosition;
-using ::android::hardware::tv::tuner::V1_0::LnbTone;
-using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
-using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
-using ::android::hardware::tv::tuner::V1_0::RecordSettings;
-using ::android::hardware::tv::tuner::V1_1::AudioStreamType;
-using ::android::hardware::tv::tuner::V1_1::AvStreamType;
-using ::android::hardware::tv::tuner::V1_1::Constant;
-using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
-using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag;
-using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
-using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
-using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
-using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
-using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendDvbtSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCodeRate;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbSettings;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
-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::FrontendStatusExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode;
-using ::android::hardware::tv::tuner::V1_1::VideoStreamType;
+using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::AudioStreamType;
+using ::aidl::android::hardware::tv::tuner::AvStreamType;
+using ::aidl::android::hardware::tv::tuner::Constant;
+using ::aidl::android::hardware::tv::tuner::Constant64Bit;
+using ::aidl::android::hardware::tv::tuner::DataFormat;
+using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxAlpLengthType;
+using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterAvSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterDownloadEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterDownloadSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterIpPayloadEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMainType;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMediaEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMediaEventExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMmtpRecordEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterPesDataSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterPesEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterRecordSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterScIndexMask;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionBits;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettingsCondition;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSectionSettingsConditionTableInfo;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSubType;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterTemiEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterTsRecordEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxIpAddress;
+using ::aidl::android::hardware::tv::tuner::DemuxIpAddressIpAddress;
+using ::aidl::android::hardware::tv::tuner::DemuxIpFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxIpFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxIpFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxQueueNotifyBits;
+using ::aidl::android::hardware::tv::tuner::DemuxRecordScIndexType;
+using ::aidl::android::hardware::tv::tuner::DemuxScHevcIndex;
+using ::aidl::android::hardware::tv::tuner::DemuxScIndex;
+using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxTlvFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxTsFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxTsIndex;
+using ::aidl::android::hardware::tv::tuner::DvrSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendAnalogAftFlag;
+using ::aidl::android::hardware::tv::tuner::FrontendAnalogSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendAnalogSifStandard;
+using ::aidl::android::hardware::tv::tuner::FrontendAnalogType;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Bandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3CodeRate;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3DemodOutputFormat;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Fec;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Modulation;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3PlpSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3Settings;
+using ::aidl::android::hardware::tv::tuner::FrontendAtsc3TimeInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendAtscModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendAtscSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendBandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendCableTimeInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbBandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbCapabilities;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbCodeRate;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbGuardInterval;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbTimeInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendDtmbTransmissionMode;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbcAnnex;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbcBandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbcModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbcOuterFec;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbcSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsCodeRate;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsPilot;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsRolloff;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsScanType;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsStandard;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbsVcmMode;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtBandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtCoderate;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtConstellation;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtGuardInterval;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtHierarchy;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtPlpMode;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtStandard;
+using ::aidl::android::hardware::tv::tuner::FrontendDvbtTransmissionMode;
+using ::aidl::android::hardware::tv::tuner::FrontendGuardInterval;
+using ::aidl::android::hardware::tv::tuner::FrontendInnerFec;
+using ::aidl::android::hardware::tv::tuner::FrontendInterleaveMode;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Coderate;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Modulation;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Rolloff;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbs3Settings;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbsCoderate;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbsModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbsRolloff;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbsSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbsStreamIdType;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtBandwidth;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtCoderate;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtGuardInterval;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtMode;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendIsdbtSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendModulation;
+using ::aidl::android::hardware::tv::tuner::FrontendModulationStatus;
+using ::aidl::android::hardware::tv::tuner::FrontendRollOff;
+using ::aidl::android::hardware::tv::tuner::FrontendScanAtsc3PlpInfo;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessageStandard;
+using ::aidl::android::hardware::tv::tuner::FrontendSpectralInversion;
+using ::aidl::android::hardware::tv::tuner::FrontendStatus;
+using ::aidl::android::hardware::tv::tuner::FrontendStatusAtsc3PlpInfo;
+using ::aidl::android::hardware::tv::tuner::FrontendStatusType;
+using ::aidl::android::hardware::tv::tuner::FrontendTransmissionMode;
+using ::aidl::android::hardware::tv::tuner::FrontendType;
+using ::aidl::android::hardware::tv::tuner::LnbPosition;
+using ::aidl::android::hardware::tv::tuner::LnbTone;
+using ::aidl::android::hardware::tv::tuner::LnbVoltage;
+using ::aidl::android::hardware::tv::tuner::PlaybackSettings;
+using ::aidl::android::hardware::tv::tuner::RecordSettings;
+using ::aidl::android::hardware::tv::tuner::VideoStreamType;
struct fields_t {
jfieldID tunerContext;
@@ -192,17 +318,16 @@ struct fields_t {
static fields_t gFields;
-
static int IP_V4_LENGTH = 4;
static int IP_V6_LENGTH = 16;
void DestroyCallback(const C2Buffer * buf, void *arg) {
android::sp<android::MediaEvent> event = (android::MediaEvent *)arg;
android::Mutex::Autolock autoLock(event->mLock);
- if (event->mLinearBlockObj != NULL) {
+ if (event->mLinearBlockObj != nullptr) {
JNIEnv *env = android::AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(event->mLinearBlockObj);
- event->mLinearBlockObj = NULL;
+ event->mLinearBlockObj = nullptr;
}
event->mAvHandleRefCnt--;
@@ -211,11 +336,9 @@ void DestroyCallback(const C2Buffer * buf, void *arg) {
}
namespace android {
-
/////////////// LnbClientCallbackImpl ///////////////////////
-
void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
- ALOGD("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
+ ALOGV("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject lnb(env->NewLocalRef(mLnbObj));
if (!env->IsSameObject(lnb, nullptr)) {
@@ -229,14 +352,14 @@ void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
}
}
-void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
- ALOGD("LnbClientCallbackImpl::onDiseqcMessage");
+void LnbClientCallbackImpl::onDiseqcMessage(const vector<uint8_t> &diseqcMessage) {
+ ALOGV("LnbClientCallbackImpl::onDiseqcMessage");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject lnb(env->NewLocalRef(mLnbObj));
if (!env->IsSameObject(lnb, nullptr)) {
jbyteArray array = env->NewByteArray(diseqcMessage.size());
- env->SetByteArrayRegion(
- array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0]));
+ env->SetByteArrayRegion(array, 0, diseqcMessage.size(),
+ reinterpret_cast<const jbyte *>(&diseqcMessage[0]));
env->CallVoidMethod(
lnb,
gFields.onLnbDiseqcMessageID,
@@ -248,29 +371,25 @@ void LnbClientCallbackImpl::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessa
}
void LnbClientCallbackImpl::setLnb(jweak lnbObj) {
- ALOGD("LnbClientCallbackImpl::setLnb");
+ ALOGV("LnbClientCallbackImpl::setLnb");
mLnbObj = lnbObj;
}
LnbClientCallbackImpl::~LnbClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mLnbObj != NULL) {
+ if (mLnbObj != nullptr) {
env->DeleteWeakGlobalRef(mLnbObj);
- mLnbObj = NULL;
+ mLnbObj = nullptr;
}
}
/////////////// DvrClientCallbackImpl ///////////////////////
-
void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
- ALOGD("DvrClientCallbackImpl::onRecordStatus");
+ ALOGV("DvrClientCallbackImpl::onRecordStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject dvr(env->NewLocalRef(mDvrObj));
if (!env->IsSameObject(dvr, nullptr)) {
- env->CallVoidMethod(
- dvr,
- gFields.onDvrRecordStatusID,
- (jint) status);
+ env->CallVoidMethod(dvr, gFields.onDvrRecordStatusID, (jint)status);
} else {
ALOGE("DvrClientCallbackImpl::onRecordStatus:"
"Dvr object has been freed. Ignoring callback.");
@@ -278,14 +397,11 @@ void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
}
void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) {
- ALOGD("DvrClientCallbackImpl::onPlaybackStatus");
+ ALOGV("DvrClientCallbackImpl::onPlaybackStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject dvr(env->NewLocalRef(mDvrObj));
if (!env->IsSameObject(dvr, nullptr)) {
- env->CallVoidMethod(
- dvr,
- gFields.onDvrPlaybackStatusID,
- (jint) status);
+ env->CallVoidMethod(dvr, gFields.onDvrPlaybackStatusID, (jint)status);
} else {
ALOGE("DvrClientCallbackImpl::onPlaybackStatus:"
"Dvr object has been freed. Ignoring callback.");
@@ -293,20 +409,19 @@ void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) {
}
void DvrClientCallbackImpl::setDvr(jweak dvrObj) {
- ALOGD("DvrClientCallbackImpl::setDvr");
+ ALOGV("DvrClientCallbackImpl::setDvr");
mDvrObj = dvrObj;
}
DvrClientCallbackImpl::~DvrClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mDvrObj != NULL) {
+ if (mDvrObj != nullptr) {
env->DeleteWeakGlobalRef(mDvrObj);
- mDvrObj = NULL;
+ mDvrObj = nullptr;
}
}
/////////////// C2DataIdInfo ///////////////////////
-
C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) {
CHECK(isGlobal());
CHECK_EQ(C2Param::INFO, kind());
@@ -316,40 +431,44 @@ C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize,
}
/////////////// MediaEvent ///////////////////////
-
-MediaEvent::MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle,
- uint64_t dataId, uint64_t dataSize, jobject obj) : mFilterClient(filterClient),
- mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr),
- mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) {
+MediaEvent::MediaEvent(sp<FilterClient> filterClient, native_handle_t *avHandle, int64_t dataId,
+ int64_t dataSize, jobject obj)
+ : mFilterClient(filterClient),
+ mDataId(dataId),
+ mDataSize(dataSize),
+ mBuffer(nullptr),
+ mDataIdRefCnt(0),
+ mAvHandleRefCnt(0),
+ mIonHandle(nullptr) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
mMediaEventObj = env->NewWeakGlobalRef(obj);
- mAvHandle = native_handle_clone(avHandle.getNativeHandle());
- mLinearBlockObj = NULL;
+ mAvHandle = avHandle;
+ mLinearBlockObj = nullptr;
}
MediaEvent::~MediaEvent() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mMediaEventObj);
- mMediaEventObj = NULL;
+ mMediaEventObj = nullptr;
native_handle_delete(mAvHandle);
- if (mIonHandle != NULL) {
+ if (mIonHandle != nullptr) {
delete mIonHandle;
}
std::shared_ptr<C2Buffer> pC2Buffer = mC2Buffer.lock();
- if (pC2Buffer != NULL) {
+ if (pC2Buffer != nullptr) {
pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this);
}
- if (mLinearBlockObj != NULL) {
+ if (mLinearBlockObj != nullptr) {
env->DeleteWeakGlobalRef(mLinearBlockObj);
- mLinearBlockObj = NULL;
+ mLinearBlockObj = nullptr;
}
- mFilterClient = NULL;
+ mFilterClient = nullptr;
}
void MediaEvent::finalize() {
- if (mAvHandleRefCnt == 0 && mFilterClient != NULL) {
+ if (mAvHandleRefCnt == 0 && mFilterClient != nullptr) {
mFilterClient->releaseAvHandle(
mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0);
native_handle_close(mAvHandle);
@@ -357,11 +476,11 @@ void MediaEvent::finalize() {
}
jobject MediaEvent::getLinearBlock() {
- ALOGD("MediaEvent::getLinearBlock");
- if (mAvHandle == NULL) {
- return NULL;
+ ALOGV("MediaEvent::getLinearBlock");
+ if (mAvHandle == nullptr) {
+ return nullptr;
}
- if (mLinearBlockObj != NULL) {
+ if (mLinearBlockObj != nullptr) {
return mLinearBlockObj;
}
@@ -374,13 +493,13 @@ jobject MediaEvent::getLinearBlock() {
uint64_t avSharedMemSize = info.size;
if (mAvHandle->numFds == 0) {
- if (avSharedHandle == NULL) {
+ if (avSharedHandle == nullptr) {
ALOGE("Shared AV memory handle is not initialized.");
- return NULL;
+ return nullptr;
}
if (avSharedHandle->numFds == 0) {
ALOGE("Shared AV memory handle is empty.");
- return NULL;
+ return nullptr;
}
fd = avSharedHandle->data[0];
dataSize = avSharedMemSize;
@@ -398,7 +517,7 @@ jobject MediaEvent::getLinearBlock() {
// event has value, use it as the index
memIndex = mAvHandle->data[mAvHandle->numFds];
} else {
- if (avSharedHandle != NULL) {
+ if (avSharedHandle != nullptr) {
numInts = avSharedHandle->numInts;
if (numInts > 0) {
// If the first int in the shared native handle has value, use it as the index
@@ -413,7 +532,7 @@ jobject MediaEvent::getLinearBlock() {
if (block != nullptr) {
// CreateLinearBlock delete mIonHandle after it create block successfully.
// ToDo: coordinate who is response to delete mIonHandle
- mIonHandle = NULL;
+ mIonHandle = nullptr;
JNIEnv *env = AndroidRuntime::getJNIEnv();
std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
context->mBlock = block;
@@ -440,46 +559,39 @@ jobject MediaEvent::getLinearBlock() {
mAvHandleRefCnt++;
return linearBlock;
} else {
- native_handle_close(const_cast<native_handle_t*>(
- reinterpret_cast<const native_handle_t*>(mIonHandle)));
- native_handle_delete(const_cast<native_handle_t*>(
- reinterpret_cast<const native_handle_t*>(mIonHandle)));
- mIonHandle = NULL;
- return NULL;
+ native_handle_close(const_cast<native_handle_t *>(
+ reinterpret_cast<const native_handle_t *>(mIonHandle)));
+ native_handle_delete(const_cast<native_handle_t *>(
+ reinterpret_cast<const native_handle_t *>(mIonHandle)));
+ mIonHandle = nullptr;
+ return nullptr;
}
}
-uint64_t MediaEvent::getAudioHandle() {
+int64_t MediaEvent::getAudioHandle() {
mDataIdRefCnt++;
return mDataId;
}
/////////////// FilterClientCallbackImpl ///////////////////////
-
-jobjectArray FilterClientCallbackImpl::getSectionEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterSectionEvent sectionEvent = event.section();
-
- jint tableId = static_cast<jint>(sectionEvent.tableId);
- jint version = static_cast<jint>(sectionEvent.version);
- jint sectionNum = static_cast<jint>(sectionEvent.sectionNum);
- jint dataLength = static_cast<jint>(sectionEvent.dataLength);
+ const DemuxFilterSectionEvent &sectionEvent = event.get<DemuxFilterEvent::Tag::section>();
+ jint tableId = sectionEvent.tableId;
+ jint version = sectionEvent.version;
+ jint sectionNum = sectionEvent.sectionNum;
+ jint dataLength = sectionEvent.dataLength;
- jobject obj =
- env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getMediaEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
jmethodID eventInit = env->GetMethodID(eventClazz,
@@ -488,353 +600,284 @@ jobjectArray FilterClientCallbackImpl::getMediaEvent(
"ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterMediaEvent mediaEvent = event.media();
-
- jobject audioDescriptor = NULL;
- if (mediaEvent.extraMetaData.getDiscriminator()
- == DemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) {
- jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
- jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
-
- AudioExtraMetaData ad = mediaEvent.extraMetaData.audio();
- jbyte adFade = static_cast<jbyte>(ad.adFade);
- jbyte adPan = static_cast<jbyte>(ad.adPan);
- jchar versionTextTag = static_cast<jchar>(ad.versionTextTag);
- jbyte adGainCenter = static_cast<jbyte>(ad.adGainCenter);
- jbyte adGainFront = static_cast<jbyte>(ad.adGainFront);
- jbyte adGainSurround = static_cast<jbyte>(ad.adGainSurround);
-
- audioDescriptor =
- env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag, adGainCenter,
- adGainFront, adGainSurround);
- }
-
- jlong dataLength = static_cast<jlong>(mediaEvent.dataLength);
-
- jint streamId = static_cast<jint>(mediaEvent.streamId);
- jboolean isPtsPresent = static_cast<jboolean>(mediaEvent.isPtsPresent);
- jlong pts = static_cast<jlong>(mediaEvent.pts);
- jlong offset = static_cast<jlong>(mediaEvent.offset);
- jboolean isSecureMemory = static_cast<jboolean>(mediaEvent.isSecureMemory);
- jlong avDataId = static_cast<jlong>(mediaEvent.avDataId);
- jint mpuSequenceNumber = static_cast<jint>(mediaEvent.mpuSequenceNumber);
- jboolean isPesPrivateData = static_cast<jboolean>(mediaEvent.isPesPrivateData);
-
- jobject obj =
- env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
- offset, NULL, isSecureMemory, avDataId, mpuSequenceNumber, isPesPrivateData,
- audioDescriptor);
-
- if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) {
- sp<MediaEvent> mediaEventSp =
- new MediaEvent(mFilterClient, mediaEvent.avMemory,
+ const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
+ jobject audioDescriptor = nullptr;
+ if (mediaEvent.extraMetaData.getTag() == DemuxFilterMediaEventExtraMetaData::Tag::audio) {
+ jclass adClazz = env->FindClass("android/media/tv/tuner/filter/AudioDescriptor");
+ jmethodID adInit = env->GetMethodID(adClazz, "<init>", "(BBCBBB)V");
+
+ const AudioExtraMetaData &ad =
+ mediaEvent.extraMetaData.get<DemuxFilterMediaEventExtraMetaData::Tag::audio>();
+ jbyte adFade = ad.adFade;
+ jbyte adPan = ad.adPan;
+ jchar versionTextTag = ad.versionTextTag;
+ jbyte adGainCenter = ad.adGainCenter;
+ jbyte adGainFront = ad.adGainFront;
+ jbyte adGainSurround = ad.adGainSurround;
+
+ audioDescriptor = env->NewObject(adClazz, adInit, adFade, adPan, versionTextTag,
+ adGainCenter, adGainFront, adGainSurround);
+ }
+
+ jlong dataLength = mediaEvent.dataLength;
+ jint streamId = mediaEvent.streamId;
+ jboolean isPtsPresent = mediaEvent.isPtsPresent;
+ jlong pts = mediaEvent.pts;
+ jlong offset = mediaEvent.offset;
+ jboolean isSecureMemory = mediaEvent.isSecureMemory;
+ jlong avDataId = mediaEvent.avDataId;
+ jint mpuSequenceNumber = mediaEvent.mpuSequenceNumber;
+ jboolean isPesPrivateData = mediaEvent.isPesPrivateData;
+
+ jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
+ offset, nullptr, isSecureMemory, avDataId, mpuSequenceNumber,
+ isPesPrivateData, audioDescriptor);
+
+ uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
+ if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
+ (dataLength > 0 && (dataLength + offset) < avSharedMemSize)) {
+ sp<MediaEvent> mediaEventSp =
+ new MediaEvent(mFilterClient, dupFromAidl(mediaEvent.avMemory),
mediaEvent.avDataId, dataLength + offset, obj);
- mediaEventSp->mAvHandleRefCnt++;
- env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get());
- mediaEventSp->incStrong(obj);
- }
-
- env->SetObjectArrayElement(arr, i, obj);
+ mediaEventSp->mAvHandleRefCnt++;
+ env->SetLongField(obj, eventContext, (jlong)mediaEventSp.get());
+ mediaEventSp->incStrong(obj);
}
- return arr;
+
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getPesEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(III)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterPesEvent pesEvent = event.pes();
-
- jint streamId = static_cast<jint>(pesEvent.streamId);
- jint dataLength = static_cast<jint>(pesEvent.dataLength);
- jint mpuSequenceNumber = static_cast<jint>(pesEvent.mpuSequenceNumber);
+ const DemuxFilterPesEvent &pesEvent = event.get<DemuxFilterEvent::Tag::pes>();
+ jint streamId = pesEvent.streamId;
+ jint dataLength = pesEvent.dataLength;
+ jint mpuSequenceNumber = pesEvent.mpuSequenceNumber;
- jobject obj =
- env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj = env->NewObject(eventClazz, eventInit, streamId, dataLength, mpuSequenceNumber);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getTsRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
- const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TsRecordEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJJI)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterTsRecordEvent tsRecordEvent = event.tsRecord();
- DemuxPid pid = tsRecordEvent.pid;
-
- jint jpid = static_cast<jint>(Constant::INVALID_TS_PID);
+ const DemuxFilterTsRecordEvent &tsRecordEvent = event.get<DemuxFilterEvent::Tag::tsRecord>();
+ DemuxPid pid = tsRecordEvent.pid;
- if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::tPid) {
- jpid = static_cast<jint>(pid.tPid());
- } else if (pid.getDiscriminator() == DemuxPid::hidl_discriminator::mmtpPid) {
- jpid = static_cast<jint>(pid.mmtpPid());
- }
-
- jint sc = 0;
-
- if (tsRecordEvent.scIndexMask.getDiscriminator()
- == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::sc) {
- sc = static_cast<jint>(tsRecordEvent.scIndexMask.sc());
- } else if (tsRecordEvent.scIndexMask.getDiscriminator()
- == DemuxFilterTsRecordEvent::ScIndexMask::hidl_discriminator::scHevc) {
- sc = static_cast<jint>(tsRecordEvent.scIndexMask.scHevc());
- }
+ jint jpid = static_cast<jint>(Constant::INVALID_TS_PID);
+ if (pid.getTag() == DemuxPid::Tag::tPid) {
+ jpid = pid.get<DemuxPid::Tag::tPid>();
+ } else if (pid.getTag() == DemuxPid::Tag::mmtpPid) {
+ jpid = pid.get<DemuxPid::Tag::mmtpPid>();
+ }
- jint ts = static_cast<jint>(tsRecordEvent.tsIndexMask);
+ jint sc = 0;
+ if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scIndex) {
+ sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scIndex>();
+ } else if (tsRecordEvent.scIndexMask.getTag() == DemuxFilterScIndexMask::Tag::scHevc) {
+ sc = tsRecordEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scHevc>();
+ }
- jlong byteNumber = static_cast<jlong>(tsRecordEvent.byteNumber);
+ jint ts = tsRecordEvent.tsIndexMask;
+ jlong byteNumber = tsRecordEvent.byteNumber;
+ jlong pts = tsRecordEvent.pts;
+ jint firstMbInSlice = tsRecordEvent.firstMbInSlice;
- jlong pts;
- jlong firstMbInSlice;
- if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
- DemuxFilterEventExt::Event::hidl_discriminator::tsRecord) {
- pts = static_cast<jlong>(eventsExt[i].tsRecord().pts);
- firstMbInSlice = static_cast<jint>(eventsExt[i].tsRecord().firstMbInSlice);
- } else {
- pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
- firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
- }
-
- jobject obj =
- env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber,
- pts, firstMbInSlice);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj =
+ env->NewObject(eventClazz, eventInit, jpid, ts, sc, byteNumber, pts, firstMbInSlice);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getMmtpRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events,
- const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+void FilterClientCallbackImpl::getMmtpRecordEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MmtpRecordEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IJIJII)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
-
- DemuxFilterMmtpRecordEvent mmtpRecordEvent = event.mmtpRecord();
-
- jint scHevcIndexMask = static_cast<jint>(mmtpRecordEvent.scHevcIndexMask);
- jlong byteNumber = static_cast<jlong>(mmtpRecordEvent.byteNumber);
-
- jint mpuSequenceNumber;
- jlong pts;
- jlong firstMbInSlice;
- jlong tsIndexMask;
+ const DemuxFilterMmtpRecordEvent &mmtpRecordEvent =
+ event.get<DemuxFilterEvent::Tag::mmtpRecord>();
+ jint scHevcIndexMask = mmtpRecordEvent.scHevcIndexMask;
+ jlong byteNumber = mmtpRecordEvent.byteNumber;
+ jint mpuSequenceNumber = mmtpRecordEvent.mpuSequenceNumber;
+ jlong pts = mmtpRecordEvent.pts;
+ jint firstMbInSlice = mmtpRecordEvent.firstMbInSlice;
+ jlong tsIndexMask = mmtpRecordEvent.tsIndexMask;
- if (eventsExt.size() > i && eventsExt[i].getDiscriminator() ==
- DemuxFilterEventExt::Event::hidl_discriminator::mmtpRecord) {
- mpuSequenceNumber = static_cast<jint>(eventsExt[i].mmtpRecord().mpuSequenceNumber);
- pts = static_cast<jlong>(eventsExt[i].mmtpRecord().pts);
- firstMbInSlice = static_cast<jint>(eventsExt[i].mmtpRecord().firstMbInSlice);
- tsIndexMask = static_cast<jint>(eventsExt[i].mmtpRecord().tsIndexMask);
- } else {
- mpuSequenceNumber =
- static_cast<jint>(Constant::INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM);
- pts = static_cast<jlong>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
- firstMbInSlice = static_cast<jint>(Constant::INVALID_FIRST_MACROBLOCK_IN_SLICE);
- tsIndexMask = 0;
- }
-
- jobject obj =
- env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
- mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj = env->NewObject(eventClazz, eventInit, scHevcIndexMask, byteNumber,
+ mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getDownloadEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterDownloadEvent downloadEvent = event.download();
+ const DemuxFilterDownloadEvent &downloadEvent = event.get<DemuxFilterEvent::Tag::download>();
+ jint itemId = downloadEvent.itemId;
+ jint mpuSequenceNumber = downloadEvent.mpuSequenceNumber;
+ jint itemFragmentIndex = downloadEvent.itemFragmentIndex;
+ jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
+ jint dataLength = downloadEvent.dataLength;
- jint itemId = static_cast<jint>(downloadEvent.itemId);
- jint mpuSequenceNumber = static_cast<jint>(downloadEvent.mpuSequenceNumber);
- jint itemFragmentIndex = static_cast<jint>(downloadEvent.itemFragmentIndex);
- jint lastItemFragmentIndex = static_cast<jint>(downloadEvent.lastItemFragmentIndex);
- jint dataLength = static_cast<jint>(downloadEvent.dataLength);
-
- jobject obj =
- env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber, itemFragmentIndex,
- lastItemFragmentIndex, dataLength);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj = env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber,
+ itemFragmentIndex, lastItemFragmentIndex, dataLength);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getIpPayloadEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getIpPayloadEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterIpPayloadEvent ipPayloadEvent = event.ipPayload();
- jint dataLength = static_cast<jint>(ipPayloadEvent.dataLength);
- jobject obj = env->NewObject(eventClazz, eventInit, dataLength);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ const DemuxFilterIpPayloadEvent &ipPayloadEvent = event.get<DemuxFilterEvent::Tag::ipPayload>();
+ jint dataLength = ipPayloadEvent.dataLength;
+ jobject obj = env->NewObject(eventClazz, eventInit, dataLength);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getTemiEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) {
+void FilterClientCallbackImpl::getTemiEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(JB[B)V");
- for (int i = 0; i < events.size(); i++) {
- auto event = events[i];
- DemuxFilterTemiEvent temiEvent = event.temi();
- jlong pts = static_cast<jlong>(temiEvent.pts);
- jbyte descrTag = static_cast<jbyte>(temiEvent.descrTag);
- std::vector<uint8_t> descrData = temiEvent.descrData;
+ const DemuxFilterTemiEvent &temiEvent = event.get<DemuxFilterEvent::Tag::temi>();
+ jlong pts = temiEvent.pts;
+ jbyte descrTag = temiEvent.descrTag;
+ std::vector<uint8_t> descrData = temiEvent.descrData;
- jbyteArray array = env->NewByteArray(descrData.size());
- env->SetByteArrayRegion(
- array, 0, descrData.size(), reinterpret_cast<jbyte*>(&descrData[0]));
+ jbyteArray array = env->NewByteArray(descrData.size());
+ env->SetByteArrayRegion(array, 0, descrData.size(), reinterpret_cast<jbyte *>(&descrData[0]));
- jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array);
- env->SetObjectArrayElement(arr, i, obj);
- }
- return arr;
+ jobject obj = env->NewObject(eventClazz, eventInit, pts, descrTag, array);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getScramblingStatusEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+void FilterClientCallbackImpl::getScramblingStatusEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- auto scramblingStatus = eventsExt[0].monitorEvent().scramblingStatus();
- jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(scramblingStatus));
- env->SetObjectArrayElement(arr, 0, obj);
- return arr;
+ const DemuxFilterMonitorEvent &scramblingStatus =
+ event.get<DemuxFilterEvent::Tag::monitorEvent>()
+ .get<DemuxFilterMonitorEvent::Tag::scramblingStatus>();
+ jobject obj = env->NewObject(eventClazz, eventInit, scramblingStatus);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getIpCidChangeEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+void FilterClientCallbackImpl::getIpCidChangeEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- auto cid = eventsExt[0].monitorEvent().cid();
- jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(cid));
- env->SetObjectArrayElement(arr, 0, obj);
- return arr;
+ const DemuxFilterMonitorEvent &cid = event.get<DemuxFilterEvent::Tag::monitorEvent>()
+ .get<DemuxFilterMonitorEvent::Tag::cid>();
+ jobject obj = env->NewObject(eventClazz, eventInit, cid);
+ env->SetObjectArrayElement(arr, size, obj);
}
-jobjectArray FilterClientCallbackImpl::getRestartEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) {
+void FilterClientCallbackImpl::getRestartEvent(jobjectArray &arr, const int size,
+ const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent");
jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(I)V");
- auto startId = eventsExt[0].startId();
- jobject obj = env->NewObject(eventClazz, eventInit, static_cast<jint>(startId));
- env->SetObjectArrayElement(arr, 0, obj);
- return arr;
+ const int32_t &startId = event.get<DemuxFilterEvent::Tag::startId>();
+ jobject obj = env->NewObject(eventClazz, eventInit, startId);
+ env->SetObjectArrayElement(arr, size, obj);
}
-void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
- const DemuxFilterEventExt& filterEventExt) {
- ALOGD("FilterClientCallbackImpl::onFilterEvent_1_1");
-
+void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &events) {
+ ALOGV("FilterClientCallbackImpl::onFilterEvent");
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
jobjectArray array;
- std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
- std::vector<DemuxFilterEventExt::Event> eventsExt = filterEventExt.events;
- jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/FilterEvent");
+ if (!events.empty()) {
+ array = env->NewObjectArray(events.size(), eventClazz, nullptr);
+ }
- if (events.empty() && !eventsExt.empty()) {
- // Monitor event should be sent with one DemuxFilterMonitorEvent in DemuxFilterEventExt.
- array = env->NewObjectArray(1, eventClazz, NULL);
- auto eventExt = eventsExt[0];
- switch (eventExt.getDiscriminator()) {
- case DemuxFilterEventExt::Event::hidl_discriminator::monitorEvent: {
- switch (eventExt.monitorEvent().getDiscriminator()) {
- case DemuxFilterMonitorEvent::hidl_discriminator::scramblingStatus: {
- array = getScramblingStatusEvent(array, eventsExt);
- break;
- }
- case DemuxFilterMonitorEvent::hidl_discriminator::cid: {
- array = getIpCidChangeEvent(array, eventsExt);
- break;
- }
- default: {
- break;
- }
- }
- break;
- }
- case DemuxFilterEventExt::Event::hidl_discriminator::startId: {
- array = getRestartEvent(array, eventsExt);
+ for (int i = 0, arraySize = 0; i < events.size(); i++) {
+ const DemuxFilterEvent &event = events[i];
+ switch (event.getTag()) {
+ case DemuxFilterEvent::Tag::media: {
+ getMediaEvent(array, arraySize, event);
+ arraySize++;
break;
}
- default: {
+ case DemuxFilterEvent::Tag::section: {
+ getSectionEvent(array, arraySize, event);
+ arraySize++;
break;
}
- }
- }
-
- if (!events.empty()) {
- array = env->NewObjectArray(events.size(), eventClazz, NULL);
- auto event = events[0];
- switch (event.getDiscriminator()) {
- case DemuxFilterEvent::Event::hidl_discriminator::media: {
- array = getMediaEvent(array, events);
+ case DemuxFilterEvent::Tag::pes: {
+ getPesEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::section: {
- array = getSectionEvent(array, events);
+ case DemuxFilterEvent::Tag::tsRecord: {
+ getTsRecordEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::pes: {
- array = getPesEvent(array, events);
+ case DemuxFilterEvent::Tag::mmtpRecord: {
+ getMmtpRecordEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::tsRecord: {
- array = getTsRecordEvent(array, events, eventsExt);
+ case DemuxFilterEvent::Tag::download: {
+ getDownloadEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::mmtpRecord: {
- array = getMmtpRecordEvent(array, events, eventsExt);
+ case DemuxFilterEvent::Tag::ipPayload: {
+ getIpPayloadEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::download: {
- array = getDownloadEvent(array, events);
+ case DemuxFilterEvent::Tag::temi: {
+ getTemiEvent(array, arraySize, event);
+ arraySize++;
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::ipPayload: {
- array = getIpPayloadEvent(array, events);
+ case DemuxFilterEvent::Tag::monitorEvent: {
+ switch (event.get<DemuxFilterEvent::Tag::monitorEvent>().getTag()) {
+ case DemuxFilterMonitorEvent::Tag::scramblingStatus: {
+ getScramblingStatusEvent(array, arraySize, event);
+ arraySize++;
+ break;
+ }
+ case DemuxFilterMonitorEvent::Tag::cid: {
+ getIpCidChangeEvent(array, arraySize, event);
+ arraySize++;
+ break;
+ }
+ default: {
+ ALOGE("FilterClientCallbackImpl::onFilterEvent: unknown MonitorEvent");
+ break;
+ }
+ }
break;
}
- case DemuxFilterEvent::Event::hidl_discriminator::temi: {
- array = getTemiEvent(array, events);
+ case DemuxFilterEvent::Tag::startId: {
+ getRestartEvent(array, arraySize, event);
+ arraySize++;
break;
}
default: {
+ ALOGE("FilterClientCallbackImpl::onFilterEvent: unknown DemuxFilterEvent");
break;
}
}
@@ -846,22 +889,13 @@ void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterE
gFields.onFilterEventID,
array);
} else {
- ALOGE("FilterClientCallbackImpl::onFilterEvent_1_1:"
- "Filter object has been freed. Ignoring callback.");
+ ALOGE("FilterClientCallbackImpl::onFilterEvent:"
+ "Filter object has been freed. Ignoring callback.");
}
}
-void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) {
- ALOGD("FilterClientCallbackImpl::onFilterEvent");
- std::vector<DemuxFilterEventExt::Event> emptyEventsExt;
- DemuxFilterEventExt emptyFilterEventExt {
- .events = emptyEventsExt,
- };
- return onFilterEvent_1_1(filterEvent, emptyFilterEventExt);
-}
-
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
- ALOGD("FilterClientCallbackImpl::onFilterStatus");
+ ALOGV("FilterClientCallbackImpl::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject filter(env->NewLocalRef(mFilterObj));
if (!env->IsSameObject(filter, nullptr)) {
@@ -876,7 +910,7 @@ void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
}
void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) {
- ALOGD("FilterClientCallbackImpl::setFilter");
+ ALOGV("FilterClientCallbackImpl::setFilter");
// Java Object
mFilterObj = filterObj;
mFilterClient = filterClient;
@@ -884,19 +918,18 @@ void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filte
FilterClientCallbackImpl::~FilterClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mFilterObj != NULL) {
+ if (mFilterObj != nullptr) {
env->DeleteWeakGlobalRef(mFilterObj);
- mFilterObj = NULL;
+ mFilterObj = nullptr;
}
- mFilterClient = NULL;
+ mFilterClient = nullptr;
}
/////////////// FrontendClientCallbackImpl ///////////////////////
-
FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
- ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
+ ALOGV("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject frontend(env->NewLocalRef(mObject));
if (!env->IsSameObject(frontend, nullptr)) {
@@ -912,7 +945,7 @@ void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
void FrontendClientCallbackImpl::onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message) {
- ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
+ ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
jobject frontend(env->NewLocalRef(mObject));
@@ -923,7 +956,7 @@ void FrontendClientCallbackImpl::onScanMessage(
}
switch(type) {
case FrontendScanMessageType::LOCKED: {
- if (message.isLocked()) {
+ if (message.get<FrontendScanMessage::Tag::isLocked>()) {
env->CallVoidMethod(
frontend,
env->GetMethodID(clazz, "onLocked", "()V"));
@@ -931,7 +964,7 @@ void FrontendClientCallbackImpl::onScanMessage(
break;
}
case FrontendScanMessageType::END: {
- if (message.isEnd()) {
+ if (message.get<FrontendScanMessage::Tag::isEnd>()) {
env->CallVoidMethod(
frontend,
env->GetMethodID(clazz, "onScanStopped", "()V"));
@@ -939,16 +972,18 @@ void FrontendClientCallbackImpl::onScanMessage(
break;
}
case FrontendScanMessageType::PROGRESS_PERCENT: {
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onProgress", "(I)V"),
- (jint) message.progressPercent());
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onProgress", "(I)V"),
+ message.get<FrontendScanMessage::Tag::progressPercent>());
break;
}
case FrontendScanMessageType::FREQUENCY: {
- std::vector<uint32_t> v = message.frequencies();
- jintArray freqs = env->NewIntArray(v.size());
- env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ std::vector<int64_t> v = message.get<FrontendScanMessage::Tag::frequencies>();
+ std::vector<uint32_t> jintV;
+ for (int i = 0; i < v.size(); i++) {
+ jintV.push_back(static_cast<uint32_t>(v[i]));
+ }
+ jintArray freqs = env->NewIntArray(jintV.size());
+ env->SetIntArrayRegion(freqs, 0, v.size(), reinterpret_cast<jint *>(&jintV[0]));
env->CallVoidMethod(
frontend,
@@ -957,193 +992,146 @@ void FrontendClientCallbackImpl::onScanMessage(
break;
}
case FrontendScanMessageType::SYMBOL_RATE: {
- std::vector<uint32_t> v = message.symbolRates();
+ std::vector<int32_t> v = message.get<FrontendScanMessage::Tag::symbolRates>();
jintArray symbolRates = env->NewIntArray(v.size());
- env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onSymbolRates", "([I)V"),
- symbolRates);
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"),
+ symbolRates);
break;
}
case FrontendScanMessageType::HIERARCHY: {
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onHierarchy", "(I)V"),
- (jint) message.hierarchy());
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onHierarchy", "(I)V"),
+ (jint)message.get<FrontendScanMessage::Tag::hierarchy>());
break;
}
case FrontendScanMessageType::ANALOG_TYPE: {
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onSignalType", "(I)V"),
- (jint) message.analogType());
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSignalType", "(I)V"),
+ (jint)message.get<FrontendScanMessage::Tag::analogType>());
break;
}
case FrontendScanMessageType::PLP_IDS: {
- std::vector<uint8_t> v = message.plpIds();
- std::vector<jint> jintV(v.begin(), v.end());
- jintArray plpIds = env->NewIntArray(v.size());
- env->SetIntArrayRegion(plpIds, 0, jintV.size(), &jintV[0]);
-
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onPlpIds", "([I)V"),
- plpIds);
+ std::vector<int32_t> jintV = message.get<FrontendScanMessage::Tag::plpIds>();
+ jintArray plpIds = env->NewIntArray(jintV.size());
+ env->SetIntArrayRegion(plpIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds);
break;
}
case FrontendScanMessageType::GROUP_IDS: {
- std::vector<uint8_t> v = message.groupIds();
- std::vector<jint> jintV(v.begin(), v.end());
- jintArray groupIds = env->NewIntArray(v.size());
- env->SetIntArrayRegion(groupIds, 0, jintV.size(), &jintV[0]);
-
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onGroupIds", "([I)V"),
- groupIds);
+ std::vector<int32_t> jintV = message.get<FrontendScanMessage::groupIds>();
+ jintArray groupIds = env->NewIntArray(jintV.size());
+ env->SetIntArrayRegion(groupIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds);
break;
}
case FrontendScanMessageType::INPUT_STREAM_IDS: {
- std::vector<uint16_t> v = message.inputStreamIds();
- std::vector<jint> jintV(v.begin(), v.end());
- jintArray streamIds = env->NewIntArray(v.size());
- env->SetIntArrayRegion(streamIds, 0, jintV.size(), &jintV[0]);
-
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onInputStreamIds", "([I)V"),
- streamIds);
+ std::vector<int32_t> jintV = message.get<FrontendScanMessage::inputStreamIds>();
+ jintArray streamIds = env->NewIntArray(jintV.size());
+ env->SetIntArrayRegion(streamIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"),
+ streamIds);
break;
}
case FrontendScanMessageType::STANDARD: {
- FrontendScanMessage::Standard std = message.std();
+ FrontendScanMessageStandard std = message.get<FrontendScanMessage::std>();
jint standard;
- if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sStd) {
- standard = (jint) std.sStd();
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onDvbsStandard", "(I)V"),
- standard);
- } else if (std.getDiscriminator() ==
- FrontendScanMessage::Standard::hidl_discriminator::tStd) {
- standard = (jint) std.tStd();
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onDvbtStandard", "(I)V"),
- standard);
- } else if (std.getDiscriminator() ==
- FrontendScanMessage::Standard::hidl_discriminator::sifStd) {
- standard = (jint) std.sifStd();
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"),
- standard);
+ if (std.getTag() == FrontendScanMessageStandard::Tag::sStd) {
+ standard = (jint)std.get<FrontendScanMessageStandard::Tag::sStd>();
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"),
+ standard);
+ } else if (std.getTag() == FrontendScanMessageStandard::Tag::tStd) {
+ standard = (jint)std.get<FrontendScanMessageStandard::Tag::tStd>();
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"),
+ standard);
+ } else if (std.getTag() == FrontendScanMessageStandard::Tag::sifStd) {
+ standard = (jint)std.get<FrontendScanMessageStandard::Tag::sifStd>();
+ env->CallVoidMethod(frontend,
+ env->GetMethodID(clazz, "onAnalogSifStandard", "(I)V"),
+ standard);
}
break;
}
case FrontendScanMessageType::ATSC3_PLP_INFO: {
jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo");
jmethodID init = env->GetMethodID(plpClazz, "<init>", "(IZ)V");
- std::vector<FrontendScanAtsc3PlpInfo> plpInfos = message.atsc3PlpInfos();
- jobjectArray array = env->NewObjectArray(plpInfos.size(), plpClazz, NULL);
-
+ std::vector<FrontendScanAtsc3PlpInfo> plpInfos =
+ message.get<FrontendScanMessage::atsc3PlpInfos>();
+ jobjectArray array = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
for (int i = 0; i < plpInfos.size(); i++) {
- auto info = plpInfos[i];
- jint plpId = (jint) info.plpId;
- jboolean lls = (jboolean) info.bLlsFlag;
-
+ const FrontendScanAtsc3PlpInfo &info = plpInfos[i];
+ jint plpId = info.plpId;
+ jboolean lls = info.bLlsFlag;
jobject obj = env->NewObject(plpClazz, init, plpId, lls);
env->SetObjectArrayElement(array, i, obj);
}
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onAtsc3PlpInfos",
- "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"),
- array);
+ env->CallVoidMethod(frontend,
+ env->GetMethodID(clazz, "onAtsc3PlpInfos",
+ "([Landroid/media/tv/tuner/frontend/"
+ "Atsc3PlpInfo;)V"),
+ array);
break;
}
- }
-}
-
-void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type,
- const FrontendScanMessageExt1_1& message) {
- ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type);
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
- jobject frontend(env->NewLocalRef(mObject));
- if (env->IsSameObject(frontend, nullptr)) {
- ALOGE("FrontendClientCallbackImpl::onScanMessageExt1_1:"
- "Frontend object has been freed. Ignoring callback.");
- return;
- }
- switch(type) {
- case FrontendScanMessageTypeExt1_1::MODULATION: {
- jint modulation = -1;
- switch (message.modulation().getDiscriminator()) {
- case FrontendModulation::hidl_discriminator::dvbc: {
- modulation = (jint) message.modulation().dvbc();
+ case FrontendScanMessageType::MODULATION: {
+ jint modulationType = -1;
+ FrontendModulation modulation = message.get<FrontendScanMessage::modulation>();
+ switch (modulation.getTag()) {
+ case FrontendModulation::Tag::dvbc: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::dvbc>();
break;
}
- case FrontendModulation::hidl_discriminator::dvbt: {
- modulation = (jint) message.modulation().dvbt();
+ case FrontendModulation::Tag::dvbt: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::dvbt>();
break;
}
- case FrontendModulation::hidl_discriminator::dvbs: {
- modulation = (jint) message.modulation().dvbs();
+ case FrontendModulation::Tag::dvbs: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::dvbs>();
break;
}
- case FrontendModulation::hidl_discriminator::isdbs: {
- modulation = (jint) message.modulation().isdbs();
+ case FrontendModulation::Tag::isdbs: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::isdbs>();
break;
}
- case FrontendModulation::hidl_discriminator::isdbs3: {
- modulation = (jint) message.modulation().isdbs3();
+ case FrontendModulation::Tag::isdbs3: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::isdbs3>();
break;
}
- case FrontendModulation::hidl_discriminator::isdbt: {
- modulation = (jint) message.modulation().isdbt();
+ case FrontendModulation::Tag::isdbt: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::isdbt>();
break;
}
- case FrontendModulation::hidl_discriminator::atsc: {
- modulation = (jint) message.modulation().atsc();
+ case FrontendModulation::Tag::atsc: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::atsc>();
break;
}
- case FrontendModulation::hidl_discriminator::atsc3: {
- modulation = (jint) message.modulation().atsc3();
+ case FrontendModulation::Tag::atsc3: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::atsc3>();
break;
}
- case FrontendModulation::hidl_discriminator::dtmb: {
- modulation = (jint) message.modulation().dtmb();
+ case FrontendModulation::Tag::dtmb: {
+ modulationType = (jint)modulation.get<FrontendModulation::Tag::dtmb>();
break;
}
default: {
break;
}
}
- if (modulation > 0) {
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onModulationReported", "(I)V"),
- modulation);
+ if (modulationType > 0) {
+ env->CallVoidMethod(frontend,
+ env->GetMethodID(clazz, "onModulationReported", "(I)V"),
+ modulationType);
}
break;
}
- case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: {
- bool isHighPriority = message.isHighPriority();
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onPriorityReported", "(Z)V"),
- isHighPriority);
+ case FrontendScanMessageType::HIGH_PRIORITY: {
+ bool isHighPriority = message.get<FrontendScanMessage::Tag::isHighPriority>();
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPriorityReported", "(Z)V"),
+ isHighPriority);
break;
}
- case FrontendScanMessageTypeExt1_1::DVBC_ANNEX: {
- jint dvbcAnnex = (jint) message.annex();
- env->CallVoidMethod(
- frontend,
- env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"),
- dvbcAnnex);
+ case FrontendScanMessageType::DVBC_ANNEX: {
+ jint dvbcAnnex = (jint)message.get<FrontendScanMessage::Tag::annex>();
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbcAnnexReported", "(I)V"),
+ dvbcAnnex);
break;
}
default:
@@ -1153,57 +1141,56 @@ void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1
FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mObject != NULL) {
+ if (mObject != nullptr) {
env->DeleteWeakGlobalRef(mObject);
- mObject = NULL;
+ mObject = nullptr;
}
}
/////////////// Tuner ///////////////////////
-
sp<TunerClient> JTuner::mTunerClient;
-JTuner::JTuner(JNIEnv *env, jobject thiz)
- : mClass(NULL) {
+JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) {
jclass clazz = env->GetObjectClass(thiz);
- CHECK(clazz != NULL);
+ CHECK(clazz != nullptr);
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
- if (mTunerClient == NULL) {
+ if (mTunerClient == nullptr) {
mTunerClient = new TunerClient();
}
+
+ mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
}
JTuner::~JTuner() {
- if (mFeClient != NULL) {
+ if (mFeClient != nullptr) {
mFeClient->close();
}
- if (mDemuxClient != NULL) {
+ if (mDemuxClient != nullptr) {
mDemuxClient->close();
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
- mTunerClient = NULL;
- mFeClient = NULL;
- mDemuxClient = NULL;
- mClass = NULL;
- mObject = NULL;
+ mFeClient = nullptr;
+ mDemuxClient = nullptr;
+ mClass = nullptr;
+ mObject = nullptr;
}
jint JTuner::getTunerVersion() {
- ALOGD("JTuner::getTunerVersion()");
- return (jint) mTunerClient->getHalTunerVersion();
+ ALOGV("JTuner::getTunerVersion()");
+ return (jint)mTunerClient->getHalTunerVersion();
}
jobject JTuner::getFrontendIds() {
- ALOGD("JTuner::getFrontendIds()");
- vector<FrontendId> ids = mTunerClient->getFrontendIds();
+ ALOGV("JTuner::getFrontendIds()");
+ vector<int32_t> ids = mTunerClient->getFrontendIds();
if (ids.size() == 0) {
ALOGW("Frontend isn't available");
- return NULL;
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1214,9 +1201,9 @@ jobject JTuner::getFrontendIds() {
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
- for (int i=0; i < ids.size(); i++) {
- jobject idObj = env->NewObject(integerClazz, intInit, ids[i]);
- env->CallBooleanMethod(obj, arrayListAdd, idObj);
+ for (int i = 0; i < ids.size(); i++) {
+ jobject idObj = env->NewObject(integerClazz, intInit, ids[i]);
+ env->CallBooleanMethod(obj, arrayListAdd, idObj);
}
return obj;
}
@@ -1224,14 +1211,14 @@ jobject JTuner::getFrontendIds() {
jobject JTuner::openFrontendByHandle(int feHandle) {
// TODO: Handle reopening frontend with different handle
sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle);
- if (feClient == NULL) {
+ if (feClient == nullptr) {
ALOGE("Failed to open frontend");
- return NULL;
+ return nullptr;
}
mFeClient = feClient;
mFeId = mFeClient->getId();
- if (mDemuxClient != NULL) {
+ if (mDemuxClient != nullptr) {
mDemuxClient->setFrontendDataSource(mFeClient);
}
@@ -1240,10 +1227,11 @@ jobject JTuner::openFrontendByHandle(int feHandle) {
if (env->IsSameObject(tuner, nullptr)) {
ALOGE("openFrontendByHandle"
"Tuner object has been freed. Failed to open frontend.");
- return NULL;
+ return nullptr;
}
- sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject);
+ sp<FrontendClientCallbackImpl> feClientCb =
+ new FrontendClientCallbackImpl(env->NewWeakGlobalRef(mObject));
mFeClient->setCallback(feClientCb);
// TODO: add more fields to frontend
return env->NewObject(
@@ -1253,127 +1241,135 @@ jobject JTuner::openFrontendByHandle(int feHandle) {
(jint) mFeId);
}
-jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+int JTuner::shareFrontend(int feId) {
+ if (mFeClient != nullptr) {
+ ALOGE("Cannot share frontend:%d because this session is already holding %d",
+ feId, mFeClient->getId());
+ return (int)Result::INVALID_STATE;
+ }
+
+ mSharedFeId = feId;
+ return (int)Result::SUCCESS;
+}
+
+jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
- jint typeCap = caps.analogCaps().typeCap;
- jint sifStandardCap = caps.analogCaps().sifStandardCap;
+ jint typeCap = caps.get<FrontendCapabilities::Tag::analogCaps>().typeCap;
+ jint sifStandardCap = caps.get<FrontendCapabilities::Tag::analogCaps>().sifStandardCap;
return env->NewObject(clazz, capsInit, typeCap, sifStandardCap);
}
-jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
- jint bandwidthCap = caps.atsc3Caps().bandwidthCap;
- jint modulationCap = caps.atsc3Caps().modulationCap;
- jint timeInterleaveModeCap = caps.atsc3Caps().timeInterleaveModeCap;
- jint codeRateCap = caps.atsc3Caps().codeRateCap;
- jint fecCap = caps.atsc3Caps().fecCap;
- jint demodOutputFormatCap = caps.atsc3Caps().demodOutputFormatCap;
+ jint bandwidthCap = caps.get<FrontendCapabilities::Tag::atsc3Caps>().bandwidthCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::atsc3Caps>().modulationCap;
+ jint timeInterleaveModeCap =
+ caps.get<FrontendCapabilities::Tag::atsc3Caps>().timeInterleaveModeCap;
+ jint codeRateCap = caps.get<FrontendCapabilities::Tag::atsc3Caps>().codeRateCap;
+ jint fecCap = caps.get<FrontendCapabilities::Tag::atsc3Caps>().fecCap;
+ jint demodOutputFormatCap =
+ caps.get<FrontendCapabilities::Tag::atsc3Caps>().demodOutputFormatCap;
return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap,
codeRateCap, fecCap, demodOutputFormatCap);
}
-jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(I)V");
- jint modulationCap = caps.atscCaps().modulationCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::atscCaps>().modulationCap;
return env->NewObject(clazz, capsInit, modulationCap);
}
-jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IJI)V");
- jint modulationCap = caps.dvbcCaps().modulationCap;
- jlong fecCap = caps.dvbcCaps().fecCap;
- jint annexCap = caps.dvbcCaps().annexCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::dvbcCaps>().modulationCap;
+ jlong fecCap = caps.get<FrontendCapabilities::Tag::dvbcCaps>().fecCap;
+ jint annexCap = caps.get<FrontendCapabilities::Tag::dvbcCaps>().annexCap;
return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap);
}
-jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IJI)V");
- jint modulationCap = caps.dvbsCaps().modulationCap;
- jlong innerfecCap = caps.dvbsCaps().innerfecCap;
- jint standard = caps.dvbsCaps().standard;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::dvbsCaps>().modulationCap;
+ jlong innerfecCap = caps.get<FrontendCapabilities::Tag::dvbsCaps>().innerfecCap;
+ jint standard = caps.get<FrontendCapabilities::Tag::dvbsCaps>().standard;
return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard);
}
-jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIZZ)V");
- jint transmissionModeCap = caps.dvbtCaps().transmissionModeCap;
- jint bandwidthCap = caps.dvbtCaps().bandwidthCap;
- jint constellationCap = caps.dvbtCaps().constellationCap;
- jint coderateCap = caps.dvbtCaps().coderateCap;
- jint hierarchyCap = caps.dvbtCaps().hierarchyCap;
- jint guardIntervalCap = caps.dvbtCaps().guardIntervalCap;
- jboolean isT2Supported = caps.dvbtCaps().isT2Supported;
- jboolean isMisoSupported = caps.dvbtCaps().isMisoSupported;
+ jint transmissionModeCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().transmissionModeCap;
+ jint bandwidthCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().bandwidthCap;
+ jint constellationCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().constellationCap;
+ jint coderateCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().coderateCap;
+ jint hierarchyCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().hierarchyCap;
+ jint guardIntervalCap = caps.get<FrontendCapabilities::Tag::dvbtCaps>().guardIntervalCap;
+ jboolean isT2Supported = caps.get<FrontendCapabilities::Tag::dvbtCaps>().isT2Supported;
+ jboolean isMisoSupported = caps.get<FrontendCapabilities::Tag::dvbtCaps>().isMisoSupported;
return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap,
coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported);
}
-jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
- jint modulationCap = caps.isdbs3Caps().modulationCap;
- jint coderateCap = caps.isdbs3Caps().coderateCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::isdbs3Caps>().modulationCap;
+ jint coderateCap = caps.get<FrontendCapabilities::Tag::isdbs3Caps>().coderateCap;
return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
}
-jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
- jint modulationCap = caps.isdbsCaps().modulationCap;
- jint coderateCap = caps.isdbsCaps().coderateCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::isdbsCaps>().modulationCap;
+ jint coderateCap = caps.get<FrontendCapabilities::Tag::isdbsCaps>().coderateCap;
return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
}
-jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps) {
+jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIII)V");
- jint modeCap = caps.isdbtCaps().modeCap;
- jint bandwidthCap = caps.isdbtCaps().bandwidthCap;
- jint modulationCap = caps.isdbtCaps().modulationCap;
- jint coderateCap = caps.isdbtCaps().coderateCap;
- jint guardIntervalCap = caps.isdbtCaps().guardIntervalCap;
+ jint modeCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().modeCap;
+ jint bandwidthCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().bandwidthCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().modulationCap;
+ jint coderateCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().coderateCap;
+ jint guardIntervalCap = caps.get<FrontendCapabilities::Tag::isdbtCaps>().guardIntervalCap;
return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap,
guardIntervalCap);
}
-jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, int id) {
+jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
- shared_ptr<FrontendDtmbCapabilities> dtmbCaps = mTunerClient->getFrontendDtmbCapabilities(id);
- if (dtmbCaps == NULL) {
- return NULL;
- }
-
- jint modulationCap = dtmbCaps->modulationCap;
- jint transmissionModeCap = dtmbCaps->transmissionModeCap;
- jint guardIntervalCap = dtmbCaps->guardIntervalCap;
- jint interleaveModeCap = dtmbCaps->interleaveModeCap;
- jint codeRateCap = dtmbCaps->codeRateCap;
- jint bandwidthCap = dtmbCaps->bandwidthCap;
+ jint modulationCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().modulationCap;
+ jint transmissionModeCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().transmissionModeCap;
+ jint guardIntervalCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().guardIntervalCap;
+ jint interleaveModeCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().interleaveModeCap;
+ jint codeRateCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().codeRateCap;
+ jint bandwidthCap = caps.get<FrontendCapabilities::Tag::dtmbCaps>().bandwidthCap;
return env->NewObject(clazz, capsInit, modulationCap, transmissionModeCap, guardIntervalCap,
interleaveModeCap, codeRateCap, bandwidthCap);
@@ -1382,8 +1378,8 @@ jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, int id) {
jobject JTuner::getFrontendInfo(int id) {
shared_ptr<FrontendInfo> feInfo;
feInfo = mTunerClient->getFrontendInfo(id);
- if (feInfo == NULL) {
- return NULL;
+ if (feInfo == nullptr) {
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1391,9 +1387,9 @@ jobject JTuner::getFrontendInfo(int id) {
jmethodID infoInit = env->GetMethodID(clazz, "<init>",
"(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
- jint type = (jint) feInfo->type;
- jint minFrequency = feInfo->minFrequency;
- jint maxFrequency = feInfo->maxFrequency;
+ jint type = (jint)feInfo->type;
+ jint minFrequency = static_cast<uint32_t>(feInfo->minFrequency);
+ jint maxFrequency = static_cast<uint32_t>(feInfo->maxFrequency);
jint minSymbolRate = feInfo->minSymbolRate;
jint maxSymbolRate = feInfo->maxSymbolRate;
jint acquireRange = feInfo->acquireRange;
@@ -1402,95 +1398,84 @@ jobject JTuner::getFrontendInfo(int id) {
env->SetIntArrayRegion(
statusCaps, 0, feInfo->statusCaps.size(),
reinterpret_cast<jint*>(&feInfo->statusCaps[0]));
- FrontendInfo::FrontendCapabilities caps = feInfo->frontendCaps;
-
- jobject jcaps = NULL;
-
- if (feInfo->type == static_cast<FrontendType>(
- ::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
- jcaps = getDtmbFrontendCaps(env, id);
- }
+ FrontendCapabilities caps = feInfo->frontendCaps;
+ jobject jcaps = nullptr;
switch(feInfo->type) {
case FrontendType::ANALOG:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::analogCaps == caps.getTag()) {
jcaps = getAnalogFrontendCaps(env, caps);
}
break;
case FrontendType::ATSC3:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::atsc3Caps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::atsc3Caps == caps.getTag()) {
jcaps = getAtsc3FrontendCaps(env, caps);
}
break;
case FrontendType::ATSC:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::atscCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::atscCaps == caps.getTag()) {
jcaps = getAtscFrontendCaps(env, caps);
}
break;
case FrontendType::DVBC:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbcCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::dvbcCaps == caps.getTag()) {
jcaps = getDvbcFrontendCaps(env, caps);
}
break;
case FrontendType::DVBS:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbsCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::dvbsCaps == caps.getTag()) {
jcaps = getDvbsFrontendCaps(env, caps);
}
break;
case FrontendType::DVBT:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::dvbtCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::dvbtCaps == caps.getTag()) {
jcaps = getDvbtFrontendCaps(env, caps);
}
break;
case FrontendType::ISDBS:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbsCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::isdbsCaps == caps.getTag()) {
jcaps = getIsdbsFrontendCaps(env, caps);
}
break;
case FrontendType::ISDBS3:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbs3Caps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::isdbs3Caps == caps.getTag()) {
jcaps = getIsdbs3FrontendCaps(env, caps);
}
break;
case FrontendType::ISDBT:
- if (FrontendInfo::FrontendCapabilities::hidl_discriminator::isdbtCaps
- == caps.getDiscriminator()) {
+ if (FrontendCapabilities::Tag::isdbtCaps == caps.getTag()) {
jcaps = getIsdbtFrontendCaps(env, caps);
}
break;
+ case FrontendType::DTMB:
+ if (FrontendCapabilities::Tag::dtmbCaps == caps.getTag()) {
+ jcaps = getDtmbFrontendCaps(env, caps);
+ }
+ break;
default:
break;
}
- return env->NewObject(
- clazz, infoInit, (jint) id, type, minFrequency, maxFrequency, minSymbolRate,
- maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
+ return env->NewObject(clazz, infoInit, id, type, minFrequency, maxFrequency, minSymbolRate,
+ maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
}
jobject JTuner::openLnbByHandle(int handle) {
- if (mTunerClient == NULL) {
- return NULL;
+ if (mTunerClient == nullptr) {
+ return nullptr;
}
sp<LnbClient> lnbClient;
sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
lnbClient = mTunerClient->openLnb(handle);
- if (lnbClient == NULL) {
+ if (lnbClient == nullptr) {
ALOGD("Failed to open lnb, handle = %d", handle);
- return NULL;
+ return nullptr;
}
if (lnbClient->setCallback(callback) != Result::SUCCESS) {
ALOGD("Failed to set lnb callback");
- return NULL;
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1506,8 +1491,8 @@ jobject JTuner::openLnbByHandle(int handle) {
}
jobject JTuner::openLnbByName(jstring name) {
- if (mTunerClient == NULL) {
- return NULL;
+ if (mTunerClient == nullptr) {
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1515,14 +1500,14 @@ jobject JTuner::openLnbByName(jstring name) {
sp<LnbClient> lnbClient;
sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl();
lnbClient = mTunerClient->openLnbByName(lnbName);
- if (lnbClient == NULL) {
+ if (lnbClient == nullptr) {
ALOGD("Failed to open lnb by name, name = %s", lnbName.c_str());
- return NULL;
+ return nullptr;
}
if (lnbClient->setCallback(callback) != Result::SUCCESS) {
ALOGD("Failed to set lnb callback");
- return NULL;
+ return nullptr;
}
jobject lnbObj = env->NewObject(
@@ -1536,12 +1521,12 @@ jobject JTuner::openLnbByName(jstring name) {
return lnbObj;
}
-int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) {
+int JTuner::tune(const FrontendSettings &settings) {
if (mFeClient == nullptr) {
ALOGE("frontend is not initialized");
return (int)Result::INVALID_STATE;
}
- return (int) mFeClient->tune(settings, settingsExt1_1);
+ return (int)mFeClient->tune(settings);
}
int JTuner::stopTune() {
@@ -1552,18 +1537,17 @@ int JTuner::stopTune() {
return (int) mFeClient->stopTune();
}
-int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- if (mFeClient == NULL) {
+int JTuner::scan(const FrontendSettings &settings, FrontendScanType scanType) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- Result result = mFeClient->scan(settings, scanType, settingsExt1_1);
+ Result result = mFeClient->scan(settings, scanType);
return (int)result;
}
int JTuner::stopScan() {
- if (mFeClient == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
@@ -1572,11 +1556,11 @@ int JTuner::stopScan() {
}
int JTuner::setLnb(sp<LnbClient> lnbClient) {
- if (mFeClient == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
- if (lnbClient == NULL) {
+ if (lnbClient == nullptr) {
ALOGE("lnb is not initialized");
return (int)Result::INVALID_STATE;
}
@@ -1585,7 +1569,7 @@ int JTuner::setLnb(sp<LnbClient> lnbClient) {
}
int JTuner::setLna(bool enable) {
- if (mFeClient == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
@@ -1600,12 +1584,14 @@ Result JTuner::openDemux(int handle) {
if (mDemuxClient == nullptr) {
mDemuxClient = mTunerClient->openDemux(handle);
- if (mDemuxClient == NULL) {
+ if (mDemuxClient == nullptr) {
ALOGE("Failed to open demux");
return Result::UNKNOWN_ERROR;
}
- if (mFeClient != NULL) {
- mDemuxClient->setFrontendDataSource(mFeClient);
+ if (mFeClient != nullptr) {
+ return mDemuxClient->setFrontendDataSource(mFeClient);
+ } else if (mSharedFeId != (int)Constant::INVALID_FRONTEND_ID) {
+ return mDemuxClient->setFrontendDataSourceById(mSharedFeId);
}
}
@@ -1615,26 +1601,28 @@ Result JTuner::openDemux(int handle) {
jint JTuner::close() {
Result res = Result::SUCCESS;
- if (mFeClient != NULL) {
+ if (mFeClient != nullptr) {
res = mFeClient->close();
if (res != Result::SUCCESS) {
- return (jint) res;
+ return (jint)res;
}
- mFeClient = NULL;
+ mFeClient = nullptr;
}
- if (mDemuxClient != NULL) {
+ if (mDemuxClient != nullptr) {
res = mDemuxClient->close();
if (res != Result::SUCCESS) {
- return (jint) res;
+ return (jint)res;
}
- mDemuxClient = NULL;
+ mDemuxClient = nullptr;
}
- return (jint) res;
+
+ mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
+ return (jint)res;
}
jobject JTuner::getAvSyncHwId(sp<FilterClient> filterClient) {
- if (mDemuxClient == NULL) {
- return NULL;
+ if (mDemuxClient == nullptr) {
+ return nullptr;
}
int avSyncHwId = mDemuxClient->getAvSyncHwId(filterClient);
@@ -1644,33 +1632,32 @@ jobject JTuner::getAvSyncHwId(sp<FilterClient> filterClient) {
jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
return env->NewObject(integerClazz, intInit, avSyncHwId);
}
- return NULL;
+ return nullptr;
}
jobject JTuner::getAvSyncTime(jint id) {
- if (mDemuxClient == NULL) {
- return NULL;
+ if (mDemuxClient == nullptr) {
+ return nullptr;
}
long time = mDemuxClient->getAvSyncTime((int)id);
if (time >= 0) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
- return env->NewObject(longClazz, longInit, static_cast<jlong>(time));
+ return env->NewObject(longClazz, longInit, time);
}
- return NULL;
+ return nullptr;
}
int JTuner::connectCiCam(jint id) {
- if (mDemuxClient == NULL) {
+ if (mDemuxClient == nullptr) {
return (int)Result::NOT_INITIALIZED;
}
- Result r = mDemuxClient->connectCiCam((int)id);
- return (int) r;
+ return (int)mDemuxClient->connectCiCam((int)id);
}
int JTuner::linkCiCam(int id) {
- if (mFeClient == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Constant::INVALID_LTS_ID;
}
@@ -1678,35 +1665,30 @@ int JTuner::linkCiCam(int id) {
}
int JTuner::disconnectCiCam() {
- if (mDemuxClient == NULL) {
+ if (mDemuxClient == nullptr) {
return (int)Result::NOT_INITIALIZED;
}
- Result r = mDemuxClient->disconnectCiCam();
- return (int) r;
+ return (int)mDemuxClient->disconnectCiCam();
}
-
int JTuner::unlinkCiCam(int id) {
- if (mFeClient == NULL) {
+ if (mFeClient == nullptr) {
ALOGE("frontend client is not initialized");
return (int)Result::INVALID_STATE;
}
-
- Result r = mFeClient->unlinkCiCamToFrontend(id);
-
- return (int) r;
+ return (int)mFeClient->unlinkCiCamToFrontend(id);
}
jobject JTuner::openDescrambler() {
- ALOGD("JTuner::openDescrambler");
+ ALOGV("JTuner::openDescrambler");
if (mTunerClient == nullptr || mDemuxClient == nullptr) {
- return NULL;
+ return nullptr;
}
sp<DescramblerClient> descramblerClient = mTunerClient->openDescrambler(0/*unused*/);
- if (descramblerClient == NULL) {
+ if (descramblerClient == nullptr) {
ALOGD("Failed to open descrambler");
- return NULL;
+ return nullptr;
}
descramblerClient->setDemuxSource(mDemuxClient);
@@ -1724,31 +1706,28 @@ jobject JTuner::openDescrambler() {
}
jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
- if (mDemuxClient == NULL) {
- return NULL;
+ if (mDemuxClient == nullptr) {
+ return nullptr;
}
sp<FilterClient> filterClient;
sp<FilterClientCallbackImpl> callback = new FilterClientCallbackImpl();
filterClient = mDemuxClient->openFilter(type, bufferSize, callback);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to open filter, type = %d", type.mainType);
- return NULL;
+ return nullptr;
}
- uint64_t fId;
+ int64_t fId;
Result res = filterClient->getId64Bit(fId);
if (res != Result::SUCCESS) {
- uint32_t id;
+ int32_t id;
filterClient->getId(id);
- fId = static_cast<uint64_t>(id);
+ fId = static_cast<int64_t>(id);
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject filterObj =
- env->NewObject(
- env->FindClass("android/media/tv/tuner/filter/Filter"),
- gFields.filterInitID,
- (jlong) fId);
+ jobject filterObj = env->NewObject(env->FindClass("android/media/tv/tuner/filter/Filter"),
+ gFields.filterInitID, fId);
filterClient->incStrong(filterObj);
env->SetLongField(filterObj, gFields.filterContext, (jlong)filterClient.get());
@@ -1758,8 +1737,8 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
}
jobject JTuner::openTimeFilter() {
- if (mDemuxClient == NULL) {
- return NULL;
+ if (mDemuxClient == nullptr) {
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1768,9 +1747,9 @@ jobject JTuner::openTimeFilter() {
env->FindClass("android/media/tv/tuner/filter/TimeFilter"),
gFields.timeFilterInitID);
sp<TimeFilterClient> timeFilterClient = mDemuxClient->openTimeFilter();
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed to open time filter.");
- return NULL;
+ return nullptr;
}
timeFilterClient->incStrong(timeFilterObj);
env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterClient.get());
@@ -1779,16 +1758,17 @@ jobject JTuner::openTimeFilter() {
}
jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
- ALOGD("JTuner::openDvr");
- if (mDemuxClient == NULL) {
- return NULL;
+ ALOGV("JTuner::openDvr");
+ if (mDemuxClient == nullptr) {
+ return nullptr;
}
+
sp<DvrClient> dvrClient;
sp<DvrClientCallbackImpl> callback = new DvrClientCallbackImpl();
dvrClient = mDemuxClient->openDvr(type, (int) bufferSize, callback);
-
- if (dvrClient == NULL) {
- return NULL;
+ if (dvrClient == nullptr) {
+ ALOGD("Failed to open Dvr");
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1815,14 +1795,14 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
}
jobject JTuner::getDemuxCaps() {
- if (mTunerClient == NULL) {
- return NULL;
+ if (mTunerClient == nullptr) {
+ return nullptr;
}
shared_ptr<DemuxCapabilities> caps;
caps = mTunerClient->getDemuxCaps();
- if (caps == NULL) {
- return NULL;
+ if (caps == nullptr) {
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -1839,12 +1819,12 @@ jobject JTuner::getDemuxCaps() {
jint numPesFilter = caps->numPesFilter;
jint numPcrFilter = caps->numPcrFilter;
jlong numBytesInSectionFilter = caps->numBytesInSectionFilter;
- jint filterCaps = static_cast<jint>(caps->filterCaps);
+ jint filterCaps = caps->filterCaps;
jboolean bTimeFilter = caps->bTimeFilter;
jintArray linkCaps = env->NewIntArray(caps->linkCaps.size());
- env->SetIntArrayRegion(
- linkCaps, 0, caps->linkCaps.size(), reinterpret_cast<jint*>(&caps->linkCaps[0]));
+ env->SetIntArrayRegion(linkCaps, 0, caps->linkCaps.size(),
+ reinterpret_cast<jint *>(&caps->linkCaps[0]));
return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter,
numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter,
@@ -1852,25 +1832,19 @@ jobject JTuner::getDemuxCaps() {
}
jobject JTuner::getFrontendStatus(jintArray types) {
- if (mFeClient == NULL) {
- return NULL;
+ if (mFeClient == nullptr) {
+ return nullptr;
}
JNIEnv *env = AndroidRuntime::getJNIEnv();
jsize size = env->GetArrayLength(types);
jint intTypes[size];
env->GetIntArrayRegion(types, 0, size, intTypes);
std::vector<FrontendStatusType> v;
- std::vector<FrontendStatusTypeExt1_1> v_1_1;
for (int i = 0; i < size; i++) {
- if (isV1_1ExtendedStatusType(intTypes[i])) {
- v_1_1.push_back(static_cast<FrontendStatusTypeExt1_1>(intTypes[i]));
- } else {
- v.push_back(static_cast<FrontendStatusType>(intTypes[i]));
- }
+ v.push_back(static_cast<FrontendStatusType>(intTypes[i]));
}
- hidl_vec<FrontendStatus> status = mFeClient->getStatus(v);
- hidl_vec<FrontendStatusExt1_1> status_1_1 = mFeClient->getStatusExtended_1_1(v_1_1);
+ vector<FrontendStatus> status = mFeClient->getStatus(v);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus");
jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
@@ -1881,97 +1855,106 @@ jobject JTuner::getFrontendStatus(jintArray types) {
jclass booleanClazz = env->FindClass("java/lang/Boolean");
jmethodID initBoolean = env->GetMethodID(booleanClazz, "<init>", "(Z)V");
- for (auto s : status) {
- switch(s.getDiscriminator()) {
- case FrontendStatus::hidl_discriminator::isDemodLocked: {
+ for (int i = 0; i < status.size(); i++) {
+ const FrontendStatus &s = status[i];
+ switch (s.getTag()) {
+ case FrontendStatus::Tag::isDemodLocked: {
jfieldID field = env->GetFieldID(clazz, "mIsDemodLocked", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isDemodLocked()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isDemodLocked>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatus::hidl_discriminator::snr: {
+ case FrontendStatus::Tag::snr: {
jfieldID field = env->GetFieldID(clazz, "mSnr", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.snr()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::snr>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::ber: {
+ case FrontendStatus::Tag::ber: {
jfieldID field = env->GetFieldID(clazz, "mBer", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.ber()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::ber>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::per: {
+ case FrontendStatus::Tag::per: {
jfieldID field = env->GetFieldID(clazz, "mPer", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.per()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::per>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::preBer: {
+ case FrontendStatus::Tag::preBer: {
jfieldID field = env->GetFieldID(clazz, "mPerBer", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.preBer()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::preBer>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::signalQuality: {
+ case FrontendStatus::Tag::signalQuality: {
jfieldID field = env->GetFieldID(clazz, "mSignalQuality", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.signalQuality()));
+ jobject newIntegerObj = env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::signalQuality>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::signalStrength: {
+ case FrontendStatus::Tag::signalStrength: {
jfieldID field = env->GetFieldID(clazz, "mSignalStrength", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.signalStrength()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::signalStrength>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::symbolRate: {
+ case FrontendStatus::Tag::symbolRate: {
jfieldID field = env->GetFieldID(clazz, "mSymbolRate", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.symbolRate()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::symbolRate>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::innerFec: {
+ case FrontendStatus::Tag::innerFec: {
jfieldID field = env->GetFieldID(clazz, "mInnerFec", "Ljava/lang/Long;");
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID initLong = env->GetMethodID(longClazz, "<init>", "(J)V");
- jobject newLongObj = env->NewObject(
- longClazz, initLong, static_cast<jlong>(s.innerFec()));
+ jobject newLongObj =
+ env->NewObject(longClazz, initLong,
+ static_cast<long>(s.get<FrontendStatus::Tag::innerFec>()));
env->SetObjectField(statusObj, field, newLongObj);
break;
}
- case FrontendStatus::hidl_discriminator::modulation: {
+ case FrontendStatus::Tag::modulationStatus: {
jfieldID field = env->GetFieldID(clazz, "mModulation", "Ljava/lang/Integer;");
- FrontendModulationStatus modulation = s.modulation();
+ FrontendModulationStatus modulation =
+ s.get<FrontendStatus::Tag::modulationStatus>();
jint intModulation;
bool valid = true;
- switch(modulation.getDiscriminator()) {
- case FrontendModulationStatus::hidl_discriminator::dvbc: {
- intModulation = static_cast<jint>(modulation.dvbc());
+ switch (modulation.getTag()) {
+ case FrontendModulationStatus::Tag::dvbc: {
+ intModulation = static_cast<jint>(
+ modulation.get<FrontendModulationStatus::Tag::dvbc>());
break;
}
- case FrontendModulationStatus::hidl_discriminator::dvbs: {
- intModulation = static_cast<jint>(modulation.dvbs());
+ case FrontendModulationStatus::Tag::dvbs: {
+ intModulation = static_cast<jint>(
+ modulation.get<FrontendModulationStatus::Tag::dvbs>());
break;
}
- case FrontendModulationStatus::hidl_discriminator::isdbs: {
- intModulation = static_cast<jint>(modulation.isdbs());
+ case FrontendModulationStatus::Tag::isdbs: {
+ intModulation = static_cast<jint>(
+ modulation.get<FrontendModulationStatus::Tag::isdbs>());
break;
}
- case FrontendModulationStatus::hidl_discriminator::isdbs3: {
- intModulation = static_cast<jint>(modulation.isdbs3());
+ case FrontendModulationStatus::Tag::isdbs3: {
+ intModulation = static_cast<jint>(
+ modulation.get<FrontendModulationStatus::Tag::isdbs3>());
break;
}
- case FrontendModulationStatus::hidl_discriminator::isdbt: {
- intModulation = static_cast<jint>(modulation.isdbt());
+ case FrontendModulationStatus::Tag::isdbt: {
+ intModulation = static_cast<jint>(
+ modulation.get<FrontendModulationStatus::Tag::isdbt>());
break;
}
default: {
@@ -1985,51 +1968,53 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatus::hidl_discriminator::inversion: {
+ case FrontendStatus::Tag::inversion: {
jfieldID field = env->GetFieldID(clazz, "mInversion", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.inversion()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ static_cast<jint>(s.get<FrontendStatus::Tag::inversion>()));
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::lnbVoltage: {
+ case FrontendStatus::Tag::lnbVoltage: {
jfieldID field = env->GetFieldID(clazz, "mLnbVoltage", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.lnbVoltage()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ static_cast<jint>(s.get<FrontendStatus::Tag::lnbVoltage>()));
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::plpId: {
+ case FrontendStatus::Tag::plpId: {
jfieldID field = env->GetFieldID(clazz, "mPlpId", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.plpId()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::plpId>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::isEWBS: {
+ case FrontendStatus::Tag::isEWBS: {
jfieldID field = env->GetFieldID(clazz, "mIsEwbs", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isEWBS()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isEWBS>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatus::hidl_discriminator::agc: {
+ case FrontendStatus::Tag::agc: {
jfieldID field = env->GetFieldID(clazz, "mAgc", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.agc()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::agc>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::isLnaOn: {
+ case FrontendStatus::Tag::isLnaOn: {
jfieldID field = env->GetFieldID(clazz, "mIsLnaOn", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isLnaOn()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isLnaOn>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatus::hidl_discriminator::isLayerError: {
+ case FrontendStatus::Tag::isLayerError: {
jfieldID field = env->GetFieldID(clazz, "mIsLayerErrors", "[Z");
- hidl_vec<bool> layerErr = s.isLayerError();
+ vector<bool> layerErr = s.get<FrontendStatus::Tag::isLayerError>();
jbooleanArray valObj = env->NewBooleanArray(layerErr.size());
@@ -2040,49 +2025,51 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
break;
}
- case FrontendStatus::hidl_discriminator::mer: {
+ case FrontendStatus::Tag::mer: {
jfieldID field = env->GetFieldID(clazz, "mMer", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.mer()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::mer>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::freqOffset: {
+ case FrontendStatus::Tag::freqOffset: {
jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.freqOffset()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ static_cast<uint32_t>(
+ s.get<FrontendStatus::Tag::freqOffset>()));
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::hierarchy: {
+ case FrontendStatus::Tag::hierarchy: {
jfieldID field = env->GetFieldID(clazz, "mHierarchy", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.hierarchy()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt,
+ static_cast<jint>(s.get<FrontendStatus::Tag::hierarchy>()));
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatus::hidl_discriminator::isRfLocked: {
+ case FrontendStatus::Tag::isRfLocked: {
jfieldID field = env->GetFieldID(clazz, "mIsRfLocked", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isRfLocked()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isRfLocked>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatus::hidl_discriminator::plpInfo: {
+ case FrontendStatus::Tag::plpInfo: {
jfieldID field = env->GetFieldID(clazz, "mPlpInfo",
"[Landroid/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo;");
jclass plpClazz = env->FindClass(
"android/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo");
jmethodID initPlp = env->GetMethodID(plpClazz, "<init>", "(IZI)V");
- hidl_vec<FrontendStatusAtsc3PlpInfo> plpInfos = s.plpInfo();
-
- jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, NULL);
+ vector<FrontendStatusAtsc3PlpInfo> plpInfos = s.get<FrontendStatus::Tag::plpInfo>();
+ jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
for (int i = 0; i < plpInfos.size(); i++) {
- auto info = plpInfos[i];
- jint plpId = (jint) info.plpId;
- jboolean isLocked = (jboolean) info.isLocked;
- jint uec = (jint) info.uec;
+ const FrontendStatusAtsc3PlpInfo &info = plpInfos[i];
+ jint plpId = info.plpId;
+ jboolean isLocked = info.isLocked;
+ jint uec = info.uec;
jobject plpObj = env->NewObject(plpClazz, initPlp, plpId, isLocked, uec);
env->SetObjectArrayElement(valObj, i, plpObj);
@@ -2091,74 +2078,75 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
break;
}
- default: {
- break;
- }
- }
- }
-
- for (auto s : status_1_1) {
- switch(s.getDiscriminator()) {
- case FrontendStatusExt1_1::hidl_discriminator::modulations: {
+ case FrontendStatus::Tag::modulations: {
jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I");
- std::vector<FrontendModulation> v = s.modulations();
+ std::vector<FrontendModulation> v = s.get<FrontendStatus::Tag::modulations>();
jintArray valObj = env->NewIntArray(v.size());
bool valid = false;
jint m[1];
for (int i = 0; i < v.size(); i++) {
- auto modulation = v[i];
- switch(modulation.getDiscriminator()) {
- case FrontendModulation::hidl_discriminator::dvbc: {
- m[0] = static_cast<jint>(modulation.dvbc());
+ const FrontendModulation &modulation = v[i];
+ switch (modulation.getTag()) {
+ case FrontendModulation::Tag::dvbc: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::dvbc>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::dvbs: {
- m[0] = static_cast<jint>(modulation.dvbs());
+ case FrontendModulation::Tag::dvbs: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::dvbs>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::dvbt: {
- m[0] = static_cast<jint>(modulation.dvbt());
+ case FrontendModulation::Tag::dvbt: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::dvbt>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::isdbs: {
- m[0] = static_cast<jint>(modulation.isdbs());
+ case FrontendModulation::Tag::isdbs: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::isdbs>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::isdbs3: {
- m[0] = static_cast<jint>(modulation.isdbs3());
+ case FrontendModulation::Tag::isdbs3: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::isdbs3>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::isdbt: {
- m[0] = static_cast<jint>(modulation.isdbt());
+ case FrontendModulation::Tag::isdbt: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::isdbt>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::atsc: {
- m[0] = static_cast<jint>(modulation.atsc());
+ case FrontendModulation::Tag::atsc: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::atsc>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::atsc3: {
- m[0] = static_cast<jint>(modulation.atsc3());
+ case FrontendModulation::Tag::atsc3: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::atsc3>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
}
- case FrontendModulation::hidl_discriminator::dtmb: {
- m[0] = static_cast<jint>(modulation.dtmb());
+ case FrontendModulation::Tag::dtmb: {
+ m[0] = static_cast<jint>(
+ modulation.get<FrontendModulation::Tag::dtmb>());
env->SetIntArrayRegion(valObj, i, 1, m);
valid = true;
break;
@@ -2172,51 +2160,55 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::bers: {
+ case FrontendStatus::Tag::bers: {
jfieldID field = env->GetFieldID(clazz, "mBers", "[I");
- std::vector<uint32_t> v = s.bers();
+ std::vector<int32_t> v = s.get<FrontendStatus::Tag::bers>();
jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
env->SetObjectField(statusObj, field, valObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::codeRates: {
+ case FrontendStatus::Tag::codeRates: {
jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I");
- std::vector<::android::hardware::tv::tuner::V1_1::FrontendInnerFec> v
- = s.codeRates();
+ std::vector<FrontendInnerFec> v = s.get<FrontendStatus::Tag::codeRates>();
jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
env->SetObjectField(statusObj, field, valObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::bandwidth: {
+ case FrontendStatus::Tag::bandwidth: {
jfieldID field = env->GetFieldID(clazz, "mBandwidth", "Ljava/lang/Integer;");
- auto bandwidth = s.bandwidth();
+ const FrontendBandwidth &bandwidth = s.get<FrontendStatus::Tag::bandwidth>();
jint intBandwidth;
bool valid = true;
- switch(bandwidth.getDiscriminator()) {
- case FrontendBandwidth::hidl_discriminator::atsc3: {
- intBandwidth = static_cast<jint>(bandwidth.atsc3());
+ switch (bandwidth.getTag()) {
+ case FrontendBandwidth::Tag::atsc3: {
+ intBandwidth =
+ static_cast<jint>(bandwidth.get<FrontendBandwidth::Tag::atsc3>());
break;
}
- case FrontendBandwidth::hidl_discriminator::dvbt: {
- intBandwidth = static_cast<jint>(bandwidth.dvbt());
+ case FrontendBandwidth::Tag::dvbt: {
+ intBandwidth =
+ static_cast<jint>(bandwidth.get<FrontendBandwidth::Tag::dvbt>());
break;
}
- case FrontendBandwidth::hidl_discriminator::dvbc: {
- intBandwidth = static_cast<jint>(bandwidth.dvbc());
+ case FrontendBandwidth::Tag::dvbc: {
+ intBandwidth =
+ static_cast<jint>(bandwidth.get<FrontendBandwidth::Tag::dvbc>());
break;
}
- case FrontendBandwidth::hidl_discriminator::isdbt: {
- intBandwidth = static_cast<jint>(bandwidth.isdbt());
+ case FrontendBandwidth::Tag::isdbt: {
+ intBandwidth =
+ static_cast<jint>(bandwidth.get<FrontendBandwidth::Tag::isdbt>());
break;
}
- case FrontendBandwidth::hidl_discriminator::dtmb: {
- intBandwidth = static_cast<jint>(bandwidth.dtmb());
+ case FrontendBandwidth::Tag::dtmb: {
+ intBandwidth =
+ static_cast<jint>(bandwidth.get<FrontendBandwidth::Tag::dtmb>());
break;
}
default:
@@ -2229,22 +2221,25 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::interval: {
+ case FrontendStatus::Tag::interval: {
jfieldID field = env->GetFieldID(clazz, "mGuardInterval", "Ljava/lang/Integer;");
- auto interval = s.interval();
+ const FrontendGuardInterval &interval = s.get<FrontendStatus::Tag::interval>();
jint intInterval;
bool valid = true;
- switch(interval.getDiscriminator()) {
- case FrontendGuardInterval::hidl_discriminator::dvbt: {
- intInterval = static_cast<jint>(interval.dvbt());
+ switch (interval.getTag()) {
+ case FrontendGuardInterval::Tag::dvbt: {
+ intInterval =
+ static_cast<jint>(interval.get<FrontendGuardInterval::Tag::dvbt>());
break;
}
- case FrontendGuardInterval::hidl_discriminator::isdbt: {
- intInterval = static_cast<jint>(interval.isdbt());
+ case FrontendGuardInterval::Tag::isdbt: {
+ intInterval = static_cast<jint>(
+ interval.get<FrontendGuardInterval::Tag::isdbt>());
break;
}
- case FrontendGuardInterval::hidl_discriminator::dtmb: {
- intInterval = static_cast<jint>(interval.dtmb());
+ case FrontendGuardInterval::Tag::dtmb: {
+ intInterval =
+ static_cast<jint>(interval.get<FrontendGuardInterval::Tag::dtmb>());
break;
}
default:
@@ -2257,22 +2252,26 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::transmissionMode: {
+ case FrontendStatus::Tag::transmissionMode: {
jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;");
- auto transmissionMode = s.transmissionMode();
+ const FrontendTransmissionMode &transmissionMode =
+ s.get<FrontendStatus::Tag::transmissionMode>();
jint intTransmissionMode;
bool valid = true;
- switch(transmissionMode.getDiscriminator()) {
- case FrontendTransmissionMode::hidl_discriminator::dvbt: {
- intTransmissionMode = static_cast<jint>(transmissionMode.dvbt());
+ switch (transmissionMode.getTag()) {
+ case FrontendTransmissionMode::Tag::dvbt: {
+ intTransmissionMode = static_cast<jint>(
+ transmissionMode.get<FrontendTransmissionMode::Tag::dvbt>());
break;
}
- case FrontendTransmissionMode::hidl_discriminator::isdbt: {
- intTransmissionMode = static_cast<jint>(transmissionMode.isdbt());
+ case FrontendTransmissionMode::Tag::isdbt: {
+ intTransmissionMode = static_cast<jint>(
+ transmissionMode.get<FrontendTransmissionMode::Tag::isdbt>());
break;
}
- case FrontendTransmissionMode::hidl_discriminator::dtmb: {
- intTransmissionMode = static_cast<jint>(transmissionMode.dtmb());
+ case FrontendTransmissionMode::Tag::dtmb: {
+ intTransmissionMode = static_cast<jint>(
+ transmissionMode.get<FrontendTransmissionMode::Tag::dtmb>());
break;
}
default:
@@ -2285,44 +2284,46 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::uec: {
+ case FrontendStatus::Tag::uec: {
jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.uec()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::uec>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::systemId: {
+ case FrontendStatus::Tag::systemId: {
jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(
- intClazz, initInt, static_cast<jint>(s.systemId()));
+ jobject newIntegerObj =
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::systemId>());
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::interleaving: {
+ case FrontendStatus::Tag::interleaving: {
jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I");
-
- std::vector<FrontendInterleaveMode> v = s.interleaving();
+ std::vector<FrontendInterleaveMode> v = s.get<FrontendStatus::Tag::interleaving>();
jintArray valObj = env->NewIntArray(v.size());
bool valid = false;
jint in[1];
for (int i = 0; i < v.size(); i++) {
- auto interleaving = v[i];
- switch(interleaving.getDiscriminator()) {
- case FrontendInterleaveMode::hidl_discriminator::atsc3: {
- in[0] = static_cast<jint>(interleaving.atsc3());
+ const FrontendInterleaveMode &interleaving = v[i];
+ switch (interleaving.getTag()) {
+ case FrontendInterleaveMode::Tag::atsc3: {
+ in[0] = static_cast<jint>(
+ interleaving.get<FrontendInterleaveMode::Tag::atsc3>());
env->SetIntArrayRegion(valObj, i, 1, in);
valid = true;
break;
}
- case FrontendInterleaveMode::hidl_discriminator::dvbc: {
- in[0] = static_cast<jint>(interleaving.dvbc());
+ case FrontendInterleaveMode::Tag::dvbc: {
+ in[0] = static_cast<jint>(
+ interleaving.get<FrontendInterleaveMode::Tag::dvbc>());
env->SetIntArrayRegion(valObj, i, 1, in);
valid = true;
break;
}
- case FrontendInterleaveMode::hidl_discriminator::dtmb: {
- in[0] = static_cast<jint>(interleaving.dtmb());
+ case FrontendInterleaveMode::Tag::dtmb: {
+ in[0] = static_cast<jint>(
+ interleaving.get<FrontendInterleaveMode::Tag::dtmb>());
env->SetIntArrayRegion(valObj, i, 1, in);
valid = true;
break;
@@ -2336,9 +2337,9 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::isdbtSegment: {
+ case FrontendStatus::Tag::isdbtSegment: {
jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I");
- std::vector<uint8_t> v = s.isdbtSegment();
+ std::vector<int32_t> v = s.get<FrontendStatus::Tag::isdbtSegment>();
jintArray valObj = env->NewIntArray(v.size());
env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
@@ -2346,32 +2347,32 @@ jobject JTuner::getFrontendStatus(jintArray types) {
env->SetObjectField(statusObj, field, valObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::tsDataRate: {
+ case FrontendStatus::Tag::tsDataRate: {
jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I");
- std::vector<uint32_t> v = s.tsDataRate();
+ std::vector<int32_t> v = s.get<FrontendStatus::Tag::tsDataRate>();
jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
env->SetObjectField(statusObj, field, valObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::rollOff: {
+ case FrontendStatus::Tag::rollOff: {
jfieldID field = env->GetFieldID(clazz, "mRollOff", "Ljava/lang/Integer;");
- auto rollOff = s.rollOff();
+ const FrontendRollOff &rollOff = s.get<FrontendStatus::Tag::rollOff>();
jint intRollOff;
bool valid = true;
- switch(rollOff.getDiscriminator()) {
- case FrontendRollOff::hidl_discriminator::dvbs: {
- intRollOff = static_cast<jint>(rollOff.dvbs());
+ switch (rollOff.getTag()) {
+ case FrontendRollOff::Tag::dvbs: {
+ intRollOff = static_cast<jint>(rollOff.get<FrontendRollOff::Tag::dvbs>());
break;
}
- case FrontendRollOff::hidl_discriminator::isdbs: {
- intRollOff = static_cast<jint>(rollOff.isdbs());
+ case FrontendRollOff::Tag::isdbs: {
+ intRollOff = static_cast<jint>(rollOff.get<FrontendRollOff::Tag::isdbs>());
break;
}
- case FrontendRollOff::hidl_discriminator::isdbs3: {
- intRollOff = static_cast<jint>(rollOff.isdbs3());
+ case FrontendRollOff::Tag::isdbs3: {
+ intRollOff = static_cast<jint>(rollOff.get<FrontendRollOff::Tag::isdbs3>());
break;
}
default:
@@ -2384,24 +2385,24 @@ jobject JTuner::getFrontendStatus(jintArray types) {
}
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::isMiso: {
+ case FrontendStatus::Tag::isMiso: {
jfieldID field = env->GetFieldID(clazz, "mIsMisoEnabled", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isMiso()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isMiso>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::isLinear: {
+ case FrontendStatus::Tag::isLinear: {
jfieldID field = env->GetFieldID(clazz, "mIsLinear", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isLinear()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isLinear>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
- case FrontendStatusExt1_1::hidl_discriminator::isShortFrames: {
+ case FrontendStatus::Tag::isShortFrames: {
jfieldID field = env->GetFieldID(clazz, "mIsShortFrames", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(
- booleanClazz, initBoolean, static_cast<jboolean>(s.isShortFrames()));
+ jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isShortFrames>());
env->SetObjectField(statusObj, field, newBooleanObj);
break;
}
@@ -2413,33 +2414,28 @@ jobject JTuner::getFrontendStatus(jintArray types) {
return statusObj;
}
-bool JTuner::isV1_1ExtendedStatusType(int type) {
- return (type > static_cast<int>(FrontendStatusType::ATSC3_PLP_INFO)
- && type <= static_cast<int>(FrontendStatusTypeExt1_1::IS_SHORT_FRAMES));
-}
-
jint JTuner::closeFrontend() {
Result r = Result::SUCCESS;
- if (mFeClient != NULL) {
+ if (mFeClient != nullptr) {
r = mFeClient->close();
}
if (r == Result::SUCCESS) {
- mFeClient = NULL;
+ mFeClient = nullptr;
}
- return (jint) r;
+ return (jint)r;
}
jint JTuner::closeDemux() {
Result r = Result::SUCCESS;
- if (mDemuxClient != NULL) {
+ if (mDemuxClient != nullptr) {
r = mDemuxClient->close();
}
if (r == Result::SUCCESS) {
- mDemuxClient = NULL;
+ mDemuxClient = nullptr;
}
- return (jint) r;
+ return (jint)r;
}
} // namespace android
@@ -2450,14 +2446,14 @@ using namespace android;
static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.tunerContext);
- if (tuner != NULL) {
+ if (tuner != nullptr) {
tuner->incStrong(thiz);
}
- if (old != NULL) {
+ if (old != nullptr) {
old->decStrong(thiz);
}
- if (tuner != NULL) {
+ if (tuner != nullptr) {
env->SetLongField(thiz, gFields.tunerContext, (jlong)tuner.get());
}
@@ -2474,26 +2470,24 @@ static sp<DescramblerClient> getDescramblerClient(JNIEnv *env, jobject descrambl
static DemuxPid getDemuxPid(int pidType, int pid) {
DemuxPid demuxPid;
- if ((int)pidType == 1) {
- demuxPid.tPid(static_cast<DemuxTpid>(pid));
- } else if ((int)pidType == 2) {
- demuxPid.mmtpPid(static_cast<DemuxMmtpPid>(pid));
+ if (pidType == 1) {
+ demuxPid.set<DemuxPid::tPid>(pid);
+ } else if (pidType == 2) {
+ demuxPid.set<DemuxPid::mmtpPid>(pid);
}
return demuxPid;
}
-static uint32_t getFrontendSettingsFreq(JNIEnv *env, const jobject& settings) {
+static int64_t getFrontendSettingsFreq(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
jfieldID freqField = env->GetFieldID(clazz, "mFrequency", "I");
- uint32_t freq = static_cast<uint32_t>(env->GetIntField(settings, freqField));
- return freq;
+ return static_cast<uint32_t>(env->GetIntField(settings, freqField));
}
-static uint32_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject& settings) {
+static int64_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings");
jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "I");
- uint32_t endFreq = static_cast<uint32_t>(env->GetIntField(settings, endFreqField));
- return endFreq;
+ return static_cast<uint32_t>(env->GetIntField(settings, endFreqField));
}
static FrontendSpectralInversion getFrontendSettingsSpectralInversion(
@@ -2507,7 +2501,9 @@ static FrontendSpectralInversion getFrontendSettingsSpectralInversion(
static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
FrontendAnalogType analogType =
static_cast<FrontendAnalogType>(
@@ -2515,29 +2511,21 @@ static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& se
FrontendAnalogSifStandard sifStandard =
static_cast<FrontendAnalogSifStandard>(
env->GetIntField(settings, env->GetFieldID(clazz, "mSifStandard", "I")));
- FrontendAnalogSettings frontendAnalogSettings {
+ FrontendAnalogAftFlag aftFlag = static_cast<FrontendAnalogAftFlag>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I")));
+ FrontendAnalogSettings frontendAnalogSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.type = analogType,
.sifStandard = sifStandard,
+ .aftFlag = aftFlag,
+ .inversion = inversion,
};
- frontendSettings.analog(frontendAnalogSettings);
+ frontendSettings.set<FrontendSettings::Tag::analog>(frontendAnalogSettings);
return frontendSettings;
}
-static void getAnalogFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
- FrontendSettingsExt1_1& settingsExt1_1) {
- jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings");
- FrontendAnalogAftFlag aftFlag =
- static_cast<FrontendAnalogAftFlag>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I")));
- FrontendAnalogSettingsExt1_1 analogExt1_1 {
- .aftFlag = aftFlag,
- };
- settingsExt1_1.settingExt.analog(analogExt1_1);
-}
-
-static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
- JNIEnv *env, const jobject& settings) {
+static vector<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(JNIEnv *env, const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings");
jobjectArray plpSettings =
reinterpret_cast<jobjectArray>(
@@ -2549,13 +2537,11 @@ static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
int len = env->GetArrayLength(plpSettings);
jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpSettings");
- hidl_vec<FrontendAtsc3PlpSettings> plps = hidl_vec<FrontendAtsc3PlpSettings>(len);
+ vector<FrontendAtsc3PlpSettings> plps = vector<FrontendAtsc3PlpSettings>(len);
// parse PLP settings
for (int i = 0; i < len; i++) {
jobject plp = env->GetObjectArrayElement(plpSettings, i);
- uint8_t plpId =
- static_cast<uint8_t>(
- env->GetIntField(plp, env->GetFieldID(plpClazz, "mPlpId", "I")));
+ int32_t plpId = env->GetIntField(plp, env->GetFieldID(plpClazz, "mPlpId", "I"));
FrontendAtsc3Modulation modulation =
static_cast<FrontendAtsc3Modulation>(
env->GetIntField(plp, env->GetFieldID(plpClazz, "mModulation", "I")));
@@ -2583,9 +2569,10 @@ static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
static FrontendSettings getAtsc3FrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings");
-
FrontendAtsc3Bandwidth bandwidth =
static_cast<FrontendAtsc3Bandwidth>(
env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
@@ -2593,35 +2580,42 @@ static FrontendSettings getAtsc3FrontendSettings(JNIEnv *env, const jobject& set
static_cast<FrontendAtsc3DemodOutputFormat>(
env->GetIntField(
settings, env->GetFieldID(clazz, "mDemodOutputFormat", "I")));
- hidl_vec<FrontendAtsc3PlpSettings> plps = getAtsc3PlpSettings(env, settings);
- FrontendAtsc3Settings frontendAtsc3Settings {
+ vector<FrontendAtsc3PlpSettings> plps = getAtsc3PlpSettings(env, settings);
+ FrontendAtsc3Settings frontendAtsc3Settings{
.frequency = freq,
+ .endFrequency = endFreq,
.bandwidth = bandwidth,
.demodOutputFormat = demod,
.plpSettings = plps,
+ .inversion = inversion,
};
- frontendSettings.atsc3(frontendAtsc3Settings);
+ frontendSettings.set<FrontendSettings::Tag::atsc3>(frontendAtsc3Settings);
return frontendSettings;
}
static FrontendSettings getAtscFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendSettings");
FrontendAtscModulation modulation =
static_cast<FrontendAtscModulation>(
env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
- FrontendAtscSettings frontendAtscSettings {
+ FrontendAtscSettings frontendAtscSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.modulation = modulation,
+ .inversion = inversion,
};
- frontendSettings.atsc(frontendAtscSettings);
+ frontendSettings.set<FrontendSettings::Tag::atsc>(frontendAtscSettings);
return frontendSettings;
}
static FrontendSettings getDvbcFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings");
FrontendDvbcModulation modulation =
static_cast<FrontendDvbcModulation>(
@@ -2629,49 +2623,35 @@ static FrontendSettings getDvbcFrontendSettings(JNIEnv *env, const jobject& sett
FrontendInnerFec innerFec =
static_cast<FrontendInnerFec>(
env->GetLongField(settings, env->GetFieldID(clazz, "mInnerFec", "J")));
- uint32_t symbolRate =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")));
+ int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"));
FrontendDvbcOuterFec outerFec =
static_cast<FrontendDvbcOuterFec>(
env->GetIntField(settings, env->GetFieldID(clazz, "mOuterFec", "I")));
FrontendDvbcAnnex annex =
static_cast<FrontendDvbcAnnex>(
env->GetIntField(settings, env->GetFieldID(clazz, "mAnnex", "I")));
- FrontendDvbcSpectralInversion spectralInversion =
- static_cast<FrontendDvbcSpectralInversion>(
- env->GetIntField(
- settings, env->GetFieldID(clazz, "mSpectralInversion", "I")));
- FrontendDvbcSettings frontendDvbcSettings {
+ FrontendSpectralInversion spectralInversion = static_cast<FrontendSpectralInversion>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mSpectralInversion", "I")));
+ FrontendCableTimeInterleaveMode interleaveMode = static_cast<FrontendCableTimeInterleaveMode>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
+ FrontendDvbcBandwidth bandwidth = static_cast<FrontendDvbcBandwidth>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
+ FrontendDvbcSettings frontendDvbcSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.modulation = modulation,
.fec = innerFec,
.symbolRate = symbolRate,
.outerFec = outerFec,
.annex = annex,
- .spectralInversion = spectralInversion,
+ .inversion = spectralInversion,
+ .interleaveMode = interleaveMode,
+ .bandwidth = bandwidth,
};
- frontendSettings.dvbc(frontendDvbcSettings);
+ frontendSettings.set<FrontendSettings::Tag::dvbc>(frontendDvbcSettings);
return frontendSettings;
}
-static void getDvbcFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
- FrontendSettingsExt1_1& settingsExt1_1) {
- jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings");
- FrontendCableTimeInterleaveMode interleaveMode =
- static_cast<FrontendCableTimeInterleaveMode>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
- FrontendDvbcBandwidth bandwidth =
- static_cast<FrontendDvbcBandwidth>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
-
- FrontendDvbcSettingsExt1_1 dvbcExt1_1 {
- .interleaveMode = interleaveMode,
- .bandwidth = bandwidth,
- };
- settingsExt1_1.settingExt.dvbc(dvbcExt1_1);
-}
-
static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
jobject jcodeRate =
@@ -2687,18 +2667,11 @@ static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings
env->GetLongField(
jcodeRate, env->GetFieldID(codeRateClazz, "mInnerFec", "J")));
bool isLinear =
- static_cast<bool>(
- env->GetBooleanField(
- jcodeRate, env->GetFieldID(codeRateClazz, "mIsLinear", "Z")));
+ env->GetBooleanField(jcodeRate, env->GetFieldID(codeRateClazz, "mIsLinear", "Z"));
bool isShortFrames =
- static_cast<bool>(
- env->GetBooleanField(
- jcodeRate, env->GetFieldID(codeRateClazz, "mIsShortFrames", "Z")));
- uint32_t bitsPer1000Symbol =
- static_cast<uint32_t>(
- env->GetIntField(
- jcodeRate, env->GetFieldID(
- codeRateClazz, "mBitsPer1000Symbol", "I")));
+ env->GetBooleanField(jcodeRate, env->GetFieldID(codeRateClazz, "mIsShortFrames", "Z"));
+ int32_t bitsPer1000Symbol =
+ env->GetIntField(jcodeRate, env->GetFieldID(codeRateClazz, "mBitsPer1000Symbol", "I"));
FrontendDvbsCodeRate coderate {
.fec = innerFec,
.isLinear = isLinear,
@@ -2710,33 +2683,36 @@ static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings
static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
-
FrontendDvbsModulation modulation =
static_cast<FrontendDvbsModulation>(
env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
- uint32_t symbolRate =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")));
+ int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"));
FrontendDvbsRolloff rolloff =
static_cast<FrontendDvbsRolloff>(
env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I")));
FrontendDvbsPilot pilot =
static_cast<FrontendDvbsPilot>(
env->GetIntField(settings, env->GetFieldID(clazz, "mPilot", "I")));
- uint32_t inputStreamId =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mInputStreamId", "I")));
+ int32_t inputStreamId =
+ env->GetIntField(settings, env->GetFieldID(clazz, "mInputStreamId", "I"));
FrontendDvbsStandard standard =
static_cast<FrontendDvbsStandard>(
env->GetIntField(settings, env->GetFieldID(clazz, "mStandard", "I")));
FrontendDvbsVcmMode vcmMode =
static_cast<FrontendDvbsVcmMode>(
env->GetIntField(settings, env->GetFieldID(clazz, "mVcmMode", "I")));
+ FrontendDvbsScanType scanType = static_cast<FrontendDvbsScanType>(
+ env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
+ bool isDiseqcRxMessage =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z"));
- FrontendDvbsSettings frontendDvbsSettings {
+ FrontendDvbsSettings frontendDvbsSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.modulation = modulation,
.symbolRate = symbolRate,
.rolloff = rolloff,
@@ -2744,37 +2720,26 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett
.inputStreamId = inputStreamId,
.standard = standard,
.vcmMode = vcmMode,
+ .scanType = scanType,
+ .isDiseqcRxMessage = isDiseqcRxMessage,
+ .inversion = inversion,
};
jobject jcodeRate = env->GetObjectField(settings, env->GetFieldID(clazz, "mCodeRate",
"Landroid/media/tv/tuner/frontend/DvbsCodeRate;"));
- if (jcodeRate != NULL) {
+ if (jcodeRate != nullptr) {
frontendDvbsSettings.coderate = getDvbsCodeRate(env, settings);
}
- frontendSettings.dvbs(frontendDvbsSettings);
+ frontendSettings.set<FrontendSettings::Tag::dvbs>(frontendDvbsSettings);
return frontendSettings;
}
-static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
- FrontendSettingsExt1_1& settingsExt1_1) {
- jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings");
- FrontendDvbsScanType scanType =
- static_cast<FrontendDvbsScanType>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I")));
- bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField(
- settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "Z")));
-
- FrontendDvbsSettingsExt1_1 dvbsExt1_1 {
- .scanType = scanType,
- .isDiseqcRxMessage = isDiseqcRxMessage,
- };
- settingsExt1_1.settingExt.dvbs(dvbsExt1_1);
-}
-
static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings");
FrontendDvbtTransmissionMode transmissionMode =
static_cast<FrontendDvbtTransmissionMode>(
@@ -2799,27 +2764,20 @@ static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& sett
static_cast<FrontendDvbtGuardInterval>(
env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
bool isHighPriority =
- static_cast<bool>(
- env->GetBooleanField(
- settings, env->GetFieldID(clazz, "mIsHighPriority", "Z")));
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsHighPriority", "Z"));
FrontendDvbtStandard standard =
static_cast<FrontendDvbtStandard>(
env->GetIntField(settings, env->GetFieldID(clazz, "mStandard", "I")));
- bool isMiso =
- static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsMiso", "Z")));
+ bool isMiso = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsMiso", "Z"));
FrontendDvbtPlpMode plpMode =
static_cast<FrontendDvbtPlpMode>(
env->GetIntField(settings, env->GetFieldID(clazz, "mPlpMode", "I")));
- uint8_t plpId =
- static_cast<uint8_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mPlpId", "I")));
- uint8_t plpGroupId =
- static_cast<uint8_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mPlpGroupId", "I")));
-
- FrontendDvbtSettings frontendDvbtSettings {
+ int32_t plpId = env->GetIntField(settings, env->GetFieldID(clazz, "mPlpId", "I"));
+ int32_t plpGroupId = env->GetIntField(settings, env->GetFieldID(clazz, "mPlpGroupId", "I"));
+
+ FrontendDvbtSettings frontendDvbtSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.transmissionMode = transmissionMode,
.bandwidth = bandwidth,
.constellation = constellation,
@@ -2833,37 +2791,18 @@ static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& sett
.plpMode = plpMode,
.plpId = plpId,
.plpGroupId = plpGroupId,
+ .inversion = inversion,
};
- frontendSettings.dvbt(frontendDvbtSettings);
+ frontendSettings.set<FrontendSettings::Tag::dvbt>(frontendDvbtSettings);
return frontendSettings;
}
-static void getDvbtFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings,
- FrontendSettingsExt1_1& settingsExt1_1) {
- jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings");
-
- FrontendDvbtSettingsExt1_1 dvbtExt1_1;
- int transmissionMode =
- env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I"));
- dvbtExt1_1.transmissionMode = static_cast<
- ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode>(
- transmissionMode);
-
- int constellation =
- env->GetIntField(settings, env->GetFieldID(clazz, "mConstellation", "I"));
- dvbtExt1_1.constellation = static_cast<
- ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation>(constellation);
-
- settingsExt1_1.settingExt.dvbt(dvbtExt1_1);
-}
-
static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendSettings");
- uint16_t streamId =
- static_cast<uint16_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")));
+ int32_t streamId = env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"));
FrontendIsdbsStreamIdType streamIdType =
static_cast<FrontendIsdbsStreamIdType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mStreamIdType", "I")));
@@ -2873,15 +2812,14 @@ static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& set
FrontendIsdbsCoderate coderate =
static_cast<FrontendIsdbsCoderate>(
env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I")));
- uint32_t symbolRate =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")));
+ int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"));
FrontendIsdbsRolloff rolloff =
static_cast<FrontendIsdbsRolloff>(
env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I")));
- FrontendIsdbsSettings frontendIsdbsSettings {
+ FrontendIsdbsSettings frontendIsdbsSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.streamId = streamId,
.streamIdType = streamIdType,
.modulation = modulation,
@@ -2889,17 +2827,16 @@ static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& set
.symbolRate = symbolRate,
.rolloff = rolloff,
};
- frontendSettings.isdbs(frontendIsdbsSettings);
+ frontendSettings.set<FrontendSettings::Tag::isdbs>(frontendIsdbsSettings);
return frontendSettings;
}
static FrontendSettings getIsdbs3FrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendSettings");
- uint16_t streamId =
- static_cast<uint16_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")));
+ int32_t streamId = env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"));
FrontendIsdbsStreamIdType streamIdType =
static_cast<FrontendIsdbsStreamIdType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mStreamIdType", "I")));
@@ -2909,15 +2846,14 @@ static FrontendSettings getIsdbs3FrontendSettings(JNIEnv *env, const jobject& se
FrontendIsdbs3Coderate coderate =
static_cast<FrontendIsdbs3Coderate>(
env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I")));
- uint32_t symbolRate =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")));
+ int32_t symbolRate = env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I"));
FrontendIsdbs3Rolloff rolloff =
static_cast<FrontendIsdbs3Rolloff>(
env->GetIntField(settings, env->GetFieldID(clazz, "mRolloff", "I")));
- FrontendIsdbs3Settings frontendIsdbs3Settings {
+ FrontendIsdbs3Settings frontendIsdbs3Settings{
.frequency = freq,
+ .endFrequency = endFreq,
.streamId = streamId,
.streamIdType = streamIdType,
.modulation = modulation,
@@ -2925,13 +2861,15 @@ static FrontendSettings getIsdbs3FrontendSettings(JNIEnv *env, const jobject& se
.symbolRate = symbolRate,
.rolloff = rolloff,
};
- frontendSettings.isdbs3(frontendIsdbs3Settings);
+ frontendSettings.set<FrontendSettings::Tag::isdbs3>(frontendIsdbs3Settings);
return frontendSettings;
}
static FrontendSettings getIsdbtFrontendSettings(JNIEnv *env, const jobject& settings) {
FrontendSettings frontendSettings;
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendSettings");
FrontendIsdbtModulation modulation =
static_cast<FrontendIsdbtModulation>(
@@ -2948,26 +2886,29 @@ static FrontendSettings getIsdbtFrontendSettings(JNIEnv *env, const jobject& set
FrontendIsdbtGuardInterval guardInterval =
static_cast<FrontendIsdbtGuardInterval>(
env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
- uint32_t serviceAreaId =
- static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mServiceAreaId", "I")));
+ int32_t serviceAreaId =
+ env->GetIntField(settings, env->GetFieldID(clazz, "mServiceAreaId", "I"));
- FrontendIsdbtSettings frontendIsdbtSettings {
+ FrontendIsdbtSettings frontendIsdbtSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.modulation = modulation,
.bandwidth = bandwidth,
.mode = mode,
.coderate = coderate,
.guardInterval = guardInterval,
.serviceAreaId = serviceAreaId,
+ .inversion = inversion,
};
- frontendSettings.isdbt(frontendIsdbtSettings);
+ frontendSettings.set<FrontendSettings::Tag::isdbt>(frontendIsdbtSettings);
return frontendSettings;
}
-static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings,
- FrontendSettingsExt1_1& settingsExt1_1) {
- uint32_t freq = getFrontendSettingsFreq(env, settings);
+static FrontendSettings getDtmbFrontendSettings(JNIEnv *env, const jobject &settings) {
+ FrontendSettings frontendSettings;
+ int64_t freq = getFrontendSettingsFreq(env, settings);
+ int64_t endFreq = getFrontendSettingsEndFreq(env, settings);
+ FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendSettings");
FrontendDtmbModulation modulation =
static_cast<FrontendDtmbModulation>(
@@ -2988,25 +2929,23 @@ static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings,
static_cast<FrontendDtmbTimeInterleaveMode>(
env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I")));
- FrontendDtmbSettings frontendDtmbSettings {
+ FrontendDtmbSettings frontendDtmbSettings{
.frequency = freq,
+ .endFrequency = endFreq,
.modulation = modulation,
.bandwidth = bandwidth,
.transmissionMode = transmissionMode,
.codeRate = codeRate,
.guardInterval = guardInterval,
.interleaveMode = interleaveMode,
+ .inversion = inversion,
};
- settingsExt1_1.settingExt.dtmb(frontendDtmbSettings);
+ frontendSettings.set<FrontendSettings::Tag::dtmb>(frontendDtmbSettings);
+ return frontendSettings;
}
static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) {
- ALOGD("getFrontendSettings %d", type);
-
- if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
- return FrontendSettings();
- }
-
+ ALOGV("getFrontendSettings %d", type);
FrontendType feType = static_cast<FrontendType>(type);
switch(feType) {
case FrontendType::ANALOG:
@@ -3027,6 +2966,8 @@ static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject setti
return getIsdbs3FrontendSettings(env, settings);
case FrontendType::ISDBT:
return getIsdbtFrontendSettings(env, settings);
+ case FrontendType::DTMB:
+ return getDtmbFrontendSettings(env, settings);
default:
// should never happen because a type is associated with a subclass of
// FrontendSettings and not set by users
@@ -3036,64 +2977,6 @@ static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject setti
}
}
-static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(
- JNIEnv *env, int type, jobject settings, int tunerVersion) {
- ALOGD("getFrontendSettingsExt1_1 %d", type);
-
- FrontendSettingsExt1_1 settingsExt1_1 {
- .endFrequency = static_cast<uint32_t>(Constant::INVALID_FRONTEND_SETTING_FREQUENCY),
- .inversion = FrontendSpectralInversion::UNDEFINED,
- };
- settingsExt1_1.settingExt.noinit();
-
- if (tunerVersion < TUNER_VERSION_1_1) {
- return settingsExt1_1;
- }
-
- if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) {
- getDtmbFrontendSettings(env, settings, settingsExt1_1);
- } else {
- FrontendType feType = static_cast<FrontendType>(type);
- switch(feType) {
- case FrontendType::DVBS:
- getDvbsFrontendSettingsExt1_1(env, settings, settingsExt1_1);
- break;
- case FrontendType::DVBT:
- getDvbtFrontendSettingsExt1_1(env, settings, settingsExt1_1);
- break;
- case FrontendType::ANALOG:
- getAnalogFrontendSettingsExt1_1(env, settings, settingsExt1_1);
- break;
- case FrontendType::ATSC3:
- break;
- case FrontendType::ATSC:
- break;
- case FrontendType::DVBC:
- getDvbcFrontendSettingsExt1_1(env, settings, settingsExt1_1);
- break;
- case FrontendType::ISDBS:
- break;
- case FrontendType::ISDBS3:
- break;
- case FrontendType::ISDBT:
- break;
- default:
- // should never happen because a type is associated with a subclass of
- // FrontendSettings and not set by users
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "Unsupported frontend type %d", type);
- return FrontendSettingsExt1_1();
- }
- }
-
- uint32_t endFreq = getFrontendSettingsEndFreq(env, settings);
- FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings);
- settingsExt1_1.endFrequency = endFreq;
- settingsExt1_1.inversion = inversion;
-
- return settingsExt1_1;
-}
-
static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) {
return (FilterClient *)env->GetLongField(filter, gFields.filterContext);
}
@@ -3105,30 +2988,24 @@ static sp<LnbClient> getLnbClient(JNIEnv *env, jobject lnb) {
static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) {
DvrSettings dvrSettings;
jclass clazz = env->FindClass("android/media/tv/tuner/dvr/DvrSettings");
- uint32_t statusMask =
- static_cast<uint32_t>(env->GetIntField(
- settings, env->GetFieldID(clazz, "mStatusMask", "I")));
- uint32_t lowThreshold =
- static_cast<uint32_t>(env->GetLongField(
- settings, env->GetFieldID(clazz, "mLowThreshold", "J")));
- uint32_t highThreshold =
- static_cast<uint32_t>(env->GetLongField(
- settings, env->GetFieldID(clazz, "mHighThreshold", "J")));
- uint8_t packetSize =
- static_cast<uint8_t>(env->GetLongField(
- settings, env->GetFieldID(clazz, "mPacketSize", "J")));
+ int32_t statusMask = env->GetIntField(settings, env->GetFieldID(clazz, "mStatusMask", "I"));
+ int64_t lowThreshold =
+ env->GetLongField(settings, env->GetFieldID(clazz, "mLowThreshold", "J"));
+ int64_t highThreshold =
+ env->GetLongField(settings, env->GetFieldID(clazz, "mHighThreshold", "J"));
+ int64_t packetSize = env->GetLongField(settings, env->GetFieldID(clazz, "mPacketSize", "J"));
DataFormat dataFormat =
static_cast<DataFormat>(env->GetIntField(
settings, env->GetFieldID(clazz, "mDataFormat", "I")));
if (isRecorder) {
- RecordSettings recordSettings {
- .statusMask = static_cast<unsigned char>(statusMask),
+ RecordSettings recordSettings{
+ .statusMask = statusMask,
.lowThreshold = lowThreshold,
.highThreshold = highThreshold,
.dataFormat = dataFormat,
.packetSize = packetSize,
};
- dvrSettings.record(recordSettings);
+ dvrSettings.set<DvrSettings::Tag::record>(recordSettings);
} else {
PlaybackSettings PlaybackSettings {
.statusMask = statusMask,
@@ -3137,7 +3014,7 @@ static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder
.dataFormat = dataFormat,
.packetSize = packetSize,
};
- dvrSettings.playback(PlaybackSettings);
+ dvrSettings.set<DvrSettings::Tag::playback>(PlaybackSettings);
}
return dvrSettings;
}
@@ -3152,10 +3029,10 @@ static sp<DvrClient> getDvrClient(JNIEnv *env, jobject dvr) {
static void android_media_tv_Tuner_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
- CHECK(clazz != NULL);
+ CHECK(clazz != nullptr);
gFields.tunerContext = env->GetFieldID(clazz, "mNativeContext", "J");
- CHECK(gFields.tunerContext != NULL);
+ CHECK(gFields.tunerContext != nullptr);
gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V");
@@ -3229,12 +3106,16 @@ static jobject android_media_tv_Tuner_open_frontend_by_handle(
return tuner->openFrontendByHandle(handle);
}
+static int android_media_tv_Tuner_share_frontend(
+ JNIEnv *env, jobject thiz, jint id) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->shareFrontend(id);
+}
+
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
- FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(
- env, type, settings, tuner->getTunerVersion());
- return tuner->tune(setting, settingExt);
+ return tuner->tune(setting);
}
static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) {
@@ -3246,9 +3127,7 @@ static int android_media_tv_Tuner_scan(
JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, settingsType, settings);
- FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(
- env, settingsType, settings, tuner->getTunerVersion());
- return tuner->scan(setting, static_cast<FrontendScanType>(scanType), settingExt);
+ return tuner->scan(setting, static_cast<FrontendScanType>(scanType));
}
static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) {
@@ -3259,7 +3138,7 @@ static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) {
static int android_media_tv_Tuner_set_lnb(JNIEnv *env, jobject thiz, jobject lnb) {
sp<JTuner> tuner = getTuner(env, thiz);
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
- if (lnbClient == NULL) {
+ if (lnbClient == nullptr) {
ALOGE("lnb is not initialized");
return (int)Result::INVALID_STATE;
}
@@ -3280,9 +3159,9 @@ static jobject android_media_tv_Tuner_get_frontend_status(
static jobject android_media_tv_Tuner_get_av_sync_hw_id(
JNIEnv *env, jobject thiz, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to get sync ID. Filter client not found");
- return NULL;
+ return nullptr;
}
sp<JTuner> tuner = getTuner(env, thiz);
return tuner->getAvSyncHwId(filterClient);
@@ -3337,22 +3216,30 @@ static jobject android_media_tv_Tuner_open_filter(
.mainType = mainType,
};
- switch(mainType) {
+ switch (mainType) {
case DemuxFilterMainType::TS:
- filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType));
+ filterType.subType.set<DemuxFilterSubType::Tag::tsFilterType>(
+ static_cast<DemuxTsFilterType>(subType));
break;
case DemuxFilterMainType::MMTP:
- filterType.subType.mmtpFilterType(static_cast<DemuxMmtpFilterType>(subType));
+ filterType.subType.set<DemuxFilterSubType::Tag::mmtpFilterType>(
+ static_cast<DemuxMmtpFilterType>(subType));
break;
case DemuxFilterMainType::IP:
- filterType.subType.ipFilterType(static_cast<DemuxIpFilterType>(subType));
+ filterType.subType.set<DemuxFilterSubType::Tag::ipFilterType>(
+ static_cast<DemuxIpFilterType>(subType));
break;
case DemuxFilterMainType::TLV:
- filterType.subType.tlvFilterType(static_cast<DemuxTlvFilterType>(subType));
+ filterType.subType.set<DemuxFilterSubType::Tag::tlvFilterType>(
+ static_cast<DemuxTlvFilterType>(subType));
break;
case DemuxFilterMainType::ALP:
- filterType.subType.alpFilterType(static_cast<DemuxAlpFilterType>(subType));
+ filterType.subType.set<DemuxFilterSubType::Tag::alpFilterType>(
+ static_cast<DemuxAlpFilterType>(subType));
break;
+ default:
+ ALOGD("Demux Filter Main Type is undefined.");
+ return nullptr;
}
return tuner->openFilter(filterType, bufferSize);
@@ -3369,20 +3256,19 @@ static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& s
env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B")));
jsize size = env->GetArrayLength(jfilterBytes);
std::vector<uint8_t> filterBytes(size);
- env->GetByteArrayRegion(
- jfilterBytes, 0, size, reinterpret_cast<jbyte*>(&filterBytes[0]));
+ env->GetByteArrayRegion(jfilterBytes, 0, size, reinterpret_cast<jbyte *>(&filterBytes[0]));
jbyteArray jmask = static_cast<jbyteArray>(
env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B")));
size = env->GetArrayLength(jmask);
std::vector<uint8_t> mask(size);
- env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte*>(&mask[0]));
+ env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte *>(&mask[0]));
jbyteArray jmode = static_cast<jbyteArray>(
env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B")));
size = env->GetArrayLength(jmode);
std::vector<uint8_t> mode(size);
- env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte*>(&mode[0]));
+ env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte *>(&mode[0]));
DemuxFilterSectionBits filterSectionBits {
.filter = filterBytes,
@@ -3392,28 +3278,23 @@ static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& s
return filterSectionBits;
}
-static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo(
- JNIEnv *env, const jobject& settings) {
+static DemuxFilterSectionSettingsConditionTableInfo getFilterTableInfo(JNIEnv *env,
+ const jobject &settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo");
- uint16_t tableId = static_cast<uint16_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I")));
- uint16_t version = static_cast<uint16_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I")));
- DemuxFilterSectionSettings::Condition::TableInfo tableInfo {
- .tableId = tableId,
- .version = version,
+ int32_t tableId = env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I"));
+ int32_t version = env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I"));
+ DemuxFilterSectionSettingsConditionTableInfo tableInfo{
+ .tableId = tableId,
+ .version = version,
};
return tableInfo;
}
static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings");
- bool isCheckCrc = static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z")));
- bool isRepeat = static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z")));
- bool isRaw = static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+ bool isCheckCrc = env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"));
+ bool isRepeat = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"));
+ bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"));
DemuxFilterSectionSettings filterSectionSettings {
.isCheckCrc = isCheckCrc,
@@ -3423,19 +3304,21 @@ static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jo
if (env->IsInstanceOf(
settings,
env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) {
- filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings));
+ filterSectionSettings.condition.set<DemuxFilterSectionSettingsCondition::Tag::sectionBits>(
+ getFilterSectionBits(env, settings));
} else if (env->IsInstanceOf(
settings,
env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) {
- filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings));
+ filterSectionSettings.condition.set<DemuxFilterSectionSettingsCondition::Tag::tableInfo>(
+ getFilterTableInfo(env, settings));
}
return filterSectionSettings;
}
static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings");
- bool isPassthrough = static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z")));
+ bool isPassthrough =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z"));
DemuxFilterAvSettings filterAvSettings {
.isPassthrough = isPassthrough,
};
@@ -3455,13 +3338,13 @@ static bool getAvStreamType(JNIEnv *env, jobject filterConfigObj, AvStreamType&
AudioStreamType audioStreamType = static_cast<AudioStreamType>(
env->GetIntField(settingsObj, env->GetFieldID(clazz, "mAudioStreamType", "I")));
if (audioStreamType != AudioStreamType::UNDEFINED) {
- type.audio(audioStreamType);
+ type.set<AvStreamType::Tag::audio>(audioStreamType);
return true;
}
VideoStreamType videoStreamType = static_cast<VideoStreamType>(
env->GetIntField(settingsObj, env->GetFieldID(clazz, "mVideoStreamType", "I")));
if (videoStreamType != VideoStreamType::UNDEFINED) {
- type.video(videoStreamType);
+ type.set<AvStreamType::Tag::video>(videoStreamType);
return true;
}
return false;
@@ -3469,10 +3352,8 @@ static bool getAvStreamType(JNIEnv *env, jobject filterConfigObj, AvStreamType&
static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings");
- uint16_t streamId = static_cast<uint16_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I")));
- bool isRaw = static_cast<bool>(
- env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")));
+ int32_t streamId = env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"));
+ bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"));
DemuxFilterPesDataSettings filterPesDataSettings {
.streamId = streamId,
.isRaw = isRaw,
@@ -3482,8 +3363,7 @@ static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jo
static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings");
- hidl_bitfield<DemuxTsIndex> tsIndexMask = static_cast<hidl_bitfield<DemuxTsIndex>>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I")));
+ int32_t tsIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I"));
DemuxRecordScIndexType scIndexType = static_cast<DemuxRecordScIndexType>(
env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I")));
jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I"));
@@ -3493,18 +3373,16 @@ static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobj
.scIndexType = scIndexType,
};
if (scIndexType == DemuxRecordScIndexType::SC) {
- filterRecordSettings.scIndexMask.sc(static_cast<hidl_bitfield<DemuxScIndex>>(scIndexMask));
+ filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scIndex>(scIndexMask);
} else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) {
- filterRecordSettings.scIndexMask.scHevc(
- static_cast<hidl_bitfield<DemuxScHevcIndex>>(scIndexMask));
+ filterRecordSettings.scIndexMask.set<DemuxFilterScIndexMask::Tag::scHevc>(scIndexMask);
}
return filterRecordSettings;
}
static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
- uint32_t downloadId = static_cast<uint32_t>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I")));
+ int32_t downloadId = env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"));
DemuxFilterDownloadSettings filterDownloadSettings {
.downloadId = downloadId,
@@ -3532,23 +3410,23 @@ static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
}
if (srcSize == IP_V4_LENGTH) {
- uint8_t srcAddr[IP_V4_LENGTH];
- uint8_t dstAddr[IP_V4_LENGTH];
- env->GetByteArrayRegion(
- jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
- env->GetByteArrayRegion(
- jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
- res.srcIpAddress.v4(srcAddr);
- res.dstIpAddress.v4(dstAddr);
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V4_LENGTH);
+ dstAddr.resize(IP_V4_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v4>(dstAddr);
} else if (srcSize == IP_V6_LENGTH) {
- uint8_t srcAddr[IP_V6_LENGTH];
- uint8_t dstAddr[IP_V6_LENGTH];
- env->GetByteArrayRegion(
- jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte*>(srcAddr));
- env->GetByteArrayRegion(
- jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte*>(dstAddr));
- res.srcIpAddress.v6(srcAddr);
- res.dstIpAddress.v6(dstAddr);
+ vector<uint8_t> srcAddr;
+ vector<uint8_t> dstAddr;
+ srcAddr.resize(IP_V6_LENGTH);
+ dstAddr.resize(IP_V6_LENGTH);
+ env->GetByteArrayRegion(jsrcIpAddress, 0, srcSize, reinterpret_cast<jbyte *>(&srcAddr[0]));
+ env->GetByteArrayRegion(jdstIpAddress, 0, dstSize, reinterpret_cast<jbyte *>(&dstAddr[0]));
+ res.srcIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(srcAddr);
+ res.dstIpAddress.set<DemuxIpAddressIpAddress::Tag::v6>(dstAddr);
} else {
// should never happen. Validated on Java size.
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
@@ -3556,13 +3434,8 @@ static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config) {
return res;
}
- uint16_t srcPort = static_cast<uint16_t>(
- env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I")));
- uint16_t dstPort = static_cast<uint16_t>(
- env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I")));
-
- res.srcPort = srcPort;
- res.dstPort = dstPort;
+ res.srcPort = env->GetIntField(config, env->GetFieldID(clazz, "mSrcPort", "I"));
+ res.dstPort = env->GetIntField(config, env->GetFieldID(clazz, "mDstPort", "I"));
return res;
}
@@ -3581,74 +3454,84 @@ static DemuxFilterSettings getFilterConfiguration(
switch (mainType) {
case DemuxFilterMainType::TS: {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration");
- uint16_t tpid = static_cast<uint16_t>(
- env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I")));
+ int32_t tpid = env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mTpid", "I"));
DemuxTsFilterSettings tsFilterSettings {
.tpid = tpid,
};
- if (settingsObj != NULL) {
+ if (settingsObj != nullptr) {
DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype);
switch (tsType) {
case DemuxTsFilterType::SECTION:
- tsFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
+ tsFilterSettings.filterSettings
+ .set<DemuxTsFilterSettingsFilterSettings::Tag::section>(
+ getFilterSectionSettings(env, settingsObj));
break;
case DemuxTsFilterType::AUDIO:
case DemuxTsFilterType::VIDEO:
- tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ tsFilterSettings.filterSettings
+ .set<DemuxTsFilterSettingsFilterSettings::Tag::av>(
+ getFilterAvSettings(env, settingsObj));
break;
case DemuxTsFilterType::PES:
- tsFilterSettings.filterSettings.pesData(
- getFilterPesDataSettings(env, settingsObj));
+ tsFilterSettings.filterSettings
+ .set<DemuxTsFilterSettingsFilterSettings::Tag::pesData>(
+ getFilterPesDataSettings(env, settingsObj));
break;
case DemuxTsFilterType::RECORD:
- tsFilterSettings.filterSettings.record(
- getFilterRecordSettings(env, settingsObj));
+ tsFilterSettings.filterSettings
+ .set<DemuxTsFilterSettingsFilterSettings::Tag::record>(
+ getFilterRecordSettings(env, settingsObj));
break;
default:
break;
}
}
- filterSettings.ts(tsFilterSettings);
+ filterSettings.set<DemuxFilterSettings::Tag::ts>(tsFilterSettings);
break;
}
case DemuxFilterMainType::MMTP: {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/MmtpFilterConfiguration");
- uint16_t mmtpPid = static_cast<uint16_t>(
- env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I")));
+ int32_t mmtpPid =
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mMmtpPid", "I"));
DemuxMmtpFilterSettings mmtpFilterSettings {
.mmtpPid = mmtpPid,
};
- if (settingsObj != NULL) {
+ if (settingsObj != nullptr) {
DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype);
switch (mmtpType) {
case DemuxMmtpFilterType::SECTION:
- mmtpFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
+ mmtpFilterSettings.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::section>(
+ getFilterSectionSettings(env, settingsObj));
break;
case DemuxMmtpFilterType::AUDIO:
case DemuxMmtpFilterType::VIDEO:
- mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj));
+ mmtpFilterSettings.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::av>(
+ getFilterAvSettings(env, settingsObj));
break;
case DemuxMmtpFilterType::PES:
- mmtpFilterSettings.filterSettings.pesData(
- getFilterPesDataSettings(env, settingsObj));
+ mmtpFilterSettings.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::pesData>(
+ getFilterPesDataSettings(env, settingsObj));
break;
case DemuxMmtpFilterType::RECORD:
- mmtpFilterSettings.filterSettings.record(
- getFilterRecordSettings(env, settingsObj));
+ mmtpFilterSettings.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::record>(
+ getFilterRecordSettings(env, settingsObj));
break;
case DemuxMmtpFilterType::DOWNLOAD:
- mmtpFilterSettings.filterSettings.download(
- getFilterDownloadSettings(env, settingsObj));
+ mmtpFilterSettings.filterSettings
+ .set<DemuxMmtpFilterSettingsFilterSettings::Tag::download>(
+ getFilterDownloadSettings(env, settingsObj));
break;
default:
break;
}
}
- filterSettings.mmtp(mmtpFilterSettings);
+ filterSettings.set<DemuxFilterSettings::Tag::mmtp>(mmtpFilterSettings);
break;
}
case DemuxFilterMainType::IP: {
@@ -3658,28 +3541,29 @@ static DemuxFilterSettings getFilterConfiguration(
};
DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype);
- if (ipType == DemuxIpFilterType::SECTION && settingsObj != NULL) {
- ipFilterSettings.filterSettings.section(
+ if (ipType == DemuxIpFilterType::SECTION && settingsObj != nullptr) {
+ ipFilterSettings.filterSettings
+ .set<DemuxIpFilterSettingsFilterSettings::Tag::section>(
getFilterSectionSettings(env, settingsObj));
} else if (ipType == DemuxIpFilterType::IP) {
jclass clazz = env->FindClass(
"android/media/tv/tuner/filter/IpFilterConfiguration");
- bool bPassthrough = static_cast<bool>(
- env->GetBooleanField(
- filterConfigObj, env->GetFieldID(
- clazz, "mPassthrough", "Z")));
- ipFilterSettings.filterSettings.bPassthrough(bPassthrough);
+ bool bPassthrough =
+ env->GetBooleanField(filterConfigObj,
+ env->GetFieldID(clazz, "mPassthrough", "Z"));
+ ipFilterSettings.filterSettings
+ .set<DemuxIpFilterSettingsFilterSettings::Tag::bPassthrough>(bPassthrough);
}
- filterSettings.ip(ipFilterSettings);
+ filterSettings.set<DemuxFilterSettings::Tag::ip>(ipFilterSettings);
break;
}
case DemuxFilterMainType::TLV: {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/TlvFilterConfiguration");
- uint8_t packetType = static_cast<uint8_t>(
- env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
- bool isCompressedIpPacket = static_cast<bool>(
- env->GetBooleanField(
- filterConfigObj, env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z")));
+ int32_t packetType =
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"));
+ bool isCompressedIpPacket =
+ env->GetBooleanField(filterConfigObj,
+ env->GetFieldID(clazz, "mIsCompressedIpPacket", "Z"));
DemuxTlvFilterSettings tlvFilterSettings {
.packetType = packetType,
@@ -3687,23 +3571,24 @@ static DemuxFilterSettings getFilterConfiguration(
};
DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype);
- if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != NULL) {
- tlvFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
+ if (tlvType == DemuxTlvFilterType::SECTION && settingsObj != nullptr) {
+ tlvFilterSettings.filterSettings
+ .set<DemuxTlvFilterSettingsFilterSettings::Tag::section>(
+ getFilterSectionSettings(env, settingsObj));
} else if (tlvType == DemuxTlvFilterType::TLV) {
- bool bPassthrough = static_cast<bool>(
- env->GetBooleanField(
- filterConfigObj, env->GetFieldID(
- clazz, "mPassthrough", "Z")));
- tlvFilterSettings.filterSettings.bPassthrough(bPassthrough);
+ bool bPassthrough =
+ env->GetBooleanField(filterConfigObj,
+ env->GetFieldID(clazz, "mPassthrough", "Z"));
+ tlvFilterSettings.filterSettings
+ .set<DemuxTlvFilterSettingsFilterSettings::Tag::bPassthrough>(bPassthrough);
}
- filterSettings.tlv(tlvFilterSettings);
+ filterSettings.set<DemuxFilterSettings::Tag::tlv>(tlvFilterSettings);
break;
}
case DemuxFilterMainType::ALP: {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/AlpFilterConfiguration");
- uint8_t packetType = static_cast<uint8_t>(
- env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I")));
+ int32_t packetType =
+ env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mPacketType", "I"));
DemuxAlpLengthType lengthType = static_cast<DemuxAlpLengthType>(
env->GetIntField(filterConfigObj, env->GetFieldID(clazz, "mLengthType", "I")));
DemuxAlpFilterSettings alpFilterSettings {
@@ -3711,18 +3596,19 @@ static DemuxFilterSettings getFilterConfiguration(
.lengthType = lengthType,
};
- if (settingsObj != NULL) {
+ if (settingsObj != nullptr) {
DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype);
switch (alpType) {
case DemuxAlpFilterType::SECTION:
- alpFilterSettings.filterSettings.section(
- getFilterSectionSettings(env, settingsObj));
+ alpFilterSettings.filterSettings
+ .set<DemuxAlpFilterSettingsFilterSettings::Tag::section>(
+ getFilterSectionSettings(env, settingsObj));
break;
default:
break;
}
}
- filterSettings.alp(alpFilterSettings);
+ filterSettings.set<DemuxFilterSettings::Tag::alp>(alpFilterSettings);
break;
}
default: {
@@ -3743,34 +3629,33 @@ static Result configureIpFilterContextId(
}
static bool isAvFilterSettings(DemuxFilterSettings filterSettings) {
- return (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts
- && filterSettings.ts().filterSettings.getDiscriminator()
- == DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av)
- ||
- (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::mmtp
- && filterSettings.mmtp().filterSettings.getDiscriminator()
- == DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av);
+ return (filterSettings.getTag() == DemuxFilterSettings::Tag::ts &&
+ filterSettings.get<DemuxFilterSettings::Tag::ts>().filterSettings.getTag() ==
+ DemuxTsFilterSettingsFilterSettings::Tag::av) ||
+ (filterSettings.getTag() == DemuxFilterSettings::Tag::mmtp &&
+ filterSettings.get<DemuxFilterSettings::Tag::mmtp>().filterSettings.getTag() ==
+ DemuxMmtpFilterSettingsFilterSettings::Tag::av);
}
static jint android_media_tv_Tuner_configure_filter(
JNIEnv *env, jobject filter, int type, int subtype, jobject settings) {
- ALOGD("configure filter type=%d, subtype=%d", type, subtype);
+ ALOGV("configure filter type=%d, subtype=%d", type, subtype);
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to configure filter: filter not found");
- return (jint) Result::NOT_INITIALIZED;
+ return (jint)Result::NOT_INITIALIZED;
}
DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings);
Result res = filterClient->configure(filterSettings);
if (res != Result::SUCCESS) {
- return (jint) res;
+ return (jint)res;
}
if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) {
res = configureIpFilterContextId(env, filterClient, settings);
if (res != Result::SUCCESS) {
- return (jint) res;
+ return (jint)res;
}
}
@@ -3778,99 +3663,98 @@ static jint android_media_tv_Tuner_configure_filter(
if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) {
res = filterClient->configureAvStreamType(streamType);
}
- return (jint) res;
+ return (jint)res;
}
static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to get filter ID: filter client not found");
return (int) Result::NOT_INITIALIZED;
}
- uint32_t id;
+ int32_t id;
Result res = filterClient->getId(id);
if (res != Result::SUCCESS) {
- return (jint) Constant::INVALID_FILTER_ID;
+ return (jint)Constant::INVALID_FILTER_ID;
}
- return (jint) id;
+ return (jint)id;
}
static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to get filter ID 64 bit: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
- uint64_t id;
+ int64_t id;
Result res = filterClient->getId64Bit(id);
- return (res == Result::SUCCESS) ?
- static_cast<jlong>(id) : static_cast<jlong>(
- ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT);
+ return (res == Result::SUCCESS) ? id
+ : static_cast<jlong>(Constant64Bit::INVALID_FILTER_ID_64BIT);
}
static jint android_media_tv_Tuner_configure_monitor_event(
JNIEnv* env, jobject filter, int monitorEventType) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to configure scrambling event: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
Result res = filterClient->configureMonitorEvent(monitorEventType);
- return (jint) res;
+ return (jint)res;
}
static jint android_media_tv_Tuner_set_filter_data_source(
JNIEnv* env, jobject filter, jobject srcFilter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to set filter data source: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
Result res;
- if (srcFilter == NULL) {
- res = filterClient->setDataSource(NULL);
+ if (srcFilter == nullptr) {
+ res = filterClient->setDataSource(nullptr);
} else {
sp<FilterClient> srcClient = getFilterClient(env, srcFilter);
- if (srcClient == NULL) {
+ if (srcClient == nullptr) {
ALOGD("Failed to set filter data source: src filter not found");
- return (jint) Result::INVALID_ARGUMENT;
+ return (jint)Result::INVALID_ARGUMENT;
}
res = filterClient->setDataSource(srcClient);
}
- return (jint) res;
+ return (jint)res;
}
static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to start filter: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
- return (jint) filterClient->start();
+ return (jint)filterClient->start();
}
static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to stop filter: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
- return (jint) filterClient->stop();
+ return (jint)filterClient->stop();
}
static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
ALOGD("Failed to flush filter: filter client not found");
- return (int) Result::NOT_INITIALIZED;
+ return (int)Result::NOT_INITIALIZED;
}
- return (jint) filterClient->flush();
+ return (jint)filterClient->flush();
}
static jint android_media_tv_Tuner_read_filter_fmq(
JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to read filter FMQ: filter client not found");
return -1;
@@ -3878,25 +3762,25 @@ static jint android_media_tv_Tuner_read_filter_fmq(
jboolean isCopy;
jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
- ALOGD("copyData, isCopy=%d", isCopy);
+ ALOGV("copyData, isCopy=%d", isCopy);
if (dst == nullptr) {
jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
return -1;
}
- int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
+ int realReadSize = filterClient->read(reinterpret_cast<int8_t *>(dst) + offset, size);
env->ReleaseByteArrayElements(buffer, dst, 0);
- return (jint) realReadSize;
+ return (jint)realReadSize;
}
static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
+ if (filterClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to close filter: filter client not found");
return 0;
}
- return (jint) filterClient->close();
+ return (jint)filterClient->close();
}
static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
@@ -3906,63 +3790,61 @@ static sp<TimeFilterClient> getTimeFilterClient(JNIEnv *env, jobject filter) {
static int android_media_tv_Tuner_time_filter_set_timestamp(
JNIEnv *env, jobject filter, jlong timestamp) {
sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed set timestamp: time filter client not found");
return (int) Result::INVALID_STATE;
}
- Result r = timeFilterClient->setTimeStamp(static_cast<uint64_t>(timestamp));
- return (int) r;
+ return (int)timeFilterClient->setTimeStamp(timestamp);
}
static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) {
sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed clear timestamp: time filter client not found");
return (int) Result::INVALID_STATE;
}
- Result r = timeFilterClient->clearTimeStamp();
- return (int) r;
+ return (int)timeFilterClient->clearTimeStamp();
}
static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) {
sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed get timestamp: time filter client not found");
- return NULL;
+ return nullptr;
}
- uint64_t timestamp = timeFilterClient->getTimeStamp();
+ int64_t timestamp = timeFilterClient->getTimeStamp();
if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) {
- return NULL;
+ return nullptr;
}
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
- jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp));
+ jobject longObj = env->NewObject(longClazz, longInit, timestamp);
return longObj;
}
static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) {
sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed get source time: time filter client not found");
- return NULL;
+ return nullptr;
}
- uint64_t timestamp = timeFilterClient->getSourceTime();
+ int64_t timestamp = timeFilterClient->getSourceTime();
if (timestamp == (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP) {
- return NULL;
+ return nullptr;
}
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
- jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp));
+ jobject longObj = env->NewObject(longClazz, longInit, timestamp);
return longObj;
}
static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) {
sp<TimeFilterClient> timeFilterClient = getTimeFilterClient(env, filter);
- if (timeFilterClient == NULL) {
+ if (timeFilterClient == nullptr) {
ALOGD("Failed close time filter: time filter client not found");
return (int) Result::INVALID_STATE;
}
@@ -3972,7 +3854,7 @@ static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter)
timeFilterClient->decStrong(filter);
env->SetLongField(filter, gFields.timeFilterContext, 0);
}
- return (int) r;
+ return (int)r;
}
static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jint) {
@@ -3983,48 +3865,48 @@ static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz
static jint android_media_tv_Tuner_descrambler_add_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
- if (descramblerClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (descramblerClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
- sp<FilterClient> filterClient = (filter == NULL) ? NULL : getFilterClient(env, filter);
+ sp<FilterClient> filterClient = (filter == nullptr) ? nullptr : getFilterClient(env, filter);
Result result = descramblerClient->addPid(getDemuxPid((int)pidType, (int)pid), filterClient);
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_descrambler_remove_pid(
JNIEnv *env, jobject descrambler, jint pidType, jint pid, jobject filter) {
sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
- if (descramblerClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (descramblerClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
- sp<FilterClient> filterClient = (filter == NULL) ? NULL : getFilterClient(env, filter);
+ sp<FilterClient> filterClient = (filter == nullptr) ? nullptr : getFilterClient(env, filter);
Result result = descramblerClient->removePid(getDemuxPid((int)pidType, (int)pid), filterClient);
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_descrambler_set_key_token(
JNIEnv* env, jobject descrambler, jbyteArray keyToken) {
sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
- if (descramblerClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (descramblerClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
int size = env->GetArrayLength(keyToken);
std::vector<uint8_t> v(size);
- env->GetByteArrayRegion(keyToken, 0, size, reinterpret_cast<jbyte*>(&v[0]));
+ env->GetByteArrayRegion(keyToken, 0, size, reinterpret_cast<jbyte *>(&v[0]));
Result result = descramblerClient->setKeyToken(v);
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_close_descrambler(JNIEnv* env, jobject descrambler) {
sp<DescramblerClient> descramblerClient = getDescramblerClient(env, descrambler);
- if (descramblerClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (descramblerClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
Result r = descramblerClient->close();
if (r == Result::SUCCESS) {
descramblerClient->decStrong(descrambler);
}
- return (jint) r;
+ return (jint)r;
}
static jobject android_media_tv_Tuner_open_dvr_recorder(
@@ -4046,13 +3928,13 @@ static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz)
static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) {
sp<JTuner> tuner = getTuner(env, thiz);
- return (jint) tuner->openDemux(handle);
+ return (jint)tuner->openDemux(handle);
}
static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) {
sp<JTuner> tuner = getTuner(env, thiz);
- setTuner(env, thiz, NULL);
- return (jint) tuner->close();
+ setTuner(env, thiz, nullptr);
+ return (jint)tuner->close();
}
static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) {
@@ -4067,106 +3949,102 @@ static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jin
static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
- return (jint) Result::INVALID_ARGUMENT;
+ if (filterClient == nullptr) {
+ return (jint)Result::INVALID_ARGUMENT;
}
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (dvrClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
Result result = dvrClient->attachFilter(filterClient);
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
sp<FilterClient> filterClient = getFilterClient(env, filter);
- if (filterClient == NULL) {
- return (jint) Result::INVALID_ARGUMENT;
+ if (filterClient == nullptr) {
+ return (jint)Result::INVALID_ARGUMENT;
}
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
- return (jint) Result::NOT_INITIALIZED;
+ if (dvrClient == nullptr) {
+ return (jint)Result::NOT_INITIALIZED;
}
Result result = dvrClient->detachFilter(filterClient);
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to configure dvr: dvr client not found");
return (int)Result::NOT_INITIALIZED;
}
bool isRecorder =
env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
Result result = dvrClient->configure(getDvrSettings(env, settings, isRecorder));
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to start dvr: dvr client not found");
- return (jint) Result::NOT_INITIALIZED;
+ return (jint)Result::NOT_INITIALIZED;
}
Result result = dvrClient->start();
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to stop dvr: dvr client not found");
- return (jint) Result::NOT_INITIALIZED;
+ return (jint)Result::NOT_INITIALIZED;
}
Result result = dvrClient->stop();
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to flush dvr: dvr client not found");
- return (jint) Result::NOT_INITIALIZED;
+ return (jint)Result::NOT_INITIALIZED;
}
Result result = dvrClient->flush();
- return (jint) result;
+ return (jint)result;
}
static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to close dvr: dvr client not found");
- return (jint) Result::NOT_INITIALIZED;
+ return (jint)Result::NOT_INITIALIZED;
}
- return (jint) dvrClient->close();
+ return (jint)dvrClient->close();
}
static jint android_media_tv_Tuner_lnb_set_voltage(JNIEnv* env, jobject lnb, jint voltage) {
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
- Result r = lnbClient->setVoltage(static_cast<LnbVoltage>(voltage));
- return (jint) r;
+ return (jint)lnbClient->setVoltage(static_cast<LnbVoltage>(voltage));
}
static int android_media_tv_Tuner_lnb_set_tone(JNIEnv* env, jobject lnb, jint tone) {
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
- Result r = lnbClient->setTone(static_cast<LnbTone>(tone));
- return (jint) r;
+ return (jint)lnbClient->setTone(static_cast<LnbTone>(tone));
}
static int android_media_tv_Tuner_lnb_set_position(JNIEnv* env, jobject lnb, jint position) {
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
- Result r = lnbClient->setSatellitePosition(static_cast<LnbPosition>(position));
- return (jint) r;
+ return (jint)lnbClient->setSatellitePosition(static_cast<LnbPosition>(position));
}
static int android_media_tv_Tuner_lnb_send_diseqc_msg(JNIEnv* env, jobject lnb, jbyteArray msg) {
sp<LnbClient> lnbClient = getLnbClient(env, lnb);
int size = env->GetArrayLength(msg);
std::vector<uint8_t> v(size);
- env->GetByteArrayRegion(msg, 0, size, reinterpret_cast<jbyte*>(&v[0]));
- Result r = lnbClient->sendDiseqcMessage(v);
- return (jint) r;
+ env->GetByteArrayRegion(msg, 0, size, reinterpret_cast<jbyte *>(&v[0]));
+ return (jint)lnbClient->sendDiseqcMessage(v);
}
static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) {
@@ -4176,34 +4054,34 @@ static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) {
lnbClient->decStrong(lnb);
env->SetLongField(lnb, gFields.lnbContext, 0);
}
- return (jint) r;
+ return (jint)r;
}
static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jint fd) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGD("Failed to set FD for dvr: dvr client not found");
return;
}
- dvrClient->setFd((int)fd);
- ALOGD("set fd = %d", fd);
+ dvrClient->setFd(fd);
+ ALOGV("set fd = %d", fd);
}
static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to read dvr: dvr client not found");
return -1;
}
- return (jlong) dvrClient->readFromFile(size);
+ return (jlong)dvrClient->readFromFile(size);
}
static jlong android_media_tv_Tuner_read_dvr_from_array(
JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGW("Failed to read dvr: dvr client not found");
return -1;
}
@@ -4214,42 +4092,41 @@ static jlong android_media_tv_Tuner_read_dvr_from_array(
ALOGD("Failed to GetByteArrayElements");
return -1;
}
- long realSize = dvrClient->readFromBuffer(reinterpret_cast<signed char*>(src) + offset, size);
+ long realSize = dvrClient->readFromBuffer(reinterpret_cast<signed char *>(src) + offset, size);
env->ReleaseByteArrayElements(buffer, src, 0);
- return (jlong) realSize;
-
+ return (jlong)realSize;
}
static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException",
"Failed to write dvr: dvr client not found");
return -1;
}
- return (jlong) dvrClient->writeToFile(size);
+ return (jlong)dvrClient->writeToFile(size);
}
static jlong android_media_tv_Tuner_write_dvr_to_array(
JNIEnv *env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
- if (dvrClient == NULL) {
+ if (dvrClient == nullptr) {
ALOGW("Failed to read dvr: dvr client not found");
return -1;
}
jboolean isCopy;
jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
- ALOGD("copyData, isCopy=%d", isCopy);
+ ALOGV("copyData, isCopy=%d", isCopy);
if (dst == nullptr) {
jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
return -1;
}
- long realSize = dvrClient->writeToBuffer(reinterpret_cast<signed char*>(dst) + offset, size);
+ long realSize = dvrClient->writeToBuffer(reinterpret_cast<signed char *>(dst) + offset, size);
env->ReleaseByteArrayElements(buffer, dst, 0);
- return (jlong) realSize;
+ return (jlong)realSize;
}
static sp<MediaEvent> getMediaEventSp(JNIEnv *env, jobject mediaEventObj) {
@@ -4259,9 +4136,9 @@ static sp<MediaEvent> getMediaEventSp(JNIEnv *env, jobject mediaEventObj) {
static jobject android_media_tv_Tuner_media_event_get_linear_block(
JNIEnv* env, jobject mediaEventObj) {
sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
- if (mediaEventSp == NULL) {
+ if (mediaEventSp == nullptr) {
ALOGD("Failed get MediaEvent");
- return NULL;
+ return nullptr;
}
android::Mutex::Autolock autoLock(mediaEventSp->mLock);
@@ -4271,23 +4148,23 @@ static jobject android_media_tv_Tuner_media_event_get_linear_block(
static jobject android_media_tv_Tuner_media_event_get_audio_handle(
JNIEnv* env, jobject mediaEventObj) {
sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
- if (mediaEventSp == NULL) {
+ if (mediaEventSp == nullptr) {
ALOGD("Failed get MediaEvent");
- return NULL;
+ return nullptr;
}
android::Mutex::Autolock autoLock(mediaEventSp->mLock);
- uint64_t audioHandle = mediaEventSp->getAudioHandle();
+ int64_t audioHandle = mediaEventSp->getAudioHandle();
jclass longClazz = env->FindClass("java/lang/Long");
jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
- jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(audioHandle));
+ jobject longObj = env->NewObject(longClazz, longInit, audioHandle);
return longObj;
}
static void android_media_tv_Tuner_media_event_finalize(JNIEnv* env, jobject mediaEventObj) {
sp<MediaEvent> mediaEventSp = getMediaEventSp(env, mediaEventObj);
- if (mediaEventSp == NULL) {
+ if (mediaEventSp == nullptr) {
ALOGD("Failed get MediaEvent");
return;
}
@@ -4307,6 +4184,8 @@ static const JNINativeMethod gTunerMethods[] = {
(void *)android_media_tv_Tuner_get_frontend_ids },
{ "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
(void *)android_media_tv_Tuner_open_frontend_by_handle },
+ { "nativeShareFrontend", "(I)I",
+ (void *)android_media_tv_Tuner_share_frontend },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -4494,19 +4373,18 @@ static bool register_android_media_tv_Tuner(JNIEnv *env) {
return true;
}
-jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
- JNIEnv* env = NULL;
+jint JNI_OnLoad(JavaVM *vm, void * /* reserved */) {
+ JNIEnv *env = nullptr;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- ALOGE("ERROR: GetEnv failed\n");
+ ALOGE("ERROR: GetEnv failed");
return result;
}
- assert(env != NULL);
+ assert(env != nullptr);
if (!register_android_media_tv_Tuner(env)) {
- ALOGE("ERROR: Tuner native registration failed\n");
+ ALOGE("ERROR: Tuner native registration failed");
return result;
}
return JNI_VERSION_1_4;
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 2a933b207902..5401ddd10f5d 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -17,18 +17,35 @@
#ifndef _ANDROID_MEDIA_TV_TUNER_H_
#define _ANDROID_MEDIA_TV_TUNER_H_
-#include <android/hardware/tv/tuner/1.1/types.h>
-
#include <C2BlockInternal.h>
#include <C2HandleIonInternal.h>
#include <C2ParamDef.h>
-#include <fmq/MessageQueue.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMonitorEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterStatus.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxPid.h>
+#include <aidl/android/hardware/tv/tuner/DvrType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendCapabilities.h>
+#include <aidl/android/hardware/tv/tuner/FrontendEventType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendInfo.h>
+#include <aidl/android/hardware/tv/tuner/FrontendScanMessage.h>
+#include <aidl/android/hardware/tv/tuner/FrontendScanMessageType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendScanType.h>
+#include <aidl/android/hardware/tv/tuner/FrontendSettings.h>
+#include <aidl/android/hardware/tv/tuner/LnbEventType.h>
+#include <aidl/android/hardware/tv/tuner/PlaybackStatus.h>
+#include <aidl/android/hardware/tv/tuner/RecordStatus.h>
+#include <aidl/android/hardware/tv/tuner/Result.h>
+#include <fmq/AidlMessageQueue.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
#include <fstream>
#include <string>
#include <unordered_map>
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
+#include "jni.h"
#include "tuner/DemuxClient.h"
#include "tuner/DescramblerClient.h"
#include "tuner/FilterClient.h"
@@ -39,48 +56,36 @@
#include "tuner/LnbClientCallback.h"
#include "tuner/TimeFilterClient.h"
#include "tuner/TunerClient.h"
-#include "jni.h"
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMonitorEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterStatus;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxPid;
+using ::aidl::android::hardware::tv::tuner::DvrType;
+using ::aidl::android::hardware::tv::tuner::FrontendCapabilities;
+using ::aidl::android::hardware::tv::tuner::FrontendEventType;
+using ::aidl::android::hardware::tv::tuner::FrontendInfo;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessage;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessageType;
+using ::aidl::android::hardware::tv::tuner::FrontendScanType;
+using ::aidl::android::hardware::tv::tuner::FrontendSettings;
+using ::aidl::android::hardware::tv::tuner::LnbEventType;
+using ::aidl::android::hardware::tv::tuner::PlaybackStatus;
+using ::aidl::android::hardware::tv::tuner::RecordStatus;
+using ::aidl::android::hardware::tv::tuner::Result;
using ::android::hardware::EventFlag;
-using ::android::hardware::MQDescriptorSync;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::Return;
-using ::android::hardware::hidl_handle;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::kSynchronizedReadWrite;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxPid;
-using ::android::hardware::tv::tuner::V1_0::DvrType;
-using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
-using ::android::hardware::tv::tuner::V1_0::FrontendId;
-using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
-using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
-using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_0::LnbEventType;
-using ::android::hardware::tv::tuner::V1_0::LnbId;
-using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
-using ::android::hardware::tv::tuner::V1_0::RecordStatus;
-using ::android::hardware::tv::tuner::V1_0::Result;
-using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
-using ::android::hardware::tv::tuner::V1_1::DemuxFilterMonitorEvent;
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
-
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
-const static int TUNER_VERSION_1_1 = ((1 << 16) | 1);
+
+using MQ = MQDescriptor<int8_t, SynchronizedReadWrite>;
namespace android {
struct LnbClientCallbackImpl : public LnbClientCallback {
~LnbClientCallbackImpl();
virtual void onEvent(LnbEventType lnbEventType);
- virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+ virtual void onDiseqcMessage(const vector<uint8_t>& diseqcMessage);
void setLnb(jweak lnbObj);
private:
@@ -98,17 +103,17 @@ private:
};
struct MediaEvent : public RefBase {
- MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle, uint64_t dataId,
- uint64_t dataSize, jobject obj);
+ MediaEvent(sp<FilterClient> filterClient, native_handle_t* avHandle, int64_t dataId,
+ int64_t dataSize, jobject obj);
~MediaEvent();
jobject getLinearBlock();
- uint64_t getAudioHandle();
+ int64_t getAudioHandle();
void finalize();
sp<FilterClient> mFilterClient;
native_handle_t* mAvHandle;
- uint64_t mDataId;
- uint64_t mDataSize;
+ int64_t mDataId;
+ int64_t mDataSize;
uint8_t* mBuffer;
android::Mutex mLock;
int mDataIdRefCnt;
@@ -121,39 +126,24 @@ struct MediaEvent : public RefBase {
struct FilterClientCallbackImpl : public FilterClientCallback {
~FilterClientCallbackImpl();
- virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
- const DemuxFilterEventExt& filterEventExt);
- virtual void onFilterEvent(const DemuxFilterEvent& filterEvent);
+ virtual void onFilterEvent(const vector<DemuxFilterEvent>& events);
virtual void onFilterStatus(const DemuxFilterStatus status);
void setFilter(jweak filterObj, sp<FilterClient> filterClient);
private:
jweak mFilterObj;
sp<FilterClient> mFilterClient;
- jobjectArray getSectionEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getMediaEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getPesEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getTsRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
- const std::vector<DemuxFilterEventExt::Event>& eventsExt);
- jobjectArray getMmtpRecordEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>&events,
- const std::vector<DemuxFilterEventExt::Event>& eventsExt);
- jobjectArray getDownloadEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getIpPayloadEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getTemiEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events);
- jobjectArray getScramblingStatusEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
- jobjectArray getIpCidChangeEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
- jobjectArray getRestartEvent(
- jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt);
+ void getSectionEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getMediaEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getPesEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getTsRecordEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getMmtpRecordEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getDownloadEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getIpPayloadEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getTemiEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getScramblingStatusEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getIpCidChangeEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getRestartEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
};
struct FrontendClientCallbackImpl : public FrontendClientCallback {
@@ -162,8 +152,6 @@ struct FrontendClientCallbackImpl : public FrontendClientCallback {
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
- virtual void onScanMessageExt1_1(
- FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
jweak mObject;
};
@@ -179,12 +167,12 @@ struct JTuner : public RefBase {
int unlinkCiCam(jint id);
jobject getFrontendIds();
jobject openFrontendByHandle(int feHandle);
+ int shareFrontend(int feId);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
- int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ int tune(const FrontendSettings& settings);
int stopTune();
- int scan(const FrontendSettings& settings, FrontendScanType scanType,
- const FrontendSettingsExt1_1& settingsExt1_1);
+ int scan(const FrontendSettings& settings, FrontendScanType scanType);
int stopScan();
int setLnb(sp<LnbClient> lnbClient);
int setLna(bool enable);
@@ -210,20 +198,19 @@ private:
static sp<TunerClient> mTunerClient;
sp<FrontendClient> mFeClient;
int mFeId;
+ int mSharedFeId;
sp<LnbClient> mLnbClient;
sp<DemuxClient> mDemuxClient;
- static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps);
- static jobject getDtmbFrontendCaps(JNIEnv *env, int id);
-
- bool isV1_1ExtendedStatusType(jint type);
+ static jobject getAnalogFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getAtsc3FrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getAtscFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getDvbcFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getDvbsFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getDvbtFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getIsdbs3FrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getIsdbsFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getIsdbtFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
+ static jobject getDtmbFrontendCaps(JNIEnv* env, FrontendCapabilities& caps);
};
class C2DataIdInfo : public C2Param {
diff --git a/media/jni/tuner/ClientHelper.h b/media/jni/tuner/ClientHelper.h
index 508dccf36841..d7a847a8f5b8 100644
--- a/media/jni/tuner/ClientHelper.h
+++ b/media/jni/tuner/ClientHelper.h
@@ -17,22 +17,18 @@
#ifndef _ANDROID_MEDIA_TV_CLIENT_HELPER_H_
#define _ANDROID_MEDIA_TV_CLIENT_HELPER_H_
+#include <aidl/android/hardware/tv/tuner/Result.h>
#include <android/binder_parcel_utils.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
#include <utils/Log.h>
+using ::aidl::android::hardware::tv::tuner::Result;
using Status = ::ndk::ScopedAStatus;
-using ::android::hardware::tv::tuner::V1_0::Result;
-
-using namespace std;
-
namespace android {
-struct ClientHelper {
-
+class ClientHelper {
public:
- static Result getServiceSpecificErrorCode(Status& s) {
+ static Result getServiceSpecificErrorCode(Status& s) {
if (s.getExceptionCode() == EX_SERVICE_SPECIFIC) {
return static_cast<Result>(s.getServiceSpecificError());
} else if (s.isOk()) {
@@ -42,6 +38,7 @@ public:
return Result::UNKNOWN_ERROR;
}
};
+
} // namespace android
-#endif // _ANDROID_MEDIA_TV_CLIENT_HELPER_H_ \ No newline at end of file
+#endif // _ANDROID_MEDIA_TV_CLIENT_HELPER_H_
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 78cb5b003a38..4ee3f4803056 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -16,295 +16,156 @@
#define LOG_TAG "DemuxClient"
-#include <android-base/logging.h>
-#include <utils/Log.h>
-
#include "DemuxClient.h"
-using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+#include <aidl/android/hardware/tv/tuner/Constant.h>
+#include <aidl/android/hardware/tv/tuner/Constant64Bit.h>
+#include <android-base/logging.h>
+#include <utils/Log.h>
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
-using ::android::hardware::tv::tuner::V1_0::Result;
+using ::aidl::android::hardware::tv::tuner::Constant;
+using ::aidl::android::hardware::tv::tuner::Constant64Bit;
namespace android {
-
/////////////// DemuxClient ///////////////////////
-
DemuxClient::DemuxClient(shared_ptr<ITunerDemux> tunerDemux) {
mTunerDemux = tunerDemux;
- mId = -1;
}
DemuxClient::~DemuxClient() {
- mTunerDemux = NULL;
- mDemux = NULL;
- mId = -1;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void DemuxClient::setHidlDemux(sp<IDemux> demux) {
- mDemux = demux;
+ mTunerDemux = nullptr;
}
Result DemuxClient::setFrontendDataSource(sp<FrontendClient> frontendClient) {
- if (mTunerDemux != NULL) {
+ if (frontendClient == nullptr) {
+ return Result::INVALID_ARGUMENT;
+ }
+
+ if (mTunerDemux != nullptr) {
Status s = mTunerDemux->setFrontendDataSource(frontendClient->getAidlFrontend());
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDemux != NULL) {
- Result res = mDemux->setFrontendDataSource(frontendClient->getId());
- return res;
+ return Result::INVALID_STATE;
+}
+
+Result DemuxClient::setFrontendDataSourceById(int frontendId) {
+ if (mTunerDemux != nullptr) {
+ Status s = mTunerDemux->setFrontendDataSourceById(frontendId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
return Result::INVALID_STATE;
}
-sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize,
- sp<FilterClientCallback> cb) {
- if (mTunerDemux != NULL) {
+sp<FilterClient> DemuxClient::openFilter(const DemuxFilterType& type, int32_t bufferSize,
+ sp<FilterClientCallback> cb) {
+ if (cb == nullptr) {
+ return nullptr;
+ }
+
+ if (mTunerDemux != nullptr) {
shared_ptr<ITunerFilter> tunerFilter;
shared_ptr<TunerFilterCallback> callback =
::ndk::SharedRefBase::make<TunerFilterCallback>(cb);
- Status s = mTunerDemux->openFilter((int)type.mainType, getSubType(type),
- bufferSize, callback, &tunerFilter);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ Status s = mTunerDemux->openFilter(type, bufferSize, callback, &tunerFilter);
+ if (!s.isOk()) {
+ return nullptr;
}
return new FilterClient(type, tunerFilter);
}
- if (mDemux != NULL) {
- sp<HidlFilterCallback> callback = new HidlFilterCallback(cb);
- sp<IFilter> hidlFilter = openHidlFilter(type, bufferSize, callback);
- if (hidlFilter != NULL) {
- sp<FilterClient> filterClient = new FilterClient(type, NULL);
- filterClient->setHidlFilter(hidlFilter);
- return filterClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
sp<TimeFilterClient> DemuxClient::openTimeFilter() {
- if (mTunerDemux != NULL) {
+ if (mTunerDemux != nullptr) {
shared_ptr<ITunerTimeFilter> tunerTimeFilter;
Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
return new TimeFilterClient(tunerTimeFilter);
}
- if (mDemux != NULL) {
- sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
- if (hidlTimeFilter != NULL) {
- sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
- timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
- return timeFilterClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
-int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
- if (mTunerDemux != NULL) {
- int hwId;
- Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return INVALID_AV_SYNC_HW_ID;
- }
- return hwId;
+int32_t DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
+ if (filterClient == nullptr) {
+ return static_cast<int32_t>(Constant::INVALID_AV_SYNC_ID);
}
- if (mDemux != NULL) {
- uint32_t avSyncHwId;
- Result res;
- sp<IFilter> halFilter = filterClient->getHalFilter();
- mDemux->getAvSyncHwId(halFilter,
- [&](Result r, uint32_t id) {
- res = r;
- avSyncHwId = id;
- });
- if (res == Result::SUCCESS) {
- return (int) avSyncHwId;
+ if (mTunerDemux != nullptr) {
+ int32_t hwId;
+ Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+ if (!s.isOk()) {
+ return static_cast<int32_t>(Constant::INVALID_AV_SYNC_ID);
}
+ return hwId;
}
- return INVALID_AV_SYNC_HW_ID;
+ return static_cast<int32_t>(Constant::INVALID_AV_SYNC_ID);
}
-long DemuxClient::getAvSyncTime(int avSyncHwId) {
- if (mTunerDemux != NULL) {
+int64_t DemuxClient::getAvSyncTime(int32_t avSyncHwId) {
+ if (mTunerDemux != nullptr) {
int64_t time;
Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return INVALID_AV_SYNC_TIME;
+ if (!s.isOk()) {
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
return time;
}
- if (mDemux != NULL) {
- uint64_t time;
- Result res;
- mDemux->getAvSyncTime(static_cast<uint32_t>(avSyncHwId),
- [&](Result r, uint64_t ts) {
- res = r;
- time = ts;
- });
- if (res == Result::SUCCESS) {
- return (long) time;
- }
- }
-
- return INVALID_AV_SYNC_TIME;
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
-sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
- if (mTunerDemux != NULL) {
+sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int32_t bufferSize, sp<DvrClientCallback> cb) {
+ if (cb == nullptr) {
+ return nullptr;
+ }
+
+ if (mTunerDemux != nullptr) {
shared_ptr<ITunerDvr> tunerDvr;
shared_ptr<TunerDvrCallback> callback =
::ndk::SharedRefBase::make<TunerDvrCallback>(cb);
- Status s = mTunerDemux->openDvr((int)dvbType, bufferSize, callback, &tunerDvr);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ Status s = mTunerDemux->openDvr(dvbType, bufferSize, callback, &tunerDvr);
+ if (!s.isOk()) {
+ return nullptr;
}
return new DvrClient(tunerDvr);
}
- if (mDemux != NULL) {
- sp<HidlDvrCallback> callback = new HidlDvrCallback(cb);
- sp<IDvr> hidlDvr = openHidlDvr(dvbType, bufferSize, callback);
- if (hidlDvr != NULL) {
- sp<DvrClient> dvrClient = new DvrClient(NULL);
- dvrClient->setHidlDvr(hidlDvr);
- return dvrClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
-Result DemuxClient::connectCiCam(int ciCamId) {
- if (mTunerDemux != NULL) {
+Result DemuxClient::connectCiCam(int32_t ciCamId) {
+ if (mTunerDemux != nullptr) {
Status s = mTunerDemux->connectCiCam(ciCamId);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDemux != NULL) {
- return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
- }
-
return Result::INVALID_STATE;
}
Result DemuxClient::disconnectCiCam() {
- if (mTunerDemux != NULL) {
+ if (mTunerDemux != nullptr) {
Status s = mTunerDemux->disconnectCiCam();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDemux != NULL) {
- return mDemux->disconnectCiCam();
- }
-
return Result::INVALID_STATE;
}
Result DemuxClient::close() {
- if (mTunerDemux != NULL) {
+ if (mTunerDemux != nullptr) {
Status s = mTunerDemux->close();
- mTunerDemux = NULL;
+ mTunerDemux = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDemux != NULL) {
- Result res = mDemux->close();
- mDemux = NULL;
- return res;
- }
-
return Result::INVALID_STATE;
}
-/////////////// DemuxClient Helper Methods ///////////////////////
-
-sp<IFilter> DemuxClient::openHidlFilter(DemuxFilterType type, int bufferSize,
- sp<HidlFilterCallback> callback) {
- if (mDemux == NULL) {
- return NULL;
- }
-
- sp<IFilter> hidlFilter;
- Result res;
- mDemux->openFilter(type, bufferSize, callback,
- [&](Result r, const sp<IFilter>& filter) {
- hidlFilter = filter;
- res = r;
- });
- if (res != Result::SUCCESS || hidlFilter == NULL) {
- return NULL;
- }
-
- return hidlFilter;
-}
-
-sp<ITimeFilter> DemuxClient::openHidlTimeFilter() {
- if (mDemux == NULL) {
- return NULL;
- }
-
- sp<ITimeFilter> timeFilter;
- Result res;
- mDemux->openTimeFilter(
- [&](Result r, const sp<ITimeFilter>& timeFilterSp) {
- timeFilter = timeFilterSp;
- res = r;
- });
-
- if (res != Result::SUCCESS || timeFilter == NULL) {
- return NULL;
- }
-
- return timeFilter;
-}
-
-sp<IDvr> DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize,
- sp<HidlDvrCallback> callback) {
- if (mDemux == NULL) {
- return NULL;
- }
-
- sp<IDvr> hidlDvr;
- Result res;
- mDemux->openDvr(dvrType, bufferSize, callback,
- [&](Result r, const sp<IDvr>& dvr) {
- hidlDvr = dvr;
- res = r;
- });
- if (res != Result::SUCCESS || hidlDvr == NULL) {
- return NULL;
- }
-
- return hidlDvr;
-}
-
-int DemuxClient::getSubType(DemuxFilterType filterType) {
- switch (filterType.mainType) {
- case DemuxFilterMainType::TS:
- return (int)filterType.subType.tsFilterType();
- case DemuxFilterMainType::MMTP:
- return (int)filterType.subType.mmtpFilterType();
- case DemuxFilterMainType::IP:
- return (int)filterType.subType.ipFilterType();
- case DemuxFilterMainType::TLV:
- return (int)filterType.subType.tlvFilterType();
- case DemuxFilterMainType::ALP:
- return (int)filterType.subType.alpFilterType();
- default:
- return -1;
- }
-}
} // namespace android
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index c38a8fa34690..d5f5f2ff0dbf 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,12 +17,11 @@
#ifndef _ANDROID_MEDIA_TV_DEMUX_CLIENT_H_
#define _ANDROID_MEDIA_TV_DEMUX_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/Result.h>
#include <aidl/android/media/tv/tuner/ITunerDemux.h>
-#include <android/hardware/tv/tuner/1.0/IDemux.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
-#include "DvrClient.h"
#include "ClientHelper.h"
+#include "DvrClient.h"
#include "DvrClientCallback.h"
#include "FilterClient.h"
#include "FilterClientCallback.h"
@@ -30,20 +29,14 @@
#include "TimeFilterClient.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterType;
+using ::aidl::android::hardware::tv::tuner::DvrType;
+using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::ITunerDemux;
using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
-using ::android::hardware::tv::tuner::V1_0::IDemux;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
-using ::android::hardware::tv::tuner::V1_0::DvrType;
-using ::android::hardware::tv::tuner::V1_0::IDemux;
-using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
-
using namespace std;
-const int64_t INVALID_AV_SYNC_TIME = -1;
-const int INVALID_AV_SYNC_HW_ID = -1;
-
namespace android {
struct DemuxClient : public RefBase {
@@ -52,18 +45,21 @@ public:
DemuxClient(shared_ptr<ITunerDemux> tunerDemux);
~DemuxClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlDemux(sp<IDemux> demux);
-
/**
* Set a frontend resource as data input of the demux.
*/
Result setFrontendDataSource(sp<FrontendClient> frontendClient);
/**
+ * Set a frontend resource by handle as data input of the demux.
+ */
+ Result setFrontendDataSourceById(int frontendId);
+
+ /**
* Open a new filter client.
*/
- sp<FilterClient> openFilter(DemuxFilterType type, int bufferSize, sp<FilterClientCallback> cb);
+ sp<FilterClient> openFilter(const DemuxFilterType& type, int32_t bufferSize,
+ sp<FilterClientCallback> cb);
/**
* Open time filter of the demux.
@@ -73,22 +69,22 @@ public:
/**
* Get hardware sync ID for audio and video.
*/
- int getAvSyncHwId(sp<FilterClient> filterClient);
+ int32_t getAvSyncHwId(sp<FilterClient> filterClient);
/**
* Get current time stamp to use for A/V sync.
*/
- long getAvSyncTime(int avSyncHwId);
+ int64_t getAvSyncTime(int32_t avSyncHwId);
/**
* Open a DVR (Digital Video Record) client.
*/
- sp<DvrClient> openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb);
+ sp<DvrClient> openDvr(DvrType dvbType, int32_t bufferSize, sp<DvrClientCallback> cb);
/**
* Connect Conditional Access Modules (CAM) through Common Interface (CI).
*/
- Result connectCiCam(int ciCamId);
+ Result connectCiCam(int32_t ciCamId);
/**
* Disconnect Conditional Access Modules (CAM).
@@ -105,29 +101,12 @@ public:
*/
shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; }
- void setId(int id) { mId = id; }
- int getId() { return mId; }
-
private:
- sp<IFilter> openHidlFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> cb);
- sp<ITimeFilter> openHidlTimeFilter();
- sp<IDvr> openHidlDvr(DvrType type, int bufferSize, sp<HidlDvrCallback> cb);
- int getSubType(DemuxFilterType filterType);
-
/**
* An AIDL Tuner Demux Singleton assigned at the first time the Tuner Client
* opens a demux. Default null when demux is not opened.
*/
shared_ptr<ITunerDemux> mTunerDemux;
-
- /**
- * A Demux HAL interface that is ready before migrating to the TunerDemux.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<IDemux> mDemux;
-
- int mId;
};
} // namespace android
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
index 3e4ed8280ee6..052fa7aa6d81 100644
--- a/media/jni/tuner/DescramblerClient.cpp
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -21,118 +21,69 @@
#include "DescramblerClient.h"
-using ::android::hardware::tv::tuner::V1_0::Result;
-
namespace android {
/////////////// DescramblerClient ///////////////////////
-
DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) {
mTunerDescrambler = tunerDescrambler;
}
DescramblerClient::~DescramblerClient() {
- mTunerDescrambler = NULL;
- mDescrambler = NULL;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void DescramblerClient::setHidlDescrambler(sp<IDescrambler> descrambler) {
- mDescrambler = descrambler;
+ mTunerDescrambler = nullptr;
}
Result DescramblerClient::setDemuxSource(sp<DemuxClient> demuxClient) {
- if (demuxClient == NULL) {
+ if (demuxClient == nullptr) {
return Result::INVALID_ARGUMENT;
}
- if (mTunerDescrambler != NULL) {
+ if (mTunerDescrambler != nullptr) {
Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux());
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDescrambler != NULL) {
- return mDescrambler->setDemuxSource(demuxClient->getId());
- }
-
return Result::INVALID_STATE;
}
Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
- if (mTunerDescrambler != NULL) {
+ if (mTunerDescrambler != nullptr) {
Status s = mTunerDescrambler->setKeyToken(keyToken);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDescrambler != NULL) {
- return mDescrambler->setKeyToken(keyToken);
- }
-
return Result::INVALID_STATE;
}
Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- if (mTunerDescrambler != NULL) {
- shared_ptr<ITunerFilter> aidlFilter = (optionalSourceFilter == NULL)
- ? NULL : optionalSourceFilter->getAidlFilter();
- Status s = mTunerDescrambler->addPid(getAidlDemuxPid(pid), aidlFilter);
+ if (mTunerDescrambler != nullptr) {
+ shared_ptr<ITunerFilter> aidlFilter =
+ (optionalSourceFilter == nullptr) ? nullptr : optionalSourceFilter->getAidlFilter();
+ Status s = mTunerDescrambler->addPid(pid, aidlFilter);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDescrambler != NULL) {
- sp<IFilter> halFilter = (optionalSourceFilter == NULL)
- ? NULL : optionalSourceFilter->getHalFilter();
- return mDescrambler->addPid(pid, halFilter);
- }
-
return Result::INVALID_STATE;
}
Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- if (mTunerDescrambler != NULL) {
- shared_ptr<ITunerFilter> aidlFilter = (optionalSourceFilter == NULL)
- ? NULL : optionalSourceFilter->getAidlFilter();
- Status s = mTunerDescrambler->removePid(getAidlDemuxPid(pid), aidlFilter);
+ if (mTunerDescrambler != nullptr) {
+ shared_ptr<ITunerFilter> aidlFilter =
+ (optionalSourceFilter == nullptr) ? nullptr : optionalSourceFilter->getAidlFilter();
+ Status s = mTunerDescrambler->removePid(pid, aidlFilter);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDescrambler != NULL) {
- sp<IFilter> halFilter = (optionalSourceFilter == NULL)
- ? NULL : optionalSourceFilter->getHalFilter();
- return mDescrambler->removePid(pid, halFilter);
- }
-
return Result::INVALID_STATE;
}
Result DescramblerClient::close() {
- if (mTunerDescrambler != NULL) {
+ if (mTunerDescrambler != nullptr) {
Status s = mTunerDescrambler->close();
- mTunerDescrambler = NULL;
+ mTunerDescrambler = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDescrambler != NULL) {
- Result res = mDescrambler->close();
- mDescrambler = NULL;
- return res;
- }
-
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 a8fa1e2e0497..c851e841b2c0 100644
--- a/media/jni/tuner/DescramblerClient.h
+++ b/media/jni/tuner/DescramblerClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,19 +17,15 @@
#ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
#define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/Result.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::hardware::tv::tuner::DemuxPid;
+using ::aidl::android::hardware::tv::tuner::Result;
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;
-using ::android::hardware::tv::tuner::V1_0::DemuxPid;
using namespace std;
@@ -41,9 +37,6 @@ public:
DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler);
~DescramblerClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlDescrambler(sp<IDescrambler> descrambler);
-
/**
* Set a demux as source of the descrambler.
*/
@@ -70,20 +63,11 @@ 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.
*/
shared_ptr<ITunerDescrambler> mTunerDescrambler;
-
- /**
- * A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<IDescrambler> mDescrambler;
};
} // namespace android
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 0476216dcbed..052b465f7b84 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,48 +14,42 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
#define LOG_TAG "DvrClient"
+#include "DvrClient.h"
+
+#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
#include <android-base/logging.h>
-#include <fmq/ConvertMQDescriptors.h>
+#include <inttypes.h>
#include <utils/Log.h>
#include "ClientHelper.h"
-#include "DvrClient.h"
-using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
-using ::android::hardware::tv::tuner::V1_0::Result;
+using ::aidl::android::hardware::tv::tuner::DemuxQueueNotifyBits;
namespace android {
-
/////////////// DvrClient ///////////////////////
-
DvrClient::DvrClient(shared_ptr<ITunerDvr> tunerDvr) {
mTunerDvr = tunerDvr;
mFd = -1;
- mDvrMQ = NULL;
- mDvrMQEventFlag = NULL;
+ mDvrMQ = nullptr;
+ mDvrMQEventFlag = nullptr;
}
DvrClient::~DvrClient() {
- mTunerDvr = NULL;
- mDvr = NULL;
+ mTunerDvr = nullptr;
mFd = -1;
- mDvrMQ = NULL;
- mDvrMQEventFlag = NULL;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void DvrClient::setHidlDvr(sp<IDvr> dvr) {
- mDvr = dvr;
+ mDvrMQ = nullptr;
+ mDvrMQEventFlag = nullptr;
}
-void DvrClient::setFd(int fd) {
+void DvrClient::setFd(int32_t fd) {
mFd = fd;
}
-long DvrClient::readFromFile(long size) {
- if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+int64_t DvrClient::readFromFile(int64_t size) {
+ if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) {
ALOGE("Failed to readFromFile. DVR mq is not configured");
return -1;
}
@@ -64,16 +58,16 @@ long DvrClient::readFromFile(long size) {
return -1;
}
- long available = mDvrMQ->availableToWrite();
- long write = min(size, available);
+ int64_t available = mDvrMQ->availableToWrite();
+ int64_t write = min(size, available);
AidlMQ::MemTransaction tx;
- long ret = 0;
+ int64_t ret = 0;
if (mDvrMQ->beginWrite(write, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- long length = first.getLength();
- long firstToWrite = min(length, write);
+ int64_t length = first.getLength();
+ int64_t firstToWrite = min(length, write);
ret = read(mFd, data, firstToWrite);
if (ret < 0) {
@@ -81,17 +75,20 @@ long DvrClient::readFromFile(long size) {
return -1;
}
if (ret < firstToWrite) {
- ALOGW("file to MQ, first region: %ld bytes to write, but %ld bytes written",
- firstToWrite, ret);
+ ALOGW("file to MQ, first region: %" PRIu64 " bytes to write, but %" PRIu64
+ " bytes written",
+ firstToWrite, ret);
} else if (firstToWrite < write) {
- ALOGD("write second region: %ld bytes written, %ld bytes in total", ret, write);
+ ALOGV("write second region: %" PRIu64 " bytes written, %" PRIu64 " bytes in total", ret,
+ write);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
- int secondToWrite = std::min(length, write - firstToWrite);
+ int64_t secondToWrite = std::min(length, write - firstToWrite);
ret += read(mFd, data, secondToWrite);
}
- ALOGD("file to MQ: %ld bytes need to be written, %ld bytes written", write, ret);
+ ALOGV("file to MQ: %" PRIu64 " bytes need to be written, %" PRIu64 " bytes written", write,
+ ret);
if (!mDvrMQ->commitWrite(ret)) {
ALOGE("Error: failed to commit write!");
return -1;
@@ -106,8 +103,8 @@ long DvrClient::readFromFile(long size) {
return ret;
}
-long DvrClient::readFromBuffer(int8_t* buffer, long size) {
- if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+int64_t DvrClient::readFromBuffer(int8_t* buffer, int64_t size) {
+ if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) {
ALOGE("Failed to readFromBuffer. DVR mq is not configured");
return -1;
}
@@ -116,7 +113,7 @@ long DvrClient::readFromBuffer(int8_t* buffer, long size) {
return -1;
}
- long available = mDvrMQ->availableToWrite();
+ int64_t available = mDvrMQ->availableToWrite();
size = min(size, available);
if (mDvrMQ->write(buffer, size)) {
@@ -128,8 +125,8 @@ long DvrClient::readFromBuffer(int8_t* buffer, long size) {
return size;
}
-long DvrClient::writeToFile(long size) {
- if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+int64_t DvrClient::writeToFile(int64_t size) {
+ if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) {
ALOGE("Failed to writeToFile. DVR mq is not configured");
return -1;
}
@@ -138,16 +135,16 @@ long DvrClient::writeToFile(long size) {
return -1;
}
- long available = mDvrMQ->availableToRead();
- long toRead = min(size, available);
+ int64_t available = mDvrMQ->availableToRead();
+ int64_t toRead = min(size, available);
- long ret = 0;
+ int64_t ret = 0;
AidlMQ::MemTransaction tx;
if (mDvrMQ->beginRead(toRead, &tx)) {
auto first = tx.getFirstRegion();
auto data = first.getAddress();
- long length = first.getLength();
- long firstToRead = std::min(length, toRead);
+ int64_t length = first.getLength();
+ int64_t firstToRead = std::min(length, toRead);
ret = write(mFd, data, firstToRead);
if (ret < 0) {
@@ -155,16 +152,18 @@ long DvrClient::writeToFile(long size) {
return -1;
}
if (ret < firstToRead) {
- ALOGW("MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret);
+ ALOGW("MQ to file: %" PRIu64 " bytes read, but %" PRIu64 " bytes written", firstToRead,
+ ret);
} else if (firstToRead < toRead) {
- ALOGD("read second region: %ld bytes read, %ld bytes in total", ret, toRead);
+ ALOGV("read second region: %" PRIu64 " bytes read, %" PRIu64 " bytes in total", ret,
+ toRead);
auto second = tx.getSecondRegion();
data = second.getAddress();
length = second.getLength();
- int secondToRead = toRead - firstToRead;
+ int32_t secondToRead = toRead - firstToRead;
ret += write(mFd, data, secondToRead);
}
- ALOGD("MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret);
+ ALOGV("MQ to file: %" PRIu64 " bytes to be read, %" PRIu64 " bytes written", toRead, ret);
if (!mDvrMQ->commitRead(ret)) {
ALOGE("Error: failed to commit read!");
return 0;
@@ -179,8 +178,8 @@ long DvrClient::writeToFile(long size) {
return ret;
}
-long DvrClient::writeToBuffer(int8_t* buffer, long size) {
- if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+int64_t DvrClient::writeToBuffer(int8_t* buffer, int64_t size) {
+ if (mDvrMQ == nullptr || mDvrMQEventFlag == nullptr) {
ALOGE("Failed to writetoBuffer. DVR mq is not configured");
return -1;
}
@@ -189,7 +188,7 @@ long DvrClient::writeToBuffer(int8_t* buffer, long size) {
return -1;
}
- long available = mDvrMQ->availableToRead();
+ int64_t available = mDvrMQ->availableToRead();
size = min(size, available);
if (mDvrMQ->read(buffer, size)) {
@@ -202,9 +201,8 @@ long DvrClient::writeToBuffer(int8_t* buffer, long size) {
}
Result DvrClient::configure(DvrSettings settings) {
- if (mTunerDvr != NULL) {
- TunerDvrSettings dvrSettings = getAidlDvrSettingsFromHidl(settings);
- Status s = mTunerDvr->configure(dvrSettings);
+ if (mTunerDvr != nullptr) {
+ Status s = mTunerDvr->configure(settings);
Result res = ClientHelper::getServiceSpecificErrorCode(s);
if (res != Result::SUCCESS) {
return res;
@@ -221,196 +219,95 @@ Result DvrClient::configure(DvrSettings settings) {
return res;
}
- if (mDvr != NULL) {
- Result res = mDvr->configure(settings);
- if (res == Result::SUCCESS) {
- MQDescriptorSync<uint8_t> dvrMQDesc;
- res = getQueueDesc(dvrMQDesc);
- if (res == Result::SUCCESS) {
- AidlMQDesc aidlMQDesc;
- unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
- dvrMQDesc, &aidlMQDesc);
- mDvrMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
- EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag);
- }
- }
- return res;
- }
-
return Result::INVALID_STATE;
}
Result DvrClient::attachFilter(sp<FilterClient> filterClient) {
- if (mTunerDvr != NULL) {
- Status s = mTunerDvr->attachFilter(filterClient->getAidlFilter());
- return ClientHelper::getServiceSpecificErrorCode(s);
+ if (filterClient == nullptr) {
+ return Result::INVALID_ARGUMENT;
}
- if (mDvr != NULL) {
- sp<IFilter> hidlFilter = filterClient->getHalFilter();
- if (hidlFilter == NULL) {
- return Result::INVALID_ARGUMENT;
- }
- return mDvr->attachFilter(hidlFilter);
+ if (mTunerDvr != nullptr) {
+ Status s = mTunerDvr->attachFilter(filterClient->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
return Result::INVALID_STATE;
}
Result DvrClient::detachFilter(sp<FilterClient> filterClient) {
- if (mTunerDvr != NULL) {
- Status s = mTunerDvr->detachFilter(filterClient->getAidlFilter());
- return ClientHelper::getServiceSpecificErrorCode(s);
+ if (filterClient == nullptr) {
+ return Result::INVALID_ARGUMENT;
}
- if (mDvr != NULL) {
- sp<IFilter> hidlFilter = filterClient->getHalFilter();
- if (hidlFilter == NULL) {
- return Result::INVALID_ARGUMENT;
- }
- return mDvr->detachFilter(hidlFilter);
+ if (mTunerDvr != nullptr) {
+ Status s = mTunerDvr->detachFilter(filterClient->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
return Result::INVALID_STATE;
}
Result DvrClient::start() {
- if (mTunerDvr != NULL) {
+ if (mTunerDvr != nullptr) {
Status s = mTunerDvr->start();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDvr != NULL) {
- return mDvr->start();
- }
-
return Result::INVALID_STATE;
}
Result DvrClient::stop() {
- if (mTunerDvr != NULL) {
+ if (mTunerDvr != nullptr) {
Status s = mTunerDvr->stop();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDvr != NULL) {
- return mDvr->stop();
- }
-
return Result::INVALID_STATE;
}
Result DvrClient::flush() {
- if (mTunerDvr != NULL) {
+ if (mTunerDvr != nullptr) {
Status s = mTunerDvr->flush();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDvr != NULL) {
- return mDvr->flush();
- }
-
return Result::INVALID_STATE;
}
Result DvrClient::close() {
- if (mDvrMQEventFlag != NULL) {
+ if (mDvrMQEventFlag != nullptr) {
EventFlag::deleteEventFlag(&mDvrMQEventFlag);
}
- mDvrMQ = NULL;
+ mDvrMQ = nullptr;
- if (mTunerDvr != NULL) {
+ if (mTunerDvr != nullptr) {
Status s = mTunerDvr->close();
- mTunerDvr = NULL;
+ mTunerDvr = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mDvr != NULL) {
- Result res = mDvr->close();
- mDvr = NULL;
- return res;
- }
-
return Result::INVALID_STATE;
}
-/////////////// IDvrCallback ///////////////////////
-
-HidlDvrCallback::HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback)
- : mDvrClientCallback(dvrClientCallback) {}
-
-Return<void> HidlDvrCallback::onRecordStatus(const RecordStatus status) {
- if (mDvrClientCallback != NULL) {
- mDvrClientCallback->onRecordStatus(status);
- }
- return Void();
-}
-
-Return<void> HidlDvrCallback::onPlaybackStatus(const PlaybackStatus status) {
- if (mDvrClientCallback != NULL) {
- mDvrClientCallback->onPlaybackStatus(status);
- }
- return Void();
-}
-
/////////////// TunerDvrCallback ///////////////////////
-
TunerDvrCallback::TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback)
: mDvrClientCallback(dvrClientCallback) {}
-Status TunerDvrCallback::onRecordStatus(int status) {
- if (mDvrClientCallback != NULL) {
- mDvrClientCallback->onRecordStatus(static_cast<RecordStatus>(status));
+Status TunerDvrCallback::onRecordStatus(RecordStatus status) {
+ if (mDvrClientCallback != nullptr) {
+ mDvrClientCallback->onRecordStatus(status);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-Status TunerDvrCallback::onPlaybackStatus(int status) {
- if (mDvrClientCallback != NULL) {
- mDvrClientCallback->onPlaybackStatus(static_cast<PlaybackStatus>(status));
+Status TunerDvrCallback::onPlaybackStatus(PlaybackStatus status) {
+ if (mDvrClientCallback != nullptr) {
+ mDvrClientCallback->onPlaybackStatus(status);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-/////////////// DvrClient Helper Methods ///////////////////////
-
-Result DvrClient::getQueueDesc(MQDesc& dvrMQDesc) {
- if (mDvr != NULL) {
- Result res = Result::UNKNOWN_ERROR;
- mDvr->getQueueDesc([&](Result r, const MQDesc& desc) {
- dvrMQDesc = desc;
- res = r;
- });
- return res;
- }
-
- return Result::INVALID_STATE;
-}
-
-TunerDvrSettings DvrClient::getAidlDvrSettingsFromHidl(DvrSettings settings) {
- TunerDvrSettings s;
- switch (settings.getDiscriminator()) {
- case DvrSettings::hidl_discriminator::record: {
- s.statusMask = static_cast<int>(settings.record().statusMask);
- s.lowThreshold = static_cast<int>(settings.record().lowThreshold);
- s.highThreshold = static_cast<int>(settings.record().highThreshold);
- s.dataFormat = static_cast<int>(settings.record().dataFormat);
- s.packetSize = static_cast<int>(settings.record().packetSize);
- return s;
- }
- case DvrSettings::hidl_discriminator::playback: {
- s.statusMask = static_cast<int>(settings.playback().statusMask);
- s.lowThreshold = static_cast<int>(settings.playback().lowThreshold);
- s.highThreshold = static_cast<int>(settings.playback().highThreshold);
- s.dataFormat = static_cast<int>(settings.playback().dataFormat);
- s.packetSize = static_cast<int>(settings.playback().packetSize);
- return s;
- }
- default:
- break;
- }
- return s;
-}
} // namespace android
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
index 252554e7fc9e..9080c7288f76 100644
--- a/media/jni/tuner/DvrClient.h
+++ b/media/jni/tuner/DvrClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,36 +17,29 @@
#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_H_
#define _ANDROID_MEDIA_TV_DVR_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/DvrSettings.h>
+#include <aidl/android/hardware/tv/tuner/Result.h>
#include <aidl/android/media/tv/tuner/BnTunerDvrCallback.h>
#include <aidl/android/media/tv/tuner/ITunerDvr.h>
-#include <android/hardware/tv/tuner/1.0/IDvr.h>
-#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
#include <fmq/AidlMessageQueue.h>
-#include <fmq/MessageQueue.h>
#include "DvrClientCallback.h"
#include "FilterClient.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::hardware::tv::tuner::DvrSettings;
+using ::aidl::android::hardware::tv::tuner::PlaybackStatus;
+using ::aidl::android::hardware::tv::tuner::RecordStatus;
+using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::BnTunerDvrCallback;
using ::aidl::android::media::tv::tuner::ITunerDvr;
-using ::aidl::android::media::tv::tuner::TunerDvrSettings;
-
-using ::android::hardware::EventFlag;
-using ::android::hardware::MQDescriptorSync;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::tv::tuner::V1_0::DvrSettings;
-using ::android::hardware::tv::tuner::V1_0::IDvr;
-using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
using namespace std;
namespace android {
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-using MQDesc = MQDescriptorSync<uint8_t>;
using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
@@ -55,19 +48,8 @@ class TunerDvrCallback : public BnTunerDvrCallback {
public:
TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback);
- Status onRecordStatus(int status);
- Status onPlaybackStatus(int status);
-
-private:
- sp<DvrClientCallback> mDvrClientCallback;
-};
-
-struct HidlDvrCallback : public IDvrCallback {
-
-public:
- HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback);
- virtual Return<void> onRecordStatus(const RecordStatus status);
- virtual Return<void> onPlaybackStatus(const PlaybackStatus status);
+ Status onRecordStatus(RecordStatus status);
+ Status onPlaybackStatus(PlaybackStatus status);
private:
sp<DvrClientCallback> mDvrClientCallback;
@@ -79,33 +61,30 @@ public:
DvrClient(shared_ptr<ITunerDvr> tunerDvr);
~DvrClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlDvr(sp<IDvr> dvr);
-
/**
* Set the DVR file descriptor.
*/
- void setFd(int fd);
+ void setFd(int32_t fd);
/**
* Read data from file with given size. Return the actual read size.
*/
- long readFromFile(long size);
+ int64_t readFromFile(int64_t size);
/**
* Read data from the given buffer with given size. Return the actual read size.
*/
- long readFromBuffer(int8_t* buffer, long size);
+ int64_t readFromBuffer(int8_t* buffer, int64_t size);
/**
* Write data to file with given size. Return the actual write size.
*/
- long writeToFile(long size);
+ int64_t writeToFile(int64_t size);
/**
* Write data to the given buffer with given size. Return the actual write size.
*/
- long writeToBuffer(int8_t* buffer, long size);
+ int64_t writeToBuffer(int8_t* buffer, int64_t size);
/**
* Configure the DVR.
@@ -143,26 +122,16 @@ public:
Result close();
private:
- Result getQueueDesc(MQDesc& dvrMQDesc);
- TunerDvrSettings getAidlDvrSettingsFromHidl(DvrSettings settings);
-
/**
* An AIDL Tuner Dvr Singleton assigned at the first time the Tuner Client
* opens a dvr. Default null when dvr is not opened.
*/
shared_ptr<ITunerDvr> mTunerDvr;
- /**
- * A Dvr HAL interface that is ready before migrating to the TunerDvr.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<IDvr> mDvr;
-
AidlMQ* mDvrMQ;
EventFlag* mDvrMQEventFlag;
string mFilePath;
- int mFd;
+ int32_t mFd;
};
} // namespace android
diff --git a/media/jni/tuner/DvrClientCallback.h b/media/jni/tuner/DvrClientCallback.h
index 66844249cc05..a75f199961a7 100644
--- a/media/jni/tuner/DvrClientCallback.h
+++ b/media/jni/tuner/DvrClientCallback.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,8 +17,12 @@
#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
#define _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
-using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
-using ::android::hardware::tv::tuner::V1_0::RecordStatus;
+#include <aidl/android/hardware/tv/tuner/PlaybackStatus.h>
+#include <aidl/android/hardware/tv/tuner/RecordStatus.h>
+#include <utils/RefBase.h>
+
+using ::aidl::android::hardware::tv::tuner::PlaybackStatus;
+using ::aidl::android::hardware::tv::tuner::RecordStatus;
using namespace std;
@@ -30,4 +34,4 @@ struct DvrClientCallback : public RefBase {
};
} // namespace android
-#endif // _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_ \ No newline at end of file
+#endif // _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 324c09a94b63..d4d8837c554a 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -16,65 +16,43 @@
#define LOG_TAG "FilterClient"
+#include "FilterClient.h"
+
+#include <aidl/android/hardware/tv/tuner/DemuxFilterMainType.h>
+#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
-#include <fmq/ConvertMQDescriptors.h>
#include <utils/Log.h>
-#include "FilterClient.h"
-
-using ::aidl::android::media::tv::tuner::TunerDemuxIpAddressSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterAlpConfiguration;
-using ::aidl::android::media::tv::tuner::TunerFilterIpConfiguration;
-using ::aidl::android::media::tv::tuner::TunerFilterMmtpConfiguration;
-using ::aidl::android::media::tv::tuner::TunerFilterMonitorEvent;
-using ::aidl::android::media::tv::tuner::TunerFilterScIndexMask;
-using ::aidl::android::media::tv::tuner::TunerFilterSectionBits;
-using ::aidl::android::media::tv::tuner::TunerFilterSectionCondition;
-using ::aidl::android::media::tv::tuner::TunerFilterSectionTableInfo;
-using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
-using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
-using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
-using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
-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;
+using ::aidl::android::hardware::common::NativeHandle;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterMainType;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSubType;
+using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxMmtpFilterType;
+using ::aidl::android::hardware::tv::tuner::DemuxQueueNotifyBits;
+using ::aidl::android::hardware::tv::tuner::DemuxTsFilterSettingsFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxTsFilterType;
+using ::aidl::android::hardware::tv::tuner::ScramblingStatus;
namespace android {
-
/////////////// FilterClient ///////////////////////
-
FilterClient::FilterClient(DemuxFilterType type, shared_ptr<ITunerFilter> tunerFilter) {
mTunerFilter = tunerFilter;
- mAvSharedHandle = NULL;
+ mAvSharedHandle = nullptr;
checkIsMediaFilter(type);
}
FilterClient::~FilterClient() {
- mTunerFilter = NULL;
- mFilter = NULL;
- mFilter_1_1 = NULL;
- mAvSharedHandle = NULL;
+ mTunerFilter = nullptr;
+ mAvSharedHandle = nullptr;
mAvSharedMemSize = 0;
mIsMediaFilter = false;
mIsPassthroughFilter = false;
- mFilterMQ = NULL;
- mFilterMQEventFlag = NULL;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void FilterClient::setHidlFilter(sp<IFilter> filter) {
- mFilter = filter;
- mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
+ mFilterMQ = nullptr;
+ mFilterMQEventFlag = nullptr;
}
-int FilterClient::read(int8_t* buffer, int size) {
+int64_t FilterClient::read(int8_t* buffer, int64_t size) {
Result res = getFilterMq();
if (res != Result::SUCCESS) {
return -1;
@@ -85,8 +63,8 @@ int FilterClient::read(int8_t* buffer, int size) {
SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
handleAvShareMemory();
SharedHandleInfo info{
- .sharedHandle = (mIsMediaFilter && !mIsPassthroughFilter) ? mAvSharedHandle : NULL,
- .size = mAvSharedMemSize,
+ .sharedHandle = (mIsMediaFilter && !mIsPassthroughFilter) ? mAvSharedHandle : nullptr,
+ .size = mAvSharedMemSize,
};
return info;
@@ -96,8 +74,8 @@ Result FilterClient::configure(DemuxFilterSettings configure) {
Result res;
checkIsPassthroughFilter(configure);
- if (mTunerFilter != NULL) {
- Status s = mTunerFilter->configure(getAidlFilterSettings(configure));
+ if (mTunerFilter != nullptr) {
+ Status s = mTunerFilter->configure(configure);
res = ClientHelper::getServiceSpecificErrorCode(s);
if (res == Result::SUCCESS) {
getAvSharedHandleInfo();
@@ -105,171 +83,96 @@ Result FilterClient::configure(DemuxFilterSettings configure) {
return res;
}
- if (mFilter != NULL) {
- res = mFilter->configure(configure);
- if (res == Result::SUCCESS) {
- getAvSharedHandleInfo();
- }
- return res;
- }
-
return Result::INVALID_STATE;
}
-Result FilterClient::configureMonitorEvent(int monitorEventType) {
- if (mTunerFilter != NULL) {
+Result FilterClient::configureMonitorEvent(int32_t monitorEventType) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter_1_1 != NULL) {
- return mFilter_1_1->configureMonitorEvent(monitorEventType);
- }
-
return Result::INVALID_STATE;
}
-Result FilterClient::configureIpFilterContextId(int cid) {
- if (mTunerFilter != NULL) {
+Result FilterClient::configureIpFilterContextId(int32_t cid) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->configureIpFilterContextId(cid);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter_1_1 != NULL) {
- return mFilter_1_1->configureIpCid(cid);
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
- if (mTunerFilter != NULL) {
- int type;
- switch (avStreamType.getDiscriminator()) {
- case AvStreamType::hidl_discriminator::audio:
- type = (int)avStreamType.audio();
- break;
- case AvStreamType::hidl_discriminator::video:
- type = (int)avStreamType.video();
- break;
- }
- Status s = mTunerFilter->configureAvStreamType(type);
+ if (mTunerFilter != nullptr) {
+ Status s = mTunerFilter->configureAvStreamType(avStreamType);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter_1_1 != NULL) {
- return mFilter_1_1->configureAvStreamType(avStreamType);
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::start() {
- if (mTunerFilter != NULL) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->start();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- return mFilter->start();
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::stop() {
- if (mTunerFilter != NULL) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->stop();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- return mFilter->stop();
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::flush() {
- if (mTunerFilter != NULL) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->flush();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- return mFilter->flush();
- }
-
return Result::INVALID_STATE;
}
-Result FilterClient::getId(uint32_t& id) {
- if (mTunerFilter != NULL) {
- int32_t id32Bit;
- Status s = mTunerFilter->getId(&id32Bit);
- id = static_cast<uint32_t>(id32Bit);
+Result FilterClient::getId(int32_t& id) {
+ if (mTunerFilter != nullptr) {
+ Status s = mTunerFilter->getId(&id);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- Result res;
- mFilter->getId([&](Result r, uint32_t filterId) {
- res = r;
- id = filterId;
- });
- return res;
- }
-
return Result::INVALID_STATE;
}
-Result FilterClient::getId64Bit(uint64_t& id) {
- if (mTunerFilter != NULL) {
- int64_t id64Bit;
- Status s = mTunerFilter->getId64Bit(&id64Bit);
- id = static_cast<uint64_t>(id64Bit);
+Result FilterClient::getId64Bit(int64_t& id) {
+ if (mTunerFilter != nullptr) {
+ Status s = mTunerFilter->getId64Bit(&id);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter_1_1 != NULL) {
- Result res;
- mFilter_1_1->getId64Bit([&](Result r, uint64_t filterId) {
- res = r;
- id = filterId;
- });
- return res;
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) {
- if (mTunerFilter != NULL) {
- Status s = mTunerFilter->releaseAvHandle(makeToAidl(handle), avDataId);
+ if (mTunerFilter != nullptr) {
+ Status s = mTunerFilter->releaseAvHandle(dupToAidl(handle), avDataId);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- return mFilter->releaseAvHandle(hidl_handle(handle), avDataId);
- }
-
return Result::INVALID_STATE;
}
Result FilterClient::setDataSource(sp<FilterClient> filterClient){
- if (mTunerFilter != NULL) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- sp<IFilter> sourceFilter = filterClient->getHalFilter();
- if (sourceFilter == NULL) {
- return Result::INVALID_ARGUMENT;
- }
- return mFilter->setDataSource(sourceFilter);
- }
-
return Result::INVALID_STATE;
}
@@ -277,648 +180,38 @@ Result FilterClient::close() {
if (mFilterMQEventFlag) {
EventFlag::deleteEventFlag(&mFilterMQEventFlag);
}
- mFilterMQEventFlag = NULL;
- mFilterMQ = NULL;
+ mFilterMQEventFlag = nullptr;
+ mFilterMQ = nullptr;
- if (mTunerFilter != NULL) {
+ if (mTunerFilter != nullptr) {
Status s = mTunerFilter->close();
closeAvSharedMemory();
- mTunerFilter = NULL;
+ mTunerFilter = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFilter != NULL) {
- Result res = mFilter->close();
- mFilter = NULL;
- mFilter_1_1 = NULL;
- closeAvSharedMemory();
- return res;
- }
-
return Result::INVALID_STATE;
}
-/////////////// IFilterCallback ///////////////////////
-
-HidlFilterCallback::HidlFilterCallback(sp<FilterClientCallback> filterClientCallback)
- : mFilterClientCallback(filterClientCallback) {}
-
-Return<void> HidlFilterCallback::onFilterStatus(const DemuxFilterStatus status) {
- if (mFilterClientCallback != NULL) {
- mFilterClientCallback->onFilterStatus(status);
- }
- return Void();
-}
-
-Return<void> HidlFilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
- if (mFilterClientCallback != NULL) {
- mFilterClientCallback->onFilterEvent(filterEvent);
- }
- return Void();
-}
-
-Return<void> HidlFilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
- const DemuxFilterEventExt& filterEventExt) {
- if (mFilterClientCallback != NULL) {
- mFilterClientCallback->onFilterEvent_1_1(filterEvent, filterEventExt);
- }
- return Void();
-}
-
/////////////// TunerFilterCallback ///////////////////////
TunerFilterCallback::TunerFilterCallback(sp<FilterClientCallback> filterClientCallback)
: mFilterClientCallback(filterClientCallback) {}
-Status TunerFilterCallback::onFilterStatus(int status) {
- if (mFilterClientCallback != NULL) {
- mFilterClientCallback->onFilterStatus(static_cast<DemuxFilterStatus>(status));
+Status TunerFilterCallback::onFilterStatus(DemuxFilterStatus status) {
+ if (mFilterClientCallback != nullptr) {
+ mFilterClientCallback->onFilterStatus(status);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-Status TunerFilterCallback::onFilterEvent(const vector<TunerFilterEvent>& filterEvents) {
- if (mFilterClientCallback == NULL) {
- return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
- }
-
- if (filterEvents.size() == 0) {
- return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_ARGUMENT));
- }
-
- DemuxFilterEvent event;
- DemuxFilterEventExt eventExt;
- getHidlFilterEvent(filterEvents, event, eventExt);
- if (eventExt.events.size() > 0) {
- mFilterClientCallback->onFilterEvent_1_1(event, eventExt);
- } else {
- mFilterClientCallback->onFilterEvent(event);
- }
-
- return Status::ok();
-}
-
-/////////////// FilterClient Helper Methods ///////////////////////
-
-TunerFilterConfiguration FilterClient::getAidlFilterSettings(DemuxFilterSettings configure) {
- TunerFilterConfiguration config;
- switch (configure.getDiscriminator()) {
- case DemuxFilterSettings::hidl_discriminator::ts:
- return getAidlTsSettings(configure.ts());
- case DemuxFilterSettings::hidl_discriminator::mmtp:
- return getAidlMmtpSettings(configure.mmtp());
- case DemuxFilterSettings::hidl_discriminator::ip:
- return getAidlIpSettings(configure.ip());
- case DemuxFilterSettings::hidl_discriminator::tlv:
- return getAidlTlvSettings(configure.tlv());
- case DemuxFilterSettings::hidl_discriminator::alp:
- return getAidlAlpSettings(configure.alp());
- default:
- break;
- }
- ALOGE("Wrong DemuxFilterSettings union.");
- return config;
-}
-
-TunerFilterConfiguration FilterClient::getAidlTsSettings(DemuxTsFilterSettings ts) {
- TunerFilterConfiguration config;
- TunerFilterSettings filterSettings;
- switch (ts.filterSettings.getDiscriminator()) {
- case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av: {
- filterSettings.set<TunerFilterSettings::av>(
- getAidlAvSettings(ts.filterSettings.av()));
- break;
- }
- case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::section: {
- filterSettings.set<TunerFilterSettings::section>(
- getAidlSectionSettings(ts.filterSettings.section()));
- break;
- }
- case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::pesData: {
- filterSettings.set<TunerFilterSettings::pesData>(
- getAidlPesDataSettings(ts.filterSettings.pesData()));
- break;
- }
- case DemuxTsFilterSettings::FilterSettings::hidl_discriminator::record: {
- filterSettings.set<TunerFilterSettings::record>(
- getAidlRecordSettings(ts.filterSettings.record()));
- break;
- }
- default:
- filterSettings.set<TunerFilterSettings::nothing>(true);
- break;
- }
-
- TunerFilterTsConfiguration aidlTs{
- .tpid = static_cast<char16_t>(ts.tpid),
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::ts>(aidlTs);
-
- return config;
-}
-
-TunerFilterConfiguration FilterClient::getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp) {
- TunerFilterConfiguration config;
- TunerFilterSettings filterSettings;
- switch (mmtp.filterSettings.getDiscriminator()) {
- case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av: {
- filterSettings.set<TunerFilterSettings::av>(
- getAidlAvSettings(mmtp.filterSettings.av()));
- break;
- }
- case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::section: {
- filterSettings.set<TunerFilterSettings::section>(
- getAidlSectionSettings(mmtp.filterSettings.section()));
- break;
- }
- case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::pesData: {
- filterSettings.set<TunerFilterSettings::pesData>(
- getAidlPesDataSettings(mmtp.filterSettings.pesData()));
- break;
- }
- case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::record: {
- filterSettings.set<TunerFilterSettings::record>(
- getAidlRecordSettings(mmtp.filterSettings.record()));
- break;
- }
- case DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::download: {
- filterSettings.set<TunerFilterSettings::download>(
- getAidlDownloadSettings(mmtp.filterSettings.download()));
- break;
- }
- default:
- filterSettings.set<TunerFilterSettings::nothing>(true);
- break;
- }
-
- TunerFilterMmtpConfiguration aidlMmtp{
- .mmtpPid = static_cast<char16_t>(mmtp.mmtpPid),
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::mmtp>(aidlMmtp);
-
- return config;
-}
-
-TunerFilterConfiguration FilterClient::getAidlIpSettings(DemuxIpFilterSettings ip) {
- TunerFilterConfiguration config;
- TunerFilterSettings filterSettings;
- switch (ip.filterSettings.getDiscriminator()) {
- case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::section: {
- filterSettings.set<TunerFilterSettings::section>(
- getAidlSectionSettings(ip.filterSettings.section()));
- break;
- }
- case DemuxIpFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
- filterSettings.set<TunerFilterSettings::isPassthrough>(
- ip.filterSettings.bPassthrough());
- break;
- }
- default:
- filterSettings.set<TunerFilterSettings::nothing>(true);
- break;
- }
-
- TunerDemuxIpAddressSettings ipAddr{
- .srcPort = static_cast<char16_t>(ip.ipAddr.srcPort),
- .dstPort = static_cast<char16_t>(ip.ipAddr.dstPort),
- };
- getAidlIpAddress(ip.ipAddr, ipAddr.srcIpAddress, ipAddr.dstIpAddress);
-
- TunerFilterIpConfiguration aidlIp{
- .ipAddr = ipAddr,
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::ip>(aidlIp);
-
- return config;
-}
-
-void FilterClient::getAidlIpAddress(DemuxIpAddress ipAddr,
- TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress) {
- switch (ipAddr.srcIpAddress.getDiscriminator()) {
- case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v4: {
- int size = ipAddr.srcIpAddress.v4().size();
- srcIpAddress.isIpV6 = false;
- srcIpAddress.addr.resize(size);
- copy(&ipAddr.srcIpAddress.v4()[0], &ipAddr.srcIpAddress.v4()[size],
- srcIpAddress.addr.begin());
- break;
- }
- case DemuxIpAddress::SrcIpAddress::hidl_discriminator::v6: {
- int size = ipAddr.srcIpAddress.v6().size();
- srcIpAddress.isIpV6 = true;
- srcIpAddress.addr.resize(size);
- copy(&ipAddr.srcIpAddress.v6()[0], &ipAddr.srcIpAddress.v6()[size],
- srcIpAddress.addr.begin());
- break;
- }
- }
- switch (ipAddr.dstIpAddress.getDiscriminator()) {
- case DemuxIpAddress::DstIpAddress::hidl_discriminator::v4: {
- int size = ipAddr.dstIpAddress.v4().size();
- dstIpAddress.isIpV6 = false;
- dstIpAddress.addr.resize(size);
- copy(&ipAddr.dstIpAddress.v4()[0], &ipAddr.dstIpAddress.v4()[size],
- dstIpAddress.addr.begin());
- break;
- }
- case DemuxIpAddress::DstIpAddress::hidl_discriminator::v6: {
- int size = ipAddr.dstIpAddress.v6().size();
- dstIpAddress.isIpV6 = true;
- dstIpAddress.addr.resize(size);
- copy(&ipAddr.dstIpAddress.v6()[0], &ipAddr.dstIpAddress.v6()[size],
- dstIpAddress.addr.begin());
- break;
- }
- }
-}
-
-TunerFilterConfiguration FilterClient::getAidlTlvSettings(DemuxTlvFilterSettings tlv) {
- TunerFilterConfiguration config;
- TunerFilterSettings filterSettings;
- switch (tlv.filterSettings.getDiscriminator()) {
- case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::section: {
- filterSettings.set<TunerFilterSettings::section>(
- getAidlSectionSettings(tlv.filterSettings.section()));
- break;
- }
- case DemuxTlvFilterSettings::FilterSettings::hidl_discriminator::bPassthrough: {
- filterSettings.set<TunerFilterSettings::isPassthrough>(
- tlv.filterSettings.bPassthrough());
- break;
- }
- default:
- filterSettings.set<TunerFilterSettings::nothing>(true);
- break;
- }
-
- TunerFilterTlvConfiguration aidlTlv{
- .packetType = static_cast<int8_t>(tlv.packetType),
- .isCompressedIpPacket = tlv.isCompressedIpPacket,
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::tlv>(aidlTlv);
-
- return config;
-}
-
-TunerFilterConfiguration FilterClient::getAidlAlpSettings(DemuxAlpFilterSettings alp) {
- TunerFilterConfiguration config;
- TunerFilterSettings filterSettings;
- switch (alp.filterSettings.getDiscriminator()) {
- case DemuxAlpFilterSettings::FilterSettings::hidl_discriminator::section: {
- filterSettings.set<TunerFilterSettings::section>(
- getAidlSectionSettings(alp.filterSettings.section()));
- break;
- }
- default:
- filterSettings.set<TunerFilterSettings::nothing>(true);
- break;
- }
-
- TunerFilterAlpConfiguration aidlAlp{
- .packetType = static_cast<int8_t>(alp.packetType),
- .lengthType = static_cast<int8_t>(alp.lengthType),
- .filterSettings = filterSettings,
- };
- config.set<TunerFilterConfiguration::alp>(aidlAlp);
-
- return config;
-}
-
-TunerFilterAvSettings FilterClient::getAidlAvSettings(DemuxFilterAvSettings hidlAv) {
- TunerFilterAvSettings aidlAv{
- .isPassthrough = hidlAv.isPassthrough,
- };
- return aidlAv;
-}
-
-TunerFilterSectionSettings FilterClient::getAidlSectionSettings(
- DemuxFilterSectionSettings hidlSection) {
- TunerFilterSectionSettings aidlSection;
-
- switch (hidlSection.condition.getDiscriminator()) {
- case DemuxFilterSectionSettings::Condition::hidl_discriminator::sectionBits: {
- TunerFilterSectionBits sectionBits;
- auto hidlSectionBits = hidlSection.condition.sectionBits();
- sectionBits.filter.resize(hidlSectionBits.filter.size());
- sectionBits.mask.resize(hidlSectionBits.mask.size());
- sectionBits.mode.resize(hidlSectionBits.mode.size());
- copy(hidlSectionBits.filter.begin(), hidlSectionBits.filter.end(),
- sectionBits.filter.begin());
- copy(hidlSectionBits.mask.begin(), hidlSectionBits.mask.end(),
- sectionBits.mask.begin());
- copy(hidlSectionBits.mode.begin(), hidlSectionBits.mode.end(),
- sectionBits.mode.begin());
- aidlSection.condition.set<TunerFilterSectionCondition::sectionBits>(sectionBits);
- break;
- }
- case DemuxFilterSectionSettings::Condition::hidl_discriminator::tableInfo: {
- TunerFilterSectionTableInfo tableInfo{
- .tableId = static_cast<char16_t>(hidlSection.condition.tableInfo().tableId),
- .version = static_cast<char16_t>(hidlSection.condition.tableInfo().version),
- };
- aidlSection.condition.set<TunerFilterSectionCondition::tableInfo>(tableInfo);
- break;
- }
- }
- aidlSection.isCheckCrc = hidlSection.isCheckCrc;
- aidlSection.isRepeat = hidlSection.isRepeat;
- aidlSection.isRaw = hidlSection.isRaw;
- return aidlSection;
-}
-
-TunerFilterPesDataSettings FilterClient::getAidlPesDataSettings(
- DemuxFilterPesDataSettings hidlPesData) {
- TunerFilterPesDataSettings aidlPesData{
- .streamId = static_cast<char16_t>(hidlPesData.streamId),
- .isRaw = hidlPesData.isRaw,
- };
- return aidlPesData;
-}
-
-TunerFilterRecordSettings FilterClient::getAidlRecordSettings(
- DemuxFilterRecordSettings hidlRecord) {
- TunerFilterScIndexMask mask;
- switch (hidlRecord.scIndexMask.getDiscriminator()) {
- case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::sc: {
- mask.set<TunerFilterScIndexMask::sc>(hidlRecord.scIndexMask.sc());
- break;
- }
- case DemuxFilterRecordSettings::ScIndexMask::hidl_discriminator::scHevc: {
- mask.set<TunerFilterScIndexMask::scHevc>(hidlRecord.scIndexMask.scHevc());
- break;
- }
- default:
- break;
- }
- TunerFilterRecordSettings aidlRecord{
- .tsIndexMask = static_cast<int32_t>(hidlRecord.tsIndexMask),
- .scIndexType = static_cast<int32_t>(hidlRecord.scIndexType),
- .scIndexMask = mask,
- };
- return aidlRecord;
-}
-
-TunerFilterDownloadSettings FilterClient::getAidlDownloadSettings(
- DemuxFilterDownloadSettings hidlDownload) {
- TunerFilterDownloadSettings aidlDownload{
- .downloadId = static_cast<int32_t>(hidlDownload.downloadId),
- };
- return aidlDownload;
-}
-
-void TunerFilterCallback::getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
- switch (filterEvents[0].getTag()) {
- case TunerFilterEvent::media: {
- getHidlMediaEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::section: {
- getHidlSectionEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::pes: {
- getHidlPesEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::tsRecord: {
- getHidlTsRecordEvent(filterEvents, event, eventExt);
- break;
- }
- case TunerFilterEvent::mmtpRecord: {
- getHidlMmtpRecordEvent(filterEvents, event, eventExt);
- break;
- }
- case TunerFilterEvent::download: {
- getHidlDownloadEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::ipPayload: {
- getHidlIpPayloadEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::temi: {
- getHidlTemiEvent(filterEvents, event);
- break;
- }
- case TunerFilterEvent::monitor: {
- getHidlMonitorEvent(filterEvents, eventExt);
- break;
- }
- case TunerFilterEvent::startId: {
- getHidlRestartEvent(filterEvents, eventExt);
- break;
- }
- }
-}
-
-void TunerFilterCallback::getHidlMediaEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
- .get<TunerFilterEvent::media>().avMemory));
- event.events[i].media({
- .avMemory = handle,
- .streamId = static_cast<DemuxStreamId>(filterEvents[i]
- .get<TunerFilterEvent::media>().streamId),
- .isPtsPresent = filterEvents[i]
- .get<TunerFilterEvent::media>().isPtsPresent,
- .pts = static_cast<uint64_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().pts),
- .dataLength = static_cast<uint32_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().dataLength),
- .offset = static_cast<uint32_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().offset),
- .isSecureMemory = filterEvents[i]
- .get<TunerFilterEvent::media>().isSecureMemory,
- .avDataId = static_cast<uint64_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().avDataId),
- .mpuSequenceNumber = static_cast<uint32_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().offset),
- .isPesPrivateData = filterEvents[i]
- .get<TunerFilterEvent::media>().isPesPrivateData,
- });
-
- if (filterEvents[i].get<TunerFilterEvent::media>().isAudioExtraMetaData) {
- event.events[i].media().extraMetaData.audio({
- .adFade = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.adFade),
- .adPan = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.adPan),
- .versionTextTag = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.versionTextTag),
- .adGainCenter = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.adGainCenter),
- .adGainFront = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.adGainFront),
- .adGainSurround = static_cast<uint8_t>(filterEvents[i]
- .get<TunerFilterEvent::media>().audio.adGainSurround),
- });
- } else {
- event.events[i].media().extraMetaData.noinit();
- }
- }
-}
-
-void TunerFilterCallback::getHidlSectionEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto section = filterEvents[i].get<TunerFilterEvent::section>();
- event.events[i].section({
- .tableId = static_cast<uint16_t>(section.tableId),
- .version = static_cast<uint16_t>(section.version),
- .sectionNum = static_cast<uint16_t>(section.sectionNum),
- .dataLength = static_cast<uint16_t>(section.dataLength),
- });
- }
-}
-
-void TunerFilterCallback::getHidlPesEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
- event.events[i].pes({
- .streamId = static_cast<DemuxStreamId>(pes.streamId),
- .dataLength = static_cast<uint16_t>(pes.dataLength),
- .mpuSequenceNumber = static_cast<uint32_t>(pes.mpuSequenceNumber),
- });
- }
-}
-
-void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
- event.events.resize(filterEvents.size());
- eventExt.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
- event.events[i].tsRecord({
- .tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
- .byteNumber = static_cast<uint64_t>(ts.byteNumber),
- });
- event.events[i].tsRecord().pid.tPid(static_cast<DemuxTpid>(ts.pid));
-
- switch (ts.scIndexMask.getTag()) {
- case TunerFilterScIndexMask::sc: {
- event.events[i].tsRecord().scIndexMask.sc(
- ts.scIndexMask.get<TunerFilterScIndexMask::sc>());
- break;
- }
- case TunerFilterScIndexMask::scHevc: {
- event.events[i].tsRecord().scIndexMask.scHevc(
- ts.scIndexMask.get<TunerFilterScIndexMask::scHevc>());
- break;
- }
- default:
- break;
- }
-
- if (ts.isExtended) {
- eventExt.events[i].tsRecord({
- .pts = static_cast<uint64_t>(ts.pts),
- .firstMbInSlice = static_cast<uint32_t>(ts.firstMbInSlice),
- });
- } else {
- eventExt.events[i].noinit();
- }
- }
-}
-
-void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
- event.events.resize(filterEvents.size());
- eventExt.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
- event.events[i].mmtpRecord({
- .scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
- .byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
- });
-
- if (mmtp.isExtended) {
- eventExt.events[i].mmtpRecord({
- .pts = static_cast<uint64_t>(mmtp.pts),
- .mpuSequenceNumber = static_cast<uint32_t>(mmtp.mpuSequenceNumber),
- .firstMbInSlice = static_cast<uint32_t>(mmtp.firstMbInSlice),
- .tsIndexMask = static_cast<uint32_t>(mmtp.tsIndexMask),
- });
- } else {
- eventExt.events[i].noinit();
- }
- }
-}
-
-void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto download = filterEvents[i].get<TunerFilterEvent::download>();
- event.events[i].download({
- .itemId = static_cast<uint32_t>(download.itemId),
- .mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
- .itemFragmentIndex = static_cast<uint32_t>(download.itemFragmentIndex),
- .lastItemFragmentIndex = static_cast<uint32_t>(download.lastItemFragmentIndex),
- .dataLength = static_cast<uint16_t>(download.dataLength),
- });
- }
-}
-
-void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
- event.events[i].ipPayload({
- .dataLength = static_cast<uint16_t>(ip.dataLength),
- });
- }
-}
-
-void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event) {
- event.events.resize(filterEvents.size());
- for (int i = 0; i < filterEvents.size(); i++) {
- auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
- event.events[i].temi({
- .pts = static_cast<uint64_t>(temi.pts),
- .descrTag = static_cast<uint8_t>(temi.descrTag),
- });
- hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
- event.events[i].temi().descrData = descrData;
- }
-}
-
-void TunerFilterCallback::getHidlMonitorEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEventExt& eventExt) {
- auto monitor = filterEvents[0].get<TunerFilterEvent::monitor>();
- eventExt.events.resize(1);
- DemuxFilterMonitorEvent monitorEvent;
- switch (monitor.getTag()) {
- case TunerFilterMonitorEvent::scramblingStatus: {
- monitorEvent.scramblingStatus(static_cast<ScramblingStatus>(monitor.scramblingStatus));
- eventExt.events[0].monitorEvent(monitorEvent);
- break;
- }
- case TunerFilterMonitorEvent::cid: {
- monitorEvent.cid(static_cast<uint32_t>(monitor.cid));
- eventExt.events[0].monitorEvent(monitorEvent);
- break;
- }
+Status TunerFilterCallback::onFilterEvent(const vector<DemuxFilterEvent>& filterEvents) {
+ if (mFilterClientCallback != nullptr) {
+ mFilterClientCallback->onFilterEvent(filterEvents);
+ return Status::ok();
}
-}
-
-void TunerFilterCallback::getHidlRestartEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEventExt& eventExt) {
- uint32_t startId = filterEvents[0].get<TunerFilterEvent::startId>();
- eventExt.events.resize(1);
- eventExt.events[0].startId(static_cast<uint32_t>(startId));
+ return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
Result FilterClient::getFilterMq() {
@@ -931,39 +224,21 @@ Result FilterClient::getFilterMq() {
if (mTunerFilter != NULL) {
Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
- res = ClientHelper::getServiceSpecificErrorCode(s);
- if (res == Result::SUCCESS) {
+ if (s.isOk()) {
mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc, false/*resetPointer*/);
EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
}
- return res;
- }
-
- if (mFilter != NULL) {
- MQDescriptorSync<uint8_t> filterMQDesc;
- mFilter->getQueueDesc(
- [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
- filterMQDesc = desc;
- res = r;
- });
- if (res == Result::SUCCESS) {
- AidlMQDesc aidlMQDesc;
- unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
- filterMQDesc, &aidlMQDesc);
- mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc, false/*resetPointer*/);
- EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
- }
}
return res;
}
-int FilterClient::copyData(int8_t* buffer, int size) {
- if (mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
+int64_t FilterClient::copyData(int8_t* buffer, int64_t size) {
+ if (mFilterMQ == nullptr || mFilterMQEventFlag == nullptr) {
return -1;
}
- int available = mFilterMQ->availableToRead();
+ int64_t available = mFilterMQ->availableToRead();
size = min(size, available);
if (mFilterMQ->read(buffer, size)) {
@@ -977,16 +252,18 @@ int FilterClient::copyData(int8_t* buffer, int size) {
void FilterClient::checkIsMediaFilter(DemuxFilterType type) {
if (type.mainType == DemuxFilterMainType::MMTP) {
- if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO ||
- type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) {
+ if (type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>() ==
+ DemuxMmtpFilterType::AUDIO ||
+ type.subType.get<DemuxFilterSubType::Tag::mmtpFilterType>() ==
+ DemuxMmtpFilterType::VIDEO) {
mIsMediaFilter = true;
return;
}
}
if (type.mainType == DemuxFilterMainType::TS) {
- if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO ||
- type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) {
+ if (type.subType.get<DemuxFilterSubType::Tag::tsFilterType>() == DemuxTsFilterType::AUDIO ||
+ type.subType.get<DemuxFilterSubType::Tag::tsFilterType>() == DemuxTsFilterType::VIDEO) {
mIsMediaFilter = true;
return;
}
@@ -1001,15 +278,19 @@ void FilterClient::checkIsPassthroughFilter(DemuxFilterSettings configure) {
return;
}
- if (configure.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts) {
- if (configure.ts().filterSettings.av().isPassthrough) {
+ if (configure.getTag() == DemuxFilterSettings::Tag::ts) {
+ if (configure.get<DemuxFilterSettings::Tag::ts>()
+ .filterSettings.get<DemuxTsFilterSettingsFilterSettings::Tag::av>()
+ .isPassthrough) {
mIsPassthroughFilter = true;
return;
}
}
- if (configure.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::mmtp) {
- if (configure.mmtp().filterSettings.av().isPassthrough) {
+ if (configure.getTag() == DemuxFilterSettings::Tag::mmtp) {
+ if (configure.get<DemuxFilterSettings::Tag::mmtp>()
+ .filterSettings.get<DemuxMmtpFilterSettingsFilterSettings::Tag::av>()
+ .isPassthrough) {
mIsPassthroughFilter = true;
return;
}
@@ -1019,37 +300,28 @@ void FilterClient::checkIsPassthroughFilter(DemuxFilterSettings configure) {
}
void FilterClient::handleAvShareMemory() {
- if (mAvSharedHandle != NULL) {
+ if (mAvSharedHandle != nullptr) {
return;
}
- if (mTunerFilter != NULL && mIsMediaFilter && !mIsPassthroughFilter) {
- TunerFilterSharedHandleInfo aidlHandleInfo;
- Status s = mTunerFilter->getAvSharedHandleInfo(&aidlHandleInfo);
- if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
- mAvSharedHandle = native_handle_clone(makeFromAidl(aidlHandleInfo.handle));
- mAvSharedMemSize = aidlHandleInfo.size;
+ if (mTunerFilter != nullptr && mIsMediaFilter && !mIsPassthroughFilter) {
+ int64_t size;
+ NativeHandle avMemory;
+ Status s = mTunerFilter->getAvSharedHandle(&avMemory, &size);
+ if (s.isOk()) {
+ mAvSharedHandle = dupFromAidl(avMemory);
+ mAvSharedMemSize = size;
}
- return;
- }
-
- if (mFilter_1_1 != NULL && mIsMediaFilter && !mIsPassthroughFilter) {
- mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
- if (r == Result::SUCCESS) {
- mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle());
- mAvSharedMemSize = avMemSize;
- }
- });
}
}
void FilterClient::closeAvSharedMemory() {
- if (mAvSharedHandle == NULL) {
+ if (mAvSharedHandle == nullptr) {
mAvSharedMemSize = 0;
return;
}
native_handle_close(mAvSharedHandle);
native_handle_delete(mAvSharedHandle);
mAvSharedMemSize = 0;
- mAvSharedHandle = NULL;
+ mAvSharedHandle = nullptr;
}
} // namespace android
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 5d78bfdb2349..136d1f58b205 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,64 +17,30 @@
#ifndef _ANDROID_MEDIA_TV_FILTER_CLIENT_H_
#define _ANDROID_MEDIA_TV_FILTER_CLIENT_H_
-#include <aidl/android/media/tv/tuner/ITunerFilter.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterType.h>
#include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h>
-#include <aidl/android/media/tv/tuner/TunerFilterEvent.h>
-#include <aidl/android/media/tv/tuner/TunerFilterSettings.h>
-#include <aidlcommonsupport/NativeHandle.h>
-#include <android/hardware/tv/tuner/1.1/IFilter.h>
-#include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
+#include <aidl/android/media/tv/tuner/ITunerFilter.h>
#include <fmq/AidlMessageQueue.h>
-#include <fmq/MessageQueue.h>
#include "ClientHelper.h"
#include "FilterClientCallback.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::MQDescriptor;
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using ::aidl::android::hardware::tv::tuner::AvStreamType;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterSettings;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterStatus;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterType;
using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
using ::aidl::android::media::tv::tuner::ITunerFilter;
-using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
-using ::aidl::android::media::tv::tuner::TunerFilterAvSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterConfiguration;
-using ::aidl::android::media::tv::tuner::TunerFilterDownloadSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterEvent;
-using ::aidl::android::media::tv::tuner::TunerFilterPesDataSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterRecordSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterSectionSettings;
-using ::aidl::android::media::tv::tuner::TunerFilterSettings;
-
using ::android::hardware::EventFlag;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::MQDescriptorSync;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_handle;
-using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
-using ::android::hardware::tv::tuner::V1_0::DemuxIpAddress;
-using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
-using ::android::hardware::tv::tuner::V1_0::IFilter;
-using ::android::hardware::tv::tuner::V1_0::Result;
-using ::android::hardware::tv::tuner::V1_1::AvStreamType;
-using ::android::hardware::tv::tuner::V1_1::IFilterCallback;
using namespace std;
namespace android {
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-using MQDesc = MQDescriptorSync<uint8_t>;
using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
@@ -84,47 +50,10 @@ struct SharedHandleInfo {
};
class TunerFilterCallback : public BnTunerFilterCallback {
-
public:
TunerFilterCallback(sp<FilterClientCallback> filterClientCallback);
- Status onFilterStatus(int status);
- Status onFilterEvent(const vector<TunerFilterEvent>& filterEvents);
-
-private:
- void getHidlFilterEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
- void getHidlMediaEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlSectionEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlPesEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
- void getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
- DemuxFilterEvent& event, DemuxFilterEventExt& eventExt);
- void getHidlDownloadEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlIpPayloadEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlTemiEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event);
- void getHidlMonitorEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
- void getHidlRestartEvent(
- const vector<TunerFilterEvent>& filterEvents, DemuxFilterEventExt& eventExt);
-
- sp<FilterClientCallback> mFilterClientCallback;
-};
-
-struct HidlFilterCallback : public IFilterCallback {
-
-public:
- HidlFilterCallback(sp<FilterClientCallback> filterClientCallback);
- virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
- const DemuxFilterEventExt& filterEventExt);
- virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent);
- virtual Return<void> onFilterStatus(const DemuxFilterStatus status);
+ Status onFilterStatus(DemuxFilterStatus status);
+ Status onFilterEvent(const vector<DemuxFilterEvent>& filterEvents);
private:
sp<FilterClientCallback> mFilterClientCallback;
@@ -136,15 +65,12 @@ public:
FilterClient(DemuxFilterType type, shared_ptr<ITunerFilter> tunerFilter);
~FilterClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlFilter(sp<IFilter> filter);
-
/**
* Read size of data from filter FMQ into buffer.
*
* @return the actual reading size. -1 if failed to read.
*/
- int read(int8_t* buffer, int size);
+ int64_t read(int8_t* buffer, int64_t size);
/**
* Get the a/v shared memory handle information
@@ -159,12 +85,12 @@ public:
/**
* Configure the monitor event of the Filter.
*/
- Result configureMonitorEvent(int monitorEventType);
+ Result configureMonitorEvent(int32_t monitorEventType);
/**
* Configure the context id of the IP Filter.
*/
- Result configureIpFilterContextId(int cid);
+ Result configureIpFilterContextId(int32_t cid);
/**
* Configure the stream type of the media Filter.
@@ -189,12 +115,12 @@ public:
/**
* Get the 32-bit filter Id.
*/
- Result getId(uint32_t& id);
+ Result getId(int32_t& id);
/**
* Get the 64-bit filter Id.
*/
- Result getId64Bit(uint64_t& id);
+ Result getId64Bit(int64_t& id);
/**
* Release the handle reported by the HAL for AV memory.
@@ -207,11 +133,6 @@ public:
Result setDataSource(sp<FilterClient> filterClient);
/**
- * Get the Hal filter to build up filter linkage.
- */
- sp<IFilter> getHalFilter() { return mFilter; }
-
- /**
* Get the Aidl filter to build up filter linkage.
*/
shared_ptr<ITunerFilter> getAidlFilter() { return mTunerFilter; }
@@ -222,24 +143,8 @@ public:
Result close();
private:
- TunerFilterConfiguration getAidlFilterSettings(DemuxFilterSettings configure);
-
- TunerFilterConfiguration getAidlTsSettings(DemuxTsFilterSettings configure);
- TunerFilterConfiguration getAidlMmtpSettings(DemuxMmtpFilterSettings mmtp);
- TunerFilterConfiguration getAidlIpSettings(DemuxIpFilterSettings ip);
- TunerFilterConfiguration getAidlTlvSettings(DemuxTlvFilterSettings tlv);
- TunerFilterConfiguration getAidlAlpSettings(DemuxAlpFilterSettings alp);
-
- TunerFilterAvSettings getAidlAvSettings(DemuxFilterAvSettings hidlAv);
- TunerFilterSectionSettings getAidlSectionSettings(DemuxFilterSectionSettings hidlSection);
- TunerFilterPesDataSettings getAidlPesDataSettings(DemuxFilterPesDataSettings hidlPesData);
- TunerFilterRecordSettings getAidlRecordSettings(DemuxFilterRecordSettings hidlRecord);
- TunerFilterDownloadSettings getAidlDownloadSettings(DemuxFilterDownloadSettings hidlDownload);
-
- void getAidlIpAddress(DemuxIpAddress ipAddr,
- TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
Result getFilterMq();
- int copyData(int8_t* buffer, int size);
+ int64_t copyData(int8_t* buffer, int64_t size);
void checkIsMediaFilter(DemuxFilterType type);
void checkIsPassthroughFilter(DemuxFilterSettings configure);
void handleAvShareMemory();
@@ -251,22 +156,8 @@ private:
*/
shared_ptr<ITunerFilter> mTunerFilter;
- /**
- * A 1.0 Filter HAL interface that is ready before migrating to the TunerFilter.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<IFilter> mFilter;
-
- /**
- * A 1.1 Filter HAL interface that is ready before migrating to the TunerFilter.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
-
- AidlMQ* mFilterMQ = NULL;
- EventFlag* mFilterMQEventFlag = NULL;
+ AidlMQ* mFilterMQ = nullptr;
+ EventFlag* mFilterMQEventFlag = nullptr;
native_handle_t* mAvSharedHandle;
uint64_t mAvSharedMemSize;
diff --git a/media/jni/tuner/FilterClientCallback.h b/media/jni/tuner/FilterClientCallback.h
index 94b7821a8db7..05e7ff08f68e 100644
--- a/media/jni/tuner/FilterClientCallback.h
+++ b/media/jni/tuner/FilterClientCallback.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,20 +17,21 @@
#ifndef _ANDROID_MEDIA_TV_FILTER_CLIENT_CALLBACK_H_
#define _ANDROID_MEDIA_TV_FILTER_CLIENT_CALLBACK_H_
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
-using ::android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
-using ::android::hardware::tv::tuner::V1_1::DemuxFilterEventExt;
+#include <aidl/android/hardware/tv/tuner/DemuxFilterEvent.h>
+#include <aidl/android/hardware/tv/tuner/DemuxFilterStatus.h>
+#include <utils/RefBase.h>
+
+using ::aidl::android::hardware::tv::tuner::DemuxFilterEvent;
+using ::aidl::android::hardware::tv::tuner::DemuxFilterStatus;
using namespace std;
namespace android {
struct FilterClientCallback : public RefBase {
- virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent,
- const DemuxFilterEventExt& filterEventExt);
- virtual void onFilterEvent(const DemuxFilterEvent& filterEvent);
+ virtual void onFilterEvent(const vector<DemuxFilterEvent>& filterEvents);
virtual void onFilterStatus(const DemuxFilterStatus status);
};
} // namespace android
-#endif // _ANDROID_MEDIA_TV_FILTER_CLIENT_CALLBACK_H_ \ No newline at end of file
+#endif // _ANDROID_MEDIA_TV_FILTER_CLIENT_CALLBACK_H_
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 5d9b12d59482..70309a087163 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -16,1235 +16,168 @@
#define LOG_TAG "FrontendClient"
-#include <android-base/logging.h>
-#include <utils/Log.h>
-
#include "FrontendClient.h"
-using ::aidl::android::media::tv::tuner::TunerFrontendScanAtsc3PlpInfo;
-using ::aidl::android::media::tv::tuner::TunerFrontendUnionSettings;
+#include <aidl/android/hardware/tv/tuner/Constant.h>
+#include <android-base/logging.h>
+#include <utils/Log.h>
-using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
-using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
-using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
-using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
-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;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
-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;
+using ::aidl::android::hardware::tv::tuner::Constant;
namespace android {
-
/////////////// FrontendClient ///////////////////////
-
-FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type) {
+FrontendClient::FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, FrontendType type) {
mTunerFrontend = tunerFrontend;
mType = type;
}
FrontendClient::~FrontendClient() {
- mTunerFrontend = NULL;
- mFrontend = NULL;
- mFrontend_1_1 = NULL;
- mId = -1;
- mType = -1;
+ mTunerFrontend = nullptr;
+ mType = FrontendType::UNDEFINED;
}
Result FrontendClient::setCallback(sp<FrontendClientCallback> frontendClientCallback) {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
shared_ptr<TunerFrontendCallback> aidlCallback =
::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
- aidlCallback->setFrontendType(mType);
Status s = mTunerFrontend->setCallback(aidlCallback);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- sp<HidlFrontendCallback> hidlCallback = new HidlFrontendCallback(frontendClientCallback);
- return mFrontend->setCallback(hidlCallback);
-}
-
-void FrontendClient::setHidlFrontend(sp<IFrontend> frontend) {
- mFrontend = 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;
+ return Result::INVALID_STATE;
}
-Result FrontendClient::tune(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- if (mTunerFrontend != NULL) {
- TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
- Status s = mTunerFrontend->tune(tunerFeSettings);
+Result FrontendClient::tune(const FrontendSettings& settings) {
+ if (mTunerFrontend != nullptr) {
+ Status s = mTunerFrontend->tune(settings);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- Result result;
- if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
- result = mFrontend_1_1->tune_1_1(settings, settingsExt1_1);
- return result;
- }
-
- if (mFrontend != NULL) {
- result = mFrontend->tune(settings);
- return result;
- }
-
return Result::INVALID_STATE;
}
Result FrontendClient::stopTune() {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->stopTune();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend != NULL) {
- Result result = mFrontend->stopTune();
- return result;
- }
-
return Result::INVALID_STATE;
}
-Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- if (mTunerFrontend != NULL) {
- TunerFrontendSettings tunerFeSettings = getAidlFrontendSettings(settings, settingsExt1_1);
- Status s = mTunerFrontend->scan(tunerFeSettings, (int)type);
+Result FrontendClient::scan(const FrontendSettings& settings, FrontendScanType type) {
+ if (mTunerFrontend != nullptr) {
+ Status s = mTunerFrontend->scan(settings, type);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- Result result;
- if (mFrontend_1_1 != NULL && validateExtendedSettings(settingsExt1_1)) {
- result = mFrontend_1_1->scan_1_1(settings, type, settingsExt1_1);
- return result;
- }
-
- if (mFrontend != NULL) {
- result = mFrontend->scan(settings, type);
- return result;
- }
-
return Result::INVALID_STATE;
}
Result FrontendClient::stopScan() {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->stopScan();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend != NULL) {
- Result result = mFrontend->stopScan();
- return result;
- }
-
return Result::INVALID_STATE;
}
vector<FrontendStatus> FrontendClient::getStatus(vector<FrontendStatusType> statusTypes) {
vector<FrontendStatus> status;
- if (mTunerFrontend != NULL) {
- vector<TunerFrontendStatus> aidlStatus;
- vector<int> types;
- for (auto t : statusTypes) {
- types.push_back((int)t);
- }
- Status s = mTunerFrontend->getStatus(types, &aidlStatus);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return status;
- }
- return getHidlStatus(aidlStatus);
- }
-
- if (mFrontend != NULL && statusTypes.size() > 0) {
- Result res;
- mFrontend->getStatus(statusTypes,
- [&](Result r, const hidl_vec<FrontendStatus>& s) {
- res = r;
- status = s;
- });
- if (res != Result::SUCCESS) {
- status.clear();
- return status;
- }
- }
-
- return status;
-}
-
-vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
- vector<FrontendStatusTypeExt1_1> statusTypes) {
- vector<FrontendStatusExt1_1> status;
-
- if (mTunerFrontend != NULL) {
- vector<TunerFrontendStatus> aidlStatus;
- vector<int> types;
- for (auto t : statusTypes) {
- types.push_back((int)t);
- }
- Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return status;
- }
- return getHidlStatusExt(aidlStatus);
- }
-
- if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
- Result res;
- mFrontend_1_1->getStatusExt1_1(statusTypes,
- [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) {
- res = r;
- status = s;
- });
- if (res != Result::SUCCESS) {
- status.clear();
- return status;
- }
+ if (mTunerFrontend != nullptr) {
+ mTunerFrontend->getStatus(statusTypes, &status);
}
return status;
}
Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb());
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend != NULL) {
- Result result = mFrontend->setLnb(lnbClient->getId());
- return result;
- }
-
return Result::INVALID_STATE;
}
Result FrontendClient::setLna(bool bEnable) {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->setLna(bEnable);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend != NULL) {
- Result result = mFrontend->setLna(bEnable);
- return result;
- }
-
return Result::INVALID_STATE;
}
-int FrontendClient::linkCiCamToFrontend(int ciCamId) {
- int ltsId = (int)Constant::INVALID_LTS_ID;
+int32_t FrontendClient::linkCiCamToFrontend(int32_t ciCamId) {
+ int32_t ltsId = static_cast<int32_t>(Constant::INVALID_LTS_ID);
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
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) {
- Result res;
- mFrontend_1_1->linkCiCam(static_cast<uint32_t>(ciCamId),
- [&](Result r, uint32_t id) {
- res = r;
- ltsId = id;
- });
- if (res != Result::SUCCESS) {
- return (int)Constant::INVALID_LTS_ID;
+ if (!s.isOk()) {
+ return static_cast<int32_t>(Constant::INVALID_LTS_ID);
}
}
return ltsId;
}
-Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
- if (mTunerFrontend != NULL) {
+Result FrontendClient::unlinkCiCamToFrontend(int32_t ciCamId) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend_1_1 != NULL) {
- return mFrontend_1_1->unlinkCiCam(static_cast<uint32_t>(ciCamId));
- }
-
return Result::INVALID_STATE;
}
Result FrontendClient::close() {
- if (mTunerFrontend != NULL) {
+ if (mTunerFrontend != nullptr) {
Status s = mTunerFrontend->close();
- mTunerFrontend = NULL;
+ mTunerFrontend = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mFrontend != NULL) {
- Result result = mFrontend->close();
- mFrontend = NULL;
- mFrontend_1_1 = NULL;
- return result;
- }
-
return Result::INVALID_STATE;
}
-/////////////// TunerFrontend Helper Methods ///////////////////////
-
shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
return mTunerFrontend;
}
-int FrontendClient::getId() {
- 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();
- switch (s.getTag()) {
- case TunerFrontendStatus::isDemodLocked: {
- status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::snr: {
- status.snr(s.get<TunerFrontendStatus::snr>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::ber: {
- status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::per: {
- status.per((uint32_t)s.get<TunerFrontendStatus::per>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::preBer: {
- status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::signalQuality: {
- status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::signalStrength: {
- status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::symbolRate: {
- status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::innerFec: {
- status.innerFec(static_cast<FrontendInnerFec>(
- s.get<TunerFrontendStatus::innerFec>()));
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::modulation: {
- auto aidlMod = s.get<TunerFrontendStatus::modulation>();
- FrontendModulationStatus modulation;
- switch (mType) {
- case (int)FrontendType::DVBC:
- modulation.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
- status.modulation(modulation);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DVBS:
- modulation.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
- status.modulation(modulation);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBS:
- modulation.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
- status.modulation(modulation);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBS3:
- modulation.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
- status.modulation(modulation);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBT:
- modulation.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
- status.modulation(modulation);
- hidlStatus.push_back(status);
- break;
- default:
- break;
- }
- break;
- }
- case TunerFrontendStatus::inversion: {
- status.inversion(static_cast<FrontendDvbcSpectralInversion>(
- s.get<TunerFrontendStatus::inversion>()));
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::lnbVoltage: {
- status.lnbVoltage(static_cast<LnbVoltage>(
- s.get<TunerFrontendStatus::lnbVoltage>()));
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::plpId: {
- status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isEWBS: {
- status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::agc: {
- status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isLnaOn: {
- status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isLayerError: {
- auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
- hidl_vec<bool> e(aidlE.begin(), aidlE.end());
- status.isLayerError(e);
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::mer: {
- status.mer(s.get<TunerFrontendStatus::mer>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::freqOffset: {
- status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::hierarchy: {
- status.hierarchy(static_cast<FrontendDvbtHierarchy>(
- s.get<TunerFrontendStatus::hierarchy>()));
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isRfLocked: {
- status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::plpInfo: {
- int size = s.get<TunerFrontendStatus::plpInfo>().size();
- hidl_vec<FrontendStatusAtsc3PlpInfo> info(size);
- for (int i = 0; i < size; i++) {
- auto aidlInfo = s.get<TunerFrontendStatus::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;
- }
- default:
- break;
- }
- }
- return hidlStatus;
-}
-
-vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
- vector<TunerFrontendStatus>& aidlStatus) {
- vector<FrontendStatusExt1_1> hidlStatus;
- for (TunerFrontendStatus s : aidlStatus) {
- FrontendStatusExt1_1 status;
- switch (s.getTag()) {
- case TunerFrontendStatus::modulations: {
- vector<FrontendModulation> ms;
- for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
- FrontendModulation m;
- switch (mType) {
- case (int)FrontendType::DVBC:
- m.dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::DVBS:
- m.dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::DVBT:
- m.dvbt(static_cast<FrontendDvbtConstellation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::ISDBS:
- m.isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::ISDBS3:
- m.isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::ISDBT:
- m.isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::ATSC:
- m.atsc(static_cast<FrontendAtscModulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::ATSC3:
- m.atsc3(static_cast<FrontendAtsc3Modulation>(aidlMod));
- ms.push_back(m);
- break;
- case (int)FrontendType::DTMB:
- m.dtmb(static_cast<FrontendDtmbModulation>(aidlMod));
- ms.push_back(m);
- break;
- default:
- break;
- }
- }
- if (ms.size() > 0) {
- status.modulations(ms);
- hidlStatus.push_back(status);
- }
- break;
- }
- case TunerFrontendStatus::bers: {
- auto aidlB = s.get<TunerFrontendStatus::bers>();
- hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
- status.bers(b);
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::codeRates: {
- vector<hardware::tv::tuner::V1_1::FrontendInnerFec> codeRates;
- for (auto aidlCodeRate : s.get<TunerFrontendStatus::codeRates>()) {
- codeRates.push_back(
- static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate));
- }
- if (codeRates.size() > 0) {
- status.codeRates(codeRates);
- hidlStatus.push_back(status);
- }
- break;
- }
- case TunerFrontendStatus::bandwidth: {
- auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
- FrontendBandwidth band;
- switch (mType) {
- case (int)FrontendType::ATSC3:
- band.atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
- status.bandwidth(band);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DVBC:
- band.dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
- status.bandwidth(band);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DVBT:
- band.dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
- status.bandwidth(band);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBT:
- band.isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
- status.bandwidth(band);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DTMB:
- band.dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
- status.bandwidth(band);
- hidlStatus.push_back(status);
- break;
- default:
- break;
- }
- break;
- }
- case TunerFrontendStatus::interval: {
- auto aidlInter = s.get<TunerFrontendStatus::interval>();
- FrontendGuardInterval inter;
- switch (mType) {
- case (int)FrontendType::DVBT:
- inter.dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
- status.interval(inter);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBT:
- inter.isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
- status.interval(inter);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DTMB:
- inter.dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
- status.interval(inter);
- hidlStatus.push_back(status);
- break;
- default:
- break;
- }
- break;
- }
- case TunerFrontendStatus::transmissionMode: {
- auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
- FrontendTransmissionMode trans;
- switch (mType) {
- case (int)FrontendType::DVBT:
- trans.dvbt(static_cast<FrontendDvbtTransmissionMode>(aidlTran));
- status.transmissionMode(trans);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBT:
- trans.isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
- status.transmissionMode(trans);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::DTMB:
- trans.dtmb(static_cast<FrontendDtmbTransmissionMode>(aidlTran));
- status.transmissionMode(trans);
- hidlStatus.push_back(status);
- break;
- default:
- break;
- }
- break;
- }
- case TunerFrontendStatus::uec: {
- status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::systemId: {
- status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::interleaving: {
- vector<FrontendInterleaveMode> modes;
- for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
- FrontendInterleaveMode mode;
- switch (mType) {
- case (int)FrontendType::DVBC:
- mode.dvbc(static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
- modes.push_back(mode);
- break;
- case (int)FrontendType::ATSC3:
- mode.atsc3(static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
- modes.push_back(mode);
- break;
- case (int)FrontendType::DTMB:
- mode.dtmb(static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
- modes.push_back(mode);
- break;
- default:
- break;
- }
- }
- if (modes.size() > 0) {
- status.interleaving(modes);
- hidlStatus.push_back(status);
- }
- break;
- }
- case TunerFrontendStatus::isdbtSegment: {
- auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
- hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
- status.isdbtSegment(s);
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::tsDataRate: {
- auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
- hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
- status.tsDataRate(ts);
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::rollOff: {
- auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
- FrontendRollOff roll;
- switch (mType) {
- case (int)FrontendType::DVBS:
- roll.dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
- status.rollOff(roll);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBS:
- roll.isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
- status.rollOff(roll);
- hidlStatus.push_back(status);
- break;
- case (int)FrontendType::ISDBS3:
- roll.isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
- status.rollOff(roll);
- hidlStatus.push_back(status);
- break;
- default:
- break;
- }
- break;
- }
- case TunerFrontendStatus::isMiso: {
- status.isMiso(s.get<TunerFrontendStatus::isMiso>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isLinear: {
- status.isLinear(s.get<TunerFrontendStatus::isLinear>());
- hidlStatus.push_back(status);
- break;
- }
- case TunerFrontendStatus::isShortFrames: {
- status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
- hidlStatus.push_back(status);
- break;
- }
- default:
- break;
- }
- }
- return hidlStatus;
-}
-
-TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- bool isExtended = validateExtendedSettings(settingsExt1_1);
- TunerFrontendSettings s{
- .isExtended = isExtended,
- .endFrequency = (int) settingsExt1_1.endFrequency,
- .inversion = (int) settingsExt1_1.inversion,
- };
-
- if (settingsExt1_1.settingExt.getDiscriminator()
- == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dtmb) {
- s.settings.set<TunerFrontendUnionSettings::dtmb>(getAidlDtmbSettings(settingsExt1_1));
- return s;
- }
-
- switch (settings.getDiscriminator()) {
- case FrontendSettings::hidl_discriminator::analog: {
- s.settings.set<TunerFrontendUnionSettings::analog>(
- getAidlAnalogSettings(settings, settingsExt1_1));
- break;
- }
- case FrontendSettings::hidl_discriminator::atsc: {
- s.settings.set<TunerFrontendUnionSettings::atsc>(getAidlAtscSettings(settings));
- break;
- }
- case FrontendSettings::hidl_discriminator::atsc3: {
- s.settings.set<TunerFrontendUnionSettings::atsc3>(getAidlAtsc3Settings(settings));
- break;
- }
- case FrontendSettings::hidl_discriminator::dvbs: {
- s.settings.set<TunerFrontendUnionSettings::dvbs>(
- getAidlDvbsSettings(settings, settingsExt1_1));
- break;
- }
- case FrontendSettings::hidl_discriminator::dvbc: {
- s.settings.set<TunerFrontendUnionSettings::cable>(
- getAidlCableSettings(settings, settingsExt1_1));
- break;
- }
- case FrontendSettings::hidl_discriminator::dvbt: {
- s.settings.set<TunerFrontendUnionSettings::dvbt>(
- getAidlDvbtSettings(settings, settingsExt1_1));
- break;
- }
- case FrontendSettings::hidl_discriminator::isdbs: {
- s.settings.set<TunerFrontendUnionSettings::isdbs>(getAidlIsdbsSettings(settings));
- break;
- }
- case FrontendSettings::hidl_discriminator::isdbs3: {
- s.settings.set<TunerFrontendUnionSettings::isdbs3>(getAidlIsdbs3Settings(settings));
- break;
- }
- case FrontendSettings::hidl_discriminator::isdbt: {
- s.settings.set<TunerFrontendUnionSettings::isdbt>(getAidlIsdbtSettings(settings));
- break;
+int32_t FrontendClient::getId() {
+ if (mTunerFrontend != nullptr) {
+ int32_t id;
+ Status s = mTunerFrontend->getFrontendId(&id);
+ if (s.isOk()) {
+ return id;
}
- default:
- break;
- }
- return s;
-}
-
-TunerFrontendAnalogSettings FrontendClient::getAidlAnalogSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- TunerFrontendAnalogSettings analogSettings{
- .frequency = (int)settings.analog().frequency,
- .signalType = (int)settings.analog().type,
- .sifStandard = (int)settings.analog().sifStandard,
- };
- if (settingsExt1_1.settingExt.getDiscriminator()
- == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::analog) {
- analogSettings.isExtended = true;
- analogSettings.aftFlag = (int)settingsExt1_1.settingExt.analog().aftFlag;
- } else {
- analogSettings.isExtended = false;
- }
- return analogSettings;
-}
-
-TunerFrontendDvbsSettings FrontendClient::getAidlDvbsSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- TunerFrontendDvbsSettings dvbsSettings{
- .frequency = (int)settings.dvbs().frequency,
- .modulation = (int)settings.dvbs().modulation,
- .codeRate = {
- .fec = (long)settings.dvbs().coderate.fec,
- .isLinear = settings.dvbs().coderate.isLinear,
- .isShortFrames = settings.dvbs().coderate.isShortFrames,
- .bitsPer1000Symbol = (int)settings.dvbs().coderate.bitsPer1000Symbol,
- },
- .symbolRate = (int)settings.dvbs().symbolRate,
- .rolloff = (int)settings.dvbs().rolloff,
- .pilot = (int)settings.dvbs().pilot,
- .inputStreamId = (int)settings.dvbs().inputStreamId,
- .standard = (int)settings.dvbs().standard,
- .vcm = (int)settings.dvbs().vcmMode,
- };
- if (settingsExt1_1.settingExt.getDiscriminator()
- == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbs) {
- dvbsSettings.isExtended = true;
- dvbsSettings.scanType = (int)settingsExt1_1.settingExt.dvbs().scanType;
- dvbsSettings.isDiseqcRxMessage = settingsExt1_1.settingExt.dvbs().isDiseqcRxMessage;
- } else {
- dvbsSettings.isExtended = false;
- }
- return dvbsSettings;
-}
-
-TunerFrontendCableSettings FrontendClient::getAidlCableSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- TunerFrontendCableSettings cableSettings{
- .frequency = (int)settings.dvbc().frequency,
- .modulation = (int)settings.dvbc().modulation,
- .innerFec = (long)settings.dvbc().fec,
- .symbolRate = (int)settings.dvbc().symbolRate,
- .outerFec = (int)settings.dvbc().outerFec,
- .annex = (int)settings.dvbc().annex,
- .spectralInversion = (int)settings.dvbc().spectralInversion,
- };
- if (settingsExt1_1.settingExt.getDiscriminator()
- == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbc) {
- cableSettings.isExtended = true;
- cableSettings.interleaveMode = (int)settingsExt1_1.settingExt.dvbc().interleaveMode;
- cableSettings.bandwidth = (int)settingsExt1_1.settingExt.dvbc().bandwidth;
- } else {
- cableSettings.isExtended = false;
}
- return cableSettings;
-}
-
-TunerFrontendDvbtSettings FrontendClient::getAidlDvbtSettings(const FrontendSettings& settings,
- const FrontendSettingsExt1_1& settingsExt1_1) {
- TunerFrontendDvbtSettings dvbtSettings{
- .frequency = (int)settings.dvbt().frequency,
- .transmissionMode = (int)settings.dvbt().transmissionMode,
- .bandwidth = (int)settings.dvbt().bandwidth,
- .constellation = (int)settings.dvbt().constellation,
- .hierarchy = (int)settings.dvbt().hierarchy,
- .hpCodeRate = (int)settings.dvbt().hpCoderate,
- .lpCodeRate = (int)settings.dvbt().lpCoderate,
- .guardInterval = (int)settings.dvbt().guardInterval,
- .isHighPriority = settings.dvbt().isHighPriority,
- .standard = (int)settings.dvbt().standard,
- .isMiso = settings.dvbt().isMiso,
- .plpMode = (int)settings.dvbt().plpMode,
- .plpId = (int)settings.dvbt().plpId,
- .plpGroupId = (int)settings.dvbt().plpGroupId,
- };
- if (settingsExt1_1.settingExt.getDiscriminator()
- == FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::dvbt) {
- dvbtSettings.isExtended = true;
- dvbtSettings.constellation = (int)settingsExt1_1.settingExt.dvbt().constellation;
- dvbtSettings.transmissionMode =
- (int)settingsExt1_1.settingExt.dvbt().transmissionMode;
- } else {
- dvbtSettings.isExtended = false;
- }
- return dvbtSettings;
-}
-
-TunerFrontendDtmbSettings FrontendClient::getAidlDtmbSettings(
- const FrontendSettingsExt1_1& settingsExt1_1) {
- TunerFrontendDtmbSettings dtmbSettings{
- .frequency = (int)settingsExt1_1.settingExt.dtmb().frequency,
- .transmissionMode = (int)settingsExt1_1.settingExt.dtmb().transmissionMode,
- .bandwidth = (int)settingsExt1_1.settingExt.dtmb().bandwidth,
- .modulation = (int)settingsExt1_1.settingExt.dtmb().modulation,
- .codeRate = (int)settingsExt1_1.settingExt.dtmb().codeRate,
- .guardInterval = (int)settingsExt1_1.settingExt.dtmb().guardInterval,
- .interleaveMode = (int)settingsExt1_1.settingExt.dtmb().interleaveMode,
- };
- return dtmbSettings;
-}
-
-TunerFrontendAtscSettings FrontendClient::getAidlAtscSettings(const FrontendSettings& settings) {
- TunerFrontendAtscSettings atscSettings{
- .frequency = (int)settings.atsc().frequency,
- .modulation = (int)settings.atsc().modulation,
- };
- return atscSettings;
-}
-
-TunerFrontendAtsc3Settings FrontendClient::getAidlAtsc3Settings(const FrontendSettings& settings) {
- TunerFrontendAtsc3Settings atsc3Settings{
- .frequency = (int)settings.atsc3().frequency,
- .bandwidth = (int)settings.atsc3().bandwidth,
- .demodOutputFormat = (int)settings.atsc3().demodOutputFormat,
- };
- atsc3Settings.plpSettings.resize(settings.atsc3().plpSettings.size());
- for (auto plpSetting : settings.atsc3().plpSettings) {
- atsc3Settings.plpSettings.push_back({
- .plpId = (int)plpSetting.plpId,
- .modulation = (int)plpSetting.modulation,
- .interleaveMode = (int)plpSetting.interleaveMode,
- .codeRate = (int)plpSetting.codeRate,
- .fec = (int)plpSetting.fec,
- });
- }
- return atsc3Settings;
-}
-
-TunerFrontendIsdbsSettings FrontendClient::getAidlIsdbsSettings(const FrontendSettings& settings) {
- TunerFrontendIsdbsSettings isdbsSettings{
- .frequency = (int)settings.isdbs().frequency,
- .streamId = (char16_t)settings.isdbs().streamId,
- .streamIdType = (int)settings.isdbs().streamIdType,
- .modulation = (int)settings.isdbs().modulation,
- .codeRate = (int)settings.isdbs().coderate,
- .symbolRate = (int)settings.isdbs().symbolRate,
- .rolloff = (int)settings.isdbs().rolloff,
- };
- return isdbsSettings;
-}
-
-TunerFrontendIsdbs3Settings FrontendClient::getAidlIsdbs3Settings(
- const FrontendSettings& settings) {
- TunerFrontendIsdbs3Settings isdbs3Settings{
- .frequency = (int)settings.isdbs3().frequency,
- .streamId = (char16_t)settings.isdbs3().streamId,
- .streamIdType = (int)settings.isdbs3().streamIdType,
- .modulation = (int)settings.isdbs3().modulation,
- .codeRate = (int)settings.isdbs3().coderate,
- .symbolRate = (int)settings.isdbs3().symbolRate,
- .rolloff = (int)settings.isdbs3().rolloff,
- };
- return isdbs3Settings;
-}
-
-TunerFrontendIsdbtSettings FrontendClient::getAidlIsdbtSettings(const FrontendSettings& settings) {
- TunerFrontendIsdbtSettings isdbtSettings{
- .frequency = (int)settings.isdbt().frequency,
- .modulation = (int)settings.isdbt().modulation,
- .bandwidth = (int)settings.isdbt().bandwidth,
- .mode = (int)settings.isdbt().mode,
- .codeRate = (int)settings.isdbt().coderate,
- .guardInterval = (int)settings.isdbt().guardInterval,
- .serviceAreaId = (int)settings.isdbt().serviceAreaId,
- };
- return isdbtSettings;
-}
-bool FrontendClient::validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1) {
- return settingsExt1_1.endFrequency != (uint32_t)Constant::INVALID_FRONTEND_SETTING_FREQUENCY
- || settingsExt1_1.inversion != FrontendSpectralInversion::UNDEFINED
- || settingsExt1_1.settingExt.getDiscriminator()
- != FrontendSettingsExt1_1::SettingsExt::hidl_discriminator::noinit;
+ return static_cast<int32_t>(Constant::INVALID_FRONTEND_ID);
}
-/////////////// TunerFrontendCallback ///////////////////////
-
+/////////////// IFrontendCallback ///////////////////////
TunerFrontendCallback::TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback)
: mFrontendClientCallback(frontendClientCallback) {}
-Status TunerFrontendCallback::onEvent(int frontendEventType) {
- if (mFrontendClientCallback != NULL) {
- mFrontendClientCallback->onEvent(static_cast<FrontendEventType>(frontendEventType));
+Status TunerFrontendCallback::onEvent(FrontendEventType frontendEventType) {
+ if (mFrontendClientCallback != nullptr) {
+ mFrontendClientCallback->onEvent(frontendEventType);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-Status TunerFrontendCallback::onScanMessage(int messageType,
- const TunerFrontendScanMessage& message) {
- if (mFrontendClientCallback != NULL) {
- if (!is1_1ExtendedScanMessage(messageType)) {
- mFrontendClientCallback->onScanMessage(
- static_cast<FrontendScanMessageType>(messageType),
- getHalScanMessage(messageType, message));
- } else {
- mFrontendClientCallback->onScanMessageExt1_1(
- static_cast<FrontendScanMessageTypeExt1_1>(messageType),
- getHalScanMessageExt1_1(messageType, message));
- }
+Status TunerFrontendCallback::onScanMessage(FrontendScanMessageType messageType,
+ const FrontendScanMessage& message) {
+ if (mFrontendClientCallback != nullptr) {
+ mFrontendClientCallback->onScanMessage(messageType, message);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
-/////////////// IFrontendCallback ///////////////////////
-
-HidlFrontendCallback::HidlFrontendCallback(sp<FrontendClientCallback> frontendClientCallback)
- : mFrontendClientCallback(frontendClientCallback) {}
-
-Return<void> HidlFrontendCallback::onEvent(FrontendEventType frontendEventType) {
- if (mFrontendClientCallback != NULL) {
- mFrontendClientCallback->onEvent(frontendEventType);
- }
- return Void();
-}
-
-Return<void> HidlFrontendCallback::onScanMessage(FrontendScanMessageType type,
- const FrontendScanMessage& message) {
- if (mFrontendClientCallback != NULL) {
- mFrontendClientCallback->onScanMessage(type, message);
- }
- return Void();
-}
-
-Return<void> HidlFrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type,
- const FrontendScanMessageExt1_1& message) {
- if (mFrontendClientCallback != NULL) {
- mFrontendClientCallback->onScanMessageExt1_1(type, message);
- }
- return Void();
-}
-
-/////////////// FrontendClientCallback Helper Methods ///////////////////////
-
-FrontendScanMessage TunerFrontendCallback::getHalScanMessage(
- int messageType, const TunerFrontendScanMessage& message) {
- FrontendScanMessage scanMessage;
- switch (messageType) {
- case (int) FrontendScanMessageType::LOCKED:
- scanMessage.isLocked(message.get<TunerFrontendScanMessage::isLocked>());
- break;
- case (int) FrontendScanMessageType::END:
- scanMessage.isEnd(message.get<TunerFrontendScanMessage::isEnd>());
- break;
- case (int) FrontendScanMessageType::PROGRESS_PERCENT:
- scanMessage.progressPercent(message.get<TunerFrontendScanMessage::progressPercent>());
- break;
- case (int) FrontendScanMessageType::FREQUENCY: {
- vector<int> f = message.get<TunerFrontendScanMessage::frequencies>();
- hidl_vec<uint32_t> frequencies(begin(f), end(f));
- scanMessage.frequencies(frequencies);
- break;
- }
- case (int) FrontendScanMessageType::SYMBOL_RATE: {
- vector<int> s = message.get<TunerFrontendScanMessage::symbolRates>();
- hidl_vec<uint32_t> symbolRates(begin(s), end(s));
- scanMessage.symbolRates(symbolRates);
- break;
- }
- case (int) FrontendScanMessageType::HIERARCHY:
- scanMessage.hierarchy(static_cast<FrontendDvbtHierarchy>(
- message.get<TunerFrontendScanMessage::hierarchy>()));
- break;
- case (int) FrontendScanMessageType::ANALOG_TYPE:
- scanMessage.analogType(static_cast<FrontendAnalogType>(
- message.get<TunerFrontendScanMessage::analogType>()));
- break;
- case (int) FrontendScanMessageType::PLP_IDS: {
- vector<uint8_t> p = message.get<TunerFrontendScanMessage::plpIds>();
- hidl_vec<uint8_t> plpIds(begin(p), end(p));
- scanMessage.plpIds(plpIds);
- break;
- }
- case (int) FrontendScanMessageType::GROUP_IDS: {
- vector<uint8_t> g = message.get<TunerFrontendScanMessage::groupIds>();
- hidl_vec<uint8_t> groupIds(begin(g), end(g));
- scanMessage.groupIds(groupIds);
- break;
- }
- case (int) FrontendScanMessageType::INPUT_STREAM_IDS: {
- vector<char16_t> i = message.get<TunerFrontendScanMessage::inputStreamIds>();
- hidl_vec<uint16_t> inputStreamIds(begin(i), end(i));
- scanMessage.inputStreamIds(inputStreamIds);
- break;
- }
- case (int) FrontendScanMessageType::STANDARD: {
- FrontendScanMessage::Standard std;
- int standard = message.get<TunerFrontendScanMessage::std>();
- switch (mType) {
- case (int) FrontendType::DVBS:
- std.sStd(static_cast<FrontendDvbsStandard>(standard));
- scanMessage.std(std);
- break;
- case (int) FrontendType::DVBT:
- std.tStd(static_cast<FrontendDvbtStandard>(standard));
- scanMessage.std(std);
- break;
- case (int) FrontendType::ANALOG:
- std.sifStd(static_cast<FrontendAnalogSifStandard>(standard));
- scanMessage.std(std);
- break;
- default:
- break;
- }
- break;
- }
- case (int) FrontendScanMessageType::ATSC3_PLP_INFO: {
- vector<TunerFrontendScanAtsc3PlpInfo> plp =
- message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
- hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
- int size = plp.size();
- plpInfo.resize(size);
- for (int i = 0; i < size; i++) {
- auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
- FrontendScanAtsc3PlpInfo p{
- .plpId = static_cast<uint8_t>(info.plpId),
- .bLlsFlag = info.llsFlag,
- };
- plpInfo[i] = p;
- }
- scanMessage.atsc3PlpInfos(plpInfo);
- break;
- }
- default:
- break;
- }
- return scanMessage;
-}
-
-FrontendScanMessageExt1_1 TunerFrontendCallback::getHalScanMessageExt1_1(
- int messageType, const TunerFrontendScanMessage& message) {
- FrontendScanMessageExt1_1 scanMessage;
- switch (messageType) {
- case (int) FrontendScanMessageTypeExt1_1::HIGH_PRIORITY:
- scanMessage.isHighPriority(message.get<TunerFrontendScanMessage::isHighPriority>());
- break;
- case (int) FrontendScanMessageTypeExt1_1::DVBC_ANNEX:
- scanMessage.annex(static_cast<FrontendDvbcAnnex>(
- message.get<TunerFrontendScanMessage::annex>()));
- break;
- case (int) FrontendScanMessageTypeExt1_1::MODULATION: {
- FrontendModulation m;
- int modulation = message.get<TunerFrontendScanMessage::modulation>();
- switch (mType) {
- case (int) FrontendType::DVBC:
- m.dvbc(static_cast<FrontendDvbcModulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::DVBS:
- m.dvbs(static_cast<FrontendDvbsModulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::DVBT:
- m.dvbt(static_cast<FrontendDvbtConstellation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::ISDBS:
- m.isdbs(static_cast<FrontendIsdbsModulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::ISDBS3:
- m.isdbs3(static_cast<FrontendIsdbs3Modulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::ISDBT:
- m.isdbt(static_cast<FrontendIsdbtModulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::ATSC:
- m.atsc(static_cast<FrontendAtscModulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) FrontendType::ATSC3:
- m.atsc3(static_cast<FrontendAtsc3Modulation>(modulation));
- scanMessage.modulation(m);
- break;
- case (int) hardware::tv::tuner::V1_1::FrontendType::DTMB:
- m.dtmb(static_cast<FrontendDtmbModulation>(modulation));
- scanMessage.modulation(m);
- break;
- default:
- break;
- }
- break;
- }
- default:
- break;
- }
- return scanMessage;
-}
-
-bool TunerFrontendCallback::is1_1ExtendedScanMessage(int messageType) {
- return messageType >= (int)FrontendScanMessageTypeExt1_1::MODULATION
- && messageType <= (int)FrontendScanMessageTypeExt1_1::HIGH_PRIORITY;
-}
} // namespace android
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 1dd950eee9e1..08c0b20d18ed 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,53 +17,29 @@
#ifndef _ANDROID_MEDIA_TV_FRONTEND_CLIENT_H_
#define _ANDROID_MEDIA_TV_FRONTEND_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/DemuxFilterSettings.h>
+#include <aidl/android/hardware/tv/tuner/FrontendType.h>
+#include <aidl/android/hardware/tv/tuner/Result.h>
#include <aidl/android/media/tv/tuner/BnTunerFrontendCallback.h>
#include <aidl/android/media/tv/tuner/ITunerFrontend.h>
-#include <android/hardware/tv/tuner/1.1/IFrontend.h>
-#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
+#include <utils/RefBase.h>
#include "ClientHelper.h"
#include "FrontendClientCallback.h"
#include "LnbClient.h"
using Status = ::ndk::ScopedAStatus;
-
+using ::aidl::android::hardware::tv::tuner::FrontendEventType;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessage;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessageType;
+using ::aidl::android::hardware::tv::tuner::FrontendScanType;
+using ::aidl::android::hardware::tv::tuner::FrontendSettings;
+using ::aidl::android::hardware::tv::tuner::FrontendStatus;
+using ::aidl::android::hardware::tv::tuner::FrontendStatusType;
+using ::aidl::android::hardware::tv::tuner::FrontendType;
+using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::BnTunerFrontendCallback;
using ::aidl::android::media::tv::tuner::ITunerFrontend;
-using ::aidl::android::media::tv::tuner::TunerFrontendAnalogSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendAtscSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendAtsc3Settings;
-using ::aidl::android::media::tv::tuner::TunerFrontendCableSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendDvbsSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendDvbtSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendDtmbSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendIsdbsSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendIsdbs3Settings;
-using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
-using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
-using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
-
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
-using ::android::hardware::tv::tuner::V1_0::FrontendSettings;
-using ::android::hardware::tv::tuner::V1_0::FrontendStatus;
-using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
-using ::android::hardware::tv::tuner::V1_0::IFrontend;
-using ::android::hardware::tv::tuner::V1_0::Result;
-
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1;
-using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
using namespace std;
@@ -74,32 +50,8 @@ class TunerFrontendCallback : public BnTunerFrontendCallback {
public:
TunerFrontendCallback(sp<FrontendClientCallback> frontendClientCallback);
- Status onEvent(int frontendEventType);
-
- Status onScanMessage(int messageType, const TunerFrontendScanMessage& message);
-
- void setFrontendType(int frontendType) { mType = frontendType; }
-
-private:
- FrontendScanMessage getHalScanMessage(int messageType, const TunerFrontendScanMessage& message);
- FrontendScanMessageExt1_1 getHalScanMessageExt1_1(int messageType,
- const TunerFrontendScanMessage& message);
- bool is1_1ExtendedScanMessage(int messageType);
-
- sp<FrontendClientCallback> mFrontendClientCallback;
- int mType;
-};
-
-struct HidlFrontendCallback : public IFrontendCallback {
-
-public:
- HidlFrontendCallback(sp<FrontendClientCallback> frontendClientCallback);
-
- virtual Return<void> onEvent(FrontendEventType frontendEventType);
- virtual Return<void> onScanMessage(
- FrontendScanMessageType type, const FrontendScanMessage& message);
- virtual Return<void> onScanMessageExt1_1(
- FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
+ Status onEvent(FrontendEventType frontendEventType);
+ Status onScanMessage(FrontendScanMessageType messageType, const FrontendScanMessage& message);
private:
sp<FrontendClientCallback> mFrontendClientCallback;
@@ -108,7 +60,7 @@ private:
struct FrontendClient : public RefBase {
public:
- FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, int type);
+ FrontendClient(shared_ptr<ITunerFrontend> tunerFrontend, FrontendType type);
~FrontendClient();
/**
@@ -116,13 +68,10 @@ public:
*/
Result setCallback(sp<FrontendClientCallback> frontendClientCallback);
- // TODO: remove after migration to Tuner Service is done.
- void setHidlFrontend(sp<IFrontend> frontend);
-
/**
* Tuner Frontend with Frontend Settings.
*/
- Result tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
+ Result tune(const FrontendSettings& settings);
/**
* Stop tune Frontend.
@@ -132,8 +81,7 @@ public:
/**
* Scan the frontend to use the settings given.
*/
- Result scan(const FrontendSettings& settings, FrontendScanType frontendScanType,
- const FrontendSettingsExt1_1& settingsExt1_1);
+ Result scan(const FrontendSettings& settings, FrontendScanType frontendScanType);
/**
* Stop the previous scanning.
@@ -146,12 +94,6 @@ public:
vector<FrontendStatus> getStatus(vector<FrontendStatusType> statusTypes);
/**
- * Gets the 1.1 extended statuses of the frontend.
- */
- vector<FrontendStatusExt1_1> getStatusExtended_1_1(
- vector<FrontendStatusTypeExt1_1> statusTypes);
-
- /**
* Sets Low-Noise Block downconverter (LNB) for satellite frontend.
*/
Result setLnb(sp<LnbClient> lnbClient);
@@ -166,68 +108,28 @@ public:
*
* @return lts id
*/
- int linkCiCamToFrontend(int ciCamId);
+ int32_t linkCiCamToFrontend(int32_t ciCamId);
/**
* Unink Frontend to the cicam with given id.
*/
- Result unlinkCiCamToFrontend(int ciCamId);
+ Result unlinkCiCamToFrontend(int32_t ciCamId);
/**
* Close Frontend.
*/
Result close();
+ int32_t getId();
shared_ptr<ITunerFrontend> getAidlFrontend();
-
- void setId(int id);
- int getId();
-
private:
- vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
- vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
-
- TunerFrontendSettings getAidlFrontendSettings(
- const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendAnalogSettings getAidlAnalogSettings(
- const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendDvbsSettings getAidlDvbsSettings(
- const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendCableSettings getAidlCableSettings(
- const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendDvbtSettings getAidlDvbtSettings(
- const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendDtmbSettings getAidlDtmbSettings(const FrontendSettingsExt1_1& settingsExt1_1);
- TunerFrontendAtscSettings getAidlAtscSettings(const FrontendSettings& settings);
- TunerFrontendAtsc3Settings getAidlAtsc3Settings(const FrontendSettings& settings);
- TunerFrontendIsdbsSettings getAidlIsdbsSettings(const FrontendSettings& settings);
- TunerFrontendIsdbs3Settings getAidlIsdbs3Settings(const FrontendSettings& settings);
- TunerFrontendIsdbtSettings getAidlIsdbtSettings(const FrontendSettings& settings);
-
- bool validateExtendedSettings(const FrontendSettingsExt1_1& settingsExt1_1);
-
/**
* An AIDL Tuner Frontend Singleton assigned at the first time when the Tuner Client
* opens a frontend cient. Default null when the service does not exist.
*/
shared_ptr<ITunerFrontend> mTunerFrontend;
- /**
- * A Frontend 1.0 HAL interface as a fall back interface when the Tuner Service does not exist.
- * This is a temprary connection before the Tuner Framework fully migrates to the TunerService.
- * Default null.
- */
- sp<IFrontend> mFrontend;
-
- /**
- * A Frontend 1.1 HAL interface as a fall back interface when the Tuner Service does not exist.
- * This is a temprary connection before the Tuner Framework fully migrates to the TunerService.
- * Default null.
- */
- sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFrontend_1_1;
-
- int mId;
- int mType;
+ FrontendType mType;
};
} // namespace android
diff --git a/media/jni/tuner/FrontendClientCallback.h b/media/jni/tuner/FrontendClientCallback.h
index 94f8c406688e..15b08ef673cb 100644
--- a/media/jni/tuner/FrontendClientCallback.h
+++ b/media/jni/tuner/FrontendClientCallback.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,11 +17,11 @@
#ifndef _ANDROID_MEDIA_TV_FRONTEND_CLIENT_CALLBACK_H_
#define _ANDROID_MEDIA_TV_FRONTEND_CLIENT_CALLBACK_H_
-using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
-using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
-using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1;
+#include <utils/RefBase.h>
+
+using ::aidl::android::hardware::tv::tuner::FrontendEventType;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessage;
+using ::aidl::android::hardware::tv::tuner::FrontendScanMessageType;
using namespace std;
@@ -30,8 +30,6 @@ namespace android {
struct FrontendClientCallback : public RefBase {
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(FrontendScanMessageType type, const FrontendScanMessage& message);
- virtual void onScanMessageExt1_1(
- FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt);
};
} // namespace android
diff --git a/media/jni/tuner/LnbClient.cpp b/media/jni/tuner/LnbClient.cpp
index 073c49a2d6ac..43198e3a3fc6 100644
--- a/media/jni/tuner/LnbClient.cpp
+++ b/media/jni/tuner/LnbClient.cpp
@@ -21,148 +21,89 @@
#include "LnbClient.h"
-using ::android::hardware::tv::tuner::V1_0::Result;
-
namespace android {
/////////////// LnbClient ///////////////////////
-
LnbClient::LnbClient(shared_ptr<ITunerLnb> tunerLnb) {
mTunerLnb = tunerLnb;
- mId = -1;
}
LnbClient::~LnbClient() {
- mTunerLnb = NULL;
- mLnb = NULL;
- mId = -1;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void LnbClient::setHidlLnb(sp<ILnb> lnb) {
- mLnb = lnb;
+ mTunerLnb = nullptr;
}
Result LnbClient::setCallback(sp<LnbClientCallback> cb) {
- if (mTunerLnb != NULL) {
+ if (mTunerLnb != nullptr) {
shared_ptr<TunerLnbCallback> aidlCallback =
::ndk::SharedRefBase::make<TunerLnbCallback>(cb);
Status s = mTunerLnb->setCallback(aidlCallback);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- sp<HidlLnbCallback> hidlCallback = new HidlLnbCallback(cb);
- return mLnb->setCallback(hidlCallback);
- }
-
return Result::INVALID_STATE;
}
Result LnbClient::setVoltage(LnbVoltage voltage) {
- if (mTunerLnb != NULL) {
- Status s = mTunerLnb->setVoltage((int)voltage);
+ if (mTunerLnb != nullptr) {
+ Status s = mTunerLnb->setVoltage(voltage);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- return mLnb->setVoltage(voltage);
- }
-
return Result::INVALID_STATE;
}
Result LnbClient::setTone(LnbTone tone) {
- if (mTunerLnb != NULL) {
- Status s = mTunerLnb->setTone((int)tone);
+ if (mTunerLnb != nullptr) {
+ Status s = mTunerLnb->setTone(tone);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- return mLnb->setTone(tone);
- }
-
return Result::INVALID_STATE;
}
Result LnbClient::setSatellitePosition(LnbPosition position) {
- if (mTunerLnb != NULL) {
- Status s = mTunerLnb->setSatellitePosition((int)position);
+ if (mTunerLnb != nullptr) {
+ Status s = mTunerLnb->setSatellitePosition(position);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- return mLnb->setSatellitePosition(position);
- }
-
return Result::INVALID_STATE;
}
Result LnbClient::sendDiseqcMessage(vector<uint8_t> diseqcMessage) {
- if (mTunerLnb != NULL) {
+ if (mTunerLnb != nullptr) {
Status s = mTunerLnb->sendDiseqcMessage(diseqcMessage);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- return mLnb->sendDiseqcMessage(diseqcMessage);
- }
-
return Result::INVALID_STATE;
}
Result LnbClient::close() {
- if (mTunerLnb != NULL) {
+ if (mTunerLnb != nullptr) {
Status s = mTunerLnb->close();
- mTunerLnb = NULL;
+ mTunerLnb = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mLnb != NULL) {
- Result res = mLnb->close();
- mLnb = NULL;
- return res;
- }
-
return Result::INVALID_STATE;
}
-/////////////// ILnbCallback ///////////////////////
-
-HidlLnbCallback::HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback)
- : mLnbClientCallback(lnbClientCallback) {}
-
-Return<void> HidlLnbCallback::onEvent(const LnbEventType lnbEventType) {
- if (mLnbClientCallback != NULL) {
- mLnbClientCallback->onEvent(lnbEventType);
- }
- return Void();
-}
-
-Return<void> HidlLnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
- if (mLnbClientCallback != NULL) {
- mLnbClientCallback->onDiseqcMessage(diseqcMessage);
- }
- return Void();
-}
-
/////////////// TunerLnbCallback ///////////////////////
-
TunerLnbCallback::TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback)
: mLnbClientCallback(lnbClientCallback) {}
-Status TunerLnbCallback::onEvent(int lnbEventType) {
- if (mLnbClientCallback != NULL) {
- mLnbClientCallback->onEvent(static_cast<LnbEventType>(lnbEventType));
+Status TunerLnbCallback::onEvent(LnbEventType lnbEventType) {
+ if (mLnbClientCallback != nullptr) {
+ mLnbClientCallback->onEvent(lnbEventType);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
}
Status TunerLnbCallback::onDiseqcMessage(const vector<uint8_t>& diseqcMessage) {
- if (mLnbClientCallback != NULL) {
- hidl_vec<uint8_t> msg(begin(diseqcMessage), end(diseqcMessage));
- mLnbClientCallback->onDiseqcMessage(msg);
+ if (mLnbClientCallback != nullptr) {
+ mLnbClientCallback->onDiseqcMessage(diseqcMessage);
return Status::ok();
}
return Status::fromServiceSpecificError(static_cast<int32_t>(Result::INVALID_STATE));
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index 7c6118c98eb5..86e3f679576d 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -17,31 +17,24 @@
#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_H_
#define _ANDROID_MEDIA_TV_LNB_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/LnbPosition.h>
+#include <aidl/android/hardware/tv/tuner/LnbTone.h>
+#include <aidl/android/hardware/tv/tuner/LnbVoltage.h>
#include <aidl/android/media/tv/tuner/BnTunerLnbCallback.h>
#include <aidl/android/media/tv/tuner/ITunerLnb.h>
-#include <android/hardware/tv/tuner/1.0/ILnb.h>
-#include <android/hardware/tv/tuner/1.0/ILnbCallback.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
+#include <utils/RefBase.h>
#include "ClientHelper.h"
#include "LnbClientCallback.h"
using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::tv::tuner::LnbPosition;
+using ::aidl::android::hardware::tv::tuner::LnbTone;
+using ::aidl::android::hardware::tv::tuner::LnbVoltage;
using ::aidl::android::media::tv::tuner::BnTunerLnbCallback;
using ::aidl::android::media::tv::tuner::ITunerLnb;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::tv::tuner::V1_0::ILnb;
-using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
-using ::android::hardware::tv::tuner::V1_0::LnbId;
-using ::android::hardware::tv::tuner::V1_0::LnbPosition;
-using ::android::hardware::tv::tuner::V1_0::LnbTone;
-using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
-using ::android::hardware::tv::tuner::V1_0::Result;
-
using namespace std;
namespace android {
@@ -51,33 +44,19 @@ class TunerLnbCallback : public BnTunerLnbCallback {
public:
TunerLnbCallback(sp<LnbClientCallback> lnbClientCallback);
- Status onEvent(int lnbEventType);
+ Status onEvent(LnbEventType lnbEventType);
Status onDiseqcMessage(const vector<uint8_t>& diseqcMessage);
private:
sp<LnbClientCallback> mLnbClientCallback;
};
-struct HidlLnbCallback : public ILnbCallback {
-
-public:
- HidlLnbCallback(sp<LnbClientCallback> lnbClientCallback);
- virtual Return<void> onEvent(const LnbEventType lnbEventType);
- virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
-
-private:
- sp<LnbClientCallback> mLnbClientCallback;
-};
-
struct LnbClient : public RefBase {
public:
LnbClient(shared_ptr<ITunerLnb> tunerLnb);
~LnbClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlLnb(sp<ILnb> lnb);
-
/**
* Set the lnb callback.
*/
@@ -109,8 +88,6 @@ public:
Result close();
shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
- void setId(LnbId id) { mId = id; }
- LnbId getId() { return mId; }
private:
/**
@@ -118,15 +95,6 @@ private:
* opens an Lnb. Default null when lnb is not opened.
*/
shared_ptr<ITunerLnb> mTunerLnb;
-
- /**
- * A Lnb HAL interface that is ready before migrating to the TunerLnb.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<ILnb> mLnb;
-
- LnbId mId;
};
} // namespace android
diff --git a/media/jni/tuner/LnbClientCallback.h b/media/jni/tuner/LnbClientCallback.h
index 253d7ef110e3..612514f60fef 100644
--- a/media/jni/tuner/LnbClientCallback.h
+++ b/media/jni/tuner/LnbClientCallback.h
@@ -17,8 +17,9 @@
#ifndef _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
#define _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
-using ::android::hardware::hidl_vec;
-using ::android::hardware::tv::tuner::V1_0::LnbEventType;
+#include <utils/RefBase.h>
+
+using ::aidl::android::hardware::tv::tuner::LnbEventType;
using namespace std;
@@ -26,8 +27,8 @@ namespace android {
struct LnbClientCallback : public RefBase {
virtual void onEvent(const LnbEventType lnbEventType);
- virtual void onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
+ virtual void onDiseqcMessage(const vector<uint8_t>& diseqcMessage);
};
} // namespace android
-#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_ \ No newline at end of file
+#endif // _ANDROID_MEDIA_TV_LNB_CLIENT_CALLBACK_H_
diff --git a/media/jni/tuner/OWNERS b/media/jni/tuner/OWNERS
new file mode 100644
index 000000000000..bf9fe3408d16
--- /dev/null
+++ b/media/jni/tuner/OWNERS
@@ -0,0 +1,2 @@
+hgchen@google.com
+quxiangfang@google.com
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index e123c9f57ce7..40dba8b4c420 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -16,14 +16,15 @@
#define LOG_TAG "TimeFilterClient"
+#include "TimeFilterClient.h"
+
+#include <aidl/android/hardware/tv/tuner/Constant64Bit.h>
#include <android-base/logging.h>
#include <utils/Log.h>
#include "ClientHelper.h"
-#include "TimeFilterClient.h"
-using ::android::hardware::tv::tuner::V1_0::Result;
-using ::android::hardware::tv::tuner::V1_1::Constant64Bit;
+using ::aidl::android::hardware::tv::tuner::Constant64Bit;
namespace android {
@@ -34,108 +35,60 @@ TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter)
}
TimeFilterClient::~TimeFilterClient() {
- mTunerTimeFilter = NULL;
- mTimeFilter = NULL;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void TimeFilterClient::setHidlTimeFilter(sp<ITimeFilter> timeFilter) {
- mTimeFilter = timeFilter;
+ mTunerTimeFilter = nullptr;
}
-Result TimeFilterClient::setTimeStamp(long timeStamp) {
- if (mTunerTimeFilter != NULL) {
+Result TimeFilterClient::setTimeStamp(int64_t timeStamp) {
+ if (mTunerTimeFilter != nullptr) {
Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mTimeFilter != NULL) {
- return mTimeFilter->setTimeStamp(timeStamp);
- }
-
return Result::INVALID_STATE;
}
Result TimeFilterClient::clearTimeStamp() {
- if (mTunerTimeFilter != NULL) {
+ if (mTunerTimeFilter != nullptr) {
Status s = mTunerTimeFilter->clearTimeStamp();
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mTimeFilter != NULL) {
- return mTimeFilter->clearTimeStamp();
- }
-
return Result::INVALID_STATE;
}
-long TimeFilterClient::getTimeStamp() {
- if (mTunerTimeFilter != NULL) {
+int64_t TimeFilterClient::getTimeStamp() {
+ if (mTunerTimeFilter != nullptr) {
int64_t timeStamp;
Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ if (!s.isOk()) {
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
return timeStamp;
}
- if (mTimeFilter != NULL) {
- Result res;
- long timestamp;
- mTimeFilter->getTimeStamp(
- [&](Result r, uint64_t t) {
- res = r;
- timestamp = t;
- });
- if (res != Result::SUCCESS) {
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- }
- return timestamp;
- }
-
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
-long TimeFilterClient::getSourceTime() {
- if (mTunerTimeFilter != NULL) {
+int64_t TimeFilterClient::getSourceTime() {
+ if (mTunerTimeFilter != nullptr) {
int64_t sourceTime;
Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ if (!s.isOk()) {
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
return sourceTime;
}
- if (mTimeFilter != NULL) {
- Result res;
- long sourceTime;
- mTimeFilter->getSourceTime(
- [&](Result r, uint64_t t) {
- res = r;
- sourceTime = t;
- });
- if (res != Result::SUCCESS) {
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
- }
- return sourceTime;
- }
-
- return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+ return static_cast<int64_t>(Constant64Bit::INVALID_PRESENTATION_TIME_STAMP);
}
Result TimeFilterClient::close() {
- if (mTunerTimeFilter != NULL) {
+ if (mTunerTimeFilter != nullptr) {
Status s = mTunerTimeFilter->close();
- mTunerTimeFilter = NULL;
+ mTunerTimeFilter = nullptr;
return ClientHelper::getServiceSpecificErrorCode(s);
}
- if (mTimeFilter != NULL) {
- Result res = mTimeFilter->close();
- mTimeFilter = NULL;
- return res;
- }
-
return Result::INVALID_STATE;
}
} // namespace android
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 56ddd68d363e..46f33beedd8d 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,18 +17,14 @@
#ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
+#include <aidl/android/hardware/tv/tuner/Result.h>
#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
-#include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
-
-using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+#include <utils/RefBase.h>
using Status = ::ndk::ScopedAStatus;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
-using ::android::hardware::tv::tuner::V1_0::Result;
+
+using ::aidl::android::hardware::tv::tuner::Result;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
using namespace std;
@@ -40,13 +36,10 @@ public:
TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
~TimeFilterClient();
- // TODO: remove after migration to Tuner Service is done.
- void setHidlTimeFilter(sp<ITimeFilter> timeFilter);
-
/**
* Set time stamp for time based filter.
*/
- Result setTimeStamp(long timeStamp);
+ Result setTimeStamp(int64_t timeStamp);
/**
* Clear the time stamp in the time filter.
@@ -56,12 +49,12 @@ public:
/**
* Get the current time in the time filter.
*/
- long getTimeStamp();
+ int64_t getTimeStamp();
/**
* Get the time from the beginning of current data source.
*/
- long getSourceTime();
+ int64_t getSourceTime();
/**
* Releases the Time Filter instance.
@@ -74,13 +67,6 @@ private:
* opens an TimeFilter. Default null when time filter is not opened.
*/
shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
-
- /**
- * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
- * This is a temprary interface before Tuner Framework migrates to use TunerService.
- * Default null when the HAL service does not exist.
- */
- sp<ITimeFilter> mTimeFilter;
};
} // namespace android
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index e05dba629d7f..d19ee0d460ea 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -22,31 +22,20 @@
#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;
+using ::aidl::android::hardware::tv::tuner::FrontendStatusType;
+using ::aidl::android::hardware::tv::tuner::FrontendType;
namespace android {
-sp<ITuner> TunerClient::mTuner;
-sp<::android::hardware::tv::tuner::V1_1::ITuner> TunerClient::mTuner_1_1;
shared_ptr<ITunerService> TunerClient::mTunerService;
-int TunerClient::mTunerVersion;
+int32_t TunerClient::mTunerVersion;
/////////////// TunerClient ///////////////////////
TunerClient::TunerClient() {
- // Get HIDL Tuner in migration stage.
- getHidlTuner();
- if (mTuner != NULL) {
- updateTunerResources();
- }
- // Connect with Tuner Service.
::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
mTunerService = ITunerService::fromBinder(binder);
- if (mTunerService == NULL) {
+ if (mTunerService == nullptr) {
ALOGE("Failed to get tuner service");
} else {
mTunerService->getTunerHalVersion(&mTunerVersion);
@@ -54,575 +43,124 @@ TunerClient::TunerClient() {
}
TunerClient::~TunerClient() {
- mTuner = NULL;
- mTuner_1_1 = NULL;
mTunerVersion = 0;
- mTunerService = NULL;
+ mTunerService = nullptr;
}
-vector<FrontendId> TunerClient::getFrontendIds() {
- vector<FrontendId> ids;
+vector<int32_t> TunerClient::getFrontendIds() {
+ vector<int32_t> ids;
- if (mTunerService != NULL) {
- vector<int32_t> v;
- Status s = mTunerService->getFrontendIds(&v);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS || v.size() == 0) {
+ if (mTunerService != nullptr) {
+ Status s = mTunerService->getFrontendIds(&ids);
+ if (!s.isOk()) {
ids.clear();
- return ids;
- }
- for (int32_t id : v) {
- ids.push_back(static_cast<FrontendId>(id));
}
- return ids;
- }
-
- if (mTuner != NULL) {
- Result res;
- mTuner->getFrontendIds([&](Result r, const hardware::hidl_vec<FrontendId>& frontendIds) {
- res = r;
- ids = frontendIds;
- });
- if (res != Result::SUCCESS || ids.size() == 0) {
- ALOGW("Frontend ids not available");
- ids.clear();
- return ids;
- }
- return ids;
}
return ids;
}
-
-sp<FrontendClient> TunerClient::openFrontend(int frontendHandle) {
- if (mTunerService != NULL) {
+sp<FrontendClient> TunerClient::openFrontend(int32_t frontendHandle) {
+ if (mTunerService != nullptr) {
shared_ptr<ITunerFrontend> tunerFrontend;
Status s = mTunerService->openFrontend(frontendHandle, &tunerFrontend);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS
- || tunerFrontend == NULL) {
- return NULL;
+ if (!s.isOk() || tunerFrontend == nullptr) {
+ return nullptr;
}
- int id;
+ int32_t id;
s = tunerFrontend->getFrontendId(&id);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ tunerFrontend->close();
+ return nullptr;
}
- TunerFrontendInfo aidlFrontendInfo;
- s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ FrontendInfo frontendInfo;
+ s = mTunerService->getFrontendInfo(id, &frontendInfo);
+ if (!s.isOk()) {
+ tunerFrontend->close();
+ return nullptr;
}
- return new FrontendClient(tunerFrontend, aidlFrontendInfo.type);
+ return new FrontendClient(tunerFrontend, frontendInfo.type);
}
- if (mTuner != NULL) {
- int id = getResourceIdFromHandle(frontendHandle, FRONTEND);
- sp<IFrontend> hidlFrontend = openHidlFrontendById(id);
- if (hidlFrontend != NULL) {
- FrontendInfo hidlInfo;
- Result res = getHidlFrontendInfo(id, hidlInfo);
- if (res != Result::SUCCESS) {
- return NULL;
- }
- sp<FrontendClient> frontendClient = new FrontendClient(
- NULL, (int)hidlInfo.type);
- frontendClient->setHidlFrontend(hidlFrontend);
- frontendClient->setId(id);
- return frontendClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
-shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
- if (mTunerService != NULL) {
- TunerFrontendInfo aidlFrontendInfo;
+shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int32_t id) {
+ if (mTunerService != nullptr) {
+ FrontendInfo aidlFrontendInfo;
Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
- }
- return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo));
- }
-
- if (mTuner != NULL) {
- FrontendInfo hidlInfo;
- Result res = getHidlFrontendInfo(id, hidlInfo);
- if (res != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
- return make_shared<FrontendInfo>(hidlInfo);
+ return make_shared<FrontendInfo>(aidlFrontendInfo);
}
- return NULL;
+ return nullptr;
}
-shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) {
- 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;
- FrontendDtmbCapabilities dtmbCaps;
- mTuner_1_1->getFrontendDtmbCapabilities(id,
- [&](Result r, const FrontendDtmbCapabilities& caps) {
- dtmbCaps = caps;
- result = r;
- });
- if (result == Result::SUCCESS) {
- return make_shared<FrontendDtmbCapabilities>(dtmbCaps);
- }
- }
-
- return NULL;
-}
-
-sp<DemuxClient> TunerClient::openDemux(int demuxHandle) {
- if (mTunerService != NULL) {
+sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) {
+ if (mTunerService != nullptr) {
shared_ptr<ITunerDemux> tunerDemux;
Status s = mTunerService->openDemux(demuxHandle, &tunerDemux);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
return new DemuxClient(tunerDemux);
}
- if (mTuner != NULL) {
- sp<DemuxClient> demuxClient = new DemuxClient(NULL);
- int demuxId;
- sp<IDemux> hidlDemux = openHidlDemux(demuxId);
- if (hidlDemux != NULL) {
- demuxClient->setHidlDemux(hidlDemux);
- demuxClient->setId(demuxId);
- return demuxClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
- if (mTunerService != NULL) {
- TunerDemuxCapabilities aidlCaps;
+ if (mTunerService != nullptr) {
+ DemuxCapabilities aidlCaps;
Status s = mTunerService->getDemuxCaps(&aidlCaps);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
- return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+ return make_shared<DemuxCapabilities>(aidlCaps);
}
- if (mTuner != NULL) {
- Result res;
- DemuxCapabilities caps;
- mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
- caps = demuxCaps;
- res = r;
- });
- if (res == Result::SUCCESS) {
- return make_shared<DemuxCapabilities>(caps);
- }
- }
-
- return NULL;
+ return nullptr;
}
-sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) {
- if (mTunerService != NULL) {
+sp<DescramblerClient> TunerClient::openDescrambler(int32_t descramblerHandle) {
+ if (mTunerService != nullptr) {
shared_ptr<ITunerDescrambler> tunerDescrambler;
Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
return new DescramblerClient(tunerDescrambler);
}
- if (mTuner != NULL) {
- sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL);
- sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
- if (hidlDescrambler != NULL) {
- descramblerClient->setHidlDescrambler(hidlDescrambler);
- return descramblerClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
-sp<LnbClient> TunerClient::openLnb(int lnbHandle) {
- if (mTunerService != NULL) {
+sp<LnbClient> TunerClient::openLnb(int32_t lnbHandle) {
+ if (mTunerService != nullptr) {
shared_ptr<ITunerLnb> tunerLnb;
Status s = mTunerService->openLnb(lnbHandle, &tunerLnb);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
return new LnbClient(tunerLnb);
}
- if (mTuner != NULL) {
- int id = getResourceIdFromHandle(lnbHandle, LNB);
- sp<LnbClient> lnbClient = new LnbClient(NULL);
- sp<ILnb> hidlLnb = openHidlLnbById(id);
- if (hidlLnb != NULL) {
- lnbClient->setHidlLnb(hidlLnb);
- lnbClient->setId(id);
- return lnbClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
sp<LnbClient> TunerClient::openLnbByName(string lnbName) {
- if (mTunerService != NULL) {
+ if (mTunerService != nullptr) {
shared_ptr<ITunerLnb> tunerLnb;
Status s = mTunerService->openLnbByName(lnbName, &tunerLnb);
- if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
- return NULL;
+ if (!s.isOk()) {
+ return nullptr;
}
return new LnbClient(tunerLnb);
}
- if (mTuner != NULL) {
- sp<LnbClient> lnbClient = new LnbClient(NULL);
- LnbId id;
- sp<ILnb> hidlLnb = openHidlLnbByName(lnbName, id);
- if (hidlLnb != NULL) {
- lnbClient->setHidlLnb(hidlLnb);
- lnbClient->setId(id);
- return lnbClient;
- }
- }
-
- return NULL;
+ return nullptr;
}
-/////////////// TunerClient Helper Methods ///////////////////////
-
-void TunerClient::updateTunerResources() {
- if (mTuner == NULL) {
- return;
- }
-
- // Connect with Tuner Resource Manager.
- ::ndk::SpAIBinder binder(AServiceManager_getService("tv_tuner_resource_mgr"));
- mTunerResourceManager = ITunerResourceManager::fromBinder(binder);
-
- updateFrontendResources();
- updateLnbResources();
- // TODO: update Demux, Descrambler.
-}
-
-// TODO: remove after migration to Tuner Service is done.
-void TunerClient::updateFrontendResources() {
- vector<FrontendId> ids = getFrontendIds();
- if (ids.size() == 0) {
- return;
- }
- vector<TunerFrontendInfo> infos;
- for (int i = 0; i < ids.size(); i++) {
- shared_ptr<FrontendInfo> frontendInfo = getFrontendInfo((int)ids[i]);
- if (frontendInfo == NULL) {
- continue;
- }
- TunerFrontendInfo tunerFrontendInfo{
- .handle = getResourceHandleFromId((int)ids[i], FRONTEND),
- .type = static_cast<int>(frontendInfo->type),
- .exclusiveGroupId = static_cast<int>(frontendInfo->exclusiveGroupId),
- };
- infos.push_back(tunerFrontendInfo);
- }
- mTunerResourceManager->setFrontendInfoList(infos);
-}
-
-void TunerClient::updateLnbResources() {
- vector<int> handles = getLnbHandles();
- if (handles.size() == 0) {
- return;
- }
- mTunerResourceManager->setLnbInfoList(handles);
-}
-
-sp<ITuner> TunerClient::getHidlTuner() {
- if (mTuner == NULL) {
- mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
- mTuner_1_1 = ::android::hardware::tv::tuner::V1_1::ITuner::getService();
-
- if (mTuner_1_1 == NULL) {
- ALOGW("Failed to get tuner 1.1 service.");
- mTuner = ITuner::getService();
- if (mTuner == NULL) {
- ALOGW("Failed to get tuner 1.0 service.");
- } else {
- mTunerVersion = TUNER_HAL_VERSION_1_0;
- }
- } else {
- mTuner = static_cast<sp<ITuner>>(mTuner_1_1);
- mTunerVersion = TUNER_HAL_VERSION_1_1;
- }
- }
- return mTuner;
-}
-
-sp<IFrontend> TunerClient::openHidlFrontendById(int id) {
- sp<IFrontend> fe;
- Result res;
- mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) {
- fe = frontend;
- res = r;
- });
- if (res != Result::SUCCESS || fe == nullptr) {
- ALOGE("Failed to open frontend");
- return NULL;
- }
- return fe;
-}
-
-Result TunerClient::getHidlFrontendInfo(int id, FrontendInfo& feInfo) {
- Result res;
- mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& info) {
- feInfo = info;
- res = r;
- });
- return res;
-}
-
-sp<IDemux> TunerClient::openHidlDemux(int& demuxId) {
- sp<IDemux> demux;
- Result res;
-
- mTuner->openDemux([&](Result result, uint32_t id, const sp<IDemux>& demuxSp) {
- demux = demuxSp;
- demuxId = id;
- res = result;
- });
- if (res != Result::SUCCESS || demux == nullptr) {
- ALOGE("Failed to open demux");
- return NULL;
- }
- return demux;
-}
-
-sp<ILnb> TunerClient::openHidlLnbById(int id) {
- sp<ILnb> lnb;
- Result res;
-
- mTuner->openLnbById(id, [&](Result r, const sp<ILnb>& lnbSp) {
- res = r;
- lnb = lnbSp;
- });
- if (res != Result::SUCCESS || lnb == nullptr) {
- ALOGE("Failed to open lnb by id");
- return NULL;
- }
- return lnb;
-}
-
-sp<ILnb> TunerClient::openHidlLnbByName(string name, LnbId& lnbId) {
- sp<ILnb> lnb;
- Result res;
-
- mTuner->openLnbByName(name, [&](Result r, LnbId id, const sp<ILnb>& lnbSp) {
- res = r;
- lnb = lnbSp;
- lnbId = id;
- });
- if (res != Result::SUCCESS || lnb == nullptr) {
- ALOGE("Failed to open lnb by name");
- return NULL;
- }
- return lnb;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-vector<int> TunerClient::getLnbHandles() {
- vector<int> lnbHandles;
- if (mTuner != NULL) {
- Result res;
- vector<LnbId> lnbIds;
- mTuner->getLnbIds([&](Result r, const hardware::hidl_vec<LnbId>& ids) {
- lnbIds = ids;
- res = r;
- });
- if (res != Result::SUCCESS || lnbIds.size() == 0) {
- ALOGW("Lnb isn't available");
- } else {
- for (int i = 0; i < lnbIds.size(); i++) {
- lnbHandles.push_back(getResourceHandleFromId((int)lnbIds[i], LNB));
- }
- }
- }
-
- return lnbHandles;
-}
-
-sp<IDescrambler> TunerClient::openHidlDescrambler() {
- sp<IDescrambler> descrambler;
- Result res;
-
- mTuner->openDescrambler([&](Result r, const sp<IDescrambler>& descramblerSp) {
- res = r;
- descrambler = descramblerSp;
- });
-
- if (res != Result::SUCCESS || descrambler == NULL) {
- return NULL;
- }
-
- return descrambler;
-}
-
-DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
- DemuxCapabilities caps{
- .numDemux = (uint32_t)aidlCaps.numDemux,
- .numRecord = (uint32_t)aidlCaps.numRecord,
- .numPlayback = (uint32_t)aidlCaps.numPlayback,
- .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
- .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
- .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
- .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
- .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
- .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
- .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
- .filterCaps = (uint32_t)aidlCaps.filterCaps,
- .bTimeFilter = aidlCaps.bTimeFilter,
- };
- caps.linkCaps.resize(aidlCaps.linkCaps.size());
- copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
- return caps;
-}
-
-FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
- FrontendInfo hidlFrontendInfo {
- .type = static_cast<FrontendType>(aidlFrontendInfo.type),
- .minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency),
- .maxFrequency = static_cast<uint32_t>(aidlFrontendInfo.maxFrequency),
- .minSymbolRate = static_cast<uint32_t>(aidlFrontendInfo.minSymbolRate),
- .maxSymbolRate = static_cast<uint32_t>(aidlFrontendInfo.maxSymbolRate),
- .acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange),
- .exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId),
- };
-
- 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;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-int TunerClient::getResourceIdFromHandle(int handle, int /*resourceType*/) {
- return (handle & 0x00ff0000) >> 16;
-}
-
-// TODO: remove after migration to Tuner Service is done.
-int TunerClient::getResourceHandleFromId(int id, int resourceType) {
- return (resourceType & 0x000000ff) << 24
- | (id << 16)
- | (mResourceRequestCount++ & 0xffff);
-}
} // namespace android
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 9671cf787e3e..641f106d6dff 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -17,12 +17,8 @@
#ifndef _ANDROID_MEDIA_TV_TUNER_CLIENT_H_
#define _ANDROID_MEDIA_TV_TUNER_CLIENT_H_
-#include <aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.h>
#include <aidl/android/media/tv/tuner/ITunerService.h>
-#include <aidl/android/media/tv/tuner/TunerFrontendInfo.h>
#include <android/binder_parcel_utils.h>
-#include <android/hardware/tv/tuner/1.1/ITuner.h>
-#include <android/hardware/tv/tuner/1.1/types.h>
#include "DemuxClient.h"
#include "ClientHelper.h"
@@ -32,32 +28,19 @@
using Status = ::ndk::ScopedAStatus;
-using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
+using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
+using ::aidl::android::hardware::tv::tuner::FrontendInfo;
+using ::aidl::android::hardware::tv::tuner::Result;
using ::aidl::android::media::tv::tuner::ITunerService;
-using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
-using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
-
-using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities;
-using ::android::hardware::tv::tuner::V1_0::FrontendId;
-using ::android::hardware::tv::tuner::V1_0::ITuner;
-using ::android::hardware::tv::tuner::V1_0::LnbId;
-using ::android::hardware::tv::tuner::V1_0::Result;
-using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities;
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,
- DEMUX,
- DESCRAMBLER,
-} TunerResourceType;
+const static int32_t TUNER_HAL_VERSION_UNKNOWN = 0;
+const static int32_t TUNER_HAL_VERSION_1_0 = 1 << 16;
+const static int32_t TUNER_HAL_VERSION_1_1 = (1 << 16) | 1;
+const static int32_t TUNER_HAL_VERSION_2_0 = 2 << 16;
struct TunerClient : public RefBase {
@@ -70,7 +53,7 @@ public:
*
* @return a list of the available frontend ids
*/
- vector<FrontendId> getFrontendIds();
+ vector<int32_t> getFrontendIds();
/**
* Open a new interface of FrontendClient given a frontendHandle.
@@ -78,7 +61,7 @@ public:
* @param frontendHandle the handle of the frontend granted by TRM.
* @return a newly created FrontendClient interface.
*/
- sp<FrontendClient> openFrontend(int frontendHandle);
+ sp<FrontendClient> openFrontend(int32_t frontendHandle);
/**
* Retrieve the granted frontend's information.
@@ -86,15 +69,7 @@ public:
* @param id the id of the frontend granted by TRM.
* @return the information for the frontend.
*/
- shared_ptr<FrontendInfo> getFrontendInfo(int id);
-
- /**
- * Retrieve the DTMB frontend's capabilities.
- *
- * @param id the id of the DTMB frontend.
- * @return the capabilities of the frontend.
- */
- shared_ptr<FrontendDtmbCapabilities> getFrontendDtmbCapabilities(int id);
+ shared_ptr<FrontendInfo> getFrontendInfo(int32_t id);
/**
* Open a new interface of DemuxClient given a demuxHandle.
@@ -102,7 +77,7 @@ public:
* @param demuxHandle the handle of the demux granted by TRM.
* @return a newly created DemuxClient interface.
*/
- sp<DemuxClient> openDemux(int demuxHandle);
+ sp<DemuxClient> openDemux(int32_t demuxHandle);
/**
* Retrieve the Demux capabilities.
@@ -117,7 +92,7 @@ public:
* @param descramblerHandle the handle of the descrambler granted by TRM.
* @return a newly created DescramblerClient interface.
*/
- sp<DescramblerClient> openDescrambler(int descramblerHandle);
+ sp<DescramblerClient> openDescrambler(int32_t descramblerHandle);
/**
* Open a new interface of LnbClient given an lnbHandle.
@@ -125,7 +100,7 @@ public:
* @param lnbHandle the handle of the LNB granted by TRM.
* @return a newly created LnbClient interface.
*/
- sp<LnbClient> openLnb(int lnbHandle);
+ sp<LnbClient> openLnb(int32_t lnbHandle);
/**
* Open a new interface of LnbClient given a LNB name.
@@ -139,54 +114,18 @@ public:
* Get the current Tuner HAL version. The high 16 bits are the major version number
* while the low 16 bits are the minor version. Default value is unknown version 0.
*/
- int getHalTunerVersion() { return mTunerVersion; }
+ int32_t getHalTunerVersion() { return mTunerVersion; }
private:
- sp<ITuner> getHidlTuner();
- sp<IFrontend> openHidlFrontendById(int id);
- sp<IDemux> openHidlDemux(int& demuxId);
- Result getHidlFrontendInfo(int id, FrontendInfo& info);
- sp<ILnb> openHidlLnbById(int id);
- sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
- sp<IDescrambler> openHidlDescrambler();
- vector<int> getLnbHandles();
- DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
- FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
- void updateTunerResources();
- void updateFrontendResources();
- void updateLnbResources();
-
- int getResourceIdFromHandle(int handle, int resourceType);
-
- int getResourceHandleFromId(int id, int resourceType);
-
/**
* An AIDL Tuner Service Singleton assigned at the first time the Tuner Client
* connects with the Tuner Service. Default null when the service does not exist.
*/
static shared_ptr<ITunerService> mTunerService;
- /**
- * A Tuner 1.0 HAL interface that is ready before connecting to the TunerService
- * This is a temprary connection before the Tuner Framework fully migrates to the TunerService.
- * Default null.
- */
- static sp<ITuner> mTuner;
-
- /**
- * A Tuner 1.1 HAL interface that is ready before connecting to the TunerService
- * This is a temprary connection before the Tuner Framework fully migrates to the TunerService.
- * Default null.
- */
- static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1;
-
// An integer that carries the Tuner version. The high 16 bits are the major version number
// while the low 16 bits are the minor version. Default value is unknown version 0.
- static int mTunerVersion;
-
- shared_ptr<ITunerResourceManager> mTunerResourceManager;
-
- int mResourceRequestCount = 0;
+ static int32_t mTunerVersion;
};
} // namespace android
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 7919723b80a8..a00031a28b4f 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -20,16 +20,15 @@ import android.filterfw.core.Filter;
import android.filterfw.core.FilterContext;
import android.filterfw.core.Frame;
import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GLFrame;
import android.filterfw.core.GenerateFieldPort;
import android.filterfw.core.GenerateFinalPort;
-import android.filterfw.core.GLFrame;
import android.filterfw.core.MutableFrameFormat;
import android.filterfw.core.ShaderProgram;
import android.filterfw.format.ImageFormat;
import android.graphics.SurfaceTexture;
-import android.os.ConditionVariable;
import android.opengl.Matrix;
-
+import android.os.ConditionVariable;
import android.util.Log;
/** <p>A filter that converts textures from a SurfaceTexture object into frames for
@@ -57,7 +56,7 @@ public class SurfaceTextureSource extends Filter {
public void onSurfaceTextureSourceReady(SurfaceTexture source);
}
/** A callback to send the internal SurfaceTexture object to, once it is
- * created. This callback will be called when the the filter graph is
+ * created. This callback will be called when the filter graph is
* preparing to execute, but before any processing has actually taken
* place. The SurfaceTexture object passed to this callback is the only way
* to feed this filter. When the filter graph is shutting down, this
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index 1131c623e428..27cf9434b34d 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -255,7 +255,7 @@ public class AudioManagerTest extends AudioVolumesTestBase {
AudioVolumeGroupCallbackHelper vgCbReceiver = new AudioVolumeGroupCallbackHelper();
mAudioManager.registerVolumeGroupCallback(mContext.getMainExecutor(), vgCbReceiver);
- final List<Integer> publicStreams = Ints.asList(PUBLIC_STREAM_TYPES);
+ final List<Integer> publicStreams = Ints.asList(AudioManager.getPublicStreamTypes());
try {
// Validate Audio Volume Groups callback reception
for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
index c0f596b974e1..0e918d13e042 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioProductStrategyTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
@@ -71,7 +72,7 @@ public class AudioProductStrategyTest extends AudioVolumesTestBase {
assertNotNull(audioProductStrategies);
assertTrue(audioProductStrategies.size() > 0);
- for (final int streamType : PUBLIC_STREAM_TYPES) {
+ for (final int streamType : AudioManager.getPublicStreamTypes()) {
AudioAttributes aaFromStreamType =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
streamType);
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
index a17d65cf7376..b30ef307855d 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumesTestBase.java
@@ -37,15 +37,10 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
// Default matches the invalid (empty) attributes from native.
// The difference is the input source default which is not aligned between native and java
public static final AudioAttributes sDefaultAttributes =
- AudioProductStrategy.sDefaultAttributes;
+ AudioProductStrategy.getDefaultAttributes();
public static final AudioAttributes sInvalidAttributes = new AudioAttributes.Builder().build();
- public final int[] PUBLIC_STREAM_TYPES = { AudioManager.STREAM_VOICE_CALL,
- AudioManager.STREAM_SYSTEM, AudioManager.STREAM_RING, AudioManager.STREAM_MUSIC,
- AudioManager.STREAM_ALARM, AudioManager.STREAM_NOTIFICATION,
- AudioManager.STREAM_DTMF, AudioManager.STREAM_ACCESSIBILITY };
-
public AudioVolumesTestBase() {
super("com.android.audiopolicytest", AudioPolicyTest.class);
}
@@ -63,7 +58,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
}
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -89,7 +84,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
assertTrue(!avg.getAudioAttributes().isEmpty());
AudioAttributes avgAttributes = sDefaultAttributes;
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(AudioProductStrategy.getDefaultAttributes())) {
avgAttributes = aa;
break;
}
@@ -114,7 +109,7 @@ public class AudioVolumesTestBase extends ActivityInstrumentationTestCase2<Audio
// Store the original volumes that that they can be recovered in tearDown().
mOriginalStreamVolumes.clear();
- for (int streamType : PUBLIC_STREAM_TYPES) {
+ for (int streamType : AudioManager.getPublicStreamTypes()) {
mOriginalStreamVolumes.put(streamType, mAudioManager.getStreamVolume(streamType));
}
// Store the original volume per attributes so that they can be recovered in tearDown()
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index e74bda8a6b35..388a65d2a904 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -1810,7 +1810,7 @@ public class CameraTestUtils extends Assert {
/**
* Simple validation of JPEG image size and format.
* <p>
- * Only validate the image object sanity. It is fast, but doesn't actually
+ * Only validate the image object consistency. It is fast, but doesn't actually
* check the buffer data. Assert is used here as it make no sense to
* continue the test if the jpeg image captured has some serious failures.
* </p>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
index cbdcc36eea3a..66288f08b5a6 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2StillCaptureTest.java
@@ -572,7 +572,7 @@ public class Camera2StillCaptureTest extends Camera2SurfaceViewTestCase {
/**
* Validate JPEG capture image object soundness and test.
* <p>
- * In addition to image object sanity, this function also does the decoding
+ * In addition to image object consistency, this function also does the decoding
* test, which is slower.
* </p>
*
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 54442b37d7d9..39add7ea8a42 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -927,7 +927,7 @@ public class CameraMetadataTest extends junit.framework.TestCase {
};
assertArrayEquals(expectedRaw16Outputs, map.getOutputs(ImageFormat.RAW_SENSOR));
- // Finally, do a round-trip check as a sanity
+ // Finally, do a round-trip check for consistency
checkKeyMarshal(
"android.scaler.availableInputOutputFormatsMap",
new ReprocessFormatsMap(contents),
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index cdf4851a3e3d..3faed5525946 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Jy het <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te bestuur"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Hierdie program is nodig om jou <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index a03ea0dfcd4c..99466d7d2e1a 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; እንዲያስተዳድር ያቀናብሩት"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
- <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> - &lt;strong&gt;&lt;/strong&gt; እንዲያስተዳደር ይፍቀዱ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"የእርስዎን <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 970c46bd6133..c3f1e73d3ad4 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعة"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"يجب توفّر تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
- <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏السماح للتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بإدارة &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"هذا التطبيق مطلوب لإدارة <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"السماح"</string>
+ <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 477844c2a477..2a2bc25c7881 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ছেট কৰক - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
- <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; পৰিচালনা কৰিবলৈ দিয়ক"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"আপোনাৰ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্‌টোৰ আৱশ্যক। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string>
+ <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index f10c639a0368..2ec13c587166 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə olunmasını ayarlayın - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME">%1$s</xliff:g> tələb olunur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
- <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı idarə etməsinə icazə verin"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu tətbiq <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizi idarə etmək üçün lazımdır. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index e8542f364027..d687b05896c6 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 13be6f245ac0..2236052f5545 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
<string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Гэта праграма неабходная для кіравання профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 3bda5e6104c8..996ca9062f80 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"За управление на <xliff:g id="PROFILE_NAME">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешаване на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Това приложение е необходимо за управление на <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index d3bc5152af9a..16d25ce57870 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"দেখুন"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; সেট করুন"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"না থাক"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"আপনার &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; -কে অনুমতি দিন"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"আপনার <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string>
+ <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 905b3061ffc6..10f753c1cf45 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Potrebna je aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja uređajem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ova aplikacija je potrebna za upravljanje profilom: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index 86dc6940abe2..e55a033cf800 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
<string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g> és necessària per gestionar el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aquesta aplicació es necessita per gestionar el teu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permet"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permetis"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 389ccd0ccc95..48fbda10cfed 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ke správě profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povolit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Tato aplikace je nutná pro správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 5a31f9bba4b6..446c301747df 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
+ <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string>
<string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angiv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillad at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; kan administrere: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Du skal bruge denne app for at administrere dit <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index b643eb2936ce..33d831d41aec 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
<string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) zu verwalten. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; erlauben, dein Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; zu verwalten"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Diese App wird zur Verwaltung des Profils „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ benötigt. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 60de2ffe0572..7a78c0669825 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
- <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να διαχειρίζεται τη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση του προφίλ σας <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index 2fed1ae7fec3..a6ebe658d622 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Set &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> is needed to manage your <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, thanks"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to manage your &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"This app is needed to manage your <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Allow"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index f3c4b1dcb434..6cc56a4f44e7 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎device‎‏‎‎‏‎"</string>
<string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎Set &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
- <string name="profile_summary" msgid="2009764182871566255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="consent_yes" msgid="4055438216605487056">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎Yes‎‏‎‎‏‎"</string>
- <string name="consent_no" msgid="1335543792857823917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎No thanks‎‏‎‎‏‎"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to manage your &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎This app is needed to manage your ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎Allow‎‏‎‎‏‎"</string>
+ <string name="consent_no" msgid="2640796915611404382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎Don’t allow‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 4fbb57ed9440..dc1315916159 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se requiere <xliff:g id="APP_NAME">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administre tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app es necesaria para administrar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 5ca9305ce4d6..7e37c1d33522 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Haz que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Se necesita <xliff:g id="APP_NAME">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Se necesita esta aplicación para gestionar tu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"No permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 357f05237b85..399556de789b 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
<string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME">%2$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallata teie seadet &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Seda rakendust on vaja teie profiili <xliff:g id="PROFILE_NAME">%1$s</xliff:g> haldamiseks. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Luba"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 14c7154acdd3..764505e52959 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
<string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kudea dezan"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kudeatzeko. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ez"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Eman &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kudeatzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aplikazioa <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kudeatzeko beharrezkoa da. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index 6bb9620f09f7..07d04aa1ecc9 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‏&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> به <xliff:g id="APP_NAME">%1$s</xliff:g> نیاز دارید. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
- <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏مجاز کردن &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"این برنامه برای مدیریت <xliff:g id="PROFILE_NAME">%1$s</xliff:g> شما لازم است. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"مجاز بودن"</string>
+ <string name="consent_no" msgid="2640796915611404382">"مجاز نبودن"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 5a9c1cd66aa8..528d16c2e425 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
<string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Aseta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnointiin. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voi hallinnoida tätä laitettasi: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Profiilin (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) ylläpitoon tarvitaan tätä sovellusta. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Salli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index b31babda1a00..1dcd3375c1cf 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette application est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 08c93a2c31c6..ba2fc8ea9424 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
<string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à gérer votre &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cette appli est nécessaire pour gérer votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index c95b90e73edc..5f9a8d786739 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Necesítase a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
- <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o teu dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta aplicación é necesaria para xestionar o teu perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index 7e4104208446..71cf012f24b3 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
- <string name="profile_summary" msgid="2009764182871566255">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
- <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"તમારા &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂર કરો"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"તમારી <xliff:g id="PROFILE_NAME">%1$s</xliff:g> મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string>
+ <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index ac95cc620bfa..d4dd1cb4f1b1 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
<string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; को प्रबंधित करने के लिए, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को सेट करें"</string>
- <string name="profile_summary" msgid="2009764182871566255">"आपके <xliff:g id="PROFILE_NAME">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
- <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को, अपनी &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; मैनेज करने की अनुमति दें"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> को मैनेज करने के लिए, यह ऐप्लिकेशन ज़रूरी है. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index df8451f5f127..87c5ae2670e9 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
<string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> potrebna je za upravljanje vašim <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Dopustite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta je aplikacija potrebna za upravljanje vašim profilom <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index ff1c6c54d7ba..c7ceb384ed16 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazás beállítása a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kezelésére"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Szükség van a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazásra a(z) <xliff:g id="PROFILE_NAME">%2$s</xliff:g> kezeléséhez. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Igen"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nem"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; engedélyezése a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; kezelésére"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index 194223d1d416..26f7990d7b66 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Ձեր <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կառավարել ձեր &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; սարքը"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Այս հավելվածն անհրաժեշտ է <xliff:g id="PROFILE_NAME">%1$s</xliff:g> սարքը կամ պրոֆիլը կառավարելու համար։ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 58bf3cb4bca4..b0618d409527 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Perlu <xliff:g id="APP_NAME">%1$s</xliff:g> untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index cc5b98939b71..b7d7c6abf749 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
<string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Þetta forrit er nauðsynlegt til að hafa umsjón með <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 4cbefd801187..ce003e7d7645 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"È richiesta l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> per gestire il tuo <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sì"</string>
- <string name="consent_no" msgid="1335543792857823917">"No, grazie"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Consenti all\'app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di gestire &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Questa app è necessaria per gestire il tuo <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 8663e56ecf14..54c523c92f4b 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
- <string name="chooser_title" msgid="2262294130493605839">"‏בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="chooser_title" msgid="2262294130493605839">"‏בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
<string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
- <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לנהל את &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"האפליקציה הזו נחוצה כדי לנהל את ה<xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string>
+ <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index ca17336bfb23..f92fafe54fde 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME">%1$s</xliff:g> が必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
- <string name="consent_no" msgid="1335543792857823917">"いいえ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理を許可する"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"このアプリは <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"許可"</string>
+ <string name="consent_no" msgid="2640796915611404382">"許可しない"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index 300c94f63cbe..34efdd2223ac 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
<string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
- <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ნება დართეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-ს&lt;/strong&gt;, რომ მართოს თქვენი &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ეს აპი საჭიროა თქვენი <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string>
+ <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index 94d6c3ed2d5f..3c7f697b984f 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; құрылғысын басқаруға рұқсат беру"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бұл қолданба <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профиліңізді басқару үшін қажет. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index db13fe7884af..74ccd84956ea 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
- <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; គ្រប់គ្រង &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; របស់អ្នក"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string>
+ <string name="consent_no" msgid="2640796915611404382">"កុំអនុញ្ញាត"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 0225166849fd..2a82d1fd6bac 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
- <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ನಿಮ್ಮ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ಅನುಮತಿಸಿ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್‌ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 1363e57e39a3..6ca01bff5364 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
<string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)을(를) 관리하도록 설정"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> 프로필을 관리하려면 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"예"</string>
- <string name="consent_no" msgid="1335543792857823917">"취소"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; 기기를 관리하도록 허용"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"이 앱은 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 프로필을 관리하는 데 필요합니다. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"허용"</string>
+ <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index c01e2350aa04..18d38a832e0b 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
<string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарсын"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME">%1$s</xliff:g> керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
- <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарууга уруксат бериңиз"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Бул колдонмо <xliff:g id="PROFILE_NAME">%1$s</xliff:g> профилиңизди башкаруу үчүн керек. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Уруксат берүү"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 68218dd79c50..a1eb71342446 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ຈັດການ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານໄດ້"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index 5fd8280affca..65f371d3d312 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
<string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"laikrodis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; būtų valdomas programos &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Norint valdyti jūsų <xliff:g id="PROFILE_NAME">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tvarkyti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ši programa reikalinga norint tvarkyti jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index bf036ec70d73..b18bfe4a4243 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
<string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) pārvaldībai"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Atļauja lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pārvaldīt ierīci &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Šī lietotne ir nepieciešama jūsu profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 427ca8f940a0..9d745c413b39 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со вашиот &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Апликацијава е потребна за управување со вашиот <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index a48c45f73ae7..28c88da9e4af 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
<string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
- <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"നിങ്ങളുടെ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; മാനേജ് ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കുക"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string>
+ <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 7ac20e613185..11e61d910ee4 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
<string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Таны <xliff:g id="PROFILE_NAME">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
- <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;-г удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г зөвшөөрнө үү"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Энэ апп таны <xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 144698b9bc4e..c73ed283c768 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -19,9 +19,9 @@
<string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
- <string name="profile_name_watch" msgid="576290739483672360">"पाहा"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
- <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
+ <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"तुमचे &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला अनुमती द्या"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"तुमची <xliff:g id="PROFILE_NAME">%1$s</xliff:g> प्रोफाइल व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 7bea2c91fd2d..d2aebb45f49e 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
<string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
- <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; anda"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Apl ini diperlukan untuk mengurus <xliff:g id="PROFILE_NAME">%1$s</xliff:g> anda. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 9c2783cdcbbb..45c9d8b240fa 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
<string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သတ်မှတ်ပါ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
- <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"သင်၏ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ခွင့်ပြုပါ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"သင်၏ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်ကိုလိုအပ်သည်။ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 26fbb0350edc..af1ffe94cd36 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Angi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillat at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; administrerer &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Denne appen kreves for å administrere <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index f289b3780672..b29f94ce1b05 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string>
+ <string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string>
<string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
<string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्न &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; तोक्नुहोस्"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
- <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"आफ्नो &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; लाई &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्ने अनुमति दिनुहोस्"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"तपाईंको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एपलाई अनुमति दिनु पर्ने हुन्छ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string>
+ <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 0c9cdffd4e17..a56fb9a62ce7 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string>
<string name="profile_name_watch" msgid="576290739483672360">"horloge"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; instellen om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; te beheren"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Je hebt <xliff:g id="APP_NAME">%1$s</xliff:g> nodig om je <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nee, bedankt"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan je &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; te beheren"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Deze app is vereist om je <xliff:g id="PROFILE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index c8c680f8f3a9..8e43213a9e43 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"ଆପଣଙ୍କ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 0da94105a576..54f4f8c42302 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
- <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ਤੁਹਾਡੇ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਇਹ ਐਪ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ਇਜਾਜ਼ਤ ਦਿਓ"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ਇਜਾਜ਼ਤ ਨਾ ਦਿਓ"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index b07af57a936a..a989baa4e36c 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Ustaw aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> na urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wymagana do zarządzania profilem <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Tak"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, dziękuję"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Zezwól na zarządzanie urządzeniem &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; przez aplikację &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacja jest niezbędna do zarządzania profilem <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 16906f62f9f1..d2724c0a8127 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index 745d1630dd86..2f5a53b2e2a7 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permita que a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça a gestão do seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esta app é necessária para gerir o seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 16906f62f9f1..d2724c0a8127 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
- <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gerencie seu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Esse app é necessário para gerenciar seu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 187cfbdfe6f0..4df74de2a441 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă gestioneze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Această aplicație este necesară pentru a gestiona <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 8dd9a392712b..ea372d51658b 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
<string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>). <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Нет"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять этим устройством: &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Это приложение необходимо для управления вашим профилем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Запретить"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index 9e7c02e0c0d9..a5c2c8842830 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට <xliff:g id="APP_NAME">%1$s</xliff:g> අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
- <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; කළමනාකරණය කිරීමට ඉඩ දෙන්න"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"මෙම යෙදුමට ඔබගේ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 55a47c2df427..a9bf77f809e5 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
<string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Na správu profilu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Povoliť aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; spravovať zariadenie &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Táto aplikácia sa vyžaduje na správu profilu <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 159afd543609..4eb8f5029fcd 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Za upravljanje naprave <xliff:g id="PROFILE_NAME">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ta aplikacija je potrebna za upravljanje profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 6fa759c15905..34357b478a17 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Nevojitet <xliff:g id="APP_NAME">%1$s</xliff:g> për të menaxhuar profilin tënd të <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
- <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të menaxhojë pajisjen tënde &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ky aplikacion nevojitet për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index fdbbe8e668f5..37af18534712 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
<string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
- <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволите апликацији &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља уређајем &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Ова апликација је потребна за управљање профилом <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index bfd25162aec6..f78fadf8d864 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
<string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Konfigurera &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> krävs för att hantera din <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
- <string name="consent_no" msgid="1335543792857823917">"Nej tack"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar din &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Appen behövs för att hantera <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 437ae7f332e8..495c44111290 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hapana"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; idhibiti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yako"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Programu hii inahitajika ili udhibiti wasifu wako wa <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 9b4a720a4863..20845bd69562 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
<string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அமையுங்கள்"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
- <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"உங்கள் &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"உங்கள் <xliff:g id="PROFILE_NAME">%1$s</xliff:g> சுயவிவரத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவைப்படுகிறது. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string>
+ <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 6e785de583aa..c855cf2a8501 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
<string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను సెటప్ చేయండి"</string>
- <string name="profile_summary" msgid="2009764182871566255">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
- <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"మీ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించండి;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"మీ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string>
+ <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index b727d42035dd..d78ada26cd8a 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
<string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"ต้องใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g> <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
- <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; จัดการ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; ของคุณ"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ของคุณ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string>
+ <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index a93282a8fae0..03165b43ab0f 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
<string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Kailangan ang <xliff:g id="APP_NAME">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na pamahalaan ang iyong &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Kinakailangan ang app na ito para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 3abe064d60bc..b2c1cf2fa832 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
<string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazınızı yönetecek şekilde ayarlayın"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
- <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulaması &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; cihazınızı yönetebilsin mi?"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu uygulama, <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilinizin yönetilmesi için gereklidir. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string>
+ <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 161d95e127e8..61b78e9a0b18 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
<string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати своїм пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME">%1$s</xliff:g>. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
- <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; керувати вашим пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Цей додаток потрібен, щоб керувати профілем \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\". <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index dce18152dff4..ee7992109a18 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
<string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"آپ کے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
- <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"‏اپنے &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; کا نظم کرنے کے لیے ‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"اس ایپ کو آپ کے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا نظم کرنے کی ضرورت ہے۔ <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string>
+ <string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 2ca27b530651..7221b6d24893 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
<string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
- <string name="profile_summary" msgid="2009764182871566255">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> qurilmasini boshqarish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> zarur. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
- <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; qurilmasini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga ruxsat bering"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Bu ilova <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index 06a1ab6846ae..2819e1df75ba 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
<string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"Cần có <xliff:g id="APP_NAME">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
- <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Cho phép &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; của bạn"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"Cần có ứng dụng này để quản lý <xliff:g id="PROFILE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 12bfcf3629cd..1440c401673e 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"设为由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"若要管理<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"好"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允许&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"需要使用此应用,才能管理您的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允许"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允许"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index 0c583b211035..e3f1eb1249f1 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -16,12 +16,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
+ <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string>
<string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"必須使用 <xliff:g id="APP_NAME">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理您的&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"必須使用此應用程式,才能管理<xliff:g id="PROFILE_NAME">%1$s</xliff:g>。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index 519f0e8a7082..9f4041dd0aff 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
<string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"如要管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"是"</string>
- <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"需使用這個應用程式,才能管理「<xliff:g id="PROFILE_NAME">%1$s</xliff:g>」。<xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"允許"</string>
+ <string name="consent_no" msgid="2640796915611404382">"不允許"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 7721b54166f5..dc933ae21599 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -20,8 +20,8 @@
<string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
<string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
- <string name="confirmation_title" msgid="4751119145078041732">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="profile_summary" msgid="2009764182871566255">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%3$s</xliff:g>"</string>
- <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
- <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
+ <string name="confirmation_title" msgid="8455544820286920304">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuthi iphathe i-&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt; yakho"</string>
+ <string name="profile_summary" msgid="2059360676631420073">"I-app iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> yakho. <xliff:g id="PRIVILEGES_DISCPLAIMER">%2$s</xliff:g>"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string>
</resources>
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
new file mode 100644
index 000000000000..eb1faa0aa25c
--- /dev/null
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityAnnotations.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Type annotations for constants used in the connectivity API surface.
+ *
+ * The annotations are maintained in a separate class so that it can be built as
+ * a separate library that other modules can build against, as Typedef should not
+ * be exposed as SystemApi.
+ *
+ * @hide
+ */
+public final class ConnectivityAnnotations {
+ private ConnectivityAnnotations() {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER,
+ ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY,
+ ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE,
+ })
+ public @interface MultipathPreference {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = false, value = {
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED,
+ ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED,
+ })
+ public @interface RestrictBackgroundStatus {}
+}
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index b4d520d7d71a..1bc498342d24 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -33,6 +33,10 @@
<intent-filter>
<action android:name="android.os.image.action.START_INSTALL" />
<category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="content" />
+ <data android:scheme="file" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
</intent-filter>
</activity>
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 48cdf1647d86..197b7b2b0eaf 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -31,7 +31,7 @@
android:directBootAware="true">
<receiver android:name=".TemporaryFileManager"
- android:exported="true">
+ android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
@@ -76,7 +76,7 @@
<receiver android:name=".InstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
- android:exported="true">
+ android:exported="false">
<intent-filter android:priority="1">
<action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
</intent-filter>
@@ -106,14 +106,14 @@
<receiver android:name=".UninstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
- android:exported="true">
+ android:exported="false">
<intent-filter android:priority="1">
<action android:name="com.android.packageinstaller.ACTION_UNINSTALL_COMMIT" />
</intent-filter>
</receiver>
<receiver android:name=".PackageInstalledReceiver"
- android:exported="true">
+ android:exported="false">
<intent-filter android:priority="1">
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5d7b9bb36f75..cef9014ec229 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -19,6 +19,9 @@
},
{
"name": "CtsPackageUninstallTestCases"
+ },
+ {
+ "name": "PackageInstallerTests"
}
]
-} \ No newline at end of file
+}
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 9cb44161b1ac..806734ffa997 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -52,7 +52,7 @@
<string name="generic_error_dlg_text" msgid="5287861443265795232">"యాప్‌ను అన్ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
<string name="uninstall_application_title" msgid="4045420072401428123">"యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయి"</string>
<string name="uninstall_update_title" msgid="824411791011583031">"అప్‌డేట్ అన్‌ఇన్‌స్టాల్ చేయి"</string>
- <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది క్రింది యాప్‌లో ఒక భాగం:"</string>
+ <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది కింది యాప్‌లో ఒక భాగం:"</string>
<string name="uninstall_application_text" msgid="3816830743706143980">"మీరు ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
<string name="uninstall_application_text_all_users" msgid="575491774380227119">"మీరు ఈ యాప్‌ను "<b>"అందరు"</b>" వినియోగదారులకు అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? అప్లికేషన్, దాని డేటా పరికరంలోని "<b>"అందరు"</b>" వినియోగదారుల నుండి తీసివేయబడుతుంది."</string>
<string name="uninstall_application_text_user" msgid="498072714173920526">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
index bc740ab15a90..6a9145db9a06 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
@@ -66,8 +66,8 @@ public class WearPackageUtil {
/**
* In order to make sure that the Wearable Asset Manager has a reasonable apk that can be used
* by the PackageManager, we will parse it before sending it to the PackageManager.
- * Unfortunately, PackageParser needs a file to parse. So, we have to temporarily convert the fd
- * to a File.
+ * Unfortunately, ParsingPackageUtils needs a file to parse. So, we have to temporarily convert
+ * the fd to a File.
*
* @param context
* @param fd FileDescriptor to convert to File
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index a65bf41210a1..e8ed88fc0222 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -11,33 +11,6 @@ android_library {
name: "SettingsLib",
- defaults: [
- "SettingsLibDependenciesWithoutWifiTracker",
- ],
-
- // TODO(b/149540986): revert this change.
- static_libs: [
- // All other dependent components should be put in
- // "SettingsLibDependenciesWithoutWifiTracker".
- "WifiTrackerLib",
- ],
-
- // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
- // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
-
- resource_dirs: ["res"],
-
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-
- min_sdk_version: "29",
-
-}
-
-java_defaults {
- name: "SettingsLibDependenciesWithoutWifiTracker",
static_libs: [
"androidx.annotation_annotation",
"androidx.legacy_legacy-support-v4",
@@ -48,6 +21,7 @@ java_defaults {
"androidx.mediarouter_mediarouter-nodeps",
"iconloader",
+ "WifiTrackerLibRes",
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
"SettingsLibActionBarShadow",
@@ -63,6 +37,7 @@ java_defaults {
"SettingsLibProgressBar",
"SettingsLibAdaptiveIcon",
"SettingsLibRadioButtonPreference",
+ "SettingsLibSelectorWithWidgetPreference",
"SettingsLibDisplayDensityUtils",
"SettingsLibUtils",
"SettingsLibEmergencyNumber",
@@ -74,6 +49,19 @@ java_defaults {
"SettingsLibTwoTargetPreference",
"SettingsLibSettingsTransition",
],
+
+ // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
+ // LOCAL_SHARED_JAVA_LIBRARIES := androidx.lifecycle_lifecycle-common
+
+ resource_dirs: ["res"],
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ min_sdk_version: "29",
+
}
// NOTE: Keep this module in sync with ./common.mk
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
new file mode 100644
index 000000000000..d7e778ff854b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Weier"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
new file mode 100644
index 000000000000..6701dea718ce
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"አሰናብት"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
new file mode 100644
index 000000000000..0f1b9acbd15b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"إغلاق"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
new file mode 100644
index 000000000000..21dd94c66e18
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"অগ্ৰাহ্য কৰক"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
new file mode 100644
index 000000000000..7f91eb49db71
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Qapadın"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..ca16c3de21db
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
new file mode 100644
index 000000000000..b0980ea12641
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Адхіліць"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
new file mode 100644
index 000000000000..cccbf964e53c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отхвърляне"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
new file mode 100644
index 000000000000..e0dfcf24700c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
new file mode 100644
index 000000000000..5e46c6c28820
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacivanje"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
new file mode 100644
index 000000000000..81bb04803aea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
new file mode 100644
index 000000000000..ac7623ecbfa4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavřít"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
new file mode 100644
index 000000000000..8c185d97fede
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Luk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
new file mode 100644
index 000000000000..006301b2e94e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
new file mode 100644
index 000000000000..65843b282770
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Παράβλεψη"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..418c1d59e6e7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..e2dae5ec9bfb
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‎‎Dismiss‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..4816be6d6921
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Descartar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
new file mode 100644
index 000000000000..5e820238c259
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cerrar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
new file mode 100644
index 000000000000..a688723554bd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Loobu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
new file mode 100644
index 000000000000..64dd1c5d52db
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Baztertu"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
new file mode 100644
index 000000000000..bd8985f81d35
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"رد شدن"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
new file mode 100644
index 000000000000..c3841576b140
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ohita"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..dd5889cbf983
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
new file mode 100644
index 000000000000..dd5889cbf983
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
new file mode 100644
index 000000000000..d7876261ebdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
new file mode 100644
index 000000000000..1fe4c5c0c946
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"છોડી દો"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
new file mode 100644
index 000000000000..f66ee7fed130
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"खारिज करें"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
new file mode 100644
index 000000000000..f7e7cd0d930f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbaci"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
new file mode 100644
index 000000000000..1551c843bab4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Bezárás"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
new file mode 100644
index 000000000000..e014cce62eca
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Փակել"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
new file mode 100644
index 000000000000..607e8117515f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Tutup"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
new file mode 100644
index 000000000000..4afc614c5a11
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hunsa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
new file mode 100644
index 000000000000..81bb04803aea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
new file mode 100644
index 000000000000..aa4c669c8e2c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"סגירה"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
new file mode 100644
index 000000000000..b42f6e683dd9
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"閉じる"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
new file mode 100644
index 000000000000..7bde8b6dd0f8
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"უარყოფა"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
new file mode 100644
index 000000000000..01235e09182b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
new file mode 100644
index 000000000000..4e14820be7c7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ច្រានចោល"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
new file mode 100644
index 000000000000..b9a542045794
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ವಜಾಗೊಳಿಸಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
new file mode 100644
index 000000000000..9b5169976a64
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"닫기"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
new file mode 100644
index 000000000000..affb8ec93fdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабуу"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
new file mode 100644
index 000000000000..7079f7c73ccb
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ປິດໄວ້"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
new file mode 100644
index 000000000000..4cee14a440ee
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Atsisakyti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
new file mode 100644
index 000000000000..120a76286a90
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Nerādīt"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
new file mode 100644
index 000000000000..76a4390a6b72
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отфрли"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
new file mode 100644
index 000000000000..5a4e14c88c36
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
new file mode 100644
index 000000000000..3974470813e4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Үл хэрэгсэх"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
new file mode 100644
index 000000000000..4bd44859ec8f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
new file mode 100644
index 000000000000..290323be8392
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ketepikan"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
new file mode 100644
index 000000000000..52ecc4940c7d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ပယ်ရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
new file mode 100644
index 000000000000..c1e39a417a87
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Lukk"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
new file mode 100644
index 000000000000..15102541bb87
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
new file mode 100644
index 000000000000..920349ff6fd4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Sluiten"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
new file mode 100644
index 000000000000..36e7d3bdb216
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
new file mode 100644
index 000000000000..250ef2e86aca
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ਖਾਰਜ ਕਰੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
new file mode 100644
index 000000000000..9ad630adc92d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zamknij"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..80b70ae95655
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..d7876261ebdd
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
new file mode 100644
index 000000000000..80b70ae95655
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
new file mode 100644
index 000000000000..18b6a0e4c830
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
new file mode 100644
index 000000000000..b6946572c400
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрыть"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
new file mode 100644
index 000000000000..d818cf7db236
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ඉවත ලන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
new file mode 100644
index 000000000000..4f59f851e574
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavrieť"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
new file mode 100644
index 000000000000..1ca68bf0709c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Opusti"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
new file mode 100644
index 000000000000..dbe792759608
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hiq"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
new file mode 100644
index 000000000000..68a2d5bbf343
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
new file mode 100644
index 000000000000..ef2df3ca45ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorera"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
new file mode 100644
index 000000000000..ebb0c0228b3a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ondoa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
new file mode 100644
index 000000000000..9b175c7b92c4
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"மூடும்"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
new file mode 100644
index 000000000000..22a6f59f091c
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"విస్మరించు"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
new file mode 100644
index 000000000000..6546bfa57ee8
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ปิด"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
new file mode 100644
index 000000000000..9b944de688ef
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"I-dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
new file mode 100644
index 000000000000..96d49e941cde
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Kapat"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
new file mode 100644
index 000000000000..f51b0e75f9ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрити"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
new file mode 100644
index 000000000000..ad3fafb14f3a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"برخاست کریں"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
new file mode 100644
index 000000000000..1e247457e554
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Yopish"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
new file mode 100644
index 000000000000..a30cdbf1829f
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Đóng"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..a8f36e4db962
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"关闭"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..b9ee658dd4fa
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..b9ee658dd4fa
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
new file mode 100644
index 000000000000..80faa175920b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cashisa"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-af/strings.xml b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
new file mode 100644
index 000000000000..c17f3edc6744
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Kom meer te wete"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-am/strings.xml b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
new file mode 100644
index 000000000000..02e61312d77c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"የበለጠ ለመረዳት"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
new file mode 100644
index 000000000000..1f279a6a9f72
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزيد من المعلومات"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-as/strings.xml b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
new file mode 100644
index 000000000000..a34b474edac2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"অধিক জানক"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-az/strings.xml b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
new file mode 100644
index 000000000000..b49036ee0bbc
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ətraflı məlumat"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-be/strings.xml b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
new file mode 100644
index 000000000000..f9d6129ba1f6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Даведацца больш"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
new file mode 100644
index 000000000000..605663d33c0c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Научете повече"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
new file mode 100644
index 000000000000..c58142d72618
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
new file mode 100644
index 000000000000..7abf10f24957
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Més informació"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
new file mode 100644
index 000000000000..decbb68efe5d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Další informace"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-da/strings.xml b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
new file mode 100644
index 000000000000..81d1c7cf5d04
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Få flere oplysninger"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
new file mode 100644
index 000000000000..fe885aa6f091
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-el/strings.xml b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
new file mode 100644
index 000000000000..5a30833222ad
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Μάθετε περισσότερα"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..924d735d4d6a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..bd12547ad6ff
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎Learn more‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..f31d9ea26005
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-es/strings.xml b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
new file mode 100644
index 000000000000..f31d9ea26005
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-et/strings.xml b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
new file mode 100644
index 000000000000..78b65edd9e57
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lisateave"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
new file mode 100644
index 000000000000..cf7fa003ca28
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lortu informazio gehiago"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
new file mode 100644
index 000000000000..464c58e02477
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"بیشتر بدانید"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
new file mode 100644
index 000000000000..856b96288cda
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lue lisää"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..6d856caab1ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
new file mode 100644
index 000000000000..6d856caab1ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
new file mode 100644
index 000000000000..cde57d868361
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Máis información"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
new file mode 100644
index 000000000000..54249b88b47e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"વધુ જાણો"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
new file mode 100644
index 000000000000..95ae240d1ea0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ज़्यादा जानें"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
new file mode 100644
index 000000000000..993ec9a92182
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
new file mode 100644
index 000000000000..ae3c94809f7c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"További információ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
new file mode 100644
index 000000000000..de9137b0f241
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Իմանալ ավելին"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-in/strings.xml b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
new file mode 100644
index 000000000000..4b5cb16f95ca
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pelajari lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-is/strings.xml b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
new file mode 100644
index 000000000000..111094c201cd
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Nánar"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-it/strings.xml b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
new file mode 100644
index 000000000000..053c80c6c4ea
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Scopri di più"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
new file mode 100644
index 000000000000..55b01873c2fc
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"מידע נוסף"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
new file mode 100644
index 000000000000..3312cb430ed7
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"詳細"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
new file mode 100644
index 000000000000..67bb223f68ec
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"შეიტყვეთ მეტი"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
new file mode 100644
index 000000000000..db11a766e8a5
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Толығырақ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-km/strings.xml b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
new file mode 100644
index 000000000000..1977dd323b20
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ស្វែងយល់បន្ថែម"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
new file mode 100644
index 000000000000..47fa3d5482ba
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
new file mode 100644
index 000000000000..d8d220068c03
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"자세히 알아보기"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
new file mode 100644
index 000000000000..74c6a497e3d0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Кеңири маалымат"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
new file mode 100644
index 000000000000..2e4124b8eb42
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ສຶກສາເພີ່ມເຕີມ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
new file mode 100644
index 000000000000..2981c665abfe
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Sužinokite daugiau"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
new file mode 100644
index 000000000000..97663056591f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Uzzināt vairāk"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
new file mode 100644
index 000000000000..1f734c5a12a4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Дознајте повеќе"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
new file mode 100644
index 000000000000..1cd466bcce34
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"കൂടുതലറിയുക"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
new file mode 100644
index 000000000000..8bac1eb110a0
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Нэмэлт мэдээлэл авах"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
new file mode 100644
index 000000000000..45387200bdc8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
new file mode 100644
index 000000000000..cd1b17a96ea4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ketahui lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-my/strings.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
new file mode 100644
index 000000000000..751a87a33da2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
new file mode 100644
index 000000000000..08de00943699
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Finn ut mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
new file mode 100644
index 000000000000..ecfec36337ee
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
new file mode 100644
index 000000000000..156408135ad1
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Meer informatie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
new file mode 100644
index 000000000000..e7924d646861
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
new file mode 100644
index 000000000000..1ce2ef246a36
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ਹੋਰ ਜਾਣੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
new file mode 100644
index 000000000000..5709f3e8cdee
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Więcej informacji"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
new file mode 100644
index 000000000000..bc410ab8b62f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
new file mode 100644
index 000000000000..2b5011764508
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Aflați mai multe"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
new file mode 100644
index 000000000000..bedde40f5ad7
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Подробнее…"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-si/strings.xml b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
new file mode 100644
index 000000000000..1a60601cf36c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"තව දැන ගන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
new file mode 100644
index 000000000000..c1008e5a433a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ďalšie informácie"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
new file mode 100644
index 000000000000..79e0a735b927
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Več o tem"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
new file mode 100644
index 000000000000..0fea476cfbb8
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Mëso më shumë"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
new file mode 100644
index 000000000000..9a73269b9d0e
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
new file mode 100644
index 000000000000..a78c3cbc6c0b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Läs mer"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
new file mode 100644
index 000000000000..52b1732043a3
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pata maelezo zaidi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
new file mode 100644
index 000000000000..75fc7c1adff2
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"மேலும் அறிக"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-te/strings.xml b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
new file mode 100644
index 000000000000..6c8d6799754b
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"మరింత తెలుసుకోండి"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-th/strings.xml b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
new file mode 100644
index 000000000000..025a2f0f3705
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ดูข้อมูลเพิ่มเติม"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
new file mode 100644
index 000000000000..4b6f830a4371
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Matuto pa"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
new file mode 100644
index 000000000000..77d151309c4c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Daha fazla bilgi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
new file mode 100644
index 000000000000..cec933d374a4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Докладніше"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
new file mode 100644
index 000000000000..1dceea7739eb
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزید جانیں"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
new file mode 100644
index 000000000000..58239498256a
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Batafsil"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
new file mode 100644
index 000000000000..d6c46389fdfa
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Tìm hiểu thêm"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..446c8ce4bc4c
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"了解详情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..8ab38c658c8f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..8ab38c658c8f
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
new file mode 100644
index 000000000000..b53eb85428b4
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Funda kabanzi"</string>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 266fc78b2b6a..1f80a3e04093 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -170,7 +170,14 @@ public class IllustrationPreference extends Preference {
}
/**
- * Sets image drawable to display image in {@link LottieAnimationView}
+ * Gets the lottie illustration resource id.
+ */
+ public int getLottieAnimationResId() {
+ return mImageResId;
+ }
+
+ /**
+ * Sets the image drawable to display image in {@link LottieAnimationView}.
*
* @param imageDrawable the drawable of an image
*/
@@ -183,7 +190,16 @@ public class IllustrationPreference extends Preference {
}
/**
- * Sets image uri to display image in {@link LottieAnimationView}
+ * Gets the image drawable from display image in {@link LottieAnimationView}.
+ *
+ * @return the drawable of an image
+ */
+ public Drawable getImageDrawable() {
+ return mImageDrawable;
+ }
+
+ /**
+ * Sets the image uri to display image in {@link LottieAnimationView}.
*
* @param imageUri the Uri of an image
*/
@@ -195,6 +211,15 @@ public class IllustrationPreference extends Preference {
}
}
+ /**
+ * Gets the image uri from display image in {@link LottieAnimationView}.
+ *
+ * @return the Uri of an image
+ */
+ public Uri getImageUri() {
+ return mImageUri;
+ }
+
private void resetImageResourceCache() {
mImageDrawable = null;
mImageUri = null;
diff --git a/packages/SettingsLib/RadioButtonPreference/Android.bp b/packages/SettingsLib/RadioButtonPreference/Android.bp
index 28ff71f22840..1387daa959be 100644
--- a/packages/SettingsLib/RadioButtonPreference/Android.bp
+++ b/packages/SettingsLib/RadioButtonPreference/Android.bp
@@ -15,6 +15,7 @@ android_library {
static_libs: [
"androidx.preference_preference",
+ "SettingsLibSelectorWithWidgetPreference",
"SettingsLibSettingsTheme",
],
diff --git a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
index f50127f6e21f..02d3c06dcea2 100644
--- a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
+++ b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
@@ -26,6 +26,10 @@ import androidx.preference.CheckBoxPreference;
import androidx.preference.PreferenceViewHolder;
/**
+ * DEPRECATED. Please use SelectorWithWidgetPreference instead.
+ *
+ * This file has been moved there and will be removed once all callers are updated.
+ *
* Check box preference with check box replaced by radio button.
*
* Functionally speaking, it's actually a CheckBoxPreference. We only modified
@@ -37,6 +41,8 @@ import androidx.preference.PreferenceViewHolder;
*
* RadioButtonPreference can assign a extraWidgetListener to show a gear icon
* on the right side that can open another page.
+ *
+ * @Deprecated
*/
public class RadioButtonPreference extends CheckBoxPreference {
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
new file mode 100644
index 000000000000..bcc64d3cd234
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -0,0 +1,27 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_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: "SettingsLibSelectorWithWidgetPreference",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.preference_preference",
+ "SettingsLibSettingsTheme",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml b/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml
new file mode 100644
index 000000000000..51fc7ed64660
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/drawable/ic_settings_accent.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/drawable/ic_settings_accent.xml
new file mode 100644
index 000000000000..6521bc9e0fb9
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/drawable/ic_settings_accent.xml
@@ -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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorAccent">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector> \ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
new file mode 100644
index 000000000000..8bb56ff0a07d
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -0,0 +1,123 @@
+<?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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/selector_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/selector_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_checkbox.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_checkbox.xml
new file mode 100644
index 000000000000..6dd16709b7b5
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_checkbox.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.
+ -->
+
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@null"
+ android:focusable="false"
+ android:clickable="false" /> \ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.xml
new file mode 100644
index 000000000000..cf6371d04f78
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_widget_radiobutton.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.
+ -->
+
+<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@null"
+ android:focusable="false"
+ android:clickable="false" />
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-af/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-af/strings.xml
new file mode 100644
index 000000000000..0a4288c771a8
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Instellings"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-am/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-am/strings.xml
new file mode 100644
index 000000000000..f8db6d877fa6
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ቅንብሮች"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ar/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ar/strings.xml
new file mode 100644
index 000000000000..778715cd21cc
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"الإعدادات"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-as/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-as/strings.xml
new file mode 100644
index 000000000000..21be4ade3906
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ছেটিং"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-az/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-az/strings.xml
new file mode 100644
index 000000000000..064f14ae6746
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ayarlar"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..d51823f3c74e
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Podešavanja"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-be/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-be/strings.xml
new file mode 100644
index 000000000000..e6c513ec5710
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Налады"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bg/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bg/strings.xml
new file mode 100644
index 000000000000..df46ee73c9fb
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Настройки"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bn/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bn/strings.xml
new file mode 100644
index 000000000000..31eb432685de
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"সেটিংস"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bs/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bs/strings.xml
new file mode 100644
index 000000000000..442217ac3e47
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Postavke"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ca/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ca/strings.xml
new file mode 100644
index 000000000000..5077025ec637
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Configuració"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-cs/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-cs/strings.xml
new file mode 100644
index 000000000000..96d9019403a4
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Nastavení"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-da/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-da/strings.xml
new file mode 100644
index 000000000000..fa2b17e41572
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Indstillinger"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-de/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-de/strings.xml
new file mode 100644
index 000000000000..bbc309c791f8
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Einstellungen"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-el/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-el/strings.xml
new file mode 100644
index 000000000000..b7b732612c82
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ρυθμίσεις"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rAU/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..b048293434f3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Settings"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rCA/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..b048293434f3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Settings"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rGB/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..b048293434f3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Settings"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rIN/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..b048293434f3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Settings"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..d5c868b2dbf6
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎Settings‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es-rUS/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..7146861a8419
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Configuración"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es/strings.xml
new file mode 100644
index 000000000000..64dddc9cf9c5
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ajustes"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-et/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-et/strings.xml
new file mode 100644
index 000000000000..b9c0e6fac355
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Seaded"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-eu/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-eu/strings.xml
new file mode 100644
index 000000000000..1033ce2526d0
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ezarpenak"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fa/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fa/strings.xml
new file mode 100644
index 000000000000..76a8bd781970
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"تنظیمات"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fi/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fi/strings.xml
new file mode 100644
index 000000000000..6c0caaa3267f
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Asetukset"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr-rCA/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..c894348b1808
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Paramètres"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr/strings.xml
new file mode 100644
index 000000000000..c894348b1808
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Paramètres"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gl/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gl/strings.xml
new file mode 100644
index 000000000000..7146861a8419
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Configuración"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gu/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gu/strings.xml
new file mode 100644
index 000000000000..7ae0ea2983da
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"સેટિંગ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hi/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hi/strings.xml
new file mode 100644
index 000000000000..f38a00fb29e5
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"सेटिंग"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hr/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hr/strings.xml
new file mode 100644
index 000000000000..442217ac3e47
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Postavke"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hu/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hu/strings.xml
new file mode 100644
index 000000000000..bdab37285e2d
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Beállítások"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hy/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hy/strings.xml
new file mode 100644
index 000000000000..d91474ac39ba
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Կարգավորումներ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-in/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-in/strings.xml
new file mode 100644
index 000000000000..ceb5aa7d5e9f
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Setelan"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-is/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-is/strings.xml
new file mode 100644
index 000000000000..bd3d86360a70
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Stillingar"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-it/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-it/strings.xml
new file mode 100644
index 000000000000..1c0204d0e837
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Impostazioni"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-iw/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-iw/strings.xml
new file mode 100644
index 000000000000..28ff3e72addf
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"הגדרות"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ja/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ja/strings.xml
new file mode 100644
index 000000000000..b8acb7b6ecee
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"設定"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ka/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ka/strings.xml
new file mode 100644
index 000000000000..671fc1c5f1ae
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"პარამეტრები"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kk/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kk/strings.xml
new file mode 100644
index 000000000000..bcd6784bc8f8
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Параметрлер"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-km/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-km/strings.xml
new file mode 100644
index 000000000000..df2c7d0fca22
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ការកំណត់"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kn/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kn/strings.xml
new file mode 100644
index 000000000000..aab8bedcda1b
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ko/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ko/strings.xml
new file mode 100644
index 000000000000..8a543188e201
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"설정"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml
new file mode 100644
index 000000000000..f4a893a1cd83
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Жөндөөлөр"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lo/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lo/strings.xml
new file mode 100644
index 000000000000..2a91bc3d199e
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ການຕັ້ງຄ່າ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lt/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lt/strings.xml
new file mode 100644
index 000000000000..e452cbb03b43
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Nustatymai"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lv/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lv/strings.xml
new file mode 100644
index 000000000000..e8bb10295922
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Iestatījumi"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mk/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mk/strings.xml
new file mode 100644
index 000000000000..6031e9fdd420
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Поставки"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ml/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ml/strings.xml
new file mode 100644
index 000000000000..cb613a12bb0e
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ക്രമീകരണം"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mn/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mn/strings.xml
new file mode 100644
index 000000000000..194aa028669c
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Тохиргоо"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mr/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mr/strings.xml
new file mode 100644
index 000000000000..f2f027adeccd
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"सेटिंग्ज"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ms/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ms/strings.xml
new file mode 100644
index 000000000000..51c2f196aaf7
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Tetapan"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-my/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-my/strings.xml
new file mode 100644
index 000000000000..a29153bf5638
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ဆက်တင်များ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nb/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nb/strings.xml
new file mode 100644
index 000000000000..fdfdecd36144
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Innstillinger"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ne/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ne/strings.xml
new file mode 100644
index 000000000000..e1a5b1b8eb49
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"सेटिङ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nl/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nl/strings.xml
new file mode 100644
index 000000000000..9a7365d10468
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Instellingen"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml
new file mode 100644
index 000000000000..8b211e790017
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ସେଟିଂସ୍"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pa/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pa/strings.xml
new file mode 100644
index 000000000000..23b79a1a7587
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ਸੈਟਿੰਗਾਂ"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pl/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pl/strings.xml
new file mode 100644
index 000000000000..45c6e985f1a4
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ustawienia"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rBR/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..87dbe7d9af9e
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Configurações"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rPT/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..5ac727828221
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Definições"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt/strings.xml
new file mode 100644
index 000000000000..87dbe7d9af9e
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Configurações"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ro/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ro/strings.xml
new file mode 100644
index 000000000000..7d20417f3085
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Setări"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ru/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ru/strings.xml
new file mode 100644
index 000000000000..df46ee73c9fb
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Настройки"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-si/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-si/strings.xml
new file mode 100644
index 000000000000..10f8475f9ac8
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"සැකසීම්"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sk/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sk/strings.xml
new file mode 100644
index 000000000000..51b5bea1ad3c
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Nastavenia"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sl/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sl/strings.xml
new file mode 100644
index 000000000000..41b33fe761ef
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Nastavitve"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sq/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sq/strings.xml
new file mode 100644
index 000000000000..8c0c32230399
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Cilësimet"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sr/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sr/strings.xml
new file mode 100644
index 000000000000..1478a00e110d
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Подешавања"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sv/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sv/strings.xml
new file mode 100644
index 000000000000..ef5420619a8a
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Inställningar"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sw/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sw/strings.xml
new file mode 100644
index 000000000000..d5dbf19b252c
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Mipangilio"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ta/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ta/strings.xml
new file mode 100644
index 000000000000..f9294292e589
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"அமைப்புகள்"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-te/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-te/strings.xml
new file mode 100644
index 000000000000..154c9ac7ea86
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"సెట్టింగ్‌లు"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-th/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-th/strings.xml
new file mode 100644
index 000000000000..8e7218dac66a
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"การตั้งค่า"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tl/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tl/strings.xml
new file mode 100644
index 000000000000..f923a180ed86
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Mga Setting"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tr/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tr/strings.xml
new file mode 100644
index 000000000000..064f14ae6746
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Ayarlar"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uk/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uk/strings.xml
new file mode 100644
index 000000000000..8f03a19ecc52
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Налаштування"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ur/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ur/strings.xml
new file mode 100644
index 000000000000..24e76a647001
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"ترتیبات"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uz/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uz/strings.xml
new file mode 100644
index 000000000000..e8fb658b8104
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Sozlamalar"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-vi/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-vi/strings.xml
new file mode 100644
index 000000000000..ab7136b06f02
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Cài đặt"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rCN/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..9c74a49eb767
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"设置"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rHK/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..b8acb7b6ecee
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"設定"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rTW/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..b8acb7b6ecee
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"設定"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zu/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zu/strings.xml
new file mode 100644
index 000000000000..bb87ec28ebe3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settings_label" msgid="5948970810295631236">"Amasethingi"</string>
+</resources>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values/strings.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/strings.xml
new file mode 100644
index 000000000000..ff3f90cfffd3
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/strings.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.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Content description for RadioButton with extra gear icon [CHAR LIMIT=NONE] -->
+ <string name="settings_label">Settings</string>
+
+</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
new file mode 100644
index 000000000000..1ecc42235c3b
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * Selector preference (checkbox or radio button) with an optional additional widget.
+ *
+ * Functionally speaking, it's a CheckBoxPreference. When styled like a radio button,
+ * it only "looks like" a RadioButtonPreference.
+ *
+ * In other words, there's no "RadioButtonPreferenceGroup" in this
+ * implementation. When you check one preference, if you want to
+ * uncheck all the other preferences, you should do that by code yourself.
+ *
+ * SelectorWithWidgetPreference can assign a extraWidgetListener to show a gear icon
+ * on the right side that can open another page.
+ */
+public class SelectorWithWidgetPreference extends CheckBoxPreference {
+
+ /**
+ * Interface definition for a callback to be invoked when the preference is clicked.
+ */
+ public interface OnClickListener {
+ /**
+ * Called when a preference has been clicked.
+ *
+ * @param emiter The clicked preference
+ */
+ void onRadioButtonClicked(SelectorWithWidgetPreference emiter);
+ }
+
+ private OnClickListener mListener = null;
+ private View mAppendix;
+ private int mAppendixVisibility = -1;
+
+ private View mExtraWidgetContainer;
+ private ImageView mExtraWidget;
+ private boolean mIsCheckBox = false; // whether to display this button as a checkbox
+
+ private View.OnClickListener mExtraWidgetOnClickListener;
+
+ /**
+ * Perform inflation from XML and apply a class-specific base style.
+ *
+ * @param context The {@link Context} this is associated with, through which it can
+ * access the current theme, resources, {@link SharedPreferences}, etc.
+ * @param attrs The attributes of the XML tag that is inflating the preference
+ * @param defStyle An attribute in the current theme that contains a reference to a style
+ * resource that supplies default values for the view. Can be 0 to not
+ * look for defaults.
+ */
+ public SelectorWithWidgetPreference(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ /**
+ * Perform inflation from XML and apply a class-specific base style.
+ *
+ * @param context The {@link Context} this is associated with, through which it can
+ * access the current theme, resources, {@link SharedPreferences}, etc.
+ * @param attrs The attributes of the XML tag that is inflating the preference
+ */
+ public SelectorWithWidgetPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ /**
+ * Constructor to create a preference, which will display with a checkbox style.
+ *
+ * @param context The {@link Context} this is associated with.
+ * @param isCheckbox Whether this preference should display as a checkbox.
+ */
+ public SelectorWithWidgetPreference(Context context, boolean isCheckbox) {
+ super(context, null);
+ mIsCheckBox = isCheckbox;
+ init();
+ }
+
+ /**
+ * Constructor to create a preference.
+ *
+ * @param context The Context this is associated with.
+ */
+ public SelectorWithWidgetPreference(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Sets the callback to be invoked when this preference is clicked by the user.
+ *
+ * @param listener The callback to be invoked
+ */
+ public void setOnClickListener(OnClickListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Processes a click on the preference.
+ */
+ @Override
+ public void onClick() {
+ if (mListener != null) {
+ mListener.onRadioButtonClicked(this);
+ }
+ }
+
+ /**
+ * Binds the created View to the data for this preference.
+ *
+ * <p>This is a good place to grab references to custom Views in the layout and set
+ * properties on them.
+ *
+ * <p>Make sure to call through to the superclass's implementation.
+ *
+ * @param holder The ViewHolder that provides references to the views to fill in. These views
+ * will be recycled, so you should not hold a reference to them after this method
+ * returns.
+ */
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ View summaryContainer = holder.findViewById(R.id.summary_container);
+ if (summaryContainer != null) {
+ summaryContainer.setVisibility(
+ TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE);
+ mAppendix = holder.findViewById(R.id.appendix);
+ if (mAppendix != null && mAppendixVisibility != -1) {
+ mAppendix.setVisibility(mAppendixVisibility);
+ }
+ }
+
+ mExtraWidget = (ImageView) holder.findViewById(R.id.selector_extra_widget);
+ mExtraWidgetContainer = holder.findViewById(R.id.selector_extra_widget_container);
+
+ setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
+ }
+
+ /**
+ * Set the visibility state of appendix view.
+ *
+ * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or {@link View#GONE}.
+ */
+ public void setAppendixVisibility(int visibility) {
+ if (mAppendix != null) {
+ mAppendix.setVisibility(visibility);
+ }
+ mAppendixVisibility = visibility;
+ }
+
+ /**
+ * Sets the callback to be invoked when extra widget is clicked by the user.
+ *
+ * @param listener The callback to be invoked
+ */
+ public void setExtraWidgetOnClickListener(View.OnClickListener listener) {
+ mExtraWidgetOnClickListener = listener;
+
+ if (mExtraWidget == null || mExtraWidgetContainer == null) {
+ return;
+ }
+
+ mExtraWidget.setOnClickListener(mExtraWidgetOnClickListener);
+
+ mExtraWidgetContainer.setVisibility((mExtraWidgetOnClickListener != null)
+ ? View.VISIBLE : View.GONE);
+ }
+
+ private void init() {
+ if (mIsCheckBox) {
+ setWidgetLayoutResource(R.layout.preference_widget_checkbox);
+ } else {
+ setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
+ }
+ setLayoutResource(R.layout.preference_selector_with_widget);
+ setIconSpaceReserved(false);
+ }
+}
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
index 44f6f5439af3..9dcb5bc8c7fa 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
@@ -52,9 +52,9 @@ public final class BuildCompatUtils {
}
return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31)
- || (VERSION.CODENAME.length() == 1
- && VERSION.CODENAME.compareTo("S") >= 0
- && VERSION.CODENAME.compareTo("Z") <= 0);
+ || (VERSION.CODENAME.length() >= 1
+ && VERSION.CODENAME.toUpperCase().charAt(0) >= 'S'
+ && VERSION.CODENAME.toUpperCase().charAt(0) <= 'Z');
}
private BuildCompatUtils() {}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0442ba20f8db..51e954f97913 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Kan nie skandeer vir netwerke nie"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Geen/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Geen"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-persoonlik"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-persoonlik"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-onderneming"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-onderneming"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-onderneming"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-onderneming"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-onderneming"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-persoonlik"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-persoonlik"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Geen/verbeterde oopsekuriteit"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Verbeterde oopsekuriteit"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-onderneming 192-bis"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Gestoor"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Ontkoppel"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Gedeaktiveer"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Draadlose skermsertifisering"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktiveer Wi-Fi-woordryke aanmelding"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Regulering van Wi-Fi-opsporing"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nie-aanhoudende MAC-verewekansiging vir wi-fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nie-aanhoudende MAC-verewekansiging vir wi-fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobiele data is altyd aktief"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardewareversnelling vir verbinding"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Wys opsies vir draadlose skermsertifisering"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Verhoog Wi-Fi-aantekeningvlak, wys per SSID RSSI in Wi‑Fi-kieser"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verlaag batteryverbruik en verbeter netwerk se werkverrigting"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wanneer hierdie modus geaktiveer is, kan hierdie toestel se MAC-adres verander elke keer wanneer dit aan \'n netwerk koppel waarvoor MAC-verewekansiging geaktiveer is."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Wanneer hierdie modus geaktiveer is, kan hierdie toestel se MAC-adres verander elke keer wanneer dit aan \'n netwerk koppel waarvoor MAC-verewekansiging geaktiveer is."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Beperk"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Onbeperk"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Loggerbuffer se groottes"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 6d314ce77718..b0ce875f6288 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"ምንም/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"የለም"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-የግል"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-የግል"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-የግል"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-የድርጅት"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-የድርጅት"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-የድርጅት"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-የድርጅት"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-የድርጅት"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"መተላለፊያ"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-የግል"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-የግል"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"የለም/የተሻሻለ ክፍት"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"የተሻሻለ ክፈት"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-የድርጅት 192-ቢት"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"ተቀምጧል"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ተቋርጧል"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ተሰናክሏል"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"የWi‑Fi ተጨማሪ ቃላት ምዝግብ ማስታወሻ መያዝ"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi scan throttling"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"የWi-Fi ወጥ ያልሆነ ማክ የዘፈቀደ ማድረግ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"የWi-Fi ወጥ ያልሆነ ማክ የዘፈቀደ ማድረግ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"የተንቀሳቃሽ ስልክ ውሂብ ሁልጊዜ ገቢር ነው"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"የሃርድዌር ማቀላጠፊያን በማስተሳሰር ላይ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ አማራጮችን አሳይ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"የWi‑Fi ምዝግብ ማስታወሻ አያያዝ ደረጃ ጨምር፣ በWi‑Fi መምረጫ ውስጥ በአንድ SSID RSSI አሳይ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"የባትሪ መላሸቅን ይቀንሳል እንዲሁም የአውታረ መረብ አፈጻጸም ብቃትን ያሻሽላል"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ይህ ሁነታ ስራ ሲጀምር ይህ መሣሪያ የዘፈቀደ የማክ አድራሻ ስራ ከነቃለት አውታረ መረብ ጋር በተገናኘ እያንዳንዱ ጊዜ የመሣሪያው የማክ አድራሻ ሊቀየር ይችላል።"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ይህ ሁነታ ሲነቃ የዚህ መሣሪያ የማክ አድራሻ የዘፈቀደ የማክ አድራሻ ከነቃለት አውታረ መረብ ጋር በተገናኘ ጊዜ ሁሉ ሊቀየር ይችላል።"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"የሚለካ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ያልተለካ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"የምዝግብ ማስታወሻ ያዥ መጠኖች"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e397035ea0ac..ba6bd707a7b2 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"لا يمكن فحص الشبكات"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"‏بدون أمان/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"بلا أمان"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"نقطة مرور"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"بدون أمان/مفتوحة ومحسّنة"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"مفتوحة ومحسّنة"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"‏WPA3-Enterprise ‏192 بت"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"تم الحفظ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"غير متصلة"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غير مفعّلة"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"‏تفعيل تسجيل Wi‑Fi Verbose"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"‏تقييد البحث عن شبكات Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"‏التوزيع العشوائي لعناوين MAC غير الثابتة لشبكة Wi‑Fi."</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"‏التوزيع العشوائي لعناوين MAC غير الثابتة لشبكة Wi‑Fi."</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"بيانات الجوّال نشطة دائمًا"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"تسريع الأجهزة للتوصيل"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"‏زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"لتقليل استهلاك البطارية وتحسين أداء الشبكة"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"‏عند تفعيل هذا الوضع، قد يتم تغيير عنوان MAC لهذا الجهاز في كل مرة تتصل فيها بشبكة تم تفعيل التوزيع العشوائي لعناوين MAC عليها."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"‏عند تفعيل هذا الوضع، قد يتم تغيير عنوان MAC لهذا الجهاز في كل مرة تتصل فيها بشبكة تم تفعيل التوزيع العشوائي لعناوين MAC عليها."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"تفرض تكلفة استخدام"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"بدون قياس"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 1ae3452ac847..82c6bc061050 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"৮০২.১ গুণ"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-১৯২"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"নাই"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"পাছপইণ্ট"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise ১৯২-বিট"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"সংযোগ বিচ্ছিন্ন"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"নিষ্ক্ৰিয় হৈ আছে"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণীকৰণ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"ৱাই-ফাই ভাৰ্ব\'ছ লগিং সক্ষম কৰক"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ৱাই-ফাই স্কেনৰ নিয়ন্ত্ৰণ"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"অবিৰত ৱাই-ফাই সংযোগ নথকা MACৰ যাদৃচ্ছিকীকৰণ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"অবিৰত ৱাই-ফাই সংযোগ নথকা MACৰ যাদৃচ্ছিকীকৰণ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ম’বাইল ডেটা সদা-সক্ৰিয়"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"টেডাৰিং হাৰ্ডৱেৰ ত্বৰণ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণপত্ৰৰ বাবে বিকল্পসমূহ দেখুৱাওক"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ৱাই-ফাই লগিঙৰ মাত্ৰা বঢ়াওক, Wi‑Fi পিকাৰত প্ৰতি SSID RSSI দেখুৱাওক"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"বেটাৰীৰ খৰচ কমায় আৰু নেটৱৰ্কৰ কাৰ্যক্ষমতা বৃদ্ধি কৰে"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"এই ম\'ডটো সক্ষম কৰিলে, এই ডিভাইচটোৱে MAC যাদৃচ্ছিকীকৰণ সক্ষম কৰি থোৱা কোনো নেটৱর্কত প্ৰতিবাৰ সংযোগ হোৱাৰ সময়ত ইয়াৰ MAC ঠিকনাটো সলনি হ\'ব পাৰে।"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"এই ম’ডটো সক্ষম হৈ থাকিলে, এই ডিভাইচটোৱে MAC যাদৃচ্ছিকীকৰণ সক্ষম কৰি থোৱা কোনো নেটৱৰ্কত প্ৰতিবাৰ সংযোগ হোৱাৰ সময়ত ইয়াৰ MAC ঠিকনাটো হয়তো সলনি হ’ব।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"নিৰিখ-নিৰ্দিষ্ট"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"নিৰিখ অনিৰ্দিষ্ট"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"লগাৰৰ বাফাৰৰ আকাৰ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index c991912d8a01..d6c4c88c953a 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Şəbəkə axtarmaq olmur"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Heç biri/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Dəst-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Heç biri"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Şəxsi"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Şəxsi"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Şəxsi"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Müəssisə"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Müəssisə"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Müəssisə"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Müəssisə"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Müəssisə"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Keçid nöqtəsi"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Şəxsi"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Şəxsi"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Heç biri/Genişləndirilmiş Açılış"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Genişləndirilmiş Açılış"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Müəssisə 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Yadda saxlanılan"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Bağlantı kəsildi"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiv"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Simsiz monitor sertifikatlaşması"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi Çoxsözlü Girişə icazə verin"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi axtarışının məhdudlaşdırılması"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi müvəqqəti MAC randomizasiyası"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi müvəqqəti MAC randomizasiyası"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobil data həmişə aktiv"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Modem rejimində cihaz sürətləndiricisi"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazları adsız göstərilsin"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Simsiz monitorların sertifikasiya parametrləri göstərilsin"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi giriş səviyyəsini qaldırın, Wi‑Fi seçəndə hər SSID RSSI üzrə göstərin"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Enerji sərfiyyatını azaldır və şəbəkənin işini yaxşılaşdırır"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Bu rejimdə şəbəkəyə hər dəfə qoşulanda cihaza təsadüfi MAC ünvanı verilə bilər."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Bu rejim aktiv edildikdə, bu cihaz hər dəfə MAC randomizasiyası aktiv olan şəbəkəyə qoşulanda onun MAC ünvanı dəyişə bilər."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Tarif sayğacılı"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Limitsiz"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Jurnal buferi ölçüsü"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 32a08ad1e3cc..da6522703275 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nije moguće skenirati mreže"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ništa/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nema"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ništa/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192-bitni"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Sačuvano"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Veza je prekinuta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Sertifikacija bežičnog ekrana"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogući detaljniju evidenciju za Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje WiFi skeniranja"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nasumično razvrstavanje MAC adresa po WiFi-ju sa prekidima"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nasumično razvrstavanje MAC adresa po WiFi-ju sa prekidima"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilni podaci su uvek aktivni"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardversko ubrzanje privezivanja"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Prikazuje opcije za sertifikaciju bežičnog ekrana"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Povećava nivo evidentiranja za Wi‑Fi. Prikaz po SSID RSSI-u u biraču Wi‑Fi mreže"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Smanjuje potrošnju baterije i poboljšava učinak mreže"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kada je ovaj režim omogućen, MAC adresa ovog uređaja može da se promeni svaki put kada se poveže sa mrežom na kojoj je omogućeno nasumično razvrstavanje MAC adresa."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kada je ovaj režim omogućen, MAC adresa ovog uređaja može da se promeni svaki put kada se poveže sa mrežom na kojoj je omogućeno nasumično razvrstavanje MAC adresa."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Sa ograničenjem"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Bez ograničenja"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Veličine bafera podataka u programu za evidentiranje"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 08ca17fd802d..3126f521ed34 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Няма/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Няма"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Няма/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-бітнае шыфраванне"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Захавана"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Адключана"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Адключана"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Сертыфікацыя бесправаднога экрана"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Уключыць падрабязны журнал Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Рэгуляванне пошуку сетак Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Рандамізацыя выпадковых MAC-адрасоў у сетках Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Рандамізацыя выпадковых MAC-адрасоў у сетках Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мабільная перадача даных заўсёды актыўная"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Апаратнае паскарэнне ў рэжыме мадэма"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Паказаць опцыі сертыфікацыі бесправаднога экрана"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Пры выбары сеткі Wi-Fi указваць у журнале RSSI для кожнага SSID"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Зніжае расход зараду акумулятара і павышае прадукцыйнасць мабільных сетак"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Калі ўключаны гэты рэжым, MAC-адрас гэтай прылады можа змяняцца падчас кожнага падключэння да сеткі з актыўнай рандамізацыяй MAC-адрасоў."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Калі ўключаны гэты рэжым, MAC-адрас гэтай прылады можа змяняцца падчас кожнага падключэння да сеткі з актыўнай рандамізацыяй MAC-адрасоў."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з улікам трафіка"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Сетка без уліку трафіка"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Памеры буфера журнала"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 78aea784319c..5a57d44989d1 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Не може да се сканира за мрежи"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Няма/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Няма"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Няма/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Стандарт за сигурност Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192-битова защита"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Запазено"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Няма връзка"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Деактивирани"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Безжичен дисплей"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Активиране на „многословно“ регистр. на Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Ограничаване на сканирането за Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Рандомизиране на временните MAC адреси за Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Рандомизиране на временните MAC адреси за Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Винаги активни мобилни данни"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Хардуерно ускорение на тетъринга"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Показване на опциите за сертифициране на безжичния дисплей"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"По-подробно регистр. на Wi‑Fi – данни за RSSI на SSID в инстр. за избор на Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Намалява изразходването на батерията и подобрява ефективността на мрежата"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Когато този режим е включен, MAC адресът на устройството може да се променя при всяко свързване с мрежа, за която е активирана функцията за рандомизиране на MAC адреса."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Когато този режим е включен, MAC адресът на устройството може да се променя при всяко свързване с мрежа, за която е активирана функцията за рандомизиране на MAC адреса."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"С отчитане"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Без отчитане"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Размери на регистрац. буфери"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 6e26ed4cacaf..1e0e8c53efd8 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"কোনও কিছুই নয়"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"সংরক্ষিত"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"কানেকশন নেই"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"অক্ষম হয়েছে"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ওয়্যারলেস ডিসপ্লে সার্টিফিকেশন"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"ওয়াই-ফাই ভারবোস লগিং চালু করুন"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ওয়াই-ফাই স্ক্যান থ্রোটলিং"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"অল্প সময় ওয়াই-ফাই নেটওয়ার্কে যুক্ত হয় এমন MAC অ্যাড্রেস র‍্যান্ডমাইজেশনের সুবিধা"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"অল্প সময় ওয়াই-ফাই নেটওয়ার্কে যুক্ত থাকে এমন MAC অ্যাড্রেসের র‍্যান্ডমাইজেশন"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"মোবাইল ডেটা সব সময় সক্রিয় থাক"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"টিথারিং হার্ডওয়্যার অ্যাক্সিলারেশন"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ব্যাটারির খরচ কমায় এবং নেটওয়ার্কের পারফর্ম্যান্স উন্নত করে"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"এই মোডটি চালু থাকার সময়, MAC র‍্যান্ডমাইজেশন চালু হওয়া এমন কোনও নেটওয়ার্কে কানেক্ট করার সময় এই ডিভাইসের MAC অ্যাড্রেস পরিবর্তিত হতে পারে।"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"এই মোডটি চালু থাকলে, MAC অ্যাড্রেসের র‍্যান্ডমাইজেশনের সুবিধা চালু আছে এমন কোনও নেটওয়ার্কে প্রত্যেকবার কানেক্ট করার সময় ডিভাইসের MAC অ্যাড্রেস পরিবর্তিত হতে পারে।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"মিটার্ড"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"পরিমাপ করা নয়"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"লগার বাফারের আকারগুলি"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 26b2db461e52..483d3b6430bf 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ne može skenirati mreže"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ništa/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ništa"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ništa/poboljšano otvaranje"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Poboljšano otvaranje"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bitni"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Sačuvano"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Nije povezano"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifikacija bežičnog prikaza"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogući detaljni zapisnik za WiFi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje skeniranja WiFi-ja"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nasumičan odabir MAC adrese prema WiFi mreži s prekidima"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nasumičan odabir MAC adrese prema WiFi mreži s prekidima"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Prijenos podataka na mobilnoj mreži uvijek aktivan"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardversko ubrzavanje za povezivanje putem mobitela"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Prikaz opcija za certifikaciju bežičnog prikaza"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Povećani nivo zapisnika za WiFi. Prikaz po SSID RSSI-ju u Biraču WiFi-ja"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Smanjuje potrošnju baterije i poboljšava performanse mreže"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kada je omogućen ovaj način rada, MAC adresa ovog uređaja se može promijeniti svaki put kada se poveže na mrežu koja ima omogućen nasumični odabir MAC adresa."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kada je omogućen ovaj način rada, MAC adresa ovog uređaja se može promijeniti svaki put kada se poveže na mrežu koja ima omogućen nasumični odabir MAC adresa."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"S naplatom"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Mreža bez naplate"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Veličine međumemorije zapisnika"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 261face0d956..1cffe78ca76b 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No es poden cercar xarxes"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Cap/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Cap"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Cap / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise de 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Desat"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconnectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desactivat"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Activa el registre Wi‑Fi detallat"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitació de la cerca de xarxes Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Aleatorització de MAC no persistent per a connexions Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Aleatorització de MAC no persistent per a connexions Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dades mòbils sempre actives"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Acceleració per maquinari per a compartició de xarxa"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra les opcions per a la certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Augmenta el nivell de registre de la connexió Wi‑Fi i es mostra per SSID RSSI al selector de Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Redueix el consum de bateria i millora el rendiment de la xarxa"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quan aquest mode està activat, és possible que l’adreça MAC d’aquest dispositiu canviï cada vegada que es connecti a una xarxa amb l\'aleatorització d\'adreces MAC activada"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quan aquest mode està activat, és possible que l’adreça MAC d\'aquest dispositiu canviï cada vegada que es connecti a una xarxa amb l\'aleatorització d\'adreces MAC activada."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"D\'ús mesurat"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"D\'ús no mesurat"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Mides de la mem. intermèdia del registrador"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f58953375125..dd41dbca659b 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nelze hledat sítě"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Žádné/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Žádné"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2 Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Žádné/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 bitů"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Uloženo"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Odpojeno"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Vypnuto"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifikace bezdrátového displeje"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Podrobné protokolování Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Přibrždění vyhledávání Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Při připojování k sítím Wi‑Fi používat proměnlivé náhodné adresy MAC"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Při připojování k sítím Wi‑Fi používat proměnlivé náhodné adresy MAC"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilní data jsou vždy aktivní"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardwarová akcelerace tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Zobrazit možnosti certifikace bezdrátového displeje"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Zvýšit úroveň protokolování Wi‑Fi zobrazenou v SSID a RSSI při výběru sítě Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Snižuje vyčerpávání baterie a vylepšuje výkon sítě"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Když je tento režim aktivován, adresa MAC tohoto zařízení se může změnit pokaždé, když se zařízení připojí k síti s aktivovanou randomizací adres MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Když je tento režim aktivován, adresa MAC tohoto zařízení se může změnit pokaždé, když se zařízení připojí k síti s aktivovanou randomizací adres MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Měřená"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neměřená"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Vyrovnávací paměť protokolovacího nástroje"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 67bc8496b9ed..f89563669e98 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Der kan ikke søges efter netværk"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ingen/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ingen"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ingen/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Gemt"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Afbrudt"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiveret"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificering af trådløs skærm"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktivér detaljeret Wi-Fi-logføring"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Begrænsning af Wi-Fi-scanning"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Ikke-vedvarende MAC-randomisering via Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Ikke-vedvarende MAC-randomisering via Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobildata er altid aktiveret"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardwareacceleration ved netdeling"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Vis valgmuligheder for certificering af trådløs skærm"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Øg mængden af Wi‑Fi-logføring. Vis opdelt efter SSID RSSI i Wi‑Fi-vælgeren"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reducerer batteriforbruget og forbedrer netværkets effektivitet"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Når denne tilstand er aktiveret, skifter enhedens MAC-adresse muligvis, hver gang den opretter forbindelse til et netværk, hvor MAC-randomisering er aktiveret."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Når denne tilstand er aktiveret, skifter enhedens MAC-adresse muligvis, hver gang den opretter forbindelse til et netværk, hvor MAC-randomisering er aktiveret."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Forbrugsafregnet"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Ikke forbrugsafregnet"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Størrelser for Logger-buffer"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 5e026e5d7735..520ac9816f9f 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Netzwerkscan nicht möglich"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Keine/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite B 192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Keine"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Keinse/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 Bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Gespeichert"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Nicht verbunden"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiviert"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Zertifizierung für kabellose Übertragung"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Ausführliche WLAN-Protokollierung aktivieren"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Drosselung der WLAN-Suche"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Unbeständige MAC-Randomisierung für WLAN"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nicht persistente, zufällige Generierung von MAC-Adressen für WLAN"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobile Datennutzung immer aktiviert"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardwarebeschleunigung für Tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"WLAN-Protokollierungsebene erhöhen, in WLAN-Auswahl für jede SSID RSSI anzeigen"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen zufällig generiert werden."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 9bf28cbf2684..d95f5b5a23c7 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Καμία/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Καμία"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Προσωπικό"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Προσωπικό"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Προσωπικό"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Εταιρικό"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Εταιρικό"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Εταιρικό"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Εταιρικό"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Εταιρικό"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Προσωπικό"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Προσωπικό"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Καμία/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Εταιρικό 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Αποθηκευμένο"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Αποσυνδεδεμένο"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Απενεργοποιημένο"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Πιστοποίηση ασύρματης οθόνης"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Ενεργοποίηση λεπτομερ. καταγραφής Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Περιορισμός σάρωσης Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Μη σταθερή τυχαία σειρά MAC σε Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Μη σταθερή τυχαία σειρά MAC σε Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Πάντα ενεργά δεδομένα κινητής τηλεφωνίας"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Σύνδεση επιτάχυνσης υλικού"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Αύξηση επιπέδου καταγ. Wi-Fi, εμφάνιση ανά SSID RSSI στο εργαλείο επιλογής Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Περιορίζει την κατανάλωση της μπαταρίας και βελτιώνει την απόδοση του δικτύου"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Όταν ενεργοποιηθεί αυτή η λειτουργία, η διεύθυνση MAC αυτής της συσκευής μπορεί να αλλάζει κάθε φορά που συνδέεται σε ένα δίκτυο όπου έχει ενεργοποιηθεί η τυχαιοποίηση διευθύνσεων MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Όταν ενεργοποιηθεί αυτή η λειτουργία, η διεύθυνση MAC αυτής της συσκευής μπορεί να αλλάζει κάθε φορά που συνδέεται σε ένα δίκτυο όπου έχει ενεργοποιηθεί η τυχαία σειρά διευθύνσεων MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Μέτρηση με βάση τη χρήση"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Χωρίς μέτρηση με βάση τη χρήση"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Μέγεθος προσωρινής μνήμης για τη λειτουργία καταγραφής"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index c1d4ab8410e3..1273a198930e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Can\'t scan for networks"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"None"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saved"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Enable Wi‑Fi verbose logging"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi scan throttling"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi non‑persistent MAC randomisation"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi non‑persistent MAC randomisation"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduces battery drain and improves network performance"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Unmetered"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index c9fc70dc04b6..70fdaac1e181 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Can\'t scan for networks"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"None"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saved"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Enable Wi‑Fi verbose logging"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi scan throttling"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi non‑persistent MAC randomisation"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi non‑persistent MAC randomisation"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduces battery drain and improves network performance"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Unmetered"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index c1d4ab8410e3..1273a198930e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Can\'t scan for networks"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"None"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saved"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Enable Wi‑Fi verbose logging"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi scan throttling"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi non‑persistent MAC randomisation"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi non‑persistent MAC randomisation"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduces battery drain and improves network performance"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Unmetered"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index c1d4ab8410e3..1273a198930e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Can\'t scan for networks"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"None"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saved"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Enable Wi‑Fi verbose logging"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi scan throttling"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi non‑persistent MAC randomisation"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi non‑persistent MAC randomisation"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduces battery drain and improves network performance"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"When this mode is enabled, this device’s MAC address may change each time that it connects to a network that has MAC randomisation enabled."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Unmetered"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 0212719dad91..56267bb793b8 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‎‎Can\'t scan for networks‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎WEP‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎WPA‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎WPA2‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎WPA/WPA2‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎802.1x‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎WPA-EAP‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎RSN-EAP‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎WPA3‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎WPA2/WPA3‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎None/OWE‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‏‎OWE‎‏‎‎‏‎"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‎Suite-B-192‎‏‎‎‏‎"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‎None‎‏‎‎‏‎"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎WEP‎‏‎‎‏‎"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‏‏‎‎‎WPA-Personal‎‏‎‎‏‎"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‎WPA2-Personal‎‏‎‎‏‎"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎WPA/WPA2-Personal‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‏‎WPA/WPA2/WPA3-Enterprise‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎WPA-Enterprise‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎WPA/WPA2-Enterprise‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎WPA2/WPA3-Enterprise‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎WPA3-Enterprise‎‏‎‎‏‎"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎Passpoint‎‏‎‎‏‎"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‎WPA3-Personal‎‏‎‎‏‎"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎WPA2/WPA3-Personal‎‏‎‎‏‎"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎None/Enhanced Open‎‏‎‎‏‎"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎Enhanced Open‎‏‎‎‏‎"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‎‎WPA3-Enterprise 192-bit‎‏‎‎‏‎"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎Saved‎‏‎‎‏‎"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎Disconnected‎‏‎‎‏‎"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎Disabled‎‏‎‎‏‎"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎Wireless display certification‎‏‎‎‏‎"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎Enable Wi‑Fi Verbose Logging‎‏‎‎‏‎"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎Wi‑Fi scan throttling‎‏‎‎‏‎"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎Wi‑Fi non‑persistent MAC randomization‎‏‎‎‏‎"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎Wi‑Fi non‑persistent MAC randomization‎‏‎‎‏‎"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎Mobile data always active‎‏‎‎‏‎"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎Tethering hardware acceleration‎‏‎‎‏‎"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎Show Bluetooth devices without names‎‏‎‎‏‎"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎Show options for wireless display certification‎‏‎‎‏‎"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker‎‏‎‎‏‎"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎Reduces battery drain &amp; improves network performance‎‏‎‎‏‎"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎When this mode is enabled, this device’s MAC address may change each time it connects to a network that has MAC randomization enabled.‎‏‎‎‏‎"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎When this mode is enabled, this device’s MAC address may change each time it connects to a network that has MAC randomization enabled.‎‏‎‎‏‎"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎Metered‎‏‎‎‏‎"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎Unmetered‎‏‎‎‏‎"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎Logger buffer sizes‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 048039f6af81..30294ce095a8 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se pueden buscar las redes."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ninguno/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2 Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ninguno/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Habilitar registro detallado de Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitación de búsqueda de Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Aleatorización de MAC no persistente para conexiones Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Aleatorización de MAC no persistente para conexiones Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Datos móviles siempre activados"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleración de hardware de conexión mediante dispositivo móvil"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostrar opciones de certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumentar nivel de registro Wi-Fi; mostrar por SSID RSSI en el selector de Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce el consumo de batería y mejora el rendimiento de la red"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"De uso medido"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Sin tarifa plana"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamaños de búfer de Logger"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d9db99e4efca..fbdb37a50bd4 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"No se puede buscar redes."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ninguna/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ninguna"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ninguna/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise de 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Guardado"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitado"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Habilitar registro de Wi-Fi detallado"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitar búsqueda de redes Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Aleatorización de MAC no persistente para conexiones Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Aleatorización de MAC no persistente en conexiones Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Datos móviles siempre activos"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleración por hardware para conexión compartida"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Muestra opciones para la certificación de la pantalla inalámbrica"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumenta el nivel de registro de la conexión Wi-Fi y se muestra por SSID RSSI en el selector Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce el consumo de batería y mejora el rendimiento de las redes"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Medida"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"No medida"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamaños del búfer para registrar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 60469a1d2b17..ca83bf83eb0b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Võrke ei saa kontrollida"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Puudub/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Puudub"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Puudub / Täiustatud avamine"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Täiustatud avamine"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise (192-bitine)"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Salvestatud"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Pole ühendatud"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Keelatud"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Juhtmeta ekraaniühenduse sertifitseerimine"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Luba WiFi sõnaline logimine"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"WiFi-skannimise ahendamine"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"WiFi-võrgu mittepüsiva MAC-aadressi juhuslikustamine"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"WiFi-võrgu mittepüsiva MAC-aadressi juhuslikustamine"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Hoia mobiilne andmeside alati aktiivne"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Ühenduse jagamise riistvaraline kiirendus"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Juhtmeta ekraaniühenduse sertifitseerimisvalikute kuvamine"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Suurenda WiFi logimistaset, kuva WiFi valijas SSID RSSI järgi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Aeglustab aku tühjenemist ja parandab võrgu toimivust"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kui see režiim on lubatud, võidakse selle seadme MAC-aadressi muuta iga kord, kui see ühendatakse võrguga, milles on juhuslikustatud MAC-aadressi määramine lubatud."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kui see režiim on lubatud, võidakse selle seadme MAC-aadressi muuta iga kord, kui see ühendatakse võrguga, milles on juhuslikustatud MAC-aadressi määramine lubatud."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Mahupõhine"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Mittemahupõhine"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logija puhvri suurused"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index a7f5eac5542b..3ae9fa3a6886 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ezin dira sareak bilatu"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Bat ere ez / OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Bat ere ez"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Bat ere ez / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192 bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Gordeta"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Deskonektatuta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desgaituta"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Hari gabe bistaratzeko ziurtagiria"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Gaitu wifi-sareetan saioa hasteko modu xehatua"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wifi-sareen bilaketaren muga"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wifi-konexioetan iraunkorrak ez diren MAC helbideak ausaz antolatzea"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wifi-konexioetan iraunkorrak ez diren MAC helbideak ausaz antolatzea"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Datu-konexioa beti aktibo"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Konexioa partekatzeko hardwarearen azelerazioa"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Erakutsi hari gabe bistaratzeko ziurtagiriaren aukerak"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Erakutsi datu gehiago wifi-sareetan saioa hastean. Erakutsi sarearen identifikatzailea eta seinalearen indarra wifi-sareen hautatzailean."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Bateria gutxiago kontsumituko da, eta sarearen errendimendua hobetuko"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Modu hau gaituta dagoenean, baliteke gailuaren MAC helbidea aldatzea MAC helbideak ausaz antolatzeko aukera gaituta daukan sare batera konektatzen den bakoitzean."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Modu hau gaituta dagoenean, baliteke gailuaren MAC helbidea aldatzea MAC helbideak ausaz antolatzeko aukera gaituta daukan sare batera konektatzen den bakoitzean."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Sare neurtua"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neurtu gabeko sarea"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Erregistroen buffer-tamainak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 39aacfdfd658..6ceea967222b 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"اسکن شبکه‌ها امکان‌پذیر نیست"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"‏هیچ‌کدام/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"خالی"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"‏هیچ‌کدام/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"‏WPA3-Enterprise ‏۱۹۲ بیت"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"ذخیره‌شده"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"اتصال قطع شد"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غیرفعال شد"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"گواهینامه نمایش بی‌سیم"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"‏فعال کردن گزارش‌گیری طولانی Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"‏محدود کردن اسکن کردن Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"‏تصادفی‌سازی MAC غیرپایای Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"‏تصادفی‌سازی MAC غیرپایای Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"داده تلفن همراه همیشه فعال باشد"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"شتاب سخت‌افزاری اشتراک‌گذاری اینترنت"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاه‌های بلوتوث بدون نام"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"نمایش گزینه‌ها برای گواهینامه نمایش بی‌سیم"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"‏افزایش سطح گزارش‌گیری Wi‑Fi، نمایش به ازای SSID RSSI در انتخاب‌کننده Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"تخلیه باتری راکاهش می‌دهد و عملکرد شبکه را بهبود می‌بخشد"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"‏اگر این حالت فعال باشد، هر بار این دستگاه به شبکه‌ای متصل شود که تصادفی‌سازی MAC در آن فعال است، ممکن است نشانی MAC آن تغییر کند."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"‏اگر این حالت فعال باشد، هر بار این دستگاه به شبکه‌ای متصل شود که تصادفی‌سازی MAC در آن فعال است، ممکن است نشانی MAC آن تغییر کند."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"محدودشده"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"محدودنشده"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"اندازه‌های حافظه موقت ثبت‌کننده"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index ca299f008d98..57780e11b633 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Verkkoja ei voi etsiä."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ei mitään / OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ei mitään"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ei mitään / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"192-bittinen WPA3-Enterprise"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Tallennettu"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Yhteys katkaistu"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Pois päältä"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Langattoman näytön sertifiointi"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Käytä Wi-Fin laajennettua lokikirjausta"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi-haun rajoitus"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"MAC-satunnaistaminen, jos Wi-Fi ei ole kiinteä"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"MAC-satunnaistaminen, jos Wi-Fi ei ole kiinteä"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobiilidata aina käytössä"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Laitteistokiihdytyksen yhteyden jakaminen"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Näytä langattoman näytön sertifiointiin liittyvät asetukset"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Lisää Wi‑Fin lokikirjaustasoa, näytä SSID RSSI -kohtaisesti Wi‑Fi-valitsimessa"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Vähentää virrankulutusta ja parantaa verkon toimintaa"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kun tämä tila on päällä, laitteen MAC-osoite voi muuttua aina, kun laite yhdistää verkkoon, jossa MAC-satunnaistaminen on käytössä"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kun tämä tila on päällä, laitteen MAC-osoite voi muuttua aina, kun laite yhdistää verkkoon, jossa MAC-satunnaistaminen on käytössä"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Maksullinen"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Maksuton"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Lokipuskurien koot"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index b503fdbbc57b..234850a60d8b 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Impossible de rechercher des réseaux."</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"Aucune"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"Enregistré"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Déconnecté"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Désactivés"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certification de l\'affichage sans fil"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser enreg. données Wi-Fi détaillées"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche de réseaux Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Réorganisation aléatoire MAC non persistante du Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Réorganisation aléatoire MAC non persistante du Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Données cellulaires toujours actives"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification d\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit l\'utilisation de la pile et améliore les performances réseau"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil pourrait changer chaque fois qu\'il se connecte à un réseau sur lequel la sélection aléatoire des adresses MAC est activée."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil pourrait changer chaque fois qu\'il se connecte à un réseau sur lequel la sélection aléatoire des adresses MAC est activée."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Non mesuré"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des mémoires tampons d\'enregistreur"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 921caba34e13..6c53ade1989c 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Impossible de rechercher des réseaux."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Aucune/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Aucune"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Aucun/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Enregistré"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Déconnecté"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Désactivé"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Sélection aléatoire de l\'adresse MAC non persistante en Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Sélection aléatoire de l\'adresse MAC non persistante en Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quand ce mode est activé, l\'adresse MAC de cet appareil peut changer chaque fois qu\'il se connecte à un réseau Wi-Fi où le changement aléatoire d\'adresse MAC est activé"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quand ce mode est activé, l\'adresse MAC de cet appareil peut changer chaque fois qu\'il se connecte à un réseau où le changement aléatoire d\'adresse MAC est activé."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 015f25679fce..dd354f1181db 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Non se poden explorar redes"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"Ningunha"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"Gardada"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Rede desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desactivadas"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificado de visualización sen fíos"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Activar rexistro detallado da wifi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitación da busca de wifi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Orde aleatoria de enderezos MAC non persistentes para conexións wifi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Orde aleatoria de enderezos MAC non persistentes para conexións wifi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Datos móbiles sempre activados"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleración de hardware para conexión compartida"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra opcións para o certificado de visualización sen fíos"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumenta o nivel de rexistro da wifi, móstrao por SSID RSSI no selector de wifi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce o consumo de batería e mellora o rendemento da rede"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Cando este modo está activado, o enderezo MAC pode cambiar cada vez que se este dispositivo se conecta a unha rede que teña activada a orde aleatoria de enderezos MAC"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Cando este modo está activado, o enderezo MAC pode cambiar cada vez que se este dispositivo se conecta a unha rede que teña activada a orde aleatoria de enderezos MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Rede sen tarifa plana"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Rede con tarifa plana"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamaño dos búfers do rexistrador"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index ac078fdcb585..49c602eb8fee 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"કોઈ નહીં"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"સાચવેલા"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ડિસ્કનેક્ટ કર્યું"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"અક્ષમ કર્યો"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"વાયરલેસ ડિસ્પ્લે પ્રમાણન"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"વાઇ-ફાઇ વર્બોઝ લૉગિંગ ચાલુ કરો"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"વાઇ-ફાઇ સ્કૅનની ક્ષમતા મર્યાદિત કરવી"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"વાઇ-ફાઇ માટે સતત બદલાતું MAC રેન્ડમાઇઝેશન"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"વાઇ-ફાઇ માટે સતત બદલાતું MAC રેન્ડમાઇઝેશન"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"મોબાઇલ ડેટા હંમેશાં સક્રિય"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ટિથરિંગ માટે હાર્ડવેર ગતિવૃદ્ધિ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ડિવાઇસ બતાવો"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"વાઇ-ફાઇ લોગિંગ સ્તર વધારો, વાઇ-ફાઇ પીકરમાં SSID RSSI દીઠ બતાવો"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"બૅટરીનો ચાર્જ ઝડપથી ઓછો થવાનું ટાળે છે અને નેટવર્કના કાર્યપ્રદર્શનમાં સુધારો કરે છે"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"આ મોડ ચાલુ કરેલો હશે, ત્યારે MAC રેન્ડમાઇઝેશન ચાલુ કરેલું હોય તેવા નેટવર્ક સાથે આ ડિવાઇસ જોડાશે ત્યારે દર વખતે તેનું MAC ઍડ્રેસ બદલાય તેમ બની શકે છે."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"આ મોડ ચાલુ કરેલો હોય ત્યારે MAC રેન્ડમાઇઝેશન ચાલુ કરેલું હોય તેવા નેટવર્ક સાથે આ ડિવાઇસ કનેક્ટ થશે, ત્યારે દર વખતે તેનું MAC ઍડ્રેસ બદલાય તેવું બની શકે છે."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"મીટર કરેલું"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"મીટર ન કરેલ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"લોગર બફર કદ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 630000fe6921..0c115ca975e8 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"नेटवर्क के लिए स्‍कैन नहीं कर सकता"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"कोई नहीं/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"कोई नहीं"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-निजी"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-निजी"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-निजी"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-एंटरप्राइज़"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-एंटरप्राइज़"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-एंटरप्राइज़"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-एंटरप्राइज़"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-एंटरप्राइज़"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"पासपॉइंट"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-निजी"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-निजी"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"कोई नहीं/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-एंटरप्राइज़ 192-बिट"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"सेव किया गया"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"डिसकनेक्ट किया गया"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिसप्ले सर्टिफ़िकेशन"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"वाई-फ़ाई वर्बोस लॉगिंग चालू करें"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"वाई-फ़ाई के लिए स्कैन की संख्या कम करें"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"थोड़े समय के लिए वाई-फ़ाई नेटवर्क से जुड़ने पर MAC पता बदलने की सुविधा"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"थोड़े समय के लिए वाई-फ़ाई नेटवर्क से जुड़ने पर MAC पता बदलने की सुविधा"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"मोबाइल डेटा हमेशा चालू"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"हार्डवेयर से तेज़ी लाने के लिए टेदर करें"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिसप्ले सर्टिफ़िकेशन के विकल्प दिखाएं"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"वाई-फ़ाई लॉगिंग का स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"बैटरी की खपत कम और नेटवर्क की परफ़ॉर्मेंस बेहतर होती है"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"जब यह मोड चालू होता है, तब नेटवर्क से कनेक्ट होने पर हर बार इस डिवाइस का मैक पता बदल सकता है. ऐसा तब होता है, जब डिवाइस किसी ऐसे नेटवर्क से जुड़ता है जिस पर मैक पते को बिना किसी तय नियम के बदलने की सुविधा चालू होती है."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"जब यह मोड चालू होता है, तब नेटवर्क से कनेक्ट होने पर हर बार इस डिवाइस का MAC पता बदल सकता है. ऐसा तब होता है, जब डिवाइस किसी ऐसे नेटवर्क से जुड़ता है जिस पर MAC पता बदलने की सुविधा चालू होती है."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"डेटा इस्तेमाल करने की सीमा तय की गई है"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"डेटा इस्तेमाल करने की सीमा तय नहीं की गई है"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लॉगर बफ़र आकार"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 51884f398801..f671e8c8bcb8 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Skeniranje mreža nije moguće"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ništa/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nema"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ništa/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192-bitna sigurnost"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Spremljeno"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Nije povezano"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifikacija bežičnog prikaza"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogući opširnu prijavu na Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Usporavanje traženja Wi-Fija"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nasumični odabir nepostojane MAC adrese za Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nasumični odabir nepostojane MAC adrese za Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilni podaci uvijek aktivni"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardversko ubrzanje za modemsko povezivanje"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Prikaz opcija za certifikaciju bežičnog prikaza"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Povećana razina prijave na Wi‑Fi, prikaz po SSID RSSI-ju u Biraču Wi‑Fi-ja"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Smanjuje potrošnju baterije i poboljšava rad mreže"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kada je omogućen ovaj način, MAC adresa ovog uređaja može se promijeniti svaki put kad se uređaj poveže s mrežom na kojoj je omogućen nasumični odabir MAC-a."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kada je omogućen ovaj način, MAC adresa ovog uređaja može se promijeniti svaki put kad se uređaj poveže s mrežom na kojoj je omogućen nasumični odabir MAC-a."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"S ograničenim prometom"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Bez ograničenja prometa"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Veličine međuspremnika zapisnika"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 8fc854c54c2f..3a703a9fb716 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nem lehet beolvasni a hálózatokat"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nincs/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nincs"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nincs/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Mentve"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Leválasztva"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Letiltva"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Vezeték nélküli kijelző tanúsítványa"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Részletes Wi-Fi-naplózás engedélyezése"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi-Fi-hálózat szabályozása"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi nem állandó MAC-randomizációja"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi nem állandó MAC-randomizációja"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"A mobilhálózati kapcsolat mindig aktív"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Internetmegosztás hardveres gyorsítása"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Vezeték nélküli kijelző tanúsítványával kapcsolatos lehetőségek megjelenítése"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi-naplózási szint növelése, RSSI/SSID megjelenítése a Wi‑Fi-választóban"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Csökkenti az akkumulátorhasználatot, és javítja a hálózat teljesítményét"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Ha ez a mód be van kapcsolva, akkor ennek az eszköznek a MAC-címe minden alkalommal módosulhat, amikor olyan hálózathoz csatlakozik, amelyen engedélyezve van a MAC-címek randomizálása."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Ha ez a mód be van kapcsolva, akkor ennek az eszköznek a MAC-címe minden alkalommal módosulhat, amikor olyan hálózathoz csatlakozik, amelyen engedélyezve van a MAC-címek randomizálása."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Forgalomkorlátos"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Nem forgalomkorlátos"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Naplózási puffer mérete"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 766eb383dffa..10f05b067c46 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Կարգավորված չէ/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ոչ մեկը"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Կարգավորված չէ/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Պահված է"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Կապ չկա"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Անջատված"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Անլար էկրանների հավաստագրում"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Միացնել Wi‑Fi մանրամասն գրանցամատյանները"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi-ի որոնման սահմանափակում"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Պատահական հերթականությամբ դասավորված MAC հասցեներ Wi‑Fi ցանցում"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Պատահական հերթականությամբ դասավորված MAC հասցեներ Wi‑Fi ցանցում"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Բջջային ինտերնետը միշտ ակտիվ է"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Սարքակազմի արագացման միացում"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Ցույց տալ անլար էկրանների հավաստագրման ընտրանքները"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Նվազեցնում է մարտկոցի սպառումը և լավացնում ցանցի աշխատանքը"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Երբ այս ռեժիմը միացված է, MAC հասցեն կարող է փոխվել ամեն անգամ, երբ սարքը միանա որևէ ցանցի, որում միացված է MAC հասցեների պատահական ընտրությունը։"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Երբ այս ռեժիմը միացված է, MAC հասցեն կարող է փոխվել ամեն անգամ, երբ սարքը միանա որևէ ցանցի, որում միացված է MAC հասցեների պատահական ընտրությունը։"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Վճարովի թրաֆիկ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Անսահմանափակ թրաֆիկ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Մատյանի բուֆերի չափերը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c1403082e37b..89101be91b3d 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Tidak dapat memindai jaringan"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Tidak Ada/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Tidak ada"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Tidak Ada/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Disimpan"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Terputus"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Nonaktif"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Sertifikasi layar nirkabel"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktifkan Pencatatan Log Panjang Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Pembatasan pemindaian Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Pengacakan MAC nonpersisten Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Pengacakan tidak tetap MAC Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Data seluler selalu aktif"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Akselerasi hardware tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Tampilkan opsi untuk sertifikasi layar nirkabel"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Tingkatkan level pencatatan log Wi-Fi, tampilkan per SSID RSSI di Pemilih Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Memperlambat kehabisan baterai &amp; meningkatkan performa jaringan"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Saat mode ini diaktifkan, alamat MAC perangkat ini dapat berubah setiap kali terhubung ke jaringan yang mengaktifkan pengacakan MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Jika mode ini diaktifkan, alamat MAC perangkat ini dapat berubah setiap kali terhubung ke jaringan yang mengaktifkan pengacakan MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Berbayar"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Tidak berbayar"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Ukuran buffer pencatat log"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 9b083033c7fa..d67b87245174 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ekki er hægt að leita að netum"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ekkert/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ekkert"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Aðgangspunktur"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ekkert/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bita"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Vistað"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Aftengt"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Óvirkt"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Vottun þráðlausra skjáa"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Kveikja á ítarlegri skráningu Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Hægja á Wi‑Fi leit"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Slembiröðun tímabundinna MAC-vistfanga um Wi-Fi tengingu"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Slembiröðun tímabundinna MAC-vistfanga um Wi-Fi tengingu"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Alltaf kveikt á farsímagögnum"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Vélbúnaðarhröðun fyrir tjóðrun"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Sýna valkosti fyrir vottun þráðlausra skjáa"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Auka skráningarstig Wi-Fi, sýna RSSI fyrir hvert SSID í Wi-Fi vali"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Dregur úr rafhlöðunotkun og eykur netafköst"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Þegar kveikt er á þessari stillingu gæti MAC-vistfang þessa tækis breyst í hvert sinn sem það tengist neti sem er með kveikt á slembivali MAC-vistfanga."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Þegar kveikt er á þessari stillingu gæti MAC-vistfang þessa tækis breyst í hvert sinn sem það tengist neti sem er með kveikt á slembivali MAC-vistfanga."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Mæld notkun"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Notkun ekki mæld"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Annálsritastærðir biðminna"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 65992a7048b8..27f5c3c67d78 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Impossibile cercare reti"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nessuna/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nessuna"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nessuna/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise a 192 bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Salvata"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Non connessa"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disattivata"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificazione display wireless"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Attiva logging dettagliato Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limita ricerca di reti Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Randomizzazione indirizzi MAC non persistenti per connessione a reti Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Randomizzazione non persistente dell\'indirizzo MAC Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dati mobili sempre attivi"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering accelerazione hardware"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra opzioni per la certificazione display wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumenta livello di logging Wi-Fi, mostra SSID RSSI nel selettore Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Riduce il consumo della batteria e migliora le prestazioni della rete"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando questa modalità è attiva, l\'indirizzo MAC del dispositivo potrebbe cambiare ogni volta che si connette a una rete con randomizzazione MAC attivata"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quando questa modalità è attiva, l\'indirizzo MAC del dispositivo potrebbe cambiare ogni volta che il dispositivo si connette a una rete con randomizzazione degli indirizzi MAC attiva."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"A consumo"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Non a consumo"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Dimensioni buffer logger"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 9f2d8a2d346e..371d20a5af48 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"לא ניתן לסרוק לאיתור רשתות"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"‏ללא/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"ללא"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"‏Passpoint : פרוטוקול Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"‏ללא/רשת Wi-Fi עם אבטחת OWE"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"‏רשת Wi-Fi עם אבטחת OWE"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"נשמר"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"מנותקת"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"מושבת"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"‏אישור של תצוגת Wi-Fi"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"‏הפעלת רישום מפורט של Wi‑Fi ביומן"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"‏ויסות סריקה לנקודות Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"‏רנדומיזציה של כתובות MAC בלי חיבור יציב ל-Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"‏רנדומיזציה של כתובות MAC משתנות ברשתות Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"חבילת הגלישה פעילה תמיד"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"שיפור מהירות באמצעות חומרה לצורך שיתוף אינטרנט בין ניידים"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"‏הצגת מכשירי Bluetooth ללא שמות"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"‏הצגת אפשרויות עבור אישור של תצוגת Wi-Fi"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"‏העלאת רמת הרישום של Wi‑Fi ביומן, הצגה לכל SSID RSSI ב-Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"אפשרות זו מפחיתה את קצב התרוקנות הסוללה ומשפרת את ביצועי הרשת"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"‏כשמצב זה מופעל, כתובת ה-MAC של המכשיר הזה עשויה להשתנות בכל פעם שהוא מתחבר לרשת שפועלת בה רנדומיזציה של כתובות MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"‏כשהמצב הזה מופעל, כתובת ה-MAC של המכשיר הזה יכולה להשתנות בכל פעם שהוא מתחבר לרשת שבה פועלת רנדומיזציה של כתובות MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"חיוב לפי שימוש בנתונים"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"לא נמדדת"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"גודלי מאגר של יומן רישום"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index dd3b00b71889..0d8597422f26 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ネットワークをスキャンできません"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"なし / OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"なし"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"なし / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 ビット"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"保存済み"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"未接続"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"無効"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ワイヤレス ディスプレイ認証"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi 詳細ログの有効化"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi スキャン スロットリング"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi-Fi 非永続的 MAC ランダム化"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi-Fi 非永続的 MAC ランダム化"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"モバイルデータを常に ON にする"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"テザリング時のハードウェア アクセラレーション"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ワイヤレス ディスプレイ認証のオプションを表示"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi ログレベルを上げて、Wi-Fi 選択ツールで SSID RSSI ごとに表示します"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"電池の消耗が軽減され、ネットワーク パフォーマンスが改善されます"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ON にすると、MAC アドレスのランダム化が有効なネットワークに接続するたびに、このデバイスの MAC アドレスが変わる可能性があります。"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ON にすると、MAC アドレスのランダム化が有効なネットワークに接続するたびに、このデバイスの MAC アドレスが変わる可能性があります。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"従量制"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"定額制"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ログバッファのサイズ"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 6f023e91b1e6..572d10921901 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"არცერთი/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"არცერთი"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"არცერთი/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-ბიტიანი"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"დამახსოვრებულია"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"კავშირი გაწყვეტილია"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"გამორთულია"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"უსადენო ეკრანის სერტიფიცირება"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi-ს დაწვრილებითი აღრიცხვის ჩართვა"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi სკანირების რეგულირება"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi-ს MAC მისამართების არამუდმივი რანდომიზაცია"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi-ის MAC მისამართების არამუდმივი რანდომიზაცია"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"მობილური ინტერნეტის ყოველთვის გააქტიურება"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ტეტერინგის აპარატურული აჩქარება"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ამცირებს ბატარეის ხარჯვას და აუმჯობესებს ქსელის მუშაობას"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"როდესაც ეს რეჟიმი ჩართულია, მოწყობილობის MAC მისამართი შეიძლება შეიცვალოს ისეთ ქსელთან ყოველ დაკავშირებაზე, რომელსაც ჩართული აქვს MAC მისამართის შემთხვევითობა."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"როდესაც ეს რეჟიმი ჩართულია, ამ მოწყობილობის MAC მისამართი შეიძლება შეიცვალოს ისეთ ქსელთან ყოველ დაკავშირებაზე, რომელსაც ჩართული აქვს MAC მისამართის რანდომიზაცია."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ლიმიტირებული"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"არალიმიტირებული"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ჟურნალიზაციის ბუფერის ზომები"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 2fbe33e88056..229ed4530a0e 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Желілерді шолу мүмкін емес"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Жоқ/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Жоқ"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Жоқ/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 бит"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Сақталды"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Ажыратылған"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Өшірілген"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Сымсыз дисплей сертификаты"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Толық мәліметті Wi‑Fi журналы"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi желілерін іздеуді шектеу"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi желісінің тұрақсыз MAC рандомизациясы"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi желісінің тұрақсыз MAC рандомизациясы"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобильдік интернет әрқашан қосулы"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Тетеринг режимінде аппаратпен жеделдету"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Сымсыз дисплей сертификаты опцияларын көрсету"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi тіркеу деңгейін арттыру, Wi‑Fi таңдағанда әр SSID RSSI бойынша көрсету"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Батарея зарядының шығынын азайтады және желі жұмысын жақсартады."</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Бұл режим қосулы болса, құрылғының MAC мекенжайы MAC рандомизациясы қосулы желіге жалғанған сайын өзгеруі мүмкін."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Бұл режим қосулы болса, құрылғының MAC мекенжайы MAC рандомизациясы қосулы желіге жалғанған сайын өзгеруі мүмкін."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Трафик саналатын желі"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Трафик саналмайды"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Журнал буферінің өлшемдері"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index f2ab2f852dc6..efc422c0ce04 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"មិន​អាច​វិភាគ​រក​បណ្ដាញ"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"គ្មាន/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"គ្មាន"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"គ្មាន/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 ប៊ីត"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"បាន​រក្សាទុក"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"បាន​ផ្ដាច់"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"បាន​បិទ"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"សេចក្តីបញ្ជាក់ការបង្ហាញ​ឥត​ខ្សែ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"បើក​កំណត់ហេតុ​រៀបរាប់​ Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ការពន្យឺតការស្កេន Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"ការតម្រៀប MAC ដែលមិនមានលក្ខណៈជាប់លាប់តាមលំដាប់​ចៃដន្យនៃ Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"ការតម្រៀប MAC ដែលមិនមានលក្ខណៈជាប់លាប់តាមលំដាប់​ចៃដន្យនៃ Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ទិន្នន័យទូរសព្ទចល័តដំណើរការជានិច្ច"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ការ​ពន្លឿនល្បឿនភ្ជាប់ដោយប្រើហាតវែរ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញ​ឧបករណ៍​ប្ល៊ូធូស​គ្មានឈ្មោះ"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"បង្ហាញ​ជម្រើស​សម្រាប់​សេចក្តីបញ្ជាក់ការបង្ហាញ​ឥត​ខ្សែ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"បង្កើនកម្រិតកំណត់ហេតុ Wi-Fi បង្ហាញក្នុង SSID RSSI ក្នុងកម្មវិធីជ្រើសរើស Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"កាត់បន្ថយ​ការប្រើប្រាស់ថ្ម និងកែលម្អប្រតិបត្តិការ​បណ្ដាញ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"នៅពេលបើក​មុខងារនេះ អាសយដ្ឋាន MAC របស់ឧបករណ៍នេះ​អាចផ្លាស់ប្ដូរ​ រាល់ពេល​ដែលវា​ភ្ជាប់ជាមួយ​បណ្ដាញ​ដែលបានបើក​ការប្រើ MAC ចៃដន្យ។"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"នៅពេលបើក​មុខងារនេះ អាសយដ្ឋាន MAC របស់ឧបករណ៍នេះ​អាចផ្លាស់ប្ដូរ​ រាល់ពេល​ដែលវា​ភ្ជាប់ជាមួយ​បណ្ដាញ​ដែលបានបើក​ការប្រើ MAC ចៃដន្យ។"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"មានការកំណត់"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"មិនមានការកំណត់"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ទំហំឡុកជើបាហ្វើ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index c4ba44925bef..77613f18f697 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ನೆಟ್‌ವರ್ಕ್‌ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್‌ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"ಯಾವುದೂ ಇಲ್ಲ"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"ಉಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ವೈರ್‌ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi ವೆರ್ಬೋಸ್ ಲಾಗಿಂಗ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ವೈ-ಫೈ ಸ್ಕ್ಯಾನ್ ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತಿದೆ"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"ವೈ-ಫೈ ನಿರಂತರವಲ್ಲದ MAC ಯಾದೃಚ್ಛಿಕರಣ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"ವೈ-ಫೈ ನಿರಂತರವಲ್ಲದ MAC ಯಾದೃಚ್ಛಿಕರಣ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ಮೊಬೈಲ್ ಡೇಟಾ ಯಾವಾಗಲೂ ಸಕ್ರಿಯ"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ಟೆಥರಿಂಗ್‍‍ಗಾಗಿ ಹಾರ್ಡ್‍ವೇರ್ ವೇಗವರ್ಧನೆ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ವೈರ್‌ಲೆಸ್‌‌‌ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ಬ್ಯಾಟರಿ ಹೆಚ್ಚು ಬಾಳಿಕೆ ಬರುವಂತೆ ಮಾಡುತ್ತದೆ ಮತ್ತು ನೆಟ್‌ವರ್ಕ್‌ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸುತ್ತದೆ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ಈ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ, ಪ್ರತಿ ಬಾರಿ MAC ಯಾದೃಚ್ಛಿಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿದಾಗ ಈ ಸಾಧನದ MAC ವಿಳಾಸವು ಬದಲಾಗಬಹುದು."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ಈ ಮೋಡ್ ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದಾಗ, MAC ಯಾದೃಚ್ಛಿಕರಣವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿದ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿದಾಗ ಈ ಸಾಧನದ MAC ವಿಳಾಸವು ಪ್ರತಿ ಬಾರಿಯೂ ಬದಲಾಗಬಹುದು."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ಮೀಟರ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ಮೀಟರ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ಲಾಗರ್ ಬಫರ್ ಗಾತ್ರಗಳು"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 97ad0a0ef347..455a0ab00ad9 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"네트워크를 검색할 수 없습니다."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"없음/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"없음"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"없음/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192비트"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"저장됨"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"연결 끊김"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"사용 중지됨"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"무선 디스플레이 인증서"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi 상세 로깅 사용"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi 검색 제한"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi 비지속적인 MAC 주소 무작위 순서 지정"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi 비지속적인 MAC 주소 무작위 순서 지정"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"항상 모바일 데이터 활성화"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"테더링 하드웨어 가속"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"무선 디스플레이 인증서 옵션 표시"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"배터리 소모를 줄이고 네트워크 성능 개선"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"이 모드를 사용 설정하면 기기가 MAC 주소 무작위 지정이 설정된 네트워크에 연결될 때마다 기기의 MAC 주소가 변경될 수 있습니다."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"이 모드를 사용 설정하면 기기가 MAC 주소 무작위 지정이 설정된 네트워크에 연결될 때마다 기기의 MAC 주소가 변경될 수 있습니다."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"종량제 네트워크"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"무제한 네트워크"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"로거 버퍼 크기"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 7387985cf457..74585da2b325 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Түйүндөрдү издөө мүмкүн эмес"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Жок/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Жок"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Жок/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 бит"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Сакталды"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Ажыратылды"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Өчүрүлгөн"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Зымсыз мониторлорду тастыктамалоо"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi таржымалы"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi тармактарын издөөнү жөнгө салуу"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi туташуусу туруксуз MAC даректерин башаламан түзүү"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi туташуусу туруксуз MAC даректерин башаламан иретте түзүү"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобилдик Интернет иштей берет"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Модем режиминде аппараттын иштешин тездетүү"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрүнсүн"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Зымсыз мониторлорду тастыктамалоо параметрлери көрүнүп турат"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi тандалганда ар бир SSID үчүн RSSI көрүнүп турат"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Батареяны үнөмдөп, тармактын иштешин жакшыртат"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Бул режим өчүрүлгөндөн кийин түзмөк MAC даректи башаламан иретте түзүү функциясы иштетилген тармакка туташкан сайын анын MAC дареги өзгөрүшү мүмкүн."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Бул режим өчүрүлгөндөн кийин, түзмөк MAC дарегин башаламан иретте түзүү функциясы иштетилген тармакка туташкан сайын анын MAC дареги өзгөрүшү мүмкүн."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Трафик ченелет"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Чектелбеген тармак"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Журнал буферинин өлчөмү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 462b53f5d5e0..5490543818d8 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"ບໍ່ໃຊ້"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"ບັນ​ທຶກແລ້ວ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ປິດການນຳໃຊ້"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ສະແດງການຮັບຮອງຂອງລະບົບໄຮ້ສາຍ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"​ເປີດ​ນຳ​ໃຊ້ການ​ເກັບ​ປະ​ຫວັດ​ Verbose Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ການຈຳກັດການສະແກນ Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"ການສຸ່ມ MAC ທີ່ມີ Wi-Fi ບໍ່ຖາວອນ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"ການສຸ່ມ MAC ທີ່ມີ Wi-Fi ແບບບໍ່ຖາວອນ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ເປີດໃຊ້ອິນເຕີເນັດມືຖືຕະຫຼອດເວລາ"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ເປີດໃຊ້ການເລັ່ງຄວາມໄວດ້ວຍຮາດແວ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ສະແດງໂຕເລືອກສຳລັບການສະແດງການຮັບຮອງລະບົບໄຮ້ສາຍ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ເພີ່ມ​ລະ​ດັບ​ການ​ເກັບ​ປະ​ຫວັດ Wi‑Fi, ສະ​ແດງ​ຕໍ່ SSID RSSI ​ໃນ​ Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ຫຼຸດການໃຊ້ແບັດເຕີຣີ ແລະ ປັບປຸງປະສິດທິພາບເຄືອຂ່າຍ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ຫາກເປີດການນຳໃຊ້ໂໝດນີ້, ທີ່ຢູ່ MAC ຂອງອຸປະກອນນີ້ອາດມີການປ່ຽນແປງໃນແຕ່ລະເທື່ອທີ່ມັນເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍໃດໜຶ່ງທີ່ເປີດການນຳໃຊ້ການສຸ່ມ MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ຫາກເປີດການນຳໃຊ້ໂໝດນີ້, ທີ່ຢູ່ MAC ຂອງອຸປະກອນນີ້ອາດມີການປ່ຽນແປງໃນແຕ່ລະເທື່ອທີ່ມັນເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍໃດໜຶ່ງທີ່ເປີດການນຳໃຊ້ການສຸ່ມ MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ມີການວັດແທກ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ບໍ່ໄດ້ວັດແທກ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ຂະໜາດບັບເຟີຕົວບັນທຶກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 491a08204364..b53e1b044c41 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nepavyksta nuskaityti tinklų"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA / WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2 / WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nėra / OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nėra"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA / „WPA2-Personal“"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA / WPA2 / „WPA3-Enterprise“"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA / „WPA2-Enterprise“"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2 / „WPA3-Enterprise“"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2 / „WPA3-Personal“"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nėra / „Enhanced Open“"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"„WPA3-Enterprise“, 192 bitų"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Išsaugotas"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Neprisijungta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Neleidžiama"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Belaidžio rodymo sertifikavimas"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Įgal. „Wi‑Fi“ daugiaž. įraš. į žurnalą"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"„Wi‑Fi“ nuskaitymo ribojimas"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"MAC atsitiktinis parinkimas esant nepastoviam „Wi‑Fi“"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"MAC atsitiktinis parinkimas esant nepastoviam „Wi‑Fi“"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobiliojo ryšio duomenys visada suaktyvinti"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Įrenginio kaip modemo naudojimo aparatinės įrangos spartinimas"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Padidinti „Wi‑Fi“ įrašymo į žurnalą lygį, rodyti SSID RSSI „Wi-Fi“ rinkiklyje"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Sumažinamas akumuliatoriaus eikvojimas ir patobulinamas tinklo našumas"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kai įgalintas šis režimas, šio įrenginio MAC adresas gali keistis kas kartą prisijungus prie tinklo, kuriame įgalintas atsitiktinis MAC parinkimas."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kai įgalintas šis režimas, šio įrenginio MAC adresas gali keistis kas kartą prisijungus prie tinklo, kuriame įgalintas atsitiktinis MAC parinkimas."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Matuojamas"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neišmatuotas"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Registruotuvo buferio dydžiai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 9fcc749a8636..053e97edf3e8 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nevar skenēt tīklus"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nav/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nav"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nav/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192 bitu"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saglabāts"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Savienojums pārtraukts"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Atspējots"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Bezvadu attēlošanas sertifikācija"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Iespējot Wi‑Fi detalizēto reģistrēšanu"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi meklēšanas ierobežošana"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nepastāvīgu MAC adrešu nejauša izveide Wi-Fi savienojumiem"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nepastāvīgu MAC adrešu nejauša izveide Wi-Fi savienojumiem"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Vienmēr aktīvs mobilo datu savienojums"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Paātrināta aparatūras darbība piesaistei"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Rādīt bezvadu attēlošanas sertifikācijas iespējas"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Palieliniet Wi‑Fi reģistrēšanas līmeni; rādīt katram SSID RSSI Wi‑Fi atlasītājā."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Samazina akumulatora izlādi un uzlabo tīkla veiktspēju"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Ja šis režīms ir iespējots, šīs ierīces MAC adrese var mainīties ikreiz, kad ierīcē tiek izveidots savienojums ar tīklu, kurā ir iespējota MAC adrešu nejauša izveide."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Ja šis režīms ir iespējots, šīs ierīces MAC adrese var mainīties ikreiz, kad ierīcē tiek izveidots savienojums ar tīklu, kurā ir iespējota MAC adrešu nejauša izveide."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Maksas"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Bezmaksas"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Reģistrētāja buferu lielumi"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index f1f163959d93..8c7728d57309 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Не може да скенира за мрежи"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Нема/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Нема"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Нема/подобрено отворено"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Подобрено отворено"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-битна"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Зачувано"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Не е поврзано"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Оневозможено"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Приказ на сертификација на безжична мрежа"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Овозможи преопширно пријавување Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Регулирање на скенирањето за Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Непостојана MAC-рандомизација на Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Непостојана MAC-рандомизација на Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобилниот интернет е секогаш активен"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Хардверско забрзување за врзување"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Покажи ги опциите за безжичен приказ на сертификат"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Зголеми Wi‑Fi ниво на пријавување, прикажи по SSID RSSI во Wi‑Fi бирач"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Го намалува искористувањето на батеријата и ја подобрува изведбата на мрежата"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Кога е овозможен режимов, MAC-адресата на уредов може да се промени секој пат кога ќе се поврзе со мрежа што има овозможена рандомизација на MAC-адреси."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Кога е овозможен режимов, MAC-адресата на уредов може да се промени секој пат кога ќе се поврзе со мрежа што има овозможена MAC-рандомизација."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Со ограничен интернет"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Без ограничен интернет"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Величини на меѓумеморија за дневникот"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index bf241c36b132..df3d309cd75e 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"നെ‌റ്റ്‌വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"ഒന്നുമില്ല"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"ഒന്നുമില്ല/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"സംരക്ഷിച്ചു"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"വിച്ഛേദിച്ചു"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"പ്രവർത്തനരഹിതമാക്കി"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"വയർലെസ് ഡിസ്‌പ്ലേ സർട്ടിഫിക്കേഷൻ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"വൈഫൈ വെർബോസ് ലോഗിംഗ് പ്രവർത്തനക്ഷമമാക്കുക"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"വൈഫൈ സ്‌കാൻ ത്രോട്ടിലിംഗ്"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"വൈഫൈ വഴിയുള്ള, സ്ഥിരതയില്ലാത്ത MAC ക്രമരഹിതമാക്കൽ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"വൈഫൈ വഴിയുള്ള, സ്ഥിരതയില്ലാത്ത MAC ക്രമരഹിതമാക്കൽ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"മൊബൈൽ ഡാറ്റ എല്ലായ്‌പ്പോഴും സജീവം"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ടെതറിംഗ് ഹാർഡ്‌വെയർ ത്വരിതപ്പെടുത്തൽ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"വയർലെസ് ഡിസ്‌പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്‌ഷനുകൾ ദൃശ്യമാക്കുക"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ബാറ്ററി ചാർജ് വേഗത്തിൽ തീരുന്ന അവസ്ഥ കുറച്ച് നെറ്റ്‌വർക്ക് പ്രകടനം മെച്ചപ്പെടുത്തുന്നു"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ഈ മോഡ് പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, MAC ക്രമരഹിതമാക്കൽ പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്ന നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റ് ചെയ്യുമ്പോഴെല്ലാം ഈ ഉപകരണത്തിന്റെ MAC വിലാസം മാറിയേക്കാം."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ഈ മോഡ് പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, MAC ക്രമരഹിതമാക്കൽ പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്ന നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റ് ചെയ്യുമ്പോഴെല്ലാം ഈ ഉപകരണത്തിന്റെ MAC വിലാസം മാറിയേക്കാം."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"മീറ്റർ ചെയ്തത്"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"മീറ്റർമാപകമല്ലാത്തത്"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ലോഗർ ബഫർ വലുപ്പം"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 474ded9972c7..d905541739a5 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Хоосон/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Иж бүрдэл-Б-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Байхгүй"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Хувийн"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Хувийн"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Хувийн"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Байгууллага"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Байгууллага"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Байгууллага"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Байгууллага"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Байгууллага"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Хувийн"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Хувийн"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Хоосон/Сайжруулсан нээлт"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Сайжруулсан нээлт"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Байгууллага 192-бит"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Хадгалагдсан"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Салсан"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Идэвхгүйжүүлсэн"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Утасгүй дэлгэцийн сертификат"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi дэлгэрэнгүй лог-г идэвхжүүлэх"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi скан бууруулалт"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi-н байнгын бус MAC-г санамсаргүй байдлаар эмхлэх"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi-н байнгын бус MAC-г санамсаргүй байдлаар эмхлэх"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобайл дата байнга идэвхтэй"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Модем болгох техник хангамжийн хурдасгуур"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Утасгүй дэлгэцийн сертификатын сонголтыг харуулах"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi логийн түвшнийг нэмэгдүүлэх, Wi‑Fi Сонгогч дээрх SSID-д ногдох RSSI-г харуулах"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Батарей зарцуулалтыг бууруулж, сүлжээний гүйцэтгэлийг сайжруулдаг"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Энэ горимыг идэвхжүүлсэн үед энэ төхөөрөмжийг MAC-н санамсаргүй байдлаар эмхлэх явцыг идэвхжүүлсэн сүлжээнд холбогдох бүрд үүний MAC хаягийг өөрчилж болзошгүй."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Энэ горимыг идэвхжүүлсэн үед энэ төхөөрөмжийг MAC-н санамсаргүй байдлаар эмхлэх явцыг идэвхжүүлсэн сүлжээнд холбогдох бүрд үүний MAC хаягийг өөрчилж болзошгүй."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Хязгаартай"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Хязгааргүй"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Логгерын буферын хэмжээ"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 631cb3b9b628..2522ed08b3bc 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"काहीही नाही"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिस्प्ले प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"वाय-फाय व्हर्बोझ लॉगिंग सुरू करा"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"वाय-फाय स्कॅन थ्रॉटलिंग"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"वाय-फायचे सातत्याने न होणारे MAC रँडमायझेशन"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"वाय-फायचे सातत्याने न होणारे MAC रँडमायझेशन"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"मोबाइल डेटा नेहमी सक्रिय"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"टेदरिंग हार्डवेअर अ‍ॅक्सिलरेशन"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्‍लूटूथ डिव्‍हाइस दाखवा"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"वाय-फाय लॉगिंग स्‍तर वाढवा, वाय-फाय सिलेक्टरमध्‍ये प्रति SSID RSSI दर्शवा"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"बॅटरी जलदरीतीने संपण्यापासून रोखते आणि नेटवर्क परफॉर्मन्समध्ये सुधारणा करते"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"हा मोड सुरू केला असल्यास, या डिव्हाइसचा MAC अ‍ॅड्रेस प्रत्येक वेळी MAC रँडमायझेशन सुरू असलेल्या नेटवर्कशी कनेक्ट झाल्यास, कदाचित बदलू शकतो."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"हा मोड सुरू केलेला असले तेव्हा, हे डिव्हाइस MAC रँडमायझेशन सुरू केलेल्या नेटवर्कशी कनेक्ट होताना प्रत्येक वेळी त्याचा MAC अ‍ॅड्रेस बदलू शकतो."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"मीटरने मोजलेले"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"मीटरने न मोजलेले"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लॉगर बफर आकार"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 5ed5f39e0ebd..e054bd0e29c0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Tidak boleh mengimbas untuk rangkaian"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Tiada/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Tiada"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Keselamatan OWE Tiada/Dipertingkat"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Keselamatan OWE Dipertingkat"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Disimpan"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Diputuskan sambungan"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Dinyahdayakan"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Pensijilan paparan wayarles"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Dayakan Pengelogan Berjela-jela Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Pendikitan pengimbasan Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Perawakan MAC tidak berterusan Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Perawakan MAC tidak berterusan Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Data mudah alih sentiasa aktif"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Pecutan perkakasan penambatan"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Tingkatkan tahap pengelogan Wi-Fi, tunjuk setiap SSID RSSI dalam Pemilih Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Mengurangkan penyusutan bateri &amp; meningkatkan prestasi rangkaian"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Apabila mod ini didayakan, alamat MAC peranti ini mungkin berubah setiap kali peranti bersambung kepada rangkaian yang telah mendayakan perawakan MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Apabila mod ini didayakan, alamat MAC peranti ini mungkin berubah pada setiap kali peranti menyambung kepada rangkaian yang telah mendayakan perawakan MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Bermeter"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Tidak bermeter"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Saiz penimbal pengelog"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2f571069b788..ca7ffc942e64 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"မရှိ/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"မရှိ"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"ဖြတ်သန်းခွင့်ပြုမှတ်"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"မရှိ/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"သိမ်းဆည်းပြီး"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ချိတ်ဆက်မထားပါ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ပိတ်ထားသည်"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ကြိုးမဲ့ပြသမှု အသိအမှတ်ပြုလက်မှတ်"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi Verbose မှတ်တမ်းတင်ခြင်းအား ဖွင့်မည်"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi ရှာဖွေခြင်း ထိန်းချုပ်မှု"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi ပြောင်းလဲသော MAC ကျပန်းပြုလုပ်ခြင်း"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi ပြောင်းလဲသော MAC ကျပန်းပြုလုပ်ခြင်း"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"မိုဘိုင်းဒေတာကို အမြဲဖွင့်ထားရန်"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ဖုန်းကို မိုဒမ်အဖြစ်အသုံးပြုမှု စက်ပစ္စည်းဖြင့် အရှိန်မြှင့်တင်ခြင်း"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ဘက်ထရီ အသုံးပြုမှုကို လျှော့ကျစေပြီး ကွန်ရက်စွမ်းဆောင်ရည်ကို ပိုမိုကောင်းမွန်စေသည်"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ဤမုဒ်ကို ဖွင့်ထားသည့်အခါ MAC ကျပန်းပြုလုပ်ထားသည့် ကွန်ရက်သို့ ချိတ်ဆက်လိုက်သည့်အခါတိုင်း ဤစက်၏ MAC လိပ်စာ ပြောင်းသွားပါမည်။"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ဤမုဒ်ကို ဖွင့်ထားသည့်အခါ MAC ကျပန်းပြုလုပ်ထားသည့် ကွန်ရက်သို့ ချိတ်ဆက်လိုက်သည့်အခါတိုင်း ဤစက်၏ MAC လိပ်စာ ပြောင်းသွားနိုင်သည်။"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"အခမဲ့ မဟုတ်ပါ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"အခမဲ့"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"မှတ်တမ်းကြားခံနယ် အရွယ်အစားများ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index f39c0dccec8b..d355ae546172 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Kan ikke søke etter nettverk"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ingen/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ingen"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ingen / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Lagret"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Frakoblet"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Slått av"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Trådløs skjerm-sertifisering"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Slå på detaljert Wi-Fi-loggføring"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Begrensning av Wi‑Fi-skanning"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Ikke-vedvarende tilfeldiggjøring av MAC-adresse for Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Ikke-vedvarende tilfeldiggjøring av MAC-adresse for Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobildata er alltid aktiv"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Maskinvareakselerasjon for internettdeling"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Vis alternativer for sertifisering av trådløs skjerm"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Øk nivået av Wi-Fi-logging – vis per SSID RSSI i Wi-Fi-velgeren"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduserer batteriforbruket og forbedrer nettverksytelsen"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Når denne modusen er slått på, kan MAC-adressen til denne enheten endres hver gang den kobler seg til et nettverk som har tilfeldiggjøring av MAC-adresse slått på."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Når denne modusen er slått på, kan MAC-adressen til denne enheten endres hver gang den kobler seg til et nettverk som har tilfeldiggjøring av MAC-adresse slått på."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Med datamåling"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Uten datamåling"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Bufferstørrelser for logg"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index bb94c2476720..c35aff328316 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"छैन"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"सेभ गरिएको छ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट गरिएको छ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"असक्षम पारियो"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"वायरलेस डिस्प्ले प्रयोग गर्ने वा नगर्ने"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi भर्बोज लग अन गरियोस्"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi स्क्यान थ्रोटलिङ"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi-Fi नन्-पर्सिस्टेन्ट MAC र्‍यान्डमाइजेसन"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi-Fi नन-पर्सिस्टेन्ट MAC र्‍यान्डमाइजेसन"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"मोबाइल डेटा सधैँ अन होस्"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"टेदरिङको लागि हार्डवेयरको प्रवेग"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ डिभाइस देखाइयोस्"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिस्प्लेसम्बन्धी विकल्प देखाइयोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi लगिङ लेभल बढाइयोस्, Wi-Fi पि‍करमा प्रति SSID RSSI देखाइयोस्"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"यसले ब्याट्रीको खपत कम गर्छ र नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"यो मोड अन गरिएका बेला यो डिभाइस MAC एड्रेस बदल्ने सुविधा अन गरिएको नेटवर्कमा जति पटक कनेक्ट हुन्छ त्यति नै पटक यस डिभाइसको MAC एड्रेस पनि परिवर्तन हुन सक्छ।"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"यो मोड अन गरिएका बेला यो डिभाइस MAC एड्रेस बदल्ने सुविधा अन गरिएको नेटवर्कमा जति पटक कनेक्ट हुन्छ त्यति नै पटक यस डिभाइसको MAC एड्रेस पनि परिवर्तन हुन सक्छ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"सशुल्क वाइफाइ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"मिटर नगरिएको"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लगर बफरका आकारहरू"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 017de6a6d462..daef7e1e37ba 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Kan niet zoeken naar netwerken"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Geen/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Geen"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Geen/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Opgeslagen"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Verbinding verbroken"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Uitgezet"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificering van draadloze weergave"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Uitgebreide wifi-logregistr. aanzetten"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wifi-scannen beperken"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Niet-persistente MAC-herschikking in willekeurige volgorde voor wifi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Niet-persistente MAC-herschikking in willekeurige volgorde voor wifi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobiele data altijd actief"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardwareversnelling voor tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder naam tonen"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Toon opties voor certificering van draadloze weergave"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Verhoog het logniveau voor wifi, toon per SSID RSSI in wifi-kiezer"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verlaag het batterijverbruik en verbeter de netwerkprestaties"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Als deze modus aanstaat, kan het MAC-adres van dit apparaat veranderen telkens als het apparaat verbinding maakt met een netwerk waarvoor MAC-herschikking aanstaat."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Als dit aanstaat, kan het MAC-adres van dit apparaat veranderen telkens als het apparaat verbinding maakt met een netwerk waarvoor MAC-herschikking aanstaat."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Met datalimiet"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Gratis"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger-buffergrootten"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index c885c16cb5bf..dd0b1f2a9830 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ନେଟ୍‌ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"କିଛି ନାହିଁ/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"କିଛି ନାହିଁ"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"ପାସପଏଣ୍ଟ"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"କିଛି ନାହିଁ/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-ବିଟ୍"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"ସେଭ୍‌ ହୋଇଗଲା"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ବିଛିନ୍ନ କରାଯାଇଛି"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ଅକ୍ଷମ ହୋଇଛି"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ୱାୟରଲେସ୍‌ ଡିସ୍‌ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"ୱାଇ-ଫାଇ ଭର୍ବୋସ୍‌ ଲଗିଙ୍ଗ ସକ୍ଷମ କରନ୍ତୁ"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ୱାଇ-ଫାଇ ସ୍କାନ୍ ନିୟନ୍ତ୍ରଣ"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"ୱାଇ-ଫାଇ ଅଣ-ଅବିରତ MAC ରେଣ୍ଡମାଇଜେସନ୍"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"ୱାଇ-ଫାଇ ଅଣ-ଅବିରତ MAC ରେଣ୍ଡମାଇଜେସନ୍"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ମୋବାଇଲ୍‌ ଡାଟା ସର୍ବଦା ସକ୍ରିୟ"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ଟିଥରିଙ୍ଗ ହାର୍ଡୱେର ଆକ୍ସିଲିରେସନ୍"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍‍‌ ଡିଭାଇସ୍‌ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ୱେୟାରଲେସ୍‌ ଡିସ୍‌ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍ ପାଇଁ ବିକଳ୍ପ ଦେଖାନ୍ତୁ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ୱାଇ-ଫାଇ ଲଗିଙ୍ଗ ସ୍ତର ବଢ଼ାନ୍ତୁ, ୱାଇ-ଫାଇ ପିକର୍‌ରେ ପ୍ରତି SSID RSSI ଦେଖାନ୍ତୁ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କମ୍ ଏବଂ ନେଟ୍‌ୱାର୍କ କାର୍ଯ୍ୟକ୍ଷମତା ଉନ୍ନତ କରିଥାଏ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ଯେତେବେଳେ ଏହି ମୋଡ୍ ସକ୍ଷମ ହୁଏ, ପ୍ରତ୍ୟେକ ଥର MAC ରେଣ୍ଡୋମାଇଜେସନ୍ ସକ୍ଷମ ଥିବା କୌଣସି ନେଟୱାର୍କ ସହ ଏହି ଡିଭାଇସ୍ ସଂଯୋଗ ହେଲେ ଏହାର MAC ଠିକଣା ବଦଳିପାରେ।"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ଯେତେବେଳେ ଏହି ମୋଡ୍ ସକ୍ଷମ କରାଯାଏ, ପ୍ରତ୍ୟେକ ଥର MAC ରେଣ୍ଡୋମାଇଜେସନ୍ ସକ୍ଷମ ଥିବା କୌଣସି ନେଟୱାର୍କ ସହ ଏହି ଡିଭାଇସ୍ ସଂଯୋଗ ହେଲେ ଏହାର MAC ଠିକଣା ବଦଳିପାରେ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ମପାଯାଉଥିବା"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ମପାଯାଉନଥିବା"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ଲଗର୍‌ ବଫର୍‌ ସାଇଜ୍"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 127f571a5529..f47661df9837 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"ਕੋਈ ਨਹੀਂ"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ਡਿਸਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ਅਯੋਗ ਬਣਾਇਆ"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"ਵਾਈ-ਫਾਈ ਵਰਬੋਸ ਲੌਗਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"ਵਾਈ‑ਫਾਈ ਸਕੈਨ ਥਰੌਟਲਿੰਗ"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"ਵਾਈ-ਫਾਈ ਲਈ ਗੈਰ-ਸਥਾਈ MAC ਬੇਤਰਤੀਬਵਾਰ"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"ਵਾਈ-ਫਾਈ ਲਈ ਗੈਰ-ਸਥਾਈ MAC ਬੇਤਰਤੀਬਵਾਰ"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ਮੋਬਾਈਲ ਡਾਟਾ ਹਮੇਸ਼ਾਂ ਕਿਰਿਆਸ਼ੀਲ"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ਟੈਦਰਿੰਗ ਹਾਰਡਵੇਅਰ ਐਕਸੈੱਲਰੇਸ਼ਨ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ਵਾਈ‑ਫਾਈ ਲੌਗਿੰਗ ਪੱਧਰ ਵਧਾਓ, ਵਾਈ‑ਫਾਈ ਚੋਣਕਾਰ ਵਿੱਚ ਪ੍ਰਤੀ SSID RSSI ਦਿਖਾਓ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਘਟਾ ਕੇ ਨੈੱਟਵਰਕ ਕਾਰਗੁਜ਼ਾਰੀ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਂਦਾ ਹੈ"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ਜਦੋਂ ਇਹ ਮੋਡ ਚਾਲੂ ਹੁੰਦਾ ਹੈ, ਤਾਂ ਇਸ ਡੀਵਾਈਸ ਦਾ MAC ਪਤਾ ਹਰ ਵਾਰ ਬਦਲ ਸਕਦਾ ਹੈ ਜਦੋਂ ਇਹ ਕਿਸੇ ਅਜਿਹੇ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੁੰਦਾ ਹੈ ਜਿਸ ਵਿੱਚ MAC ਦਾ ਬੇਤਰਤੀਬੀਕਰਨ ਚਾਲੂ ਹੁੰਦਾ ਹੈ।"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ਜਦੋਂ ਇਹ ਮੋਡ ਚਾਲੂ ਹੁੰਦਾ ਹੈ, ਤਾਂ ਇਸ ਡੀਵਾਈਸ ਦਾ MAC ਪਤਾ ਹਰ ਵਾਰ ਬਦਲ ਸਕਦਾ ਹੈ ਜਦੋਂ ਇਹ ਕਿਸੇ ਅਜਿਹੇ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੁੰਦਾ ਹੈ ਜਿਸ ਵਿੱਚ MAC ਬੇਤਰਤੀਬਵਾਰ ਚਾਲੂ ਹੁੰਦਾ ਹੈ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ਮੀਟਰਬੱਧ ਕੀਤਾ ਗਿਆ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ਗੈਰ-ਮੀਟਰਬੱਧ ਕੀਤਾ ਗਿਆ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ਲੌਗਰ ਬਫ਼ਰ ਆਕਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 2bdd617e9e58..7c656afe299a 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nie można wyszukać sieci."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Brak/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Brak"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Brak / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise (szyfrowanie 192-bitowe)"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Zapisana"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Rozłączono"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Wyłączona"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certyfikacja wyświetlacza bezprzewodowego"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Szczegółowy dziennik Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Ograniczanie skanowania Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Nietrwała randomizacja adresów MAC w sieci Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Nietrwała randomizacja adresów MAC w sieci Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilna transmisja danych zawsze aktywna"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Akceleracja sprzętowa tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokazuj urządzenia Bluetooth bez nazw"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Pokazuj opcje certyfikacji wyświetlacza bezprzewodowego"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Zwiększ poziom rejestrowania Wi‑Fi, pokazuj według RSSI SSID w selektorze Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zmniejsza zużycie baterii i zwiększa wydajność sieci"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kiedy ten tryb jest włączony, to adres MAC tego urządzenia może zmieniać się za każdym razem, kiedy urządzenie połączy się z siecią, która ma włączoną opcję randomizacji MAC"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kiedy ten tryb jest włączony, to adres MAC tego urządzenia może zmieniać się za każdym razem, kiedy urządzenie połączy się z siecią, która ma włączoną opcję randomizacji MAC"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Użycie danych jest mierzone"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Użycie danych nie jest mierzone"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Rozmiary bufora rejestratora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 4136ec23afae..2356f417be22 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nenhum/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nenhuma"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nenhum/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Salva"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificação de Display sem fio"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Ativar registro detalhado de Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitar busca por Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Ordem aleatória de MAC não persistente no Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Ordem aleatória de MAC não persistente no Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleração de hardware de tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostrar opções de certificação de Display sem fio"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumentar o nível de registro de Wi-Fi; mostrar conforme o RSSI do SSID no seletor de Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduz o consumo de bateria e melhora o desempenho da rede"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando esse modo estiver ativado, o endereço MAC do dispositivo poderá mudar toda vez que ele se conectar a uma rede com ordem aleatória de MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quando esse modo estiver ativado, o endereço MAC do dispositivo poderá mudar toda vez que ele se conectar a uma rede com ordem aleatória de MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamanhos de buffer de logger"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 5abbc248a1d1..43155a795692 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Não é possível verificar redes"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nenhum/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nenhuma"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nenhum/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise de 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Guardada"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desligada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificação de display sem fios"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Ativar o registo verboso de Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Controlo da procura de Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Seleção aleatória do MAC não persistente Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Seleção aleatória do MAC não persistente Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleração de hardware para ligação (à Internet) via telemóvel"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostrar opções da certificação de display sem fios"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumentar o nível de reg. de Wi-Fi, mostrar por RSSI de SSID no Selec. de Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduz o consumo rápido da bateria e melhora o desempenho da rede"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando este modo estiver ativado, o endereço MAC deste dispositivo pode mudar sempre que o mesmo estabelece ligação a uma rede que tenha a seleção aleatória do MAC ativada."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quando este modo estiver ativado, o endereço MAC deste dispositivo pode mudar sempre que o mesmo estabelece ligação a uma rede que tenha a seleção aleatória do MAC ativada."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Acesso limitado"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Acesso ilimitado"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamanhos da memória intermédia do registo"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 4136ec23afae..2356f417be22 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Não é possível verificar a existência de redes"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Nenhum/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Nenhuma"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Nenhum/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 bits"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Salva"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificação de Display sem fio"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Ativar registro detalhado de Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitar busca por Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Ordem aleatória de MAC não persistente no Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Ordem aleatória de MAC não persistente no Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Aceleração de hardware de tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostrar opções de certificação de Display sem fio"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumentar o nível de registro de Wi-Fi; mostrar conforme o RSSI do SSID no seletor de Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduz o consumo de bateria e melhora o desempenho da rede"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando esse modo estiver ativado, o endereço MAC do dispositivo poderá mudar toda vez que ele se conectar a uma rede com ordem aleatória de MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Quando esse modo estiver ativado, o endereço MAC do dispositivo poderá mudar toda vez que ele se conectar a uma rede com ordem aleatória de MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamanhos de buffer de logger"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 84064e63dcc9..083c2b9966f8 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nu se poate scana pentru rețele"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA / WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2 / WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Fără / OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Niciuna"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA / WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA / WPA2 / WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA / WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2 / WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2 / WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Fără / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise pe 192 biți"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Salvată"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Deconectat"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Dezactivată"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certificare Ecran wireless"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Înregistrare prin Wi-Fi de volume mari de date"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Limitare căutare de rețele Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Randomizarea adresei MAC nepersistente pentru Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Randomizarea adresei MAC nepersistente pentru Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Date mobile permanent active"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Accelerare hardware pentru tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Măriți niv. de înr. prin Wi‑Fi, afișați în fcț. de SSID RSSI în Selectorul Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce descărcarea bateriei și îmbunătățește performanța rețelei"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Când acest mod este activat, adresa MAC a dispozitivului se poate schimba de fiecare dată când se conectează la o rețea care are activată randomizarea MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Când acest mod este activat, adresa MAC a dispozitivului se poate schimba de fiecare dată când se conectează la o rețea care are activată randomizarea MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Contorizată"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Necontorizată"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Dimensiunile memoriei temporare a jurnalului"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ff799e09e428..0a0fce34a3cc 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Не удалось начать поиск сетей."</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Не настроено/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Нет"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Не настроено/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 бита"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Сохранено"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Не подключено"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Отключено"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Серт. беспроводн. мониторов"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Подробный журнал Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Ограничивать поиск сетей Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Случайные MAC-адреса в сети Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Случайные MAC-адреса в сети Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Не отключать мобильный Интернет"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Аппаратное ускорение в режиме модема"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Показывать параметры сертификации беспроводных мониторов"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Вести подробный журнал, показывать RSSI для каждого SSID при выборе сети"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Уменьшает расход заряда батареи и улучшает работу сети"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Если этот режим активирован, MAC-адрес устройства может меняться при каждом подключении к сети, в которой возможно создание случайных MAC-адресов."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Если этот режим активирован, MAC-адрес устройства может меняться при каждом подключении к сети, в которой возможно создание случайных MAC-адресов."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Сеть с тарификацией трафика"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Сеть без тарификации трафика"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Размер буфера журнала"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index ecd288862ade..5b30c81f31b3 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"නැත/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"කිසිවක් නැත"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"පාස්පොයින්ට්"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"නැත/වැඩි දියුණු කළ විවෘත"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"වැඩි දියුණු කළ විවෘත"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise බිටු-192"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"සුරකින ලදි"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"විසන්ධි විය"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"අබලයි"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"නොරැහැන් සංදර්ශක සහතිකය"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"විස්තරාත්මක Wi‑Fi ලොග් කිරීම සබල කරන්න"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi ස්කෑන් අවකරණය"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi අඛණ්ඩ නොවන MAC සසම්භාවීකරණය"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi අඛණ්ඩ නොවන MAC සසම්භාවීකරණය"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"ජංගම දත්ත සැමවිට ක්‍රියාකාරීය"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ටෙදරින් දෘඪාංග ත්වරණය"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"නොරැහැන් සංදර්ශක සහතිකය සඳහා විකල්ප පෙන්වන්න"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi ලොග් මට්ටම වැඩි කරන්න, Wi‑Fi තෝරනයෙහි SSID RSSI අනුව පෙන්වන්න"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"බැටරි බැසීම අඩු කරන අතර ජාල කාර්ය සාධනය වැඩි දියුණු කරයි"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"මෙම ප්‍රකාරය අබල කළ විට, මෙම උපාංගයේ MAC ලිපිනය එය MAC සසම්භාවීකරණය සබල කර ඇති ජාලයකට සම්බන්ධවන ඒ ඒ අවස්ථාවල වෙනස් විය හැකිය."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"මෙම ප්‍රකාරය සබල විට, මෙම උපාංගයේ MAC ලිපිනය එය MAC සසම්භාවීකරණය සබල කර ඇති ජාලයකට සම්බන්ධ වන එක් එක් අවස්ථාවල වෙනස් විය හැකිය."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"මනිනු ලැබේ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"මනින්නේ නැත"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ලෝගයේ අන්තරාවක ප්‍රමාණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5343543f0dcc..0d929dbb34dc 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Siete sa nedajú vyhľadávať"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA‑EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN‑EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Žiadne/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite‑B‑192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Žiadne"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA‑Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2‑Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2‑Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3‑Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA‑Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2‑Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3‑Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3‑Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3‑Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3‑Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Žiadne / Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3‑Enterprise (192‑bitové)"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Uložené"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Odpojené"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Vypnuté"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifikácia bezdrôtového zobrazenia"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Podrobné denníky Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Pribrzdiť vyhľadávanie sietí Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Randomizácia dočasnej adresy MAC siete Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Randomizácia dočasnej adresy MAC siete Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobilné dáta ponechať vždy aktívne"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardvérová akcelerácia tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Zobraziť možnosti certifikácie bezdrôtového zobrazenia"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Zvýšiť úroveň denníkov Wi‑Fi, zobrazovať podľa SSID RSSI pri výbere siete Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Znižuje používanie batérie a zlepšuje výkon siete"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Keď je tento režim aktivovaný, adresa MAC tohto zariadenia sa môže pri každom pripojení k sieti s aktivovanou randomizáciou adries MAC zmeniť."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Keď je tento režim aktivovaný, adresa MAC tohto zariadenia sa môže pri každom pripojení k sieti s aktivovanou randomizáciou adries MAC zmeniť."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Merané"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Bez merania dát"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Vyrovnávacia pamäť nástroja denníkov"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 055760dae173..7a559fc16d00 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ni mogoče iskati omrežij"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Brez/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Brez"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Brez/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"192-bitni WPA3-Enterprise"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Shranjeno"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Ni povezave"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogočeno"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Potrdilo brezžičnega zaslona"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Omogoči podrobno zapisovanje dnevnika za Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Omejevanje iskanja omrežij Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Dodeljevanje nestalnega naključnega naslova MAC za Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Dodeljevanje nestalnega naključnega naslova MAC za Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Prenos podatkov v mobilnem omrežju je vedno aktiven"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Strojno pospeševanje za internetno povezavo prek mobilnega telefona"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Pokaži možnosti za potrdilo brezžičnega zaslona."</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Povečaj raven zapisovanja dnevnika za Wi-Fi; v izbirniku Wi‑Fi-ja pokaži glede na SSID RSSI."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zmanjša porabo energije baterije in izboljša delovanje omrežja."</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Ko je ta način omogočen, se lahko naslov MAC te naprave spremeni vsakič, ko se naprava poveže v omrežje z omogočenim naključnim dodeljevanjem naslova MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Ko je ta način omogočen, se lahko naslov MAC te naprave spremeni vsakič, ko se naprava poveže v omrežje z omogočenim dodeljevanjem naključnega naslova MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Omejen prenos podatkov"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Z neomejenim prenosom podatkov"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Velikosti medpomnilnikov zapisovalnika dnevnika"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e39b4200b83b..1a2c9040dc89 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Nuk mund të skanojë për rrjete"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"Asnjë"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"U ruajt"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Shkëputur"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Të çaktivizuara"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifikimi i ekranit pa tel"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktivizo hyrjen Wi-Fi Verbose"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Përshpejtimi i skanimit të Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Renditje e rastësishme jo e përhershme e MAC për Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Renditje e rastësishme jo e përhershme e MAC për Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Të dhënat celulare gjithmonë aktive"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Përshpejtimi i harduerit për ndarjen e lidhjes (internet)"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Shfaq opsionet për certifikimin e ekranit pa tel"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Rrit nivelin regjistrues të Wi‑Fi duke shfaqur SSID RSSI-në te Zgjedhësi i Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zvogëlon shkarkimin e baterisë dhe përmirëson cilësinë e funksionimit të rrjetit"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kur ky modalitet është i aktivizuar, adresa MAC e kësaj pajisjeje mund të ndryshojë çdo herë që lidhet me një rrjet që ka të aktivizuar renditjen e rastësishme të adresave MAC."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kur ky modalitet është i aktivizuar, adresa MAC e kësaj pajisjeje mund të ndryshojë çdo herë që lidhet me një rrjet që ka të aktivizuar renditjen e rastësishme të adresave MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Me matje"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Pa matje"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Madhësitë e regjistruesit"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 171ec7517317..03598336ee02 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Није могуће скенирати мреже"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Ништа/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Нема"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Ништа/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192-битни"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Сачувано"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Веза је прекинута"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Онемогућено"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Сертификација бежичног екрана"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Омогући детаљнију евиденцију за Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Успоравање WiFi скенирања"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Насумично разврставање MAC адреса по WiFi-ју са прекидима"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Насумично разврставање MAC адреса по WiFi-ју са прекидима"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Мобилни подаци су увек активни"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Хардверско убрзање привезивања"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Приказује опције за сертификацију бежичног екрана"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Повећава ниво евидентирања за Wi‑Fi. Приказ по SSID RSSI-у у бирачу Wi‑Fi мреже"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Смањује потрошњу батерије и побољшава учинак мреже"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Када је овај режим омогућен, MAC адреса овог уређаја може да се промени сваки пут када се повеже са мрежом на којој је омогућено насумично разврставање MAC адреса."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Када је овај режим омогућен, MAC адреса овог уређаја може да се промени сваки пут када се повеже са мрежом на којој је омогућено насумично разврставање MAC адреса."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Са ограничењем"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Без ограничења"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Величине бафера података у програму за евидентирање"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 5517325bd0ad..a0b92a2d9fbb 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Det går inte att söka efter nätverk"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Inget/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Ingen"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Inget/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bitar"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Sparat"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Frånkopplad"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inaktiverad"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certifiering för wifi-skärmdelning"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Aktivera utförlig loggning för wifi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Begränsning av wifi-sökning"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Slumpgenerering av icke-beständig MAC för wifi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Slumpgenerering av icke-beständig MAC för wifi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobildata alltid aktiverad"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Maskinvaruacceleration för internetdelning"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Visa certifieringsalternativ för wifi-skärmdelning"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Öka loggningsnivån för wifi, visa per SSID RSSI i Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Sänker batteriförbrukningen och förbättrar nätverksprestandan"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"När det här läget är aktiverat kan enhetens MAC-adress ändras varje gång den ansluts till ett nätverk där slumpgenerering av MAC-adress har aktiverats."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"När det här läget är aktiverat kan enhetens MAC-adress ändras varje gång den ansluts till ett nätverk där slumpgenerering av MAC-adress har aktiverats."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Med datapriser"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Utan datapriser"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Buffertstorlekar för logg"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index be4479f44739..76136c8791ff 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Haiwezi kutambaza mitandao"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Hamna/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Hamna"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Hamna/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Imehifadhiwa"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Hujaunganishwa"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Imezimwa"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Chaguo za cheti cha kuonyesha pasiwaya"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Washa Uwekaji kumbukumbu za WiFi kutumia Sauti"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Kudhibiti utafutaji wa Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Kuweka kwa unasibu anwani za MAC zisizo na muunganisho endelevu wa Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Kuweka kwa unasibu anwani za MAC zisizo na muunganisho endelevu wa Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Iendelee kutumia data ya simu"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Kuongeza kasi kwa kutumia maunzi ili kusambaza mtandao"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Ongeza hatua ya uwekaji kumbukumbu ya Wi-Fi, onyesha kwa kila SSID RSSI kwenye Kichukuzi cha Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Hupunguza matumizi ya chaji ya betri na kuboresha utendaji wa mtandao"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wakati hali hii imewashwa, huenda anwani ya MAC ya kifaa hiki ikabadilika kila wakati kinapounganisha kwenye mtandao ambapo kipengele cha unasibu wa MAC kimewashwa."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Wakati hali hii imewashwa, huenda anwani ya MAC ya kifaa hiki ikabadilika kila wakati kinapounganisha kwenye mtandao ambapo kipengele cha unasibu wa MAC kimewashwa."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Mtandao unapima data"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Mtandao usiopima data"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Ukubwa wa kiweka bafa ya kumbukumbu"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 839853629569..185104f0491a 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"எதுவுமில்லை/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"ஏதுமில்லை"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"எதுவுமில்லை/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"சேமிக்கப்பட்டது"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"தொடர்பு துண்டிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"முடக்கப்பட்டது"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"வயர்லெஸ் காட்சிக்கான சான்றிதழ்"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"வைஃபை அதிவிவர நுழைவை இயக்கு"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"வைஃபை ஸ்கேனிங்கை வரம்பிடுதல்"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"வைஃபையில், மாறுபடும் MAC முகவரியைக் காண்பித்தல்"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"வைஃபையில் MAC முகவரியை ரேண்டம் ஆக்குதல்"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"மொபைல் டேட்டாவை எப்போதும் இயக்கத்திலேயே வை"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"வைஃபை நுழைவு அளவை அதிகரித்து, வைஃபை தேர்வுக் கருவியில் ஒவ்வொன்றிற்கும் SSID RSSI ஐ காட்டுக"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"பேட்டரி தீர்ந்துபோவதைக் குறைத்து நெட்வொர்க்கின் செயல்திறனை மேம்படுத்தும்"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"இந்தப் பயன்முறை இயக்கப்படும்போது இந்தச் சாதனத்தின் MAC முகவரியானது ஒவ்வொரு முறை MAC ரேண்டம் ஆக்குதல் இயக்கப்பட்டிருக்கும் நெட்வொர்க்குடன் இணைக்கப்படும்போதும் மாறக்கூடும்."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"இந்தப் பயன்முறை இயக்கப்பட்டிருக்கும்போது இந்தச் சாதனத்தின் MAC முகவரி ஒவ்வொரு முறை \'MAC முகவரியை ரேண்டம் ஆக்குதல்\' இயக்கப்பட்டிருக்கும் நெட்வொர்க்குடன் இணைக்கப்படும்போதும் மாறக்கூடும்."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"கட்டண நெட்வொர்க்"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"கட்டணமில்லா நெட்வொர்க்"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"லாகர் பஃபர் அளவுகள்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 8903f1432f5a..6ff2d2d15ff1 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"నెట్‌వర్క్‌ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"ఏదీ లేదు"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"సేవ్ చేయబడింది"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"డిస్‌కనెక్ట్ అయ్యింది"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"డిజేబుల్ చేయబడింది"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"వైర్‌లెస్ డిస్‌ప్లే సర్టిఫికేషన్‌"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi‑Fi విశదీకృత లాగింగ్‌ను ప్రారంభించండి"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi స్కాన్ కుదింపు"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi నిరంతరం కాని MAC ర్యాండమైజేషన్"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi నిరంతరం కాని MAC ర్యాండమైజేషన్"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"మొబైల్ డేటాని ఎల్లప్పుడూ యాక్టివ్‌గా ఉంచు"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"టెథెరింగ్ హార్డ్‌వేర్ వేగవృద్ధి"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు చూపించు"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"వైర్‌లెస్ డిస్‌ప్లే సర్టిఫికేషన్ ఆప్షన్‌లను చూపు"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"బ్యాటరీ శక్తి వినియోగాన్ని తగ్గించి &amp; నెట్‌వర్క్ పనితీరును మెరుగుపరుస్తుంది"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ఈ మోడ్ ఎనేబుల్ అయ్యాక, MAC ర్యాండమైజేషన్‌ను ఎనేబుల్ చేసిన నెట్‌వర్క్‌తో కనెక్ట్ అయ్యే ప్రతిసారీ ఈ పరికరం MAC అడ్రస్ మారవచ్చు."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"ఈ మోడ్ ఎనేబుల్ అయ్యాక, MAC ర్యాండమైజేషన్‌ను ఎనేబుల్ చేసిన నెట్‌వర్క్‌తో కనెక్ట్ అయ్యే ప్రతిసారీ ఈ పరికరం MAC అడ్రస్‌ను మారవచ్చు."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"గణించబడుతోంది"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"గణించబడటం లేదు"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"లాగర్ బఫర్ సైజ్‌లు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 25fe87fce1b8..51a042087038 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"ไม่สามารถสแกนหาเครือข่าย"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"ไม่มี/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"ไม่มี"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"ไม่มี/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 บิต"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"บันทึกแล้ว"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"ยกเลิกการเชื่อมต่อแล้ว"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ปิดอยู่"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"การรับรองการแสดงผลแบบไร้สาย"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"เปิดใช้การบันทึกรายละเอียด Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"การควบคุมการสแกนหา Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"การสุ่ม MAC ที่มี Wi-Fi ไม่ถาวร"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"การสุ่ม MAC ที่มี Wi-Fi ไม่ถาวร"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"เปิดใช้เน็ตมือถือเสมอ"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"การเร่งฮาร์ดแวร์การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"แสดงตัวเลือกสำหรับการรับรองการแสดงผลแบบไร้สาย"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ลดการเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์นี้อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายที่มีการเปิดใช้การสุ่ม MAC"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายซึ่งเปิดใช้การสุ่ม MAC"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"แบบจำกัดปริมาณ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ไม่มีการวัดปริมาณอินเทอร์เน็ต"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ขนาดบัฟเฟอร์ของตัวบันทึก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a219b5af8a4b..1bc84afc164b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Hindi makapag-scan ng mga network"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Wala/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Wala"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Wala/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Na-save"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Nadiskonekta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Naka-disable"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Certification ng wireless display"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"I-enable ang Pagla-log sa Wi‑Fi Verbose"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Pag-throttle ng pag-scan ng Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Pag-randomize sa MAC ng hindi persistent na Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Pag-randomize ng MAC na hindi persistent sa Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Palaging aktibo ang mobile data"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Hardware acceleration para sa pag-tether"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Ipakita ang mga opsyon para sa certification ng wireless display"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Pataasin ang antas ng Wi‑Fi logging, ipakita sa bawat SSID RSSI sa Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Binabawasan ang pagkaubos ng baterya at pinapahusay ang performance ng network"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kapag naka-enable ang mode na ito, puwedeng magbago ang MAC address ng device na ito sa tuwing kokonekta ito sa isang network na may naka-enable na MAC randomization."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Kapag naka-enable ang mode na ito, puwedeng magbago ang MAC address ng device na ito sa tuwing kokonekta ito sa isang network na may naka-enable na pag-randomize ng MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Nakametro"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Hindi Nakametro"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Mga laki ng buffer ng Logger"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index ad8cb8e9f972..a00457a35081 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ağlar taranamıyor"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Yok"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Kaydedildi"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Bağlı değil"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Devre dışı"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Kablosuz ekran sertifikası"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Kablosuz Ayrıntılı Günlük Kaydını etkinleştir"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Kablosuz ağ taramasını kısma"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Kablosuz kalıcı olmayan MAC rastgele hale getirme süreci"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Kablosuz kalıcı olmayan MAC rastgele hale getirme süreci"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobil veri her zaman etkin"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tethering donanım hızlandırıcısı"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Kablosuz ekran sertifikası seçeneklerini göster"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Kablosuz günlük kaydı seviyesini artır. Kablosuz Seçici\'de her bir SSID RSSI için göster."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Pili daha az harcar ve ağ performansını iyileştirir"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Bu mod etkinleştirildiğinde, bu cihaz MAC rastgele hale getirme işlevi açık olan bir ağa her bağlandığında cihazın MAC adresi değişebilir."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Bu mod etkinleştirildiğinde, bu cihaz MAC rastgele hale getirme işlevi açık olan bir ağa her bağlandığında cihazın MAC adresi değişebilir."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Sayaçlı"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Sayaçsız"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Günlük Kaydedici arabellek boyutları"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 4d0d9b6ede34..6aac29327f54 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Неможливо здійснити сканування мереж"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Немає/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Немає"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Немає/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise, 192-бітне шифрування"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Збережено"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Від’єднано"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Вимкнено"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Сертифікація бездрот. екрана"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Докладний запис у журнал Wi-Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Зменшити радіус пошуку мереж Wi‑Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Випадкові MAC-адреси в мережі Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Випадкові MAC-адреси в мережі Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Не вимикати мобільне передавання даних"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Апаратне прискорення під час використання телефона в режимі модема"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Показати параметри сертифікації бездротового екрана"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Показувати в журналі RSSI для кожного SSID під час вибору Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Зменшує споживання заряду акумулятора й підвищує ефективність роботи мережі"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Якщо цей режим увімкнено, MAC-адреса пристрою може змінюватися щоразу, коли він підключається до мережі з довільним вибором MAC-адрес."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Коли цей режим увімкнено, MAC-адреса пристрою може змінюватися щоразу, коли він підключається до мережі з випадковими MAC-адресами."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"З тарифікацією трафіку"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Без тарифікації трафіку"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Розміри буфера журналу"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index cf6013f969a6..c67a6fc136b3 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,7 +21,61 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+ <!-- no translation found for wifi_security_short_wep (7939809393561636237) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa (6998160832497442533) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa2 (7697856994856831026) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_wpa_wpa2 (2399839645955520093) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap (5029688687205212985) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa (8510772177310043426) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_wpa2_wpa3 (6455656470422244501) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_sae (78353562671556266) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_psk_sae (4965830739185952958) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_none_owe (8827409046261759703) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_owe (5073524307942025369) -->
+ <skip />
+ <!-- no translation found for wifi_security_short_eap_suiteb (4174071135081556115) -->
+ <skip />
<string name="wifi_security_none" msgid="7392696451280611452">"کوئی نہیں"</string>
+ <!-- no translation found for wifi_security_wep (1413627788581122366) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa (1072450904799930636) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa2 (4038267581230425543) -->
+ <skip />
+ <!-- no translation found for wifi_security_wpa_wpa2 (946853615482465986) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap (6179633834446852269) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa (6189023812330549957) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa_wpa2 (1089879674896108216) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa2_wpa3 (2952912020876252266) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_wpa3 (7961135182909018796) -->
+ <skip />
+ <!-- no translation found for wifi_security_passpoint (2209078477216565387) -->
+ <skip />
+ <!-- no translation found for wifi_security_sae (3644520541721422843) -->
+ <skip />
+ <!-- no translation found for wifi_security_psk_sae (8135104122179904684) -->
+ <skip />
+ <!-- no translation found for wifi_security_none_owe (5241745828327404101) -->
+ <skip />
+ <!-- no translation found for wifi_security_owe (3343421403561657809) -->
+ <skip />
+ <!-- no translation found for wifi_security_eap_suiteb (415842785991698142) -->
+ <skip />
<string name="wifi_remembered" msgid="3266709779723179188">"محفوظ کردیا گیا"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"غیر منسلک"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غیر فعال"</string>
@@ -252,7 +306,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"وائرلیس ڈسپلے سرٹیفیکیشن"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"‏Wi‑Fi وربوس لاگنگ فعال کریں"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"‏Wi‑Fi اسکین کو زبردستی روکا جا رہا ہے"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"‏Wi-Fi غیر مستقل MAC کی رینڈمائزیشن"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"‏Wi-Fi غیر مستقل MAC کی رینڈمائزیشن"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"موبائل ڈیٹا ہمیشہ فعال رکھیں"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"ٹیدرنگ ہارڈویئر سرعت کاری"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string>
@@ -284,7 +338,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"‏Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"بیٹری ڈرین کم کرتا ہے اور نیٹ ورک کارکردگی کو بہتر بناتا ہے"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"‏جب یہ وضع فعال ہوتا ہے تو، اس آلہ کا MAC پتہ ہر بار تبدیل ہو سکتا ہے جب یہ کسی نیٹ ورک سے منسلک ہوتا ہے جس میں MAC ہے رینڈمائزیشن کو فعال کرتا ہے۔"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"‏اس وضع کے فعال ہونے پر اس آلے کا MAC پتہ ہر بار تبدیل ہو سکتا ہے جب بھی یہ کسی ایسے نیٹ ورک سے منسلک ہوتا ہے جس میں MAC رینڈمائزیشن فعال ہو۔"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"میٹرڈ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"غیر میٹر شدہ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"لاگر بفر کے سائز"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 34ef0d6d2521..47c828de0c0e 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Sozlanmagan/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Hech qanday"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Sozlanmagan/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Saqlangan"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Ulanmagan"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Yoqilmagan"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Simsiz monitor sertifikatlari"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Batafsil Wi-Fi jurnali"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi tarmoqni taqsimlab skanlash"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi MAC manzilini muddatli tasodiflash"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi MAC manzilini muddatli tasodiflash"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Mobil internet doim yoniq tursin"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Modem rejimida apparatli tezlashtirish"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Batareya sarfini tejaydi va tarmoq samaradorligini oshiradi"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Bu rejim yoqilganda qurilmaning MAC manzili tasodifiy MAC manzillar yaratish imkoniyati mavjud tarmoqqa har safar ulanganda almashishi mumkin."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Bu rejim yoqilganda qurilmaning MAC manzili tasodifiy MAC manzillar yaratish imkoniyati mavjud tarmoqqa har safar ulanganda almashishi mumkin."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Trafik hisoblanadigan tarmoq"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Trafik hisobi yuritilmaydigan tarmoq"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Jurnal buferi hajmi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index a67ea6a06381..aaaaadab852c 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Không thể dò tìm mạng"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Không/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Không"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Không/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Đã lưu"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Đã ngắt kết nối"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Đã tắt"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Chứng nhận hiển thị không dây"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Bật ghi nhật ký chi tiết Wi‑Fi"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Hạn chế quét tìm Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Sắp xếp ngẫu nhiên địa chỉ MAC không ổn định khi kết nối Wi-Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Tạo địa chỉ MAC ngẫu nhiên, không cố định mỗi khi kết nối Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Dữ liệu di động luôn hoạt động"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"Tăng tốc phần cứng khi chia sẻ Internet"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiện các thiết bị Bluetooth không có tên"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Hiển thị tùy chọn chứng nhận hiển thị không dây"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Tăng mức ghi nhật ký Wi‑Fi, hiển thị mỗi SSID RSSI trong bộ chọn Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Giảm hao pin và cải thiện hiệu suất mạng"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Khi bật chế độ này, địa chỉ MAC của thiết bị này có thể thay đổi mỗi lần thiết bị kết nối với mạng đã bật tính năng sử dụng địa chỉ MAC ngẫu nhiên."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Khi bật chế độ này, địa chỉ MAC của thiết bị này có thể thay đổi mỗi lần thiết bị kết nối với mạng đã bật chế độ tạo địa chỉ MAC ngẫu nhiên."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Đo lượng dữ liệu"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Không đo lượng dữ liệu"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Kích thước bộ đệm của trình ghi nhật ký"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index e9914510debd..c85dba0fd08e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"无法扫描网络"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"无/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"无"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"无/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 位"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"已保存"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"已断开连接"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"无线显示认证"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"启用 WLAN 详细日志记录功能"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"WLAN 扫描调节"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"为 WLAN 热点随机生成非持久性 MAC 地址"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"为 WLAN 热点随机生成非持久性 MAC 地址"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"始终开启移动数据网络"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"网络共享硬件加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"显示无线显示认证选项"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"提升 WLAN 日志记录级别(在 WLAN 选择器中显示每个 SSID 的 RSSI)"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"降低耗电量以及改善网络性能"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"启用此模式后,每当此设备连接到已启用随机分配 MAC 地址功能的网络时,它的 MAC 地址就可能会发生更改。"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"启用此模式后,这台设备每次连接到已启用随机生成 MAC 地址功能的网络时,其 MAC 地址都可能会更改。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"按流量计费"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"不按流量计费"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"日志记录器缓冲区大小"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 6d8a0b04bd5c..dccd1df27463 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"無法掃瞄網絡"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"None/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"無"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"None/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 位元"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"已儲存"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"已中斷連線"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"無線螢幕分享認證"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"啟用 Wi‑Fi 詳細記錄"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi 掃瞄限流"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi 非持續性隨機 MAC 位址"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi 非持續性隨機 MAC 位址"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"一律保持啟用流動數據"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"網絡共享硬件加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"減低耗電量並改善網絡表現"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"啟用此模式後,每次連接至已啟用 MAC 隨機處理的網絡時,此裝置的 MAC 位址都可能會變更。"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"啟用此模式後,每次連接至已啟用 MAC 隨機處理的網絡時,此裝置的 MAC 位址都可能會變更。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"按用量收費"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"不限數據用量收費"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"記錄器緩衝區空間"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 53be010b24ef..afa27aaf060b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"無法掃描網路"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"無/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"無"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"無/Enhanced Open"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192 位元"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"已儲存"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"已中斷連線"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"無線螢幕分享認證"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"啟用 Wi‑Fi 詳細記錄設定"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi-Fi 掃描調節"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Wi‑Fi 非持續性隨機 MAC 位址"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Wi‑Fi 非持續性隨機 MAC 位址"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"行動數據連線一律保持啟用狀態"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"網路共用硬體加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細記錄"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"降低耗電量以及改善網路效能"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"啟用這個模式後,每次連線到啟用了 MAC 隨機化的網路時,這部裝置的 MAC 位址都可能會有所變更。"</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"這個模式啟用後,每當這部裝置連線到已啟用隨機 MAC 位址的網路時,裝置的 MAC 位址可能都會改變。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"計量付費"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"非計量付費"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"記錄器緩衝區空間"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 9daa41c22e13..9b3063d3d9d7 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,7 +21,34 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"Ayikwazi ukuhlola amanethiwekhi"</string>
+ <string name="wifi_security_short_wep" msgid="7939809393561636237">"I-WEP"</string>
+ <string name="wifi_security_short_wpa" msgid="6998160832497442533">"I-WPA"</string>
+ <string name="wifi_security_short_wpa2" msgid="7697856994856831026">"I-WPA2"</string>
+ <string name="wifi_security_short_wpa_wpa2" msgid="2399839645955520093">"I-WPA/WPA2"</string>
+ <string name="wifi_security_short_eap" msgid="5029688687205212985">"802.1x"</string>
+ <string name="wifi_security_short_eap_wpa" msgid="8510772177310043426">"I-WPA-EAP"</string>
+ <string name="wifi_security_short_eap_wpa2_wpa3" msgid="6455656470422244501">"I-RSN-EAP"</string>
+ <string name="wifi_security_short_sae" msgid="78353562671556266">"I-WPA3"</string>
+ <string name="wifi_security_short_psk_sae" msgid="4965830739185952958">"I-WPA2/WPA3"</string>
+ <string name="wifi_security_short_none_owe" msgid="8827409046261759703">"Akukho/OWE"</string>
+ <string name="wifi_security_short_owe" msgid="5073524307942025369">"I-OWE"</string>
+ <string name="wifi_security_short_eap_suiteb" msgid="4174071135081556115">"I-Suite-B-192"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"Lutho"</string>
+ <string name="wifi_security_wep" msgid="1413627788581122366">"I-WEP"</string>
+ <string name="wifi_security_wpa" msgid="1072450904799930636">"I-WPA-Personal"</string>
+ <string name="wifi_security_wpa2" msgid="4038267581230425543">"I-WPA2-Personal"</string>
+ <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"I-WPA/WPA2-Personal"</string>
+ <string name="wifi_security_eap" msgid="6179633834446852269">"I-WPA/WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"I-WPA-Enterprise"</string>
+ <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"I-WPA/WPA2-Enterprise"</string>
+ <string name="wifi_security_eap_wpa2_wpa3" msgid="2952912020876252266">"I-WPA2/WPA3-Enterprise"</string>
+ <string name="wifi_security_eap_wpa3" msgid="7961135182909018796">"I-WPA3-Enterprise"</string>
+ <string name="wifi_security_passpoint" msgid="2209078477216565387">"I-Passpoint"</string>
+ <string name="wifi_security_sae" msgid="3644520541721422843">"I-WPA3-Personal"</string>
+ <string name="wifi_security_psk_sae" msgid="8135104122179904684">"I-WPA2/WPA3-Personal"</string>
+ <string name="wifi_security_none_owe" msgid="5241745828327404101">"Akukho/Ukuthuthukisa Kuvuliwe"</string>
+ <string name="wifi_security_owe" msgid="3343421403561657809">"Ukuthuthukisa Kuvuliwe"</string>
+ <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"I-WPA3-Enterprise 192-bit"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Kulondoloziwe"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Inqamukile"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Akusebenzi"</string>
@@ -252,7 +279,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"Ukunikezwa isitifiketi sokubukeka okungenantambo"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Nika amandlaukungena kwe-Wi-Fi Verbose"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"I-throttling yokuskena kwe-Wi-Fi"</string>
- <string name="wifi_enhanced_mac_randomization" msgid="882650208573834301">"Okungahleliwe kwe-MAC engaphikeleli ye-Wi‑Fi"</string>
+ <string name="wifi_non_persistent_mac_randomization" msgid="7482769677894247316">"Okungahleliwe kwe-MAC engaphikeleli ye-Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"Idatha yeselula ihlala isebenza"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"I-Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string>
@@ -284,7 +311,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Bonisa izinketho zokunikeza isitifiketi ukubukeka okungenantambo"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"khuphula izinga lokungena le-Wi-Fi, bonisa nge-SSID RSSI engayodwana kusikhethi se-Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Yehlisa ukuphela kwebhethri futhi ithuthukise ukusebenza kwenethiwekhi"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Uma le modi inikwe amandla, ikheli le-MAC lale divayisi lingashintsha njalo uma ixhuma kunethiwekhi ene-MAC engahleliwe enikwe amandla."</string>
+ <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Uma le modi inikwe amandla, ikheli le-MAC lale divayisi lingashintsha njalo uma ixhuma kunethiwekhi ene-MAC engahleliwe enikwe amandla."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Kulinganisiwe"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Akulinganiselwa"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Amasayizi weloga ngebhafa"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6b840bd7901d..cab7ceec08b3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -20,68 +20,68 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Toast message when Wi-Fi cannot scan for networks -->
<string name="wifi_fail_to_scan">Can\'t scan for networks</string>
- <!-- Do not translate. Concise terminology for wifi with WEP security -->
- <string name="wifi_security_short_wep" translatable="false">WEP</string>
- <!-- Do not translate. Concise terminology for wifi with WPA security -->
- <string name="wifi_security_short_wpa" translatable="false">WPA</string>
- <!-- Do not translate. Concise terminology for wifi with WPA2 security -->
- <string name="wifi_security_short_wpa2" translatable="false">WPA2</string>
- <!-- Do not translate. Concise terminology for wifi with both WPA/WPA2 security -->
- <string name="wifi_security_short_wpa_wpa2" translatable="false">WPA/WPA2</string>
- <!-- Do not translate. Concise terminology for wifi with unknown PSK type -->
+ <!-- Concise terminology for wifi with WEP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_wep">WEP</string>
+ <!-- Concise terminology for wifi with WPA security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_wpa">WPA</string>
+ <!-- Concise terminology for wifi with WPA2 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_wpa2">WPA2</string>
+ <!-- Concise terminology for wifi with both WPA/WPA2 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_wpa_wpa2">WPA/WPA2</string>
+ <!-- Concise terminology for wifi with unknown PSK type -->
<string name="wifi_security_short_psk_generic" translatable="false">@string/wifi_security_short_wpa_wpa2</string>
- <!-- Do not translate. Concise terminology for wifi with 802.1x EAP security -->
- <string name="wifi_security_short_eap" translatable="false">802.1x</string>
- <!-- Do not translate. Concise terminology for wifi with WPA 802.1x EAP security -->
- <string name="wifi_security_short_eap_wpa" translatable="false">WPA-EAP</string>
- <!-- Do not translate. Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
- <string name="wifi_security_short_eap_wpa2_wpa3" translatable="false">RSN-EAP</string>
- <!-- Do not translate. Concise terminology for wifi with WPA3 security -->
- <string name="wifi_security_short_sae" translatable="false">WPA3</string>
- <!-- Do not translate. Concise terminology for wifi with WPA2/WPA3 transition security -->
- <string name="wifi_security_short_psk_sae" translatable="false">WPA2/WPA3</string>
- <!-- Do not translate. Concise terminology for Wi-Fi with None/OWE transition mode security -->
- <string name="wifi_security_short_none_owe" translatable="false">None/OWE</string>
- <!-- Do not translate. Concise terminology for wifi with OWE security -->
- <string name="wifi_security_short_owe" translatable="false">OWE</string>
- <!-- Do not translate. Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
- <string name="wifi_security_short_eap_suiteb" translatable="false">Suite-B-192</string>
+ <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_eap">802.1x</string>
+ <!-- Concise terminology for wifi with WPA 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_eap_wpa">WPA-EAP</string>
+ <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_eap_wpa2_wpa3">RSN-EAP</string>
+ <!-- Concise terminology for wifi with WPA3 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_sae">WPA3</string>
+ <!-- Concise terminology for wifi with WPA2/WPA3 transition security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_psk_sae">WPA2/WPA3</string>
+ <!-- Concise terminology for Wi-Fi with None/OWE transition mode security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_none_owe">None/OWE</string>
+ <!-- Concise terminology for wifi with OWE security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_owe">OWE</string>
+ <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_short_eap_suiteb">Suite-B-192</string>
<!-- Used in Wi-Fi settings dialogs when Wi-Fi does not have any security. [CHAR LIMIT=40] -->
<string name="wifi_security_none">None</string>
- <!-- Do not translate. Terminology for wifi with WEP security -->
- <string name="wifi_security_wep" translatable="false">WEP</string>
- <!-- Do not translate. Terminology for wifi with WPA security -->
- <string name="wifi_security_wpa" translatable="false">WPA-Personal</string>
- <!-- Do not translate. Terminology for wifi with WPA2 security -->
- <string name="wifi_security_wpa2" translatable="false">WPA2-Personal</string>
- <!-- Do not translate. Terminology for wifi with both WPA/WPA2 security, or unknown -->
- <string name="wifi_security_wpa_wpa2" translatable="false">WPA/WPA2-Personal</string>
- <!-- Do not translate. Terminology for wifi with unknown PSK type -->
+ <!-- Terminology for wifi with WEP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_wep">WEP</string>
+ <!-- Terminology for wifi with WPA security [CHAR LIMIT=40] -->
+ <string name="wifi_security_wpa">WPA-Personal</string>
+ <!-- Terminology for wifi with WPA2 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_wpa2">WPA2-Personal</string>
+ <!-- Terminology for wifi with both WPA/WPA2 security, or unknown [CHAR LIMIT=40] -->
+ <string name="wifi_security_wpa_wpa2">WPA/WPA2-Personal</string>
+ <!-- Terminology for wifi with unknown PSK type -->
<string name="wifi_security_psk_generic" translatable="false">@string/wifi_security_wpa_wpa2</string>
- <!-- Do not translate. Concise terminology for wifi with 802.1x EAP security -->
- <string name="wifi_security_eap" translatable="false">WPA/WPA2/WPA3-Enterprise</string>
- <!-- Do not translate. Concise terminology for wifi with WPA 802.1x EAP security -->
- <string name="wifi_security_eap_wpa" translatable="false">WPA-Enterprise</string>
- <!-- Do not translate. Concise terminology for wifi with 802.1x EAP security -->
- <string name="wifi_security_eap_wpa_wpa2" translatable="false">WPA/WPA2-Enterprise</string>
- <!-- Do not translate. Concise terminology for wifi with WPA2/WPA3 802.1x EAP security -->
- <string name="wifi_security_eap_wpa2_wpa3" translatable="false">WPA2/WPA3-Enterprise</string>
- <!-- Do not translate. Concise terminology for wifi with WPA3 802.1x EAP security -->
- <string name="wifi_security_eap_wpa3" translatable="false">WPA3-Enterprise</string>
- <!-- Do not translate. Concise terminology for Passpoint network -->
- <string name="wifi_security_passpoint" translatable="false">Passpoint</string>
- <!-- Do not translate. Terminology for wifi with WPA3 security -->
- <string name="wifi_security_sae" translatable="false">WPA3-Personal</string>
- <!-- Do not translate. Terminology for wifi with WPA2/WPA3 Transition mode security -->
- <string name="wifi_security_psk_sae" translatable="false">WPA2/WPA3-Personal</string>
- <!-- Do not translate. Terminology for Wi-Fi with None/OWE transition mode security -->
- <string name="wifi_security_none_owe" translatable="false">None/Enhanced Open</string>
- <!-- Do not translate. Terminology for wifi with OWE security -->
- <string name="wifi_security_owe" translatable="false">Enhanced Open</string>
- <!-- Do not translate. Concise terminology for wifi with 802.1x EAP Suite-B-192 security -->
- <string name="wifi_security_eap_suiteb" translatable="false">WPA3-Enterprise 192-bit</string>
+ <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap">WPA/WPA2/WPA3-Enterprise</string>
+ <!-- Concise terminology for wifi with WPA 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap_wpa">WPA-Enterprise</string>
+ <!-- Concise terminology for wifi with 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap_wpa_wpa2">WPA/WPA2-Enterprise</string>
+ <!-- Concise terminology for wifi with WPA2/WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap_wpa2_wpa3">WPA2/WPA3-Enterprise</string>
+ <!-- Concise terminology for wifi with WPA3 802.1x EAP security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap_wpa3">WPA3-Enterprise</string>
+ <!-- Concise terminology for Passpoint network [CHAR LIMIT=40] -->
+ <string name="wifi_security_passpoint">Passpoint</string>
+ <!-- Terminology for wifi with WPA3 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_sae">WPA3-Personal</string>
+ <!-- Terminology for wifi with WPA2/WPA3 Transition mode security [CHAR LIMIT=40] -->
+ <string name="wifi_security_psk_sae">WPA2/WPA3-Personal</string>
+ <!-- Terminology for Wi-Fi with None/OWE transition mode security [CHAR LIMIT=40] -->
+ <string name="wifi_security_none_owe">None/Enhanced Open</string>
+ <!-- Terminology for wifi with OWE security [CHAR LIMIT=40] -->
+ <string name="wifi_security_owe">Enhanced Open</string>
+ <!-- Concise terminology for wifi with 802.1x EAP Suite-B-192 security [CHAR LIMIT=40] -->
+ <string name="wifi_security_eap_suiteb">WPA3-Enterprise 192-bit</string>
<!-- Summary for the remembered network. -->
<string name="wifi_remembered">Saved</string>
@@ -654,7 +654,7 @@
<!-- Setting Checkbox title whether to disable WiFi Scan Throttling. [CHAR LIMIT=40] -->
<string name="wifi_scan_throttling">Wi\u2011Fi scan throttling</string>
<!-- Setting Checkbox title whether to enable WiFi non-persistent mac randomization. [CHAR LIMIT=80] -->
- <string name="wifi_enhanced_mac_randomization">Wi\u2011Fi non\u2011persistent MAC randomization</string>
+ <string name="wifi_non_persistent_mac_randomization">Wi\u2011Fi non\u2011persistent MAC randomization</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Mobile data always active</string>
<!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -722,8 +722,8 @@
<string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
<!-- Setting Checkbox summary whether to disable Wifi scan throttling [CHAR LIMIT=NONE] -->
<string name="wifi_scan_throttling_summary">Reduces battery drain &amp; improves network performance</string>
- <!-- Setting Checkbox title whether to enable WiFi enhanced mac randomization. [CHAR LIMIT=NONE] -->
- <string name="wifi_enhanced_mac_randomization_summary">When this mode is enabled, this device\u2019s MAC address may change each time it connects to a network that has MAC randomization enabled.</string>
+ <!-- Setting Checkbox summary whether to enable WiFi non-persistent mac randomization. [CHAR LIMIT=NONE] -->
+ <string name="wifi_non_persistent_mac_randomization_summary">When this mode is enabled, this device\u2019s MAC address may change each time it connects to a network that has MAC randomization enabled.</string>
<!-- Label indicating network has been manually marked as metered -->
<string name="wifi_metered_label">Metered</string>
<!-- Label indicating network has been manually marked as unmetered -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 877dd2dfa26e..2e7cfcb13d8c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 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,8 @@
* limitations under the License.
*/
-package com.android.settingslib.location;
+package com.android.settingslib.applications;
+
import android.app.AppOpsManager;
import android.content.Context;
@@ -39,14 +40,24 @@ import java.util.Comparator;
import java.util.List;
/**
- * Retrieves the information of applications which accessed location recently.
+ * Retrieval of app ops information for the specified ops.
*/
-public class RecentLocationAccesses {
- private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+public class RecentAppOpsAccess {
@VisibleForTesting
- static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+ static final int[] LOCATION_OPS = new int[]{
+ AppOpsManager.OP_FINE_LOCATION,
+ AppOpsManager.OP_COARSE_LOCATION,
+ };
+ private static final int[] MICROPHONE_OPS = new int[]{
+ AppOpsManager.OP_RECORD_AUDIO,
+ };
+
- // Keep last 24 hours of location app information.
+ private static final String TAG = RecentAppOpsAccess.class.getSimpleName();
+ @VisibleForTesting
+ public static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+ // Keep last 24 hours of access app information.
private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
/** The flags for querying ops that are trusted for showing in the UI. */
@@ -54,47 +65,55 @@ public class RecentLocationAccesses {
| AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
| AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
- @VisibleForTesting
- static final int[] LOCATION_OPS = new int[]{
- AppOpsManager.OP_FINE_LOCATION,
- AppOpsManager.OP_COARSE_LOCATION,
- };
-
private final PackageManager mPackageManager;
private final Context mContext;
+ private final int[] mOps;
private final IconDrawableFactory mDrawableFactory;
private final Clock mClock;
- public RecentLocationAccesses(Context context) {
- this(context, Clock.systemDefaultZone());
+ public RecentAppOpsAccess(Context context, int[] ops) {
+ this(context, Clock.systemDefaultZone(), ops);
}
@VisibleForTesting
- RecentLocationAccesses(Context context, Clock clock) {
+ RecentAppOpsAccess(Context context, Clock clock, int[] ops) {
mContext = context;
mPackageManager = context.getPackageManager();
+ mOps = ops;
mDrawableFactory = IconDrawableFactory.newInstance(context);
mClock = clock;
}
/**
- * Fills a list of applications which queried location recently within specified time.
- * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+ * Creates an instance of {@link RecentAppOpsAccess} for location (coarse and fine) access.
+ */
+ public static RecentAppOpsAccess createForLocation(Context context) {
+ return new RecentAppOpsAccess(context, LOCATION_OPS);
+ }
+
+ /**
+ * Creates an instance of {@link RecentAppOpsAccess} for microphone access.
+ */
+ public static RecentAppOpsAccess createForMicrophone(Context context) {
+ return new RecentAppOpsAccess(context, MICROPHONE_OPS);
+ }
+
+ /**
+ * Fills a list of applications which queried for access recently within specified time.
+ * Apps are sorted by recency. Apps with more recent accesses are in the front.
*/
@VisibleForTesting
- List<Access> getAppList(boolean showSystemApps) {
- // Retrieve a location usage list from AppOps
- PackageManager pm = mContext.getPackageManager();
- AppOpsManager aoManager =
- (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+ public List<Access> getAppList(boolean showSystemApps) {
+ // Retrieve a access usage list from AppOps
+ AppOpsManager aoManager = mContext.getSystemService(AppOpsManager.class);
+ List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(mOps);
final int appOpsCount = appOps != null ? appOps.size() : 0;
// Process the AppOps list and generate a preference list.
ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
final long now = mClock.millis();
- final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ final UserManager um = mContext.getSystemService(UserManager.class);
final List<UserHandle> profiles = um.getUserProfiles();
for (int i = 0; i < appOpsCount; ++i) {
@@ -111,9 +130,10 @@ public class RecentLocationAccesses {
// Don't show apps that do not have user sensitive location permissions
boolean showApp = true;
if (!showSystemApps) {
- for (int op : LOCATION_OPS) {
+ for (int op : mOps) {
final String permission = AppOpsManager.opToPermission(op);
- final int permissionFlags = pm.getPermissionFlags(permission, packageName,
+ final int permissionFlags = mPackageManager.getPermissionFlags(permission,
+ packageName,
user);
if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
PermissionChecker.PID_UNKNOWN, uid, packageName)
@@ -144,12 +164,11 @@ public class RecentLocationAccesses {
return accesses;
}
-
/**
- * Gets a list of apps that accessed location recently, sorting by recency.
+ * Gets a list of apps that accessed the app op recently, sorting by recency.
*
* @param showSystemApps whether includes system apps in the list.
- * @return the list of apps that recently accessed location.
+ * @return the list of apps that recently accessed the app op.
*/
public List<Access> getAppListSorted(boolean showSystemApps) {
List<Access> accesses = getAppList(showSystemApps);
@@ -174,18 +193,18 @@ public class RecentLocationAccesses {
AppOpsManager.PackageOps ops) {
String packageName = ops.getPackageName();
List<AppOpsManager.OpEntry> entries = ops.getOps();
- long locationAccessFinishTime = 0L;
- // Earliest time for a location access to end and still be shown in list.
- long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+ long accessFinishTime = 0L;
+ // Earliest time for a access to end and still be shown in list.
+ long recentAccessCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
// Compute the most recent access time from all op entries.
for (AppOpsManager.OpEntry entry : entries) {
long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
- if (lastAccessTime > locationAccessFinishTime) {
- locationAccessFinishTime = lastAccessTime;
+ if (lastAccessTime > accessFinishTime) {
+ accessFinishTime = lastAccessTime;
}
}
// Bail out if the entry is out of date.
- if (locationAccessFinishTime < recentLocationCutoffTime) {
+ if (accessFinishTime < recentAccessCutoffTime) {
return null;
}
@@ -213,13 +232,16 @@ public class RecentLocationAccesses {
badgedAppLabel = null;
}
access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
- locationAccessFinishTime);
+ accessFinishTime);
} catch (NameNotFoundException e) {
Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
}
return access;
}
+ /**
+ * Information about when an app last accessed a particular app op.
+ */
public static class Access {
public final String packageName;
public final UserHandle userHandle;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index d205eaab6656..2c0162f608ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.settingslib.RestrictedLockUtils;
@@ -32,10 +33,12 @@ public class BiometricActionDisabledByAdminController extends BaseActionDisabled
// These MUST not change, as they are the stable API between here and device admin specified
// by the component below.
- private static final String ACTION_LEARN_MORE =
- "android.intent.action.MANAGE_RESTRICTED_SETTING";
- private static final String EXTRA_SETTING_KEY = "extra_setting";
- private static final String EXTRA_SETTING_VALUE = "biometric_disabled_by_admin_controller";
+ @VisibleForTesting
+ static final String ACTION_LEARN_MORE = "android.intent.action.MANAGE_RESTRICTED_SETTING";
+ @VisibleForTesting
+ static final String EXTRA_SETTING_KEY = "extra_setting";
+ @VisibleForTesting
+ static final String EXTRA_SETTING_VALUE = "biometric_disabled_by_admin_controller";
BiometricActionDisabledByAdminController(
DeviceAdminStringProvider stringProvider) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppCopyHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppCopyHelper.java
new file mode 100644
index 000000000000..75e23e0abfbd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppCopyHelper.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import android.app.AppGlobals;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper for {@link com.android.settings.users.AppCopyFragment}, for keeping track of which
+ * packages a user has chosen to copy to a second user and fulfilling that installation.
+ *
+ * To test, use
+ * atest SettingsLibTests:com.android.settingslib.users.AppCopyingHelperTest
+ */
+public class AppCopyHelper {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AppCopyHelper";
+
+ private final PackageManager mPackageManager;
+ private final IPackageManager mIPm;
+ private final UserHandle mUser;
+ private boolean mLeanback;
+
+ /** Set of packages to be installed. */
+ private final ArraySet<String> mSelectedPackages = new ArraySet<>();
+ /** List of installable packages from which the user can choose. */
+ private List<SelectableAppInfo> mVisibleApps;
+
+ public AppCopyHelper(Context context, UserHandle user) {
+ this(new Injector(context, user));
+ }
+
+ @VisibleForTesting
+ AppCopyHelper(Injector injector) {
+ mPackageManager = injector.getPackageManager();
+ mIPm = injector.getIPackageManager();
+ mUser = injector.getUser();
+ }
+
+ /** Toggles whether the package has been selected. */
+ public void setPackageSelected(String packageName, boolean selected) {
+ if (selected) {
+ mSelectedPackages.add(packageName);
+ } else {
+ mSelectedPackages.remove(packageName);
+ }
+ }
+
+ /** Resets all packages as unselected. */
+ public void resetSelectedPackages() {
+ mSelectedPackages.clear();
+ }
+
+ public void setLeanback(boolean isLeanback) {
+ mLeanback = isLeanback;
+ }
+
+ /** List of installable packages from which the user can choose. */
+ public List<SelectableAppInfo> getVisibleApps() {
+ return mVisibleApps;
+ }
+
+ /** Installs the packages that have been selected using {@link #setPackageSelected} */
+ public void installSelectedApps() {
+ for (int i = 0; i < mSelectedPackages.size(); i++) {
+ final String packageName = mSelectedPackages.valueAt(i);
+ installSelectedApp(packageName);
+ }
+ }
+
+ private void installSelectedApp(String packageName) {
+ final int userId = mUser.getIdentifier();
+ try {
+ final ApplicationInfo info = mIPm.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER, userId);
+ if (info == null || !info.enabled
+ || (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
+ Log.i(TAG, "Installing " + packageName);
+ mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier(),
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_UNKNOWN, null);
+ }
+ if (info != null && (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
+ && (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ Log.i(TAG, "Unhiding " + packageName);
+ mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId);
+ }
+ } catch (RemoteException re) {
+ // Ignore
+ }
+ }
+
+ /**
+ * Fetches the list of installable packages to display.
+ * This list can be obtained from {@link #getVisibleApps}.
+ */
+ public void fetchAndMergeApps() {
+ mVisibleApps = new ArrayList<>();
+ addCurrentUsersApps();
+ removeSecondUsersApp();
+ }
+
+ /**
+ * Adds to {@link #mVisibleApps} packages from the current user:
+ * (1) All downloaded apps and
+ * (2) all system apps that have launcher or widgets.
+ */
+ private void addCurrentUsersApps() {
+ // Add system package launchers of the current user
+ final Intent launcherIntent = new Intent(Intent.ACTION_MAIN).addCategory(
+ mLeanback ? Intent.CATEGORY_LEANBACK_LAUNCHER : Intent.CATEGORY_LAUNCHER);
+ addSystemApps(mVisibleApps, launcherIntent);
+
+ // Add system package widgets of the current user
+ final Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
+ addSystemApps(mVisibleApps, widgetIntent);
+
+ // Add all downloaded apps of the current user
+ final List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(0);
+ for (ApplicationInfo app : installedApps) {
+ // If it's not installed, skip
+ if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+
+ if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+ && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
+ // Downloaded app
+ final SelectableAppInfo info = new SelectableAppInfo();
+ info.packageName = app.packageName;
+ info.appName = app.loadLabel(mPackageManager);
+ info.icon = app.loadIcon(mPackageManager);
+ mVisibleApps.add(info);
+ }
+ }
+
+ // Remove dupes
+ final Set<String> dedupPackages = new HashSet<>();
+ for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
+ final SelectableAppInfo info = mVisibleApps.get(i);
+ if (DEBUG) Log.i(TAG, info.toString());
+ if (!TextUtils.isEmpty(info.packageName) && dedupPackages.contains(info.packageName)) {
+ mVisibleApps.remove(i);
+ } else {
+ dedupPackages.add(info.packageName);
+ }
+ }
+
+ // Sort the list of visible apps
+ mVisibleApps.sort(new AppLabelComparator());
+ }
+
+ /** Removes from {@link #mVisibleApps} all packages already installed on mUser. */
+ private void removeSecondUsersApp() {
+ // Get the set of apps already installed for mUser
+ final Set<String> userPackages = new HashSet<>();
+ final List<ApplicationInfo> userAppInfos = mPackageManager.getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier());
+ for (int i = userAppInfos.size() - 1; i >= 0; i--) {
+ final ApplicationInfo app = userAppInfos.get(i);
+ if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
+ userPackages.add(app.packageName);
+ }
+
+ for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
+ final SelectableAppInfo info = mVisibleApps.get(i);
+ if (DEBUG) Log.i(TAG, info.toString());
+ if (!TextUtils.isEmpty(info.packageName) && userPackages.contains(info.packageName)) {
+ mVisibleApps.remove(i);
+ }
+ }
+ }
+
+ /**
+ * Add system apps that match an intent to the list.
+ * @param visibleApps list of apps to append the new list to
+ * @param intent the intent to match
+ */
+ private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent) {
+ final List<ResolveInfo> intentApps = mPackageManager.queryIntentActivities(intent, 0);
+ for (ResolveInfo app : intentApps) {
+ if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
+ final int flags = app.activityInfo.applicationInfo.flags;
+ if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+
+ final SelectableAppInfo info = new SelectableAppInfo();
+ info.packageName = app.activityInfo.packageName;
+ info.appName = app.activityInfo.applicationInfo.loadLabel(mPackageManager);
+ info.icon = app.activityInfo.loadIcon(mPackageManager);
+
+ visibleApps.add(info);
+ }
+ }
+ }
+ }
+
+ /** Container for a package, its name, and icon. */
+ public static class SelectableAppInfo {
+ public String packageName;
+ public CharSequence appName;
+ public Drawable icon;
+
+ @Override
+ public String toString() {
+ return packageName + ": appName=" + appName + "; icon=" + icon;
+ }
+ }
+
+ private static class AppLabelComparator implements Comparator<SelectableAppInfo> {
+ @Override
+ public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
+ String lhsLabel = lhs.appName.toString();
+ String rhsLabel = rhs.appName.toString();
+ return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
+ }
+ }
+
+ /**
+ * Unit test will subclass it to inject mocks.
+ */
+ @VisibleForTesting
+ static class Injector {
+ private final Context mContext;
+ private final UserHandle mUser;
+
+ Injector(Context context, UserHandle user) {
+ mContext = context;
+ mUser = user;
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+
+ PackageManager getPackageManager() {
+ return mContext.getPackageManager();
+ }
+
+ IPackageManager getIPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java b/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java
new file mode 100644
index 000000000000..7c7044c33f2e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/HandlerInjector.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import android.os.Handler;
+
+/**
+ * Wrapper the {@link android.os.Handler} for testing compatibility.
+ */
+public class HandlerInjector {
+
+ protected final Handler mHandler;
+
+ public HandlerInjector(Handler handler) {
+ mHandler = handler;
+ }
+
+ /** Wrapper the {@link android.os.Handler#postDelayed} for testing compatibility. */
+ public void postDelayed(Runnable runnable, long delayMillis) {
+ mHandler.postDelayed(runnable, delayMillis);
+ }
+
+ /** Wrapper the {@link android.os.Handler#removeCallbacks} for testing compatibility. */
+ public void removeCallbacks(Runnable runnable) {
+ mHandler.removeCallbacks(runnable);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
deleted file mode 100644
index 503d60c87bb9..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
+++ /dev/null
@@ -1,46 +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.settingslib.wifi;
-
-import android.content.Context;
-
-import androidx.fragment.app.Fragment;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * WifiEntryPreference that can be long pressed.
- */
-public class LongPressWifiEntryPreference extends WifiEntryPreference {
-
- private final Fragment mFragment;
-
- public LongPressWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment) {
- super(context, wifiEntry);
- mFragment = fragment;
- }
-
- @Override
- public void onBindViewHolder(final PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- if (mFragment != null) {
- view.itemView.setOnCreateContextMenuListener(mFragment);
- view.itemView.setTag(this);
- view.itemView.setLongClickable(true);
- }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
deleted file mode 100644
index 4dd3ff1f556c..000000000000
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ /dev/null
@@ -1,314 +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.settingslib.wifi;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.settingslib.Utils;
-import com.android.wifitrackerlib.WifiEntry;
-
-/**
- * Preference to display a WifiEntry in a wifi picker.
- */
-public class WifiEntryPreference extends Preference implements WifiEntry.WifiEntryCallback,
- View.OnClickListener {
-
- private static final int[] STATE_SECURED = {
- R.attr.state_encrypted
- };
-
- private static final int[] FRICTION_ATTRS = {
- R.attr.wifi_friction
- };
-
- // These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX]
- private static final int[] WIFI_CONNECTION_STRENGTH = {
- R.string.accessibility_no_wifi,
- R.string.accessibility_wifi_one_bar,
- R.string.accessibility_wifi_two_bars,
- R.string.accessibility_wifi_three_bars,
- R.string.accessibility_wifi_signal_full
- };
-
- // StateListDrawable to display secured lock / metered "$" icon
- @Nullable private final StateListDrawable mFrictionSld;
- private final IconInjector mIconInjector;
- private WifiEntry mWifiEntry;
- private int mLevel = -1;
- private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
- private CharSequence mContentDescription;
- private OnButtonClickListener mOnButtonClickListener;
-
- public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) {
- this(context, wifiEntry, new IconInjector(context));
- }
-
- @VisibleForTesting
- WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry,
- @NonNull IconInjector iconInjector) {
- super(context);
-
- setLayoutResource(R.layout.preference_access_point);
- setWidgetLayoutResource(R.layout.access_point_friction_widget);
- mFrictionSld = getFrictionStateListDrawable();
- mWifiEntry = wifiEntry;
- mWifiEntry.setListener(this);
- mIconInjector = iconInjector;
- refresh();
- }
-
- public WifiEntry getWifiEntry() {
- return mWifiEntry;
- }
-
- @Override
- public void onBindViewHolder(final PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- final Drawable drawable = getIcon();
- if (drawable != null) {
- drawable.setLevel(mLevel);
- }
-
- view.itemView.setContentDescription(mContentDescription);
-
- // Turn off divider
- view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
-
- // Enable the icon button when the help string in this WifiEntry is not null.
- final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
- final ImageView frictionImageView = (ImageView) view.findViewById(
- R.id.friction_icon);
- if (mWifiEntry.getHelpUriString() != null
- && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
- final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
- drawablehelp.setTintList(
- Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal));
- ((ImageView) imageButton).setImageDrawable(drawablehelp);
- imageButton.setVisibility(View.VISIBLE);
- imageButton.setOnClickListener(this);
- imageButton.setContentDescription(
- getContext().getText(R.string.help_label));
-
- if (frictionImageView != null) {
- frictionImageView.setVisibility(View.GONE);
- }
- } else {
- imageButton.setVisibility(View.GONE);
-
- if (frictionImageView != null) {
- frictionImageView.setVisibility(View.VISIBLE);
- bindFrictionImage(frictionImageView);
- }
- }
- }
-
- /**
- * Updates the title and summary; may indirectly call notifyChanged().
- */
- public void refresh() {
- setTitle(mWifiEntry.getTitle());
- final int level = mWifiEntry.getLevel();
- final boolean showX = mWifiEntry.shouldShowXLevelIcon();
- if (level != mLevel || showX != mShowX) {
- mLevel = level;
- mShowX = showX;
- updateIcon(mShowX, mLevel);
- notifyChanged();
- }
-
- setSummary(mWifiEntry.getSummary(false /* concise */));
- mContentDescription = buildContentDescription();
- }
-
- /**
- * Indicates the state of the WifiEntry has changed and clients may retrieve updates through
- * the WifiEntry getter methods.
- */
- public void onUpdated() {
- // TODO(b/70983952): Fill this method in
- refresh();
- }
-
- /**
- * Result of the connect request indicated by the WifiEntry.CONNECT_STATUS constants.
- */
- public void onConnectResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the disconnect request indicated by the WifiEntry.DISCONNECT_STATUS constants.
- */
- public void onDisconnectResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the forget request indicated by the WifiEntry.FORGET_STATUS constants.
- */
- public void onForgetResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- /**
- * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
- */
- public void onSignInResult(int status) {
- // TODO(b/70983952): Fill this method in
- }
-
- protected int getIconColorAttr() {
- final boolean accent = (mWifiEntry.hasInternetAccess()
- && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
- return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
- }
-
- private void updateIcon(boolean showX, int level) {
- if (level == -1) {
- setIcon(null);
- return;
- }
-
- final Drawable drawable = mIconInjector.getIcon(showX, level);
- if (drawable != null) {
- drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), getIconColorAttr()));
- setIcon(drawable);
- } else {
- setIcon(null);
- }
- }
-
- @Nullable
- private StateListDrawable getFrictionStateListDrawable() {
- TypedArray frictionSld;
- try {
- frictionSld = getContext().getTheme().obtainStyledAttributes(FRICTION_ATTRS);
- } catch (Resources.NotFoundException e) {
- // Fallback for platforms that do not need friction icon resources.
- frictionSld = null;
- }
- return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
- }
-
- /**
- * Binds the friction icon drawable using a StateListDrawable.
- *
- * <p>Friction icons will be rebound when notifyChange() is called, and therefore
- * do not need to be managed in refresh()</p>.
- */
- private void bindFrictionImage(ImageView frictionImageView) {
- if (frictionImageView == null || mFrictionSld == null) {
- return;
- }
- if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE)
- && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) {
- mFrictionSld.setState(STATE_SECURED);
- }
- frictionImageView.setImageDrawable(mFrictionSld.getCurrent());
- }
-
- /**
- * Helper method to generate content description string.
- */
- @VisibleForTesting
- CharSequence buildContentDescription() {
- final Context context = getContext();
-
- CharSequence contentDescription = getTitle();
- final CharSequence summary = getSummary();
- if (!TextUtils.isEmpty(summary)) {
- contentDescription = TextUtils.concat(contentDescription, ",", summary);
- }
- int level = mWifiEntry.getLevel();
- if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
- contentDescription = TextUtils.concat(contentDescription, ",",
- context.getString(WIFI_CONNECTION_STRENGTH[level]));
- }
- return TextUtils.concat(contentDescription, ",",
- mWifiEntry.getSecurity() == WifiEntry.SECURITY_NONE
- ? context.getString(R.string.accessibility_wifi_security_type_none)
- : context.getString(R.string.accessibility_wifi_security_type_secured));
- }
-
-
- static class IconInjector {
- private final Context mContext;
-
- IconInjector(Context context) {
- mContext = context;
- }
-
- public Drawable getIcon(boolean showX, int level) {
- return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
- }
- }
-
- /**
- * Set listeners, who want to listen the button client event.
- */
- public void setOnButtonClickListener(OnButtonClickListener listener) {
- mOnButtonClickListener = listener;
- notifyChanged();
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.icon_button) {
- if (mOnButtonClickListener != null) {
- mOnButtonClickListener.onButtonClick(this);
- }
- }
- }
-
- /**
- * Callback to inform the caller that the icon button is clicked.
- */
- public interface OnButtonClickListener {
-
- /**
- * Register to listen the button click event.
- */
- void onButtonClick(WifiEntryPreference preference);
- }
-
- private Drawable getDrawable(@DrawableRes int iconResId) {
- Drawable buttonIcon = null;
-
- try {
- buttonIcon = getContext().getDrawable(iconResId);
- } catch (Resources.NotFoundException exception) {
- // Do nothing
- }
- return buttonIcon;
- }
-
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 72fa25fc7a3a..bf0dc7bce5f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -157,7 +157,6 @@ public class WifiStatusTracker {
private Network mDefaultNetwork = null;
private NetworkCapabilities mDefaultNetworkCapabilities = null;
private final Runnable mCallback;
- private final boolean mSupportMergedUi;
private WifiInfo mWifiInfo;
public boolean enabled;
@@ -181,7 +180,6 @@ public class WifiStatusTracker {
mNetworkScoreManager = networkScoreManager;
mConnectivityManager = connectivityManager;
mCallback = callback;
- mSupportMergedUi = false;
}
public void setListening(boolean listening) {
@@ -223,10 +221,8 @@ public class WifiStatusTracker {
} else {
ssid = getValidSsid(mWifiInfo);
}
- if (mSupportMergedUi) {
- isCarrierMerged = mWifiInfo.isCarrierMerged();
- subId = mWifiInfo.getSubscriptionId();
- }
+ isCarrierMerged = mWifiInfo.isCarrierMerged();
+ subId = mWifiInfo.getSubscriptionId();
updateRssi(mWifiInfo.getRssi());
maybeRequestNetworkScore();
}
@@ -255,10 +251,8 @@ public class WifiStatusTracker {
} else {
ssid = getValidSsid(mWifiInfo);
}
- if (mSupportMergedUi) {
- isCarrierMerged = mWifiInfo.isCarrierMerged();
- subId = mWifiInfo.getSubscriptionId();
- }
+ isCarrierMerged = mWifiInfo.isCarrierMerged();
+ subId = mWifiInfo.getSubscriptionId();
updateRssi(mWifiInfo.getRssi());
maybeRequestNetworkScore();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 61006156d8aa..56454e975370 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -20,10 +20,13 @@ import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_
import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiInfo;
+import android.os.Bundle;
import android.os.SystemClock;
import androidx.annotation.VisibleForTesting;
@@ -36,6 +39,23 @@ public class WifiUtils {
private static final int INVALID_RSSI = -127;
+ /**
+ * The intent action shows network details settings to allow configuration of Wi-Fi.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: The calling package should put the chosen
+ * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+ * the {@link #KEY_CHOSEN_WIFIENTRY_KEY}.
+ * <p>
+ * Output: Nothing.
+ */
+ public static final String ACTION_WIFI_DETAILS_SETTINGS =
+ "android.settings.WIFI_DETAILS_SETTINGS";
+ public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
+ public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+
static final int[] WIFI_PIE = {
com.android.internal.R.drawable.ic_wifi_signal_0,
com.android.internal.R.drawable.ic_wifi_signal_1,
@@ -275,7 +295,42 @@ public class WifiUtils {
return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
}
+ /**
+ * Wrapper the {@link #getInternetIconResource} for testing compatibility.
+ */
+ public static class InternetIconInjector {
+
+ protected final Context mContext;
+
+ public InternetIconInjector(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Returns the Internet icon for a given RSSI level.
+ *
+ * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+ * @param level The number of bars to show (0-4)
+ */
+ public Drawable getIcon(boolean noInternet, int level) {
+ return mContext.getDrawable(WifiUtils.getInternetIconResource(level, noInternet));
+ }
+ }
+
public static boolean isMeteredOverridden(WifiConfiguration config) {
return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
}
+
+ /**
+ * Returns the Intent for Wi-Fi network details settings.
+ *
+ * @param key The Wi-Fi entry key
+ */
+ public static Intent getWifiDetailsSettingsIntent(String key) {
+ final Intent intent = new Intent(ACTION_WIFI_DETAILS_SETTINGS);
+ final Bundle bundle = new Bundle();
+ bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
+ return intent;
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
new file mode 100644
index 000000000000..111bcc46907e
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppCopyingHelperTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.users;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+
+import com.android.settingslib.BaseTest;
+
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for AppCopyHelper.
+ */
+@SmallTest
+public class AppCopyingHelperTest extends BaseTest {
+ private @Mock Context mContext;
+ private @Mock PackageManager mPm;
+ private @Mock IPackageManager mIpm;
+
+ private final UserHandle mTestUser = UserHandle.of(1111);
+ private AppCopyHelper mHelper;
+
+ private final ArrayList<ApplicationInfo> mCurrUserInstalledAppInfos = new ArrayList<>();
+ private final ArrayList<ApplicationInfo> mTestUserInstalledAppInfos = new ArrayList<>();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ mHelper = new AppCopyHelper(new TestInjector());
+ }
+
+ public void testFetchAndMergeApps() throws Exception {
+ // Apps on the current user.
+ final String[] sysInapplicables = new String[] {"sys.no0, sys.no1"};
+ final String[] sysLaunchables = new String[] {"sys1", "sys2", "sys3"};
+ final String[] sysWidgets = new String[] {"sys1", "sys4"};
+ final String[] downloadeds = new String[] {"app1", "app2"};
+
+ addInapplicableSystemApps(sysInapplicables);
+ addSystemAppsForIntent(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
+ sysLaunchables);
+ addSystemAppsForIntent(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
+ sysWidgets);
+ addDownloadedApps(downloadeds);
+ when(mPm.getInstalledApplications(anyInt())).thenReturn(mCurrUserInstalledAppInfos);
+
+ // Apps on the test user.
+ final String[] testUserApps =
+ new String[]{"sys.no0", "sys2", "sys4", "app2", "sys999", "app999"};
+ addAppsToTestUser(testUserApps);
+ when(mPm.getInstalledApplicationsAsUser(anyInt(), eq(mTestUser.getIdentifier())))
+ .thenReturn(mTestUserInstalledAppInfos);
+
+ mHelper.fetchAndMergeApps();
+
+ final ArraySet<String> notExpectedInVisibleApps = new ArraySet<>();
+ Collections.addAll(notExpectedInVisibleApps, sysInapplicables);
+ Collections.addAll(notExpectedInVisibleApps, testUserApps);
+
+ final ArraySet<String> expectedInVisibleApps = new ArraySet<>();
+ Collections.addAll(expectedInVisibleApps, sysLaunchables);
+ Collections.addAll(expectedInVisibleApps, sysWidgets);
+ Collections.addAll(expectedInVisibleApps, downloadeds);
+ expectedInVisibleApps.removeAll(notExpectedInVisibleApps);
+
+ for (AppCopyHelper.SelectableAppInfo info : mHelper.getVisibleApps()) {
+ if (expectedInVisibleApps.contains(info.packageName)) {
+ expectedInVisibleApps.remove(info.packageName);
+ } else if (notExpectedInVisibleApps.contains(info.packageName)) {
+ fail("Package: " + info.packageName + " should not be included in visibleApps");
+ } else {
+ fail("Unknown package: " + info.packageName);
+ }
+ }
+ assertEquals("Some expected apps are not included in visibleApps: " + expectedInVisibleApps,
+ 0, expectedInVisibleApps.size());
+ }
+
+ public void testInstallSelectedApps() throws Exception {
+ final int testUserId = mTestUser.getIdentifier();
+
+ mHelper.setPackageSelected("app1", true); // Ultimately true
+ mHelper.setPackageSelected("app2", true); // Ultimately false
+ mHelper.setPackageSelected("app3", true); // Ultimately true
+ mHelper.setPackageSelected("app4", true); // Ultimately true
+
+ mHelper.setPackageSelected("app2", false);
+ mHelper.setPackageSelected("app1", false);
+ mHelper.setPackageSelected("app1", true);
+
+
+ // app3 is installed but hidden
+ ApplicationInfo info = new ApplicationInfo();
+ info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
+ info.flags |= ApplicationInfo.FLAG_INSTALLED;
+ when(mIpm.getApplicationInfo(eq("app3"), anyInt(), eq(testUserId)))
+ .thenReturn(info);
+
+ info = new ApplicationInfo();
+ when(mIpm.getApplicationInfo(eq("app4"), anyInt(), eq(testUserId)))
+ .thenReturn(info);
+
+ mHelper.installSelectedApps();
+
+ verify(mIpm, times(1)).installExistingPackageAsUser(
+ "app1", testUserId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_UNKNOWN, null);
+ verify(mIpm, times(0)).installExistingPackageAsUser(eq(
+ "app2"), eq(testUserId),
+ anyInt(), anyInt(), any());
+ verify(mIpm, times(0)).installExistingPackageAsUser(eq(
+ "app3"), eq(testUserId),
+ anyInt(), anyInt(), any());
+ verify(mIpm, times(1)).installExistingPackageAsUser(
+ "app4", testUserId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_UNKNOWN, null);
+
+ verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
+ eq("app1"), anyBoolean(), eq(testUserId));
+ verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
+ eq("app2"), anyBoolean(), eq(testUserId));
+ verify(mIpm, times(1)).setApplicationHiddenSettingAsUser(
+ eq("app3"), eq(false), eq(testUserId));
+ verify(mIpm, times(0)).setApplicationHiddenSettingAsUser(
+ eq("app4"), anyBoolean(), eq(testUserId));
+ }
+
+ private void addSystemAppsForIntent(Intent intent, String... packages) throws Exception {
+ final List<ResolveInfo> resolveInfos = new ArrayList<>();
+ for (String pkg : packages) {
+ final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
+ resolveInfos.add(ri);
+ addInstalledApp(ri, false);
+ }
+ when(mPm.queryIntentActivities(argThat(new IntentMatcher(intent)), anyInt()))
+ .thenReturn(resolveInfos);
+ }
+
+ private void addInapplicableSystemApps(String... packages) throws Exception {
+ for (String pkg : packages) {
+ final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
+ addInstalledApp(ri, false);
+ }
+ }
+
+ private void addDownloadedApps(String... packages) throws Exception {
+ for (String pkg : packages) {
+ final ResolveInfo ri = createResolveInfo(pkg);
+ addInstalledApp(ri, false);
+ }
+ }
+
+ private void addAppsToTestUser(String... packages) throws Exception {
+ for (String pkg : packages) {
+ final ResolveInfo ri = createResolveInfo(pkg);
+ addInstalledApp(ri, true);
+ }
+ }
+
+ private void addInstalledApp(ResolveInfo ri, boolean testUser)
+ throws PackageManager.NameNotFoundException {
+ final String pkgName = ri.activityInfo.packageName;
+ final PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ if (testUser) {
+ mTestUserInstalledAppInfos.add(packageInfo.applicationInfo);
+ } else {
+ mCurrUserInstalledAppInfos.add(packageInfo.applicationInfo);
+ }
+ when(mPm.getPackageInfo(eq(pkgName), anyInt())).thenReturn(packageInfo);
+ }
+
+ private ResolveInfo createResolveInfoForSystemApp(String packageName) {
+ final ResolveInfo ri = createResolveInfo(packageName);
+ ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ ri.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ return ri;
+ }
+
+ private ResolveInfo createResolveInfo(String packageName) {
+ final ResolveInfo ri = new ResolveInfo();
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ final ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.applicationInfo = applicationInfo;
+ activityInfo.packageName = packageName;
+ activityInfo.name = "";
+ ri.activityInfo = activityInfo;
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.applicationInfo = applicationInfo;
+ serviceInfo.packageName = packageName;
+ serviceInfo.name = "";
+ ri.serviceInfo = serviceInfo;
+ return ri;
+ }
+
+ private static class IntentMatcher implements ArgumentMatcher<Intent> {
+ private final Intent mIntent;
+
+ IntentMatcher(Intent intent) {
+ mIntent = intent;
+ }
+
+ @Override
+ public boolean matches(Intent argument) {
+ return argument != null && argument.filterEquals(mIntent);
+ }
+
+ @Override
+ public String toString() {
+ return "Expected: " + mIntent;
+ }
+ }
+
+ private class TestInjector extends AppCopyHelper.Injector {
+ TestInjector() {
+ super(mContext, mTestUser);
+ }
+
+ @Override
+ UserHandle getUser() {
+ return mTestUser;
+ }
+
+ @Override
+ PackageManager getPackageManager() {
+ return mPm;
+ }
+
+ @Override
+ IPackageManager getIPackageManager() {
+ return mIpm;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/OWNERS b/packages/SettingsLib/tests/robotests/OWNERS
new file mode 100644
index 000000000000..8a7a27ee4e21
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/OWNERS
@@ -0,0 +1,2 @@
+# We do not guard tests - everyone is welcomed to contribute to tests.
+per-file *.java=* \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
index 16d73a39d551..cb62a735434d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
@@ -1,15 +1,34 @@
-package com.android.settingslib.location;
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
+import android.content.PermissionChecker;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -19,13 +38,14 @@ import android.os.UserManager;
import android.util.LongSparseArray;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowPermissionChecker;
import java.time.Clock;
import java.util.ArrayList;
@@ -34,7 +54,8 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
-public class RecentLocationAccessesTest {
+@Config(shadows = {ShadowPermissionChecker.class})
+public class RecentAppOpsAccessesTest {
private static final int TEST_UID = 1234;
private static final long NOW = 1_000_000_000; // Approximately 9/8/2001
@@ -54,7 +75,7 @@ public class RecentLocationAccessesTest {
private Clock mClock;
private Context mContext;
private int mTestUserId;
- private RecentLocationAccesses mRecentLocationAccesses;
+ private RecentAppOpsAccess mRecentAppOpsAccess;
@Before
public void setUp() throws NameNotFoundException {
@@ -69,24 +90,37 @@ public class RecentLocationAccessesTest {
.thenReturn("testApplicationLabel");
when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class)))
.thenReturn("testUserBadgedLabel");
+ when(mPackageManager.getPermissionFlags(any(), any(), any()))
+ .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+ | PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED);
+ for (String testPackageName : TEST_PACKAGE_NAMES) {
+ ShadowPermissionChecker.setResult(
+ testPackageName,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ PermissionChecker.PERMISSION_GRANTED);
+ ShadowPermissionChecker.setResult(
+ testPackageName,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ PermissionChecker.PERMISSION_GRANTED);
+ }
mTestUserId = UserHandle.getUserId(TEST_UID);
when(mUserManager.getUserProfiles())
.thenReturn(Collections.singletonList(new UserHandle(mTestUserId)));
long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
- when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+ when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
appOps);
mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
when(mClock.millis()).thenReturn(NOW);
- mRecentLocationAccesses = new RecentLocationAccesses(mContext, mClock);
+ mRecentAppOpsAccess = new RecentAppOpsAccess(mContext, mClock,
+ RecentAppOpsAccess.LOCATION_OPS);
}
@Test
- @Ignore
public void testGetAppList_shouldFilterRecentAccesses() {
- List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(false);
+ List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(false);
// Only two of the apps have requested location within 15 min.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -97,12 +131,11 @@ public class RecentLocationAccessesTest {
}
@Test
- @Ignore
public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
// Add android OS to the list of apps.
PackageOps androidSystemPackageOps =
createPackageOps(
- RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME,
+ RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME,
Process.SYSTEM_UID,
AppOpsManager.OP_FINE_LOCATION,
ONE_MIN_AGO);
@@ -110,12 +143,12 @@ public class RecentLocationAccessesTest {
{ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
appOps.add(androidSystemPackageOps);
- when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+ when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
appOps);
mockTestApplicationInfos(
- Process.SYSTEM_UID, RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME);
+ Process.SYSTEM_UID, RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME);
- List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(true);
+ List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(true);
// Android OS shouldn't show up in the list of apps.
assertThat(requests).hasSize(2);
// Make sure apps are ordered by recency
@@ -159,7 +192,7 @@ public class RecentLocationAccessesTest {
// Slot for background access timestamp.
final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
- AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
+ AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null)));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 97e6d5051fd2..ed198fa923f2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -38,6 +38,7 @@ import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -158,6 +159,7 @@ public class LifecycleTest {
}
@Test
+ @Ignore("b/188888268")
public void runThroughActivityLifecycles_shouldObserveEverything() {
ActivityController<TestActivity> ac = Robolectric.buildActivity(TestActivity.class);
TestActivity activity = ac.setup().get();
@@ -179,6 +181,7 @@ public class LifecycleTest {
}
@Test
+ @Ignore("b/188888268")
public void runThroughDialogFragmentLifecycles_shouldObserveEverything() {
final TestDialogFragment fragment = new TestDialogFragment();
FragmentController.setupFragment(fragment);
@@ -202,6 +205,7 @@ public class LifecycleTest {
}
@Test
+ @Ignore("b/188888268")
public void runThroughFragmentLifecycles_shouldObserveEverything() {
final TestFragment fragment = new TestFragment();
FragmentController.setupFragment(fragment);
@@ -241,6 +245,7 @@ public class LifecycleTest {
}
@Test
+ @Ignore("b/188888268")
public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() {
final TestFragment fragment = new TestFragment();
FragmentController.setupFragment(fragment);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
index 766c2f5f9872..c41f4db6f6d1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -22,7 +22,7 @@ import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.D
import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.assertSame;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -39,41 +39,49 @@ 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 org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class BiometricActionDisabledByAdminControllerTest {
- private final ActionDisabledByAdminControllerTestUtils mTestUtils =
- new ActionDisabledByAdminControllerTestUtils();
- private final BiometricActionDisabledByAdminController mController =
- new BiometricActionDisabledByAdminController(DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
+ @Mock
+ private Context mContext;
+
+ private ActionDisabledByAdminControllerTestUtils mTestUtils;
+ private BiometricActionDisabledByAdminController mController;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestUtils = new ActionDisabledByAdminControllerTestUtils();
+
+ mController = new BiometricActionDisabledByAdminController(
+ DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
}
@Test
public void buttonClicked() {
- Context context = mock(Context.class);
ComponentName componentName = mock(ComponentName.class);
RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
componentName, new UserHandle(UserHandle.myUserId()));
DialogInterface.OnClickListener listener =
- mController.getPositiveButtonListener(context, enforcedAdmin);
+ mController.getPositiveButtonListener(mContext, enforcedAdmin);
assertNotNull("Biometric Controller must supply a non-null listener", listener);
listener.onClick(mock(DialogInterface.class), 0 /* which */);
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(context).startActivity(intentCaptor.capture());
- assertEquals("android.settings.LEARN_MORE",
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertEquals(BiometricActionDisabledByAdminController.ACTION_LEARN_MORE,
intentCaptor.getValue().getAction());
- assertTrue("from_biometric_setup", intentCaptor.getValue()
- .getBooleanExtra("from_biometric_setup", false));
- assertEquals(componentName, intentCaptor.getValue().getComponent());
+ assertEquals(BiometricActionDisabledByAdminController.EXTRA_SETTING_VALUE,
+ intentCaptor.getValue().getStringExtra(
+ BiometricActionDisabledByAdminController.EXTRA_SETTING_KEY));
+ assertSame(componentName, intentCaptor.getValue().getComponent());
}
-
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 58ca73468316..0d03f33eadd4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -27,7 +27,6 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -108,7 +107,7 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
- mShadowRouter2Manager.setAvailableRoutes(routes);
+ mShadowRouter2Manager.setTransferableRoutes(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
@@ -159,7 +158,7 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
- mShadowRouter2Manager.setAvailableRoutes(routes);
+ mShadowRouter2Manager.setTransferableRoutes(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
@@ -195,7 +194,7 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
- mShadowRouter2Manager.setAvailableRoutes(routes);
+ mShadowRouter2Manager.setTransferableRoutes(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
@@ -618,7 +617,7 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> routes = new ArrayList<>();
routes.add(info);
- mShadowRouter2Manager.setAvailableRoutes(routes);
+ mShadowRouter2Manager.setTransferableRoutes(routes);
final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
assertThat(mediaDevice).isNull();
@@ -728,9 +727,8 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
final List<MediaRoute2Info> infos = new ArrayList<>();
infos.add(info);
- mShadowRouter2Manager.setAvailableRoutes(infos);
+ mShadowRouter2Manager.setTransferableRoutes(infos);
- when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
@@ -741,9 +739,8 @@ public class InfoMediaManagerTest {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
final List<MediaRoute2Info> infos = new ArrayList<>();
infos.add(info);
- mShadowRouter2Manager.setAvailableRoutes(infos);
+ mShadowRouter2Manager.setTransferableRoutes(infos);
- when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
@@ -757,9 +754,8 @@ public class InfoMediaManagerTest {
final List<MediaRoute2Info> infos = new ArrayList<>();
infos.add(info);
infos.add(info2);
- mShadowRouter2Manager.setAvailableRoutes(infos);
+ mShadowRouter2Manager.setTransferableRoutes(infos);
- when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java
new file mode 100644
index 000000000000..4b34377ceb4b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/HandlerInjectorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.utils;
+
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class HandlerInjectorTest {
+
+ public static final long TEST_DELAY_MILLIS = 0L;
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ public Handler mHandler;
+ @Mock
+ public Runnable mRunnable;
+
+ private HandlerInjector mHandlerInjector;
+
+ @Before
+ public void setUp() {
+ mHandlerInjector = new HandlerInjector(mHandler);
+ }
+
+ @Test
+ public void postDelayed_doByMainThreadHandler() {
+ mHandlerInjector.postDelayed(mRunnable, TEST_DELAY_MILLIS);
+
+ verify(mHandler).postDelayed(mRunnable, TEST_DELAY_MILLIS);
+ }
+
+ @Test
+ public void removeCallbacks_doByMainThreadHandler() {
+ mHandlerInjector.removeCallbacks(mRunnable);
+
+ verify(mHandler).removeCallbacks(mRunnable);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
new file mode 100644
index 000000000000..34efe82f2527
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.Application;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SelectorWithWidgetPreferenceTest {
+
+ private Application mContext;
+ private SelectorWithWidgetPreference mPreference;
+
+ private View mExtraWidgetContainer;
+ private View mExtraWidget;
+
+ private boolean mIsClickListenerCalled;
+ private View.OnClickListener mClickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mIsClickListenerCalled = true;
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+ mPreference = new SelectorWithWidgetPreference(mContext);
+
+ View view = LayoutInflater.from(mContext)
+ .inflate(R.layout.preference_selector_with_widget, null /* root */);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ mExtraWidgetContainer = view.findViewById(R.id.selector_extra_widget_container);
+ mExtraWidget = view.findViewById(R.id.selector_extra_widget);
+ }
+
+ @Test
+ public void shouldHaveRadioPreferenceLayout() {
+ assertThat(mPreference.getLayoutResource()).isEqualTo(
+ R.layout.preference_selector_with_widget);
+ }
+
+ @Test
+ public void shouldHaveRadioButtonWidgetLayoutByDefault() {
+ assertThat(mPreference.getWidgetLayoutResource())
+ .isEqualTo(R.layout.preference_widget_radiobutton);
+ }
+
+ @Test
+ public void shouldHaveCheckBoxWidgetLayoutIfSet() {
+ mPreference = new SelectorWithWidgetPreference(mContext, true);
+ assertThat(mPreference.getWidgetLayoutResource())
+ .isEqualTo(R.layout.preference_widget_checkbox);
+ }
+
+ @Test
+ public void iconSpaceReservedShouldBeFalse() {
+ assertThat(mPreference.isIconSpaceReserved()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_withSummary_containerShouldBeVisible() {
+ mPreference.setSummary("some summary");
+ View summaryContainer = new View(mContext);
+ View view = mock(View.class);
+ when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ assertEquals(View.VISIBLE, summaryContainer.getVisibility());
+ }
+
+ @Test
+ public void onBindViewHolder_emptySummary_containerShouldBeGone() {
+ mPreference.setSummary("");
+ View summaryContainer = new View(mContext);
+ View view = mock(View.class);
+ when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+
+ mPreference.onBindViewHolder(preferenceViewHolder);
+
+ assertEquals(View.GONE, summaryContainer.getVisibility());
+ }
+
+ @Test
+ public void nullSummary_containerShouldBeGone() {
+ mPreference.setSummary(null);
+ View summaryContainer = new View(mContext);
+ View view = mock(View.class);
+ when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
+ PreferenceViewHolder preferenceViewHolder =
+ PreferenceViewHolder.createInstanceForTests(view);
+ mPreference.onBindViewHolder(preferenceViewHolder);
+ assertEquals(View.GONE, summaryContainer.getVisibility());
+ }
+
+ @Test
+ public void setAppendixVisibility_setGone_shouldBeGone() {
+ mPreference.setAppendixVisibility(View.GONE);
+
+ View view = LayoutInflater.from(mContext)
+ .inflate(R.layout.preference_selector_with_widget, null /* root */);
+ PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
+ mPreference.onBindViewHolder(holder);
+ assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void setExtraWidgetListener_setNull_extraWidgetShouldInvisible() {
+ mPreference.setExtraWidgetOnClickListener(null);
+
+ assertEquals(View.GONE, mExtraWidgetContainer.getVisibility());
+ }
+
+ @Test
+ public void setExtraWidgetListener_extraWidgetShouldVisible() {
+ mPreference.setExtraWidgetOnClickListener(mClickListener);
+
+ assertEquals(View.VISIBLE, mExtraWidgetContainer.getVisibility());
+ }
+
+ @Test
+ public void onClickListener_setExtraWidgetOnClickListener_ShouldCalled() {
+ mPreference.setExtraWidgetOnClickListener(mClickListener);
+
+ assertThat(mIsClickListenerCalled).isFalse();
+ mExtraWidget.callOnClick();
+ assertThat(mIsClickListenerCalled).isTrue();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
deleted file mode 100644
index c21830b28e3a..000000000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ /dev/null
@@ -1,254 +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.settingslib.wifi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settingslib.R;
-import com.android.wifitrackerlib.WifiEntry;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class WifiEntryPreferenceTest {
-
- private Context mContext;
-
- @Mock
- private WifiEntry mMockWifiEntry;
- @Mock
- private WifiEntryPreference.IconInjector mMockIconInjector;
-
- @Mock
- private Drawable mMockDrawable0;
- @Mock
- private Drawable mMockDrawable1;
- @Mock
- private Drawable mMockDrawable2;
- @Mock
- private Drawable mMockDrawable3;
- @Mock
- private Drawable mMockDrawable4;
-
- @Mock
- private Drawable mMockShowXDrawable0;
- @Mock
- private Drawable mMockShowXDrawable1;
- @Mock
- private Drawable mMockShowXDrawable2;
- @Mock
- private Drawable mMockShowXDrawable3;
- @Mock
- private Drawable mMockShowXDrawable4;
-
- private static final String MOCK_TITLE = "title";
- private static final String MOCK_SUMMARY = "summary";
- private static final String FAKE_URI_STRING = "fakeuri";
-
- @Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
-
- MockitoAnnotations.initMocks(this);
-
- when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
- when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
-
- when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0);
- when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1);
- when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2);
- when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3);
- when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4);
-
- when(mMockIconInjector.getIcon(true /* showX */, 0))
- .thenReturn(mMockShowXDrawable0);
- when(mMockIconInjector.getIcon(true /* showX */, 1))
- .thenReturn(mMockShowXDrawable1);
- when(mMockIconInjector.getIcon(true /* showX */, 2))
- .thenReturn(mMockShowXDrawable2);
- when(mMockIconInjector.getIcon(true /* showX */, 3))
- .thenReturn(mMockShowXDrawable3);
- when(mMockIconInjector.getIcon(true /* showX */, 4))
- .thenReturn(mMockShowXDrawable4);
- }
-
- @Test
- public void constructor_shouldSetWifiEntryTitleAndSummary() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- assertThat(pref.getTitle()).isEqualTo(MOCK_TITLE);
- assertThat(pref.getSummary()).isEqualTo(MOCK_SUMMARY);
- }
-
- @Test
- public void constructor_shouldSetIcon() {
- when(mMockWifiEntry.getLevel()).thenReturn(0);
-
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- assertThat(pref.getIcon()).isEqualTo(mMockDrawable0);
- }
-
- @Test
- public void titleChanged_refresh_shouldUpdateTitle() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final String updatedTitle = "updated title";
- when(mMockWifiEntry.getTitle()).thenReturn(updatedTitle);
-
- pref.refresh();
-
- assertThat(pref.getTitle()).isEqualTo(updatedTitle);
- }
-
- @Test
- public void summaryChanged_refresh_shouldUpdateSummary() {
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final String updatedSummary = "updated summary";
- when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(updatedSummary);
-
- pref.refresh();
-
- assertThat(pref.getSummary()).isEqualTo(updatedSummary);
- }
-
- @Test
- public void levelChanged_refresh_shouldUpdateLevelIcon() {
- final List<Drawable> iconList = new ArrayList<>();
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
-
- assertThat(iconList).containsExactly(mMockDrawable0, mMockDrawable1,
- mMockDrawable2, mMockDrawable3, mMockDrawable4, null);
- }
-
- @Test
- public void levelChanged_showXWifiRefresh_shouldUpdateLevelIcon() {
- final List<Drawable> iconList = new ArrayList<>();
- when(mMockWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
-
- when(mMockWifiEntry.getLevel()).thenReturn(0);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(1);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(2);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(3);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(4);
- pref.refresh();
- iconList.add(pref.getIcon());
- when(mMockWifiEntry.getLevel()).thenReturn(-1);
- pref.refresh();
- iconList.add(pref.getIcon());
-
- assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
- mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
- }
-
- @Test
- public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
- when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE);
- }
-
- @Test
- public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() {
- when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getContentDescription()).isEqualTo(
- mContext.getString(R.string.help_label));
- }
-
- @Test
- public void subscriptionEntry_shouldSetImageButtonGone() {
- when(mMockWifiEntry.isSubscription()).thenReturn(true);
- final WifiEntryPreference pref =
- new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext),
- false);
- final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
-
- pref.onBindViewHolder(holder);
-
- assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.GONE);
- }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 89960cba2bf5..7c2b904fc576 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,9 +20,12 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.net.NetworkKey;
import android.net.RssiCurve;
import android.net.ScoredNetwork;
@@ -36,6 +39,8 @@ import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.ArraySet;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.settingslib.R;
import org.junit.Before;
@@ -44,7 +49,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Set;
@@ -69,7 +73,7 @@ public class WifiUtilsTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = spy(ApplicationProvider.getApplicationContext());
}
@Test
@@ -148,6 +152,32 @@ public class WifiUtilsTest {
assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
}
+ @Test
+ public void getWifiDetailsSettingsIntent_returnsCorrectValues() {
+ final String key = "test_key";
+
+ final Intent intent = WifiUtils.getWifiDetailsSettingsIntent(key);
+
+ assertThat(intent.getAction()).isEqualTo(WifiUtils.ACTION_WIFI_DETAILS_SETTINGS);
+ final Bundle bundle = intent.getBundleExtra(WifiUtils.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(bundle.getString(WifiUtils.KEY_CHOSEN_WIFIENTRY_KEY)).isEqualTo(key);
+ }
+
+ @Test
+ public void testInternetIconInjector_getIcon_returnsCorrectValues() {
+ WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
+
+ for (int level = 0; level <= 4; level++) {
+ iconInjector.getIcon(false /* noInternet */, level);
+ verify(mContext).getDrawable(
+ WifiUtils.getInternetIconResource(level, false /* noInternet */));
+
+ iconInjector.getIcon(true /* noInternet */, level);
+ verify(mContext).getDrawable(
+ WifiUtils.getInternetIconResource(level, true /* noInternet */));
+ }
+ }
+
private static ArrayList<ScanResult> buildScanResultCache() {
ArrayList<ScanResult> scanResults = new ArrayList<>();
for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index 5bb550053a12..595986339343 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -85,4 +85,13 @@ public class ShadowRouter2Manager {
return (ShadowRouter2Manager) Shadow.extract(
MediaRouter2Manager.getInstance(RuntimeEnvironment.application));
}
+
+ @Implementation
+ protected List<MediaRoute2Info> getTransferableRoutes(String packageName) {
+ return mAvailableRoutes;
+ }
+
+ public void setTransferableRoutes(List<MediaRoute2Info> infos) {
+ mAvailableRoutes = infos;
+ }
}
diff --git a/packages/SettingsProvider/res/values-mcc466/defaults.xml b/packages/SettingsProvider/res/values-mcc466/defaults.xml
new file mode 100644
index 000000000000..fdeda8881dcc
--- /dev/null
+++ b/packages/SettingsProvider/res/values-mcc466/defaults.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Whether to enable mute when off body by default. -->
+ <bool name="def_wearable_muteWhenOffBodyEnabled">false</bool>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 8e6e251ff3f2..a9bc3be44a29 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -214,6 +214,10 @@
<!-- Default for Settings.System.VIBRATE_WHEN_RINGING -->
<bool name="def_vibrate_when_ringing">false</bool>
+ <!-- Default for Settings.Global.CELL_ON; see PhoneConstants.CELL_ON_FLAG.
+ 0: cellular off; 1: cellular on. -->
+ <integer name="def_cell_on">1</integer>
+
<!-- Default for Settings.Global.APPLY_RAMPING_RINGER -->
<bool name="def_apply_ramping_ringer">false</bool>
@@ -259,4 +263,64 @@
<!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
<bool name="def_one_handed_mode_activated">false</bool>
+ <!-- ========================================== -->
+ <!-- Default values for wear specific settings. -->
+
+ <bool name="def_wearable_hotwordDetectionEnabled">false</bool>
+
+ <bool name="def_wearable_smartIlluminateEnabled">true</bool>
+
+ <integer name="def_wearable_offChargerWifiUsageLimitMinutes">120</integer>
+
+ <!-- Default enabled state of accelerometer-based up/down gestures. -->
+ <bool name="def_wearable_upDownGesturesEnabled">false</bool>
+
+ <!-- Whether to enable mute when off body by default. -->
+ <bool name="def_wearable_muteWhenOffBodyEnabled">true</bool>
+
+ <!-- Whether to use an alternate launcher if available. -->
+ <bool name="def_wearable_alternateLauncherEnabled">true</bool>
+
+ <!-- If a square screen, how rounded the corners are. Same as CSS border-radius property. -->
+ <integer name="def_wearable_squareScreenCornerRoundness">0</integer>
+
+ <!-- Side button present -->
+ <bool name="def_wearable_sideButtonPresent">true</bool>
+
+ <!-- Android wear version. This value is a string due to no long type in resources -->
+ <string name="def_wearable_androidWearVersion" translatable="false">2</string>
+
+ <!-- This value is the decimal representation of the capabilities bitmask as defined below:
+ 0000001 - WIFI
+ 0000010 - Accounts
+ 0000100 - Phone
+ 0001000 - Cell
+ 0010000 - Companion Legacy Calling
+ 0100000 - Speaker
+ 1000000 - Setup Protocomm Channel
+
+ Note: These must match the positions in
+ com.google.android.clockwork.common.system.WearSystemConstants -->
+ <string name="def_wearable_systemCapabilities" translatable="false">3</string>
+
+ <!-- This value is used for the default system capabilities used on LE device. -->
+ <string name="def_wearable_leSystemCapabilities" translatable="false">1</string>
+
+ <!-- Brightness levels, on a 0-255 scale -->
+ <string name="def_wearable_brightnessLevels" translatable="false">255,204,153,102,51</string>
+
+ <!-- Whether to allow mobile signal detector by default. -->
+ <bool name="def_wearable_mobileSignalDetectorAllowed">true</bool>
+
+ <!-- If ambient mode is enabled by default. -->
+ <bool name="def_wearable_ambientEnabled">true</bool>
+
+ <!-- Whether tilt to wake is enabled by default. -->
+ <bool name="def_wearable_tiltToWakeEnabled">true</bool>
+
+ <!-- Whether touch to wake is enabled by default. -->
+ <bool name="def_wearable_touchToWakeEnabled">true</bool>
+
+ <!-- Whether tilt to bright is enabled by default. -->
+ <bool name="def_wearable_tiltToBrightEnabled">false</bool>
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index eb8196176034..80b7e1052d46 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -40,6 +40,9 @@ public class GlobalSettings {
public static final String[] SETTINGS_TO_BACKUP = {
Settings.Global.APPLY_RAMPING_RINGER,
Settings.Global.BUGREPORT_IN_POWER_MENU,
+ Settings.Global.CLOCKWORK_SYSUI_PACKAGE_NAME,
+ Settings.Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME,
+ Settings.Global.CLOCKWORK_HOME_READY,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
Settings.Global.AUTO_TIME,
@@ -76,5 +79,8 @@ public class GlobalSettings {
Settings.Global.ARE_USER_DISABLED_HDR_FORMATS_ALLOWED,
Settings.Global.DEVICE_CONFIG_SYNC_DISABLED,
Settings.Global.POWER_BUTTON_LONG_PRESS,
+ Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
+ Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+ Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 60226084c70d..96f127b6a611 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -101,6 +101,7 @@ public class SecureSettings {
Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
Settings.Secure.QS_TILES,
+ Settings.Secure.QS_AUTO_ADDED_TILES,
Settings.Secure.CONTROLS_ENABLED,
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
Settings.Secure.DOZE_ENABLED,
@@ -118,7 +119,6 @@ public class SecureSettings {
Settings.Secure.VR_DISPLAY_MODE,
Settings.Secure.NOTIFICATION_BADGING,
Settings.Secure.NOTIFICATION_DISMISS_RTL,
- Settings.Secure.QS_AUTO_ADDED_TILES,
Settings.Secure.SCREENSAVER_ENABLED,
Settings.Secure.SCREENSAVER_COMPONENTS,
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 5220a04d73e6..0a75eb81734c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -20,6 +20,8 @@ import static android.media.AudioFormat.SURROUND_SOUND_ENCODING;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR;
import static android.view.Display.HdrCapabilities.HDR_TYPES;
@@ -113,6 +115,14 @@ public class GlobalSettingsValidators {
VALIDATORS.put(
Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, PERCENTAGE_INTEGER_VALIDATOR);
VALIDATORS.put(Global.BLUETOOTH_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.ENABLE_TARE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.ENABLE_TARE_ALARM_MANAGER, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.ENABLE_TARE_JOB_SCHEDULER, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.TARE_ALARM_MANAGER_CONSTANTS, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.TARE_JOB_SCHEDULER_CONSTANTS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR);
VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR);
@@ -140,6 +150,177 @@ public class GlobalSettingsValidators {
/* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
VALIDATORS.put(Global.DISABLE_WINDOW_BLURS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.AUTOMATIC_POWER_SAVE_MODE, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.ADVANCED_BATTERY_USAGE_AMOUNT, PERCENTAGE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
+
+ VALIDATORS.put(Global.Wearable.HAS_PAY_TOKENS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.HOTWORD_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SMART_REPLIES_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.RETAIL_MODE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.RETAIL_MODE_CONSUMER),
+ String.valueOf(Global.Wearable.RETAIL_MODE_RETAIL)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.PHONE_PLAY_STORE_AVAILABLE),
+ String.valueOf(Global.Wearable.PHONE_PLAY_STORE_UNAVAILABLE),
+ String.valueOf(Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY_UNKNOWN)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.BUG_REPORT,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.BUG_REPORT_ENABLED),
+ String.valueOf(Global.Wearable.BUG_REPORT_DISABLED)
+ }));
+ VALIDATORS.put(Global.Wearable.SMART_ILLUMINATE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.CLOCKWORK_AUTO_TIME,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SYNC_TIME_FROM_PHONE),
+ String.valueOf(Global.Wearable.SYNC_TIME_FROM_NETWORK),
+ String.valueOf(Global.Wearable.AUTO_TIME_OFF),
+ String.valueOf(Global.Wearable.INVALID_AUTO_TIME_STATE)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE),
+ String.valueOf(Global.Wearable.SYNC_TIME_ZONE_FROM_NETWORK),
+ String.valueOf(Global.Wearable.AUTO_TIME_ZONE_OFF),
+ String.valueOf(Global.Wearable.INVALID_AUTO_TIME_ZONE_STATE)
+ }));
+ VALIDATORS.put(Global.Wearable.CLOCKWORK_24HR_TIME, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AUTO_WIFI, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WIFI_POWER_SAVE, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+ ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.UPDOWN_GESTURES_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.SETUP_SKIPPED,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_YES),
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_NO),
+ String.valueOf(Global.Wearable.SETUP_SKIPPED_UNKNOWN)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_ON),
+ String.valueOf(Global.Wearable.CALL_FORWARD_ACTION_OFF),
+ String.valueOf(Global.Wearable.CALL_FORWARD_NO_LAST_ACTION)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_1_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_2_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.STEM_3_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.STEM_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.STEM_TYPE_APP_LAUNCH),
+ String.valueOf(Global.Wearable.STEM_TYPE_CONTACT_LAUNCH)
+ }));
+ VALIDATORS.put(Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.ALTERNATE_LAUNCHER_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.CORNER_ROUNDNESS, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SIDE_BUTTON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.BUTTON_SET, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.ANDROID_WEAR_VERSION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SYSTEM_CAPABILITIES, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.SYSTEM_EDITION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WEAR_PLATFORM_MR_NUMBER, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.BOTTOM_OFFSET, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.DISPLAY_SHAPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.DISPLAY_SHAPE_ROUND),
+ String.valueOf(Global.Wearable.DISPLAY_SHAPE_SQUARE)
+ }));
+ VALIDATORS.put(Global.Wearable.MOBILE_SIGNAL_DETECTOR, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TOUCH_TO_WAKE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.DECOMPOSABLE_WATCHFACE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_GESTURE_SENSOR_ID, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_LOW_BIT_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.AMBIENT_TILT_TO_BRIGHT, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN),
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_ANDROID),
+ String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.COMPANION_BLE_ROLE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.BLUETOOTH_ROLE_CENTRAL),
+ String.valueOf(Global.Wearable.BLUETOOTH_ROLE_PERIPHERAL)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.USER_HFP_CLIENT_SETTING,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.HFP_CLIENT_UNSET),
+ String.valueOf(Global.Wearable.HFP_CLIENT_ENABLED),
+ String.valueOf(Global.Wearable.HFP_CLIENT_DISABLED)
+ }));
+ VALIDATORS.put(Global.Wearable.HFP_CLIENT_PROFILE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.COMPANION_OS_VERSION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.ENABLE_ALL_LANGUAGES, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.OEM_SETUP_VERSION, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.MASTER_GESTURES_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.UNGAZE_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Global.Wearable.BATTERY_SAVER_MODE,
+ new DiscreteValueValidator(
+ new String[] {
+ String.valueOf(Global.Wearable.BATTERY_SAVER_MODE_NONE),
+ String.valueOf(Global.Wearable.BATTERY_SAVER_MODE_LIGHT),
+ String.valueOf(Global.Wearable.BATTERY_SAVER_MODE_TRADITIONAL_WATCH),
+ String.valueOf(Global.Wearable.BATTERY_SAVER_MODE_TIME_ONLY),
+ String.valueOf(Global.Wearable.BATTERY_SAVER_MODE_CUSTOM)
+ }));
+ VALIDATORS.put(
+ Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS,
+ NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.BURN_IN_PROTECTION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.COMBINED_LOCATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.WRIST_ORIENTATION_MODE,
+ new DiscreteValueValidator(new String[] {"0", "1", "2", "3"}));
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 6d7fb027ee99..bf8b933ef520 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -34,7 +34,9 @@ import static android.provider.settings.validators.SettingsValidators.TILE_LIST_
import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
import android.provider.Settings.Secure;
+import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import java.util.Map;
@@ -69,6 +71,7 @@ public class SecureSettingsValidators {
Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
COLON_SEPARATED_COMPONENT_LIST_VALIDATOR);
VALIDATORS.put(Secure.TOUCH_EXPLORATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.WEAR_TALKBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
@@ -276,7 +279,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACCESSIBILITY_BUTTON_MODE,
new InclusiveIntegerRangeValidator(
Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR,
- Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU));
+ Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE));
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
new DiscreteValueValidator(new String[] {"0", "1"}));
VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
@@ -287,5 +290,32 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.DEVICE_STATE_ROTATION_LOCK, value -> {
+ if (TextUtils.isEmpty(value)) {
+ return true;
+ }
+ String[] intValues = value.split(":");
+ if (intValues.length % 2 != 0) {
+ return false;
+ }
+ InclusiveIntegerRangeValidator enumValidator =
+ new InclusiveIntegerRangeValidator(
+ Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED,
+ Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ ArraySet<String> keys = new ArraySet<>();
+ for (int i = 0; i < intValues.length - 1; ) {
+ String entryKey = intValues[i++];
+ String entryValue = intValues[i++];
+ if (!NON_NEGATIVE_INTEGER_VALIDATOR.validate(entryKey)
+ || !enumValidator.validate(entryValue)) {
+ return false;
+ }
+ // If the same device state key was specified more than once, this is invalid
+ if (!keys.add(entryKey)) {
+ return false;
+ }
+ }
+ return true;
+ });
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 268603fa8b0d..cdf274f23dbb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2467,6 +2467,9 @@ class DatabaseHelper extends SQLiteOpenHelper {
loadBooleanSetting(stmt, Settings.Global.BLUETOOTH_ON,
R.bool.def_bluetooth_on);
+ loadIntegerSetting(stmt, Settings.Global.CELL_ON,
+ R.integer.def_cell_on);
+
// Enable or disable Cell Broadcast SMS
loadSetting(stmt, Settings.Global.CDMA_CELL_BROADCAST_SMS,
RILConstants.CDMA_CELL_BROADCAST_SMS_DISABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 00fd19c2b984..f83ebaacb8e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -33,6 +33,7 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.provider.Settings.Config.SyncDisabledMode;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,7 +70,7 @@ public final class DeviceConfigService extends Binder {
LIST,
RESET,
SET_SYNC_DISABLED_FOR_TESTS,
- IS_SYNC_DISABLED_FOR_TESTS,
+ GET_SYNC_DISABLED_FOR_TESTS,
}
MyShellCommand(SettingsProvider provider) {
@@ -103,8 +104,8 @@ public final class DeviceConfigService extends Binder {
verb = CommandVerb.RESET;
} else if ("set_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
verb = CommandVerb.SET_SYNC_DISABLED_FOR_TESTS;
- } else if ("is_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
- verb = CommandVerb.IS_SYNC_DISABLED_FOR_TESTS;
+ } else if ("get_sync_disabled_for_tests".equalsIgnoreCase(cmd)) {
+ verb = CommandVerb.GET_SYNC_DISABLED_FOR_TESTS;
if (peekNextArg() != null) {
perr.println("Bad arguments");
return -1;
@@ -117,7 +118,7 @@ public final class DeviceConfigService extends Binder {
}
// Parse args for those commands that have them.
- int disableSyncMode = -1;
+ int syncDisabledModeArg = -1;
int resetMode = -1;
boolean makeDefault = false;
String namespace = null;
@@ -154,15 +155,10 @@ public final class DeviceConfigService extends Binder {
}
}
} else if (verb == CommandVerb.SET_SYNC_DISABLED_FOR_TESTS) {
- if (disableSyncMode == -1) {
- // DISABLE_SYNC_FOR_TESTS 1st arg (required)
- if ("none".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_NONE;
- } else if ("persistent".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_PERSISTENT;
- } else if ("until_reboot".equalsIgnoreCase(arg)) {
- disableSyncMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
- } else {
+ if (syncDisabledModeArg == -1) {
+ // SET_SYNC_DISABLED_FOR_TESTS 1st arg (required)
+ syncDisabledModeArg = parseSyncDisabledMode(arg);
+ if (syncDisabledModeArg == -1) {
// invalid
perr.println("Invalid sync disabled mode: " + arg);
return -1;
@@ -252,10 +248,16 @@ public final class DeviceConfigService extends Binder {
DeviceConfig.resetToDefaults(resetMode, namespace);
break;
case SET_SYNC_DISABLED_FOR_TESTS:
- DeviceConfig.setSyncDisabled(disableSyncMode);
+ DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
break;
- case IS_SYNC_DISABLED_FOR_TESTS:
- pout.println(DeviceConfig.isSyncDisabled());
+ case GET_SYNC_DISABLED_FOR_TESTS:
+ int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+ String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+ if (syncDisabledModeString == null) {
+ perr.println("Unknown mode: " + syncDisabledModeInt);
+ return -1;
+ }
+ pout.println(syncDisabledModeString);
break;
default:
perr.println("Unspecified command");
@@ -295,8 +297,9 @@ public final class DeviceConfigService extends Binder {
+ " syncing.");
pw.println(" persistent: Sync is disabled, this state will survive a reboot.");
pw.println(" until_reboot: Sync is disabled until the next reboot.");
- pw.println(" is_sync_disabled_for_tests");
- pw.println(" Prints 'true' if sync is disabled, 'false' otherwise.");
+ pw.println(" get_sync_disabled_for_tests");
+ pw.println(" Prints one of the SYNC_DISABLED_MODE values, see"
+ + " set_sync_disabled_for_tests");
}
private boolean delete(IContentProvider provider, String namespace, String key) {
@@ -358,4 +361,31 @@ public final class DeviceConfigService extends Binder {
}
}
}
+
+ private static @SyncDisabledMode int parseSyncDisabledMode(String arg) {
+ int syncDisabledMode;
+ if ("none".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_NONE;
+ } else if ("persistent".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_PERSISTENT;
+ } else if ("until_reboot".equalsIgnoreCase(arg)) {
+ syncDisabledMode = SYNC_DISABLED_MODE_UNTIL_REBOOT;
+ } else {
+ syncDisabledMode = -1;
+ }
+ return syncDisabledMode;
+ }
+
+ private static String formatSyncDisabledMode(@SyncDisabledMode int syncDisabledMode) {
+ switch (syncDisabledMode) {
+ case SYNC_DISABLED_MODE_NONE:
+ return "none";
+ case SYNC_DISABLED_MODE_PERSISTENT:
+ return "persistent";
+ case SYNC_DISABLED_MODE_UNTIL_REBOOT:
+ return "until_reboot";
+ default:
+ return null;
+ }
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 9cd7083a2a11..8c669d21db81 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -848,9 +848,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
settingsHelper.restoreValue(this, cr, contentValues, destination, key, value,
mRestoredFromSdkInt);
- if (DEBUG) {
- Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
- }
+ Log.d(TAG, "Restored setting: " + destination + " : " + key + "=" + value);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index c57786888e8d..6cfcb51239a3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -72,8 +72,9 @@ public class SettingsHelper {
* {@hide}
*/
private static final ArraySet<String> sBroadcastOnRestore;
+ private static final ArraySet<String> sBroadcastOnRestoreSystemUI;
static {
- sBroadcastOnRestore = new ArraySet<String>(4);
+ sBroadcastOnRestore = new ArraySet<String>(9);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
@@ -83,6 +84,9 @@ public class SettingsHelper {
sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+ sBroadcastOnRestoreSystemUI = new ArraySet<String>(2);
+ sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_TILES);
+ sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES);
}
private interface SettingsLookup {
@@ -133,6 +137,7 @@ public class SettingsHelper {
// Will we need a post-restore broadcast for this element?
String oldValue = null;
boolean sendBroadcast = false;
+ boolean sendBroadcastSystemUI = false;
final SettingsLookup table;
if (destination.equals(Settings.Secure.CONTENT_URI)) {
@@ -143,10 +148,12 @@ public class SettingsHelper {
table = sGlobalLookup;
}
- if (sBroadcastOnRestore.contains(name)) {
+ sendBroadcast = sBroadcastOnRestore.contains(name);
+ sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
+
+ if (sendBroadcast || sendBroadcastSystemUI) {
// TODO: http://b/22388012
oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
- sendBroadcast = true;
}
try {
@@ -193,18 +200,28 @@ public class SettingsHelper {
} catch (Exception e) {
// If we fail to apply the setting, by definition nothing happened
sendBroadcast = false;
+ sendBroadcastSystemUI = false;
} finally {
// If this was an element of interest, send the "we just restored it"
// broadcast with the historical value now that the new value has
// been committed and observers kicked off.
- if (sendBroadcast) {
+ if (sendBroadcast || sendBroadcastSystemUI) {
Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
- .setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.putExtra(Intent.EXTRA_SETTING_NAME, name)
.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue)
.putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+
+ if (sendBroadcast) {
+ intent.setPackage("android");
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+ }
+ if (sendBroadcastSystemUI) {
+ intent.setPackage(
+ context.getString(com.android.internal.R.string.config_systemUi));
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+ }
}
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 073b4d00653d..20735cba44a3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -763,9 +763,6 @@ class SettingsProtoDumpUtil {
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
dumpSetting(s, p,
- Settings.Global.ANGLE_ALLOWLIST,
- GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
- dumpSetting(s, p,
Settings.Global.ANGLE_EGL_FEATURES,
GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
dumpSetting(s, p,
@@ -1195,6 +1192,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.POWER_MANAGER_CONSTANTS,
GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
+ GlobalSettingsProto.POWER_BUTTON_LONG_PRESS_DURATION_MS);
final long prepaidSetupToken = p.start(GlobalSettingsProto.PREPAID_SETUP);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index db301f698753..25211b42ae87 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -43,6 +43,7 @@ import android.app.backup.BackupManager;
import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.bluetooth.BluetoothProfile;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
@@ -85,6 +86,7 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -136,7 +138,6 @@ import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
-
/**
* <p>
* This class is a content provider that publishes the system settings.
@@ -471,16 +472,16 @@ public class SettingsProvider extends ContentProvider {
return result;
}
- case Settings.CALL_METHOD_SET_SYNC_DISABLED_CONFIG: {
+ case Settings.CALL_METHOD_SET_SYNC_DISABLED_MODE_CONFIG: {
final int mode = getSyncDisabledMode(args);
- setSyncDisabledConfig(mode);
+ setSyncDisabledModeConfig(mode);
break;
}
- case Settings.CALL_METHOD_IS_SYNC_DISABLED_CONFIG: {
+ case Settings.CALL_METHOD_GET_SYNC_DISABLED_MODE_CONFIG: {
Bundle result = new Bundle();
- result.putBoolean(Settings.KEY_CONFIG_IS_SYNC_DISABLED_RETURN,
- isSyncDisabledConfig());
+ result.putInt(Settings.KEY_CONFIG_GET_SYNC_DISABLED_MODE_RETURN,
+ getSyncDisabledModeConfig());
return result;
}
@@ -1148,7 +1149,7 @@ public class SettingsProvider extends ContentProvider {
final String callingPackage = resolveCallingPackage();
synchronized (mLock) {
- if (isSyncDisabledConfigLocked()) {
+ if (getSyncDisabledModeConfigLocked() != SYNC_DISABLED_MODE_NONE) {
return SET_ALL_RESULT_DISABLED;
}
final int key = makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
@@ -1158,32 +1159,32 @@ public class SettingsProvider extends ContentProvider {
}
}
- private void setSyncDisabledConfig(@SyncDisabledMode int syncDisabledMode) {
+ private void setSyncDisabledModeConfig(@SyncDisabledMode int syncDisabledMode) {
if (DEBUG) {
- Slog.v(LOG_TAG, "setSyncDisabledConfig(" + syncDisabledMode + ")");
+ Slog.v(LOG_TAG, "setSyncDisabledModeConfig(" + syncDisabledMode + ")");
}
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
- setSyncDisabledConfigLocked(syncDisabledMode);
+ setSyncDisabledModeConfigLocked(syncDisabledMode);
}
}
- private boolean isSyncDisabledConfig() {
+ private int getSyncDisabledModeConfig() {
if (DEBUG) {
- Slog.v(LOG_TAG, "isSyncDisabledConfig");
+ Slog.v(LOG_TAG, "getSyncDisabledModeConfig");
}
enforceWritePermission(Manifest.permission.WRITE_DEVICE_CONFIG);
synchronized (mLock) {
- return isSyncDisabledConfigLocked();
+ return getSyncDisabledModeConfigLocked();
}
}
@GuardedBy("mLock")
- private void setSyncDisabledConfigLocked(@SyncDisabledMode int syncDisabledMode) {
+ private void setSyncDisabledModeConfigLocked(@SyncDisabledMode int syncDisabledMode) {
boolean persistentValue;
boolean inMemoryValue;
if (syncDisabledMode == SYNC_DISABLED_MODE_NONE) {
@@ -1215,13 +1216,13 @@ public class SettingsProvider extends ContentProvider {
}
@GuardedBy("mLock")
- private boolean isSyncDisabledConfigLocked() {
+ private int getSyncDisabledModeConfigLocked() {
// Check the values used for both SYNC_DISABLED_MODE_PERSISTENT and
// SYNC_DISABLED_MODE_UNTIL_REBOOT.
// The SYNC_DISABLED_MODE_UNTIL_REBOOT value is cheap to check first.
if (mSyncConfigDisabledUntilReboot) {
- return true;
+ return SYNC_DISABLED_MODE_UNTIL_REBOOT;
}
// Now check the global setting used to implement SYNC_DISABLED_MODE_PERSISTENT.
@@ -1231,10 +1232,12 @@ public class SettingsProvider extends ContentProvider {
SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
Global.DEVICE_CONFIG_SYNC_DISABLED);
if (settingLocked == null) {
- return false;
+ return SYNC_DISABLED_MODE_NONE;
}
String settingValue = settingLocked.getValue();
- return settingValue != null && !"0".equals(settingValue);
+ boolean isSyncDisabledPersistent = settingValue != null && !"0".equals(settingValue);
+ return isSyncDisabledPersistent
+ ? SYNC_DISABLED_MODE_PERSISTENT : SYNC_DISABLED_MODE_NONE;
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -3585,7 +3588,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 204;
+ private static final int SETTINGS_VERSION = 205;
private final int mUserId;
@@ -5190,6 +5193,219 @@ public class SettingsProvider extends ContentProvider {
}
if (currentVersion == 203) {
+ // Version 203: initialize entries migrated from wear settings provide.
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.HAS_PAY_TOKENS, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN, 6);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.HOTWORD_DETECTION_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_hotwordDetectionEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SMART_REPLIES_ENABLED, false);
+ Setting locationMode =
+ getSecureSettingsLocked(userId).getSettingLocked(Secure.LOCATION_MODE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+ !locationMode.isNull()
+ && !Integer.toString(Secure.LOCATION_MODE_OFF)
+ .equals(locationMode.getValue()));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.RETAIL_MODE, Global.Wearable.RETAIL_MODE_CONSUMER);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY,
+ Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.BUG_REPORT,
+ "user".equals(Build.TYPE) // is user build?
+ ? Global.Wearable.BUG_REPORT_DISABLED
+ : Global.Wearable.BUG_REPORT_ENABLED);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SMART_ILLUMINATE_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_smartIlluminateEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_AUTO_TIME,
+ Global.Wearable.SYNC_TIME_FROM_PHONE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ Global.Wearable.SYNC_TIME_ZONE_FROM_PHONE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CLOCKWORK_24HR_TIME, false);
+ initGlobalSettingsDefaultValForWearLocked(Global.Wearable.AUTO_WIFI, true);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WIFI_POWER_SAVE,
+ getContext()
+ .getResources()
+ .getInteger(
+ R.integer
+ .def_wearable_offChargerWifiUsageLimitMinutes));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS, 0L);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.UPDOWN_GESTURES_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_upDownGesturesEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SETUP_SKIPPED, Global.Wearable.SETUP_SKIPPED_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ Global.Wearable.CALL_FORWARD_NO_LAST_ACTION);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_muteWhenOffBodyEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WEAR_OS_VERSION_STRING, "");
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_alternateLauncherEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.CORNER_ROUNDNESS,
+ getContext()
+ .getResources()
+ .getInteger(
+ R.integer.def_wearable_squareScreenCornerRoundness));
+ initGlobalSettingsDefaultValForWearLocked(Global.Wearable.BUTTON_SET, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SIDE_BUTTON,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_sideButtonPresent));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.ANDROID_WEAR_VERSION,
+ Long.parseLong(
+ getContext()
+ .getResources()
+ .getString(R.string.def_wearable_androidWearVersion)));
+ final int editionGlobal = 1;
+ final int editionLocal = 2;
+ boolean isLe = getContext().getPackageManager().hasSystemFeature("cn.google");
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SYSTEM_EDITION, isLe ? editionLocal : editionGlobal);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.SYSTEM_CAPABILITIES, getWearSystemCapabilities(isLe));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+ SystemProperties.getInt("ro.cw_build.platform_mr", 0));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.BOTTOM_OFFSET, 0);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.DISPLAY_SHAPE,
+ Settings.Global.Wearable.DISPLAY_SHAPE_SQUARE);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+ getContext()
+ .getResources()
+ .getString(R.string.def_wearable_brightnessLevels));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_mobileSignalDetectorAllowed));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_ENABLED,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_ambientEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TILT_TO_WAKE,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_tiltToWakeEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_touchToWakeEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+ getContext()
+ .getResources()
+ .getBoolean(R.bool.def_wearable_tiltToBrightEnabled));
+ initGlobalSettingsDefaultValForWearLocked(
+ Global.Wearable.DECOMPOSABLE_WATCHFACE, false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+ SystemProperties.getBoolean("ro.ambient.force_when_docked", false));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+ SystemProperties.getInt("ro.ambient.gesture_sensor_id", 0));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+ SystemProperties.getBoolean("ro.ambient.low_bit_enabled", false));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+ SystemProperties.getInt("ro.ambient.plugged_timeout_min", -1));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.COMPANION_ADDRESS, "");
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+ Settings.Global.Wearable.HFP_CLIENT_UNSET);
+ Setting disabledProfileSetting =
+ getGlobalSettingsLocked()
+ .getSettingLocked(Settings.Global.BLUETOOTH_DISABLED_PROFILES);
+ final long disabledProfileSettingValue =
+ disabledProfileSetting.isNull()
+ ? 0
+ : Long.parseLong(disabledProfileSetting.getValue());
+ final boolean isHfpClientProfileEnabled =
+ (disabledProfileSettingValue & (1 << BluetoothProfile.HEADSET_CLIENT))
+ == 0;
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+ isHfpClientProfileEnabled);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.COMPANION_OS_VERSION,
+ Settings.Global.Wearable.COMPANION_OS_VERSION_UNDEFINED);
+ final boolean defaultBurnInProtectionEnabled =
+ getContext()
+ .getResources()
+ .getBoolean(
+ com.android
+ .internal
+ .R
+ .bool
+ .config_enableBurnInProtection);
+ final boolean forceBurnInProtection =
+ SystemProperties.getBoolean("persist.debug.force_burn_in", false);
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED,
+ defaultBurnInProtectionEnabled || forceBurnInProtection);
+
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
+ getContext()
+ .getResources()
+ .getString(
+ com.android.internal.R.string.config_wearSysUiPackage));
+ initGlobalSettingsDefaultValForWearLocked(
+ Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
+ getContext()
+ .getResources()
+ .getString(
+ com.android
+ .internal
+ .R
+ .string
+ .config_wearSysUiMainActivity));
+
+ currentVersion = 204;
+ }
+
+ if (currentVersion == 204) {
// Version 204: Replace 'wifi' or 'cell' tiles with 'internet' if existed.
final SettingsState secureSettings = getSecureSettingsLocked(userId);
final Setting currentValue = secureSettings.getSettingLocked(Secure.QS_TILES);
@@ -5224,7 +5440,7 @@ public class SettingsProvider extends ContentProvider {
true /* makeDefault */,
SettingsState.SYSTEM_PACKAGE_NAME);
}
- currentVersion = 204;
+ currentVersion = 205;
}
// vXXX: Add new settings above this point.
@@ -5243,6 +5459,58 @@ public class SettingsProvider extends ContentProvider {
// Return the current version.
return currentVersion;
}
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, boolean val) {
+ initGlobalSettingsDefaultValForWearLocked(key, val ? "1" : "0");
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, int val) {
+ initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, long val) {
+ initGlobalSettingsDefaultValForWearLocked(key, String.valueOf(val));
+ }
+
+ private void initGlobalSettingsDefaultValForWearLocked(String key, String val) {
+ final SettingsState globalSettings = getGlobalSettingsLocked();
+ Setting currentSetting = globalSettings.getSettingLocked(key);
+ if (currentSetting.isNull()) {
+ globalSettings.insertSettingOverrideableByRestoreLocked(
+ key,
+ val,
+ null /* tag */,
+ true /* makeDefault */,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ }
+
+ private long getWearSystemCapabilities(boolean isLe) {
+ // Capability constants are imported from
+ // com.google.android.clockwork.common.system.WearableConstants.
+ final int capabilityCompanionLegacyCalling = 5;
+ final int capabilitySpeaker = 6;
+ final int capabilitySetupProtocommChannel = 7;
+ long capabilities =
+ Long.parseLong(
+ getContext().getResources()
+ .getString(
+ isLe ? R.string.def_wearable_leSystemCapabilities
+ : R.string.def_wearable_systemCapabilities));
+ PackageManager pm = getContext().getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ capabilities |= getBitMask(capabilityCompanionLegacyCalling);
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ capabilities |= getBitMask(capabilitySpeaker);
+ }
+ capabilities |= getBitMask(capabilitySetupProtocommChannel);
+ return capabilities;
+ }
+
+ private long getBitMask(int capability) {
+ return 1 << (capability - 1);
+ }
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f04acd09ae14..4aee1641fc63 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@ import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.providers.settings.SettingsOperationProto;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -47,6 +48,7 @@ import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import libcore.io.IoUtils;
@@ -806,7 +808,14 @@ final class SettingsState {
final int settingCount = settings.size();
for (int i = 0; i < settingCount; i++) {
+
Setting setting = settings.valueAt(i);
+ if (setting.isTransient()) {
+ if (DEBUG_PERSISTENCE) {
+ Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+ }
+ continue;
+ }
if (writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
@@ -843,16 +852,20 @@ final class SettingsState {
} catch (Throwable t) {
Slog.wtf(LOG_TAG, "Failed to write settings, restoring backup", t);
if (t instanceof IOException) {
- // we failed to create a directory, so log the permissions and existence
- // state for the settings file and directory
- logSettingsDirectoryInformation(destination.getBaseFile());
+ if (DEBUG) {
+ // we failed to create a directory, so log the permissions and existence
+ // state for the settings file and directory
+ logSettingsDirectoryInformation(destination.getBaseFile());
+ }
if (t.getMessage().contains("Couldn't create directory")) {
// attempt to create the directory with Files.createDirectories, which
// throws more informative errors than File.mkdirs.
Path parentPath = destination.getBaseFile().getParentFile().toPath();
try {
Files.createDirectories(parentPath);
- Slog.i(LOG_TAG, "Successfully created " + parentPath);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Successfully created " + parentPath);
+ }
} catch (Throwable t2) {
Slog.e(LOG_TAG, "Failed to write " + parentPath
+ " with Files.writeDirectories", t2);
@@ -1005,7 +1018,9 @@ final class SettingsState {
in = file.openRead();
} catch (FileNotFoundException fnfe) {
Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
- logSettingsDirectoryInformation(mStatePersistFile);
+ if (DEBUG) {
+ logSettingsDirectoryInformation(mStatePersistFile);
+ }
addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
@@ -1016,7 +1031,7 @@ final class SettingsState {
// Settings file exists but is corrupted. Retry with the fallback file
final File statePersistFallbackFile = new File(
mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
- Slog.i(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ Slog.w(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ ", retrying with fallback file: " + statePersistFallbackFile);
try {
in = new AtomicFile(statePersistFallbackFile).openRead();
@@ -1302,6 +1317,14 @@ final class SettingsState {
/* resetToDefault */ true);
}
+ public boolean isTransient() {
+ switch (getTypeFromKey(getKey())) {
+ case SETTINGS_TYPE_GLOBAL:
+ return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+ }
+ return false;
+ }
+
public boolean update(String value, boolean setDefault, String packageName, String tag,
boolean forceNonSystemPackage, boolean overrideableByRestore) {
return update(value, setDefault, packageName, tag, forceNonSystemPackage,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 3297937e3e75..b0647fa747ca 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -102,7 +102,8 @@ public class SettingsBackupTest {
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT,
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED, // form-factor/OEM specific
+ Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED
);
private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
@@ -137,7 +138,6 @@ public class SettingsBackupTest {
Settings.Global.AUTOFILL_LOGGING_LEVEL,
Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
- Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
Settings.Global.AVERAGE_TIME_TO_DISCHARGE,
Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY,
Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME,
@@ -265,10 +265,14 @@ public class SettingsBackupTest {
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
Settings.Global.ENABLE_RESTRICTED_BUCKET,
+ Settings.Global.ENABLE_TARE,
+ Settings.Global.ENABLE_TARE_ALARM_MANAGER,
+ Settings.Global.ENABLE_TARE_JOB_SCHEDULER,
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
+ Settings.Global.ENABLE_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT,
Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
@@ -473,6 +477,8 @@ public class SettingsBackupTest {
Settings.Global.SYS_UIDCPUPOWER,
Settings.Global.SYS_TRACED,
Settings.Global.FPS_DEVISOR,
+ Settings.Global.TARE_ALARM_MANAGER_CONSTANTS,
+ Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS,
Settings.Global.TCP_DEFAULT_INIT_RWND,
Settings.Global.TETHER_DUN_APN,
Settings.Global.TETHER_DUN_REQUIRED,
@@ -509,7 +515,6 @@ public class SettingsBackupTest {
Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
- Settings.Global.ANGLE_ALLOWLIST,
Settings.Global.ANGLE_EGL_FEATURES,
Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
@@ -593,7 +598,78 @@ public class SettingsBackupTest {
Settings.Global.CACHED_APPS_FREEZER_ENABLED,
Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
- Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
+ Settings.Global.Wearable.BATTERY_SAVER_MODE,
+ Settings.Global.Wearable.COMBINED_LOCATION_ENABLED,
+ Settings.Global.Wearable.HAS_PAY_TOKENS,
+ Settings.Global.Wearable.GMS_CHECKIN_TIMEOUT_MIN,
+ Settings.Global.Wearable.HOTWORD_DETECTION_ENABLED,
+ Settings.Global.Wearable.SMART_REPLIES_ENABLED,
+ Settings.Global.Wearable.DEFAULT_VIBRATION,
+ Settings.Global.Wearable.OBTAIN_PAIRED_DEVICE_LOCATION,
+ Settings.Global.Wearable.RETAIL_MODE,
+ Settings.Global.Wearable.PHONE_PLAY_STORE_AVAILABILITY,
+ Settings.Global.Wearable.BUG_REPORT,
+ Settings.Global.Wearable.SMART_ILLUMINATE_ENABLED,
+ Settings.Global.Wearable.CLOCKWORK_AUTO_TIME,
+ Settings.Global.Wearable.CLOCKWORK_AUTO_TIME_ZONE,
+ Settings.Global.Wearable.CLOCKWORK_24HR_TIME,
+ Settings.Global.Wearable.AUTO_WIFI,
+ Settings.Global.Wearable.WIFI_POWER_SAVE,
+ Settings.Global.Wearable.ALT_BYPASS_WIFI_REQUIREMENT_TIME_MILLIS,
+ Settings.Global.Wearable.UPDOWN_GESTURES_ENABLED,
+ Settings.Global.Wearable.SETUP_SKIPPED,
+ Settings.Global.Wearable.LAST_CALL_FORWARD_ACTION,
+ Settings.Global.Wearable.STEM_1_TYPE,
+ Settings.Global.Wearable.STEM_1_DATA,
+ Settings.Global.Wearable.STEM_1_DEFAULT_DATA,
+ Settings.Global.Wearable.STEM_2_TYPE,
+ Settings.Global.Wearable.STEM_2_DATA,
+ Settings.Global.Wearable.STEM_2_DEFAULT_DATA,
+ Settings.Global.Wearable.STEM_3_TYPE,
+ Settings.Global.Wearable.STEM_3_DATA,
+ Settings.Global.Wearable.STEM_3_DEFAULT_DATA,
+ Settings.Global.Wearable.MUTE_WHEN_OFF_BODY_ENABLED,
+ Settings.Global.Wearable.WEAR_OS_VERSION_STRING,
+ Settings.Global.Wearable.ALTERNATE_LAUNCHER_ENABLED,
+ Settings.Global.Wearable.CORNER_ROUNDNESS,
+ Settings.Global.Wearable.BUTTON_SET,
+ Settings.Global.Wearable.SIDE_BUTTON,
+ Settings.Global.Wearable.ANDROID_WEAR_VERSION,
+ Settings.Global.Wearable.SYSTEM_CAPABILITIES,
+ Settings.Global.Wearable.SYSTEM_EDITION,
+ Settings.Global.Wearable.WEAR_PLATFORM_MR_NUMBER,
+ Settings.Global.Wearable.COMPANION_BT_ADDRESS_DUAL,
+ Settings.Global.Wearable.DISPLAY_SHAPE,
+ Settings.Global.Wearable.BOTTOM_OFFSET,
+ Settings.Global.Wearable.SCREEN_BRIGHTNESS_LEVEL,
+ Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
+ Settings.Global.Wearable.AMBIENT_ENABLED,
+ Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV,
+ Settings.Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
+ Settings.Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+ Settings.Global.Wearable.DECOMPOSABLE_WATCHFACE,
+ Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
+ Settings.Global.Wearable.AMBIENT_GESTURE_SENSOR_ID,
+ Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
+ Settings.Global.Wearable.AMBIENT_PLUGGED_TIMEOUT_MIN,
+ Settings.Global.Wearable.COMPANION_ADDRESS,
+ Settings.Global.Wearable.PAIRED_DEVICE_OS_TYPE,
+ Settings.Global.Wearable.COMPANION_BLE_ROLE,
+ Settings.Global.Wearable.COMPANION_NAME,
+ Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+ Settings.Global.Wearable.HFP_CLIENT_PROFILE_ENABLED,
+ Settings.Global.Wearable.COMPANION_OS_VERSION,
+ Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
+ Settings.Global.Wearable.SETUP_LOCALE,
+ Settings.Global.Wearable.OEM_SETUP_VERSION,
+ Settings.Global.Wearable.MASTER_GESTURES_ENABLED,
+ Settings.Global.Wearable.UNGAZE_ENABLED,
+ Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS,
+ Settings.Global.Wearable.BURN_IN_PROTECTION_ENABLED,
+ Settings.Global.Wearable.WRIST_ORIENTATION_MODE,
+ Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE,
+ Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
@@ -763,8 +839,10 @@ public class SettingsBackupTest {
@Test
public void globalSettingsBackedUpOrDenied() {
+ Set<String> candidateSettings = getCandidateSettings(Settings.Global.class);
+ candidateSettings.addAll(getCandidateSettings(Settings.Global.Wearable.class));
checkSettingsBackedUpOrDenied(
- getCandidateSettings(Settings.Global.class),
+ candidateSettings,
newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
BACKUP_DENY_LIST_GLOBAL_SETTINGS);
}
@@ -792,8 +870,7 @@ public class SettingsBackupTest {
.that(intersect(settingsToBackup, denylist)).isEmpty();
}
- private static Set<String> getCandidateSettings(
- Class<? extends Settings.NameValueTable> clazz) {
+ private static Set<String> getCandidateSettings(Class<?> clazz) {
HashSet<String> result = new HashSet<String>();
for (Field field : clazz.getDeclaredFields()) {
if (looksLikeValidSetting(field)) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1581e240e1d7..54fb6475197a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -571,6 +571,10 @@
<!-- Permission required for GTS test - PendingSystemUpdateTest -->
<uses-permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
+ <!-- Permission required to run the `vm` tool which manages on-device virtual machines -->
+ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 504e18a1488e..56b940c534fb 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -256,6 +256,14 @@ public final class RingtonePickerActivity extends AlertActivity implements
}
setupAlert();
+
+ ListView listView = mAlert.getListView();
+ if (listView != null) {
+ // List view needs to gain focus in order for RSB to work.
+ if (!listView.requestFocus()) {
+ Log.e(TAG, "Unable to gain focus! RSB may not work properly.");
+ }
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d051290cb4b4..228ee4027107 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -63,6 +63,7 @@ android_library {
"res",
],
static_libs: [
+ "WifiTrackerLib",
"WindowManager-Shell",
"SystemUIAnimationLib",
"SystemUIPluginLib",
@@ -144,6 +145,7 @@ android_library {
"src/**/I*.aidl",
],
static_libs: [
+ "WifiTrackerLib",
"SystemUIAnimationLib",
"SystemUIPluginLib",
"SystemUISharedLib",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9de1c5ea1a3d..adf441b3722c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -656,6 +656,10 @@
</service>
<service
+ android:name=".communal.service.CommunalService"
+ android:exported="@bool/config_communalServiceEnabled"/>
+
+ <service
android:name=".keyguard.KeyguardService"
android:exported="true" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 835471d5cb94..1cf14f2362de 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -1,5 +1,7 @@
set noparent
+# Bug component: 78010
+
dsandler@android.com
aaliomer@google.com
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0d18b8dea284..31cf5303a75e 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -26,6 +26,17 @@
]
},
{
+ "name": "SystemUIGoogleTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
// Permission indicators
"name": "CtsPermission4TestCases",
"options": [
@@ -53,8 +64,9 @@
// Curious where your @Scenario tests will run?
//
- // @Ignore or @FlakyTest: nowhere
- // @Staging: in staged-postsubmit, but not postsubmit or presubmit
+ // @Ignore: nowhere
+ // @Staging or @FlakyTest: in staged-postsubmit, but not postsubmit or
+ // presubmit
// @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
// none of the above: in presubmit, postsubmit, and staged-postsubmit
//
@@ -98,9 +110,6 @@
},
{
"exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index a50efd731cf6..3cbe435026b5 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.ActivityManager
import android.app.ActivityTaskManager
-import android.app.AppGlobals
import android.app.PendingIntent
import android.app.TaskInfo
import android.content.Context
@@ -44,6 +43,7 @@ class ActivityLaunchAnimator(
context: Context
) {
companion object {
+ private const val DEBUG = false
const val ANIMATION_DURATION = 500L
private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
@@ -75,8 +75,6 @@ class ActivityLaunchAnimator(
}
}
- private val packageManager = AppGlobals.getPackageManager()
-
/** The interpolator used for the width, height, Y position and corner radius. */
private val animationInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.launch_animation_interpolator_y)
@@ -100,6 +98,10 @@ class ActivityLaunchAnimator(
* If possible, you should pass the [packageName] of the intent that will be started so that
* trampoline activity launches will also be animated.
*
+ * If the device is currently locked, the user will have to unlock it before the intent is
+ * started unless [showOverLockscreen] is true. In that case, the activity will be started
+ * directly over the lockscreen.
+ *
* This method will throw any exception thrown by [intentStarter].
*/
@JvmOverloads
@@ -107,21 +109,22 @@ class ActivityLaunchAnimator(
controller: Controller?,
animate: Boolean = true,
packageName: String? = null,
+ showOverLockscreen: Boolean = false,
intentStarter: (RemoteAnimationAdapter?) -> Int
) {
if (controller == null || !animate) {
- Log.d(TAG, "Starting intent with no animation")
+ Log.i(TAG, "Starting intent with no animation")
intentStarter(null)
controller?.callOnIntentStartedOnMainThread(willAnimate = false)
return
}
- Log.d(TAG, "Starting intent with a launch animation")
val runner = Runner(controller)
- val isOnKeyguard = callback.isOnKeyguard()
+ val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
- // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard.
- val animationAdapter = if (!isOnKeyguard) {
+ // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
+ // keyguard with the animation
+ val animationAdapter = if (!hideKeyguardWithAnimation) {
RemoteAnimationAdapter(
runner,
ANIMATION_DURATION,
@@ -149,9 +152,11 @@ class ActivityLaunchAnimator(
val willAnimate =
launchResult == ActivityManager.START_TASK_TO_FRONT ||
launchResult == ActivityManager.START_SUCCESS ||
- (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard)
+ (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
+ hideKeyguardWithAnimation)
- Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard")
+ Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " +
+ "hideKeyguardWithAnimation=$hideKeyguardWithAnimation")
controller.callOnIntentStartedOnMainThread(willAnimate)
// If we expect an animation, post a timeout to cancel it in case the remote animation is
@@ -160,7 +165,7 @@ class ActivityLaunchAnimator(
runner.postTimeout()
// Hide the keyguard using the launch animation instead of the default unlock animation.
- if (isOnKeyguard) {
+ if (hideKeyguardWithAnimation) {
callback.hideKeyguardWithAnimation(runner)
}
}
@@ -424,13 +429,16 @@ class ActivityLaunchAnimator(
nonApps: Array<out RemoteAnimationTarget>?,
iCallback: IRemoteAnimationFinishedCallback?
) {
- Log.d(TAG, "Remote animation started")
+ if (DEBUG) {
+ Log.d(TAG, "Remote animation started")
+ }
+
val window = apps?.firstOrNull {
it.mode == RemoteAnimationTarget.MODE_OPENING
}
if (window == null) {
- Log.d(TAG, "Aborting the animation as no window is opening")
+ Log.i(TAG, "Aborting the animation as no window is opening")
removeTimeout()
iCallback?.invoke()
controller.onLaunchAnimationCancelled()
@@ -500,7 +508,10 @@ class ActivityLaunchAnimator(
val launchContainerOverlay = launchContainer.overlay
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
- Log.d(TAG, "Animation started")
+ if (DEBUG) {
+ Log.d(TAG, "Animation started")
+ }
+
callback.setBlursDisabledForAppLaunch(true)
controller.onLaunchAnimationStart(isExpandingFullyAbove)
@@ -511,7 +522,10 @@ class ActivityLaunchAnimator(
}
override fun onAnimationEnd(animation: Animator?) {
- Log.d(TAG, "Animation ended")
+ if (DEBUG) {
+ Log.d(TAG, "Animation ended")
+ }
+
callback.setBlursDisabledForAppLaunch(false)
iCallback?.invoke()
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
@@ -686,7 +700,7 @@ class ActivityLaunchAnimator(
return
}
- Log.d(TAG, "Remote animation timed out")
+ Log.i(TAG, "Remote animation timed out")
timedOut = true
controller.onLaunchAnimationCancelled()
}
@@ -696,7 +710,7 @@ class ActivityLaunchAnimator(
return
}
- Log.d(TAG, "Remote animation was cancelled")
+ Log.i(TAG, "Remote animation was cancelled")
cancelled = true
removeTimeout()
context.mainExecutor.execute {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
new file mode 100644
index 000000000000..68834bc2aa23
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
@@ -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.systemui.flags
+
+interface Flag<T> {
+ val id: Int
+ val default: T
+}
+
+data class BooleanFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Boolean = false
+) : Flag<Boolean>
+
+data class StringFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: String = ""
+) : Flag<String>
+
+data class IntFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Int = 0
+) : Flag<Int>
+
+data class LongFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Long = 0
+) : Flag<Long>
+
+data class FloatFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Float = 0f
+) : Flag<Float>
+
+data class DoubleFlag @JvmOverloads constructor(
+ override val id: Int,
+ override val default: Double = 0.0
+) : Flag<Double> \ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
new file mode 100644
index 000000000000..d5b9243ebe86
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
@@ -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 com.android.systemui.flags;
+
+/**
+ * List of {@link Flag} objects for use in SystemUI.
+ */
+public class Flags {
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 7c81325d685f..6d088f090bcd 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -60,8 +60,16 @@ public interface ActivityStarter {
*/
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
void startActivity(Intent intent, boolean dismissShade);
+
+ default void startActivity(Intent intent, boolean dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController) {
+ startActivity(intent, dismissShade, animationController,
+ false /* showOverLockscreenWhenLocked */);
+ }
+
void startActivity(Intent intent, boolean dismissShade,
- @Nullable ActivityLaunchAnimator.Controller animationController);
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked);
void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
void startActivity(Intent intent, boolean dismissShade, Callback callback);
void postStartActivityDismissingKeyguard(Intent intent, int delay);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java
new file mode 100644
index 000000000000..ab1749978653
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.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.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+
+/**
+ * Plugin for loading flag values from an alternate source of truth.
+ */
+@ProvidesInterface(action = FlagReaderPlugin.ACTION, version = FlagReaderPlugin.VERSION)
+public interface FlagReaderPlugin extends Plugin {
+ int VERSION = 1;
+ String ACTION = "com.android.systemui.flags.FLAG_READER_PLUGIN";
+
+ /** Returns a boolean value for the given flag. */
+ default boolean isEnabled(int id, boolean def) {
+ return def;
+ }
+
+ /** Returns a string value for the given flag id. */
+ default String getValue(int id, String def) {
+ return def;
+ }
+
+ /** Returns a int value for the given flag. */
+ default int getValue(int id, int def) {
+ return def;
+ }
+
+ /** Returns a long value for the given flag. */
+ default long getValue(int id, long def) {
+ return def;
+ }
+
+ /** Returns a float value for the given flag. */
+ default float getValue(int id, float def) {
+ return def;
+ }
+
+ /** Returns a double value for the given flag. */
+ default double getValue(int id, double def) {
+ return def;
+ }
+
+ /** Add a listener to be alerted when any flag changes. */
+ default void addListener(Listener listener) {}
+
+ /** Remove a listener to be alerted when any flag changes. */
+ default void removeListener(Listener listener) {}
+
+ /** A simple listener to be alerted when a flag changes. */
+ interface Listener {
+ /** */
+ void onFlagChanged(int id);
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 2213d1c5c844..9a9683dbfb5c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -60,6 +60,10 @@ public abstract class QSTileView extends LinearLayout {
public abstract int getDetailY();
+ public View getLabel() {
+ return null;
+ }
+
public View getLabelContainer() {
return null;
}
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
new file mode 100644
index 000000000000..dfc3e63a4e2b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+-->
+
+<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
+<com.android.systemui.qs.FooterActionsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:gravity="center_vertical">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@android:id/edit"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:contentDescription="@string/accessibility_quick_settings_edit"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_mode_edit"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch
+ android:id="@+id/multi_user_switch"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:focusable="true">
+
+ <ImageView
+ android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside" />
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/pm_lite"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:layout_weight="1"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_lock_power_off"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/settings_button_container"
+ android:layout_width="0dp"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:background="@drawable/qs_footer_action_chip_background"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <com.android.systemui.statusbar.phone.SettingsButton
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center"
+ android:contentDescription="@string/accessibility_quick_settings_settings"
+ android:background="@drawable/qs_footer_action_chip_background_borderless"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/tuner_icon"
+ android:layout_width="8dp"
+ android:layout_height="8dp"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginBottom="@dimen/qs_footer_icon_padding"
+ android:src="@drawable/tuner"
+ android:tint="?android:attr/textColorTertiary"
+ android:visibility="invisible" />
+
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+</com.android.systemui.qs.FooterActionsView> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 28c61663bd4d..6016aafbd6ae 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -68,6 +68,16 @@
lockScreenWeight="400"
/>
</FrameLayout>
+ <FrameLayout
+ android:id="@+id/keyguard_smartspace_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/below_clock_padding_start"
+ android:paddingEnd="@dimen/below_clock_padding_end"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/lockscreen_clock_view"
+ />
+ <!-- either keyguard_status_area or keyguard_smartspace_container is visible -->
<include layout="@layout/keyguard_status_area"
android:id="@+id/keyguard_status_area"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index ce63082868bb..f613a195ea67 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -27,49 +27,44 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- androidprv:layout_maxHeight="@dimen/keyguard_security_height"
- android:gravity="center_horizontal">
+ android:clipChildren="false"
+ android:clipToPadding="false">
- <FrameLayout
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/pattern_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <LinearLayout
- android:id="@+id/pattern_container"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginBottom="8dp"
+ android:layout_weight="1"
+ android:layoutDirection="ltr">
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pattern_top_guideline"
android:layout_width="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="center_horizontal|bottom"
- android:clipChildren="false"
- android:clipToPadding="false">
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintGuide_percent="0"
+ android:orientation="horizontal" />
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPatternView"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_marginEnd="8dip"
- android:layout_marginBottom="4dip"
- android:layout_marginStart="8dip"
- android:layout_gravity="center_horizontal"
- android:gravity="center"
- android:clipChildren="false"
- android:clipToPadding="false" />
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPatternView"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintLeft_toLeftOf="parent"
+ androidprv:layout_constraintRight_toRightOf="parent"
+ androidprv:layout_constraintDimensionRatio="1.0"
+ androidprv:layout_constraintVertical_bias="1.0"
+ />
+ </androidx.constraintlayout.widget.ConstraintLayout>
- <include layout="@layout/keyguard_eca"
- android:id="@+id/keyguard_selector_fade_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginTop="@dimen/keyguard_eca_top_margin"
- android:gravity="center_horizontal" />
- </LinearLayout>
- </FrameLayout>
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+ android:gravity="center_horizontal" />
</com.android.keyguard.KeyguardPatternView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 02cb2bcfad81..a946318cb313 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -20,171 +20,174 @@
<com.android.keyguard.KeyguardPINView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/keyguard_pin_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
android:orientation="vertical"
>
- <LinearLayout
- android:id="@+id/pin_container"
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/pin_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginBottom="8dp"
+ android:layout_weight="1"
+ android:layoutDirection="ltr"
+ android:orientation="vertical">
+
+ <!-- Set this to be just above key1. It would be better to introduce a barrier above
+ key1/key2/key3, then place this View above that. Sadly, that doesn't work (the Barrier
+ drops to the bottom of the page, and key1/2/3 all shoot up to the top-left). In any
+ case, the Flow should ensure that key1/2/3 all have the same top, so this should be
+ fine. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:orientation="vertical"
- android:layout_weight="1"
- android:layoutDirection="ltr"
- android:layout_marginBottom="8dp"
- >
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
- <com.android.keyguard.AlphaOptimizedRelativeLayout
- android:id="@+id/row0"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
- >
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintStart_toStartOf="parent"
+
+ androidprv:layout_constraintTop_toTopOf="parent"
+ androidprv:layout_constraintBottom_toTopOf="@id/key1"
+ androidprv:layout_constraintVertical_bias="0.0">
+
<com.android.keyguard.PasswordTextView
- android:id="@+id/pinEntry"
- android:layout_width="@dimen/keyguard_security_width"
- android:layout_height="@dimen/keyguard_password_height"
- style="@style/Widget.TextView.Password"
- android:layout_centerHorizontal="true"
- android:layout_marginRight="72dp"
- androidprv:scaledTextSize="@integer/scaled_password_text_size"
- android:contentDescription="@string/keyguard_accessibility_pin_area"
- />
+ android:id="@+id/pinEntry"
+ style="@style/Widget.TextView.Password"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/keyguard_password_height"
+ android:layout_centerHorizontal="true"
+ android:layout_marginRight="72dp"
+ android:contentDescription="@string/keyguard_accessibility_pin_area"
+ androidprv:scaledTextSize="@integer/scaled_password_text_size" />
</com.android.keyguard.AlphaOptimizedRelativeLayout>
- <LinearLayout
- android:id="@+id/row1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
- >
- <com.android.keyguard.NumPadKey
- android:id="@+id/key1"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="1"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key2"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="2"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key3"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="3"
- />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/row2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
- >
- <com.android.keyguard.NumPadKey
- android:id="@+id/key4"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="4"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key5"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="5"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key6"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="6"
- />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/row3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="@dimen/num_pad_row_margin_bottom"
- >
- <com.android.keyguard.NumPadKey
- android:id="@+id/key7"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="7"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key8"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="8"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key9"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="9"
- />
- </LinearLayout>
- <LinearLayout
- android:id="@+id/row4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_gravity="center_horizontal"
- >
- <com.android.keyguard.NumPadButton
- android:id="@+id/delete_button"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- android:contentDescription="@string/keyboardview_keycode_delete"
- style="@style/NumPadKey.Delete"
- />
- <com.android.keyguard.NumPadKey
- android:id="@+id/key0"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/num_pad_key_margin_end"
- androidprv:textView="@+id/pinEntry"
- androidprv:digit="0"
- />
- <com.android.keyguard.NumPadButton
- android:id="@+id/key_enter"
- android:layout_width="@dimen/num_pad_key_width"
- android:layout_height="match_parent"
- style="@style/NumPadKey.Enter"
- android:contentDescription="@string/keyboardview_keycode_enter"
- />
- </LinearLayout>
- </LinearLayout>
+
+ <!-- Guideline used to place the top row of keys relative to the screen height. This will be
+ updated in KeyguardPINView to reduce the height of the PIN pad. -->
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/pin_pad_top_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ androidprv:layout_constraintGuide_percent="0"
+ android:orientation="horizontal" />
+
+ <androidx.constraintlayout.helper.widget.Flow
+ android:id="@+id/flow1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+
+ androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter"
+
+ androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end"
+
+ androidprv:flow_horizontalStyle="packed"
+ androidprv:flow_maxElementsWrap="3"
+
+ androidprv:flow_verticalBias="1.0"
+ androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom"
+ androidprv:flow_verticalStyle="packed"
+
+ androidprv:flow_wrapMode="aligned"
+ androidprv:layout_constraintBottom_toBottomOf="parent"
+ androidprv:layout_constraintEnd_toEndOf="parent"
+ androidprv:layout_constraintStart_toStartOf="parent"
+ androidprv:layout_constraintTop_toBottomOf="@id/pin_pad_top_guideline" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key1"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="1"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key2"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="2"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key3"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="3"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key4"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="4"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key5"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="5"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key6"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="6"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key7"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="7"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key8"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="8"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key9"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="9"
+ androidprv:textView="@+id/pinEntry" />
+
+
+ <com.android.keyguard.NumPadButton
+ android:id="@+id/delete_button"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ style="@style/NumPadKey.Delete"
+ android:contentDescription="@string/keyboardview_keycode_delete"
+ />
+
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key0"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ androidprv:digit="0"
+ androidprv:textView="@+id/pinEntry" />
+
+ <com.android.keyguard.NumPadButton
+ android:id="@+id/key_enter"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ style="@style/NumPadKey.Enter"
+ android:contentDescription="@string/keyboardview_keycode_enter"
+ />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+
<include layout="@layout/keyguard_eca"
android:id="@+id/keyguard_selector_fade_container"
android:layout_width="match_parent"
diff --git a/core/res/res/drawable-watch/global_action_icon_background.xml b/packages/SystemUI/res-keyguard/values-land/donottranslate.xml
index b7bc7e6ce402..9912b699507e 100644
--- a/core/res/res/drawable-watch/global_action_icon_background.xml
+++ b/packages/SystemUI/res-keyguard/values-land/donottranslate.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
@@ -13,9 +13,7 @@
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"
- android:tint="?android:attr/colorButtonNormal">
- <solid android:color="@android:color/white"/>
- <size android:height="40dp" android:width="40dp"/>
-</shape>
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="num_pad_key_ratio">1.51</string>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
index c34012dc85a8..d816b3a461cd 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml
@@ -21,4 +21,8 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">88dp</dimen>
+
+ <dimen name="qs_header_system_icons_area_height">0dp</dimen>
+ <dimen name="qs_panel_padding_top">0dp</dimen>
+
</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 7e3c87b24f07..a2ae5023166f 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -98,4 +98,10 @@
<dimen name="below_clock_padding_start">32dp</dimen>
<dimen name="below_clock_padding_end">16dp</dimen>
<dimen name="below_clock_padding_start_icons">28dp</dimen>
+
+ <!-- Proportion of the screen height to use to set the maximum height of the bouncer to when
+ the device is in the DEVICE_POSTURE_HALF_OPENED posture, for the PIN/pattern entry. 0 will
+ allow it to use the whole screen space, 0.6 will allow it to use just under half of the
+ screen. -->
+ <item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/donottranslate.xml b/packages/SystemUI/res-keyguard/values/donottranslate.xml
index a4d0ff7269a2..052d329ee63c 100644
--- a/packages/SystemUI/res-keyguard/values/donottranslate.xml
+++ b/packages/SystemUI/res-keyguard/values/donottranslate.xml
@@ -21,9 +21,14 @@
<!-- Skeleton string format for displaying the date when an alarm is set. -->
<string name="abbrev_wday_month_day_no_year_alarm">EEEMMMd</string>
+ <!-- Skeleton string format for displaying the date shorter. -->
+ <string name="abbrev_month_day_no_year">MMMd</string>
+
<!-- Skeleton string format for displaying the time in 12-hour format. -->
<string name="clock_12hr_format">hm</string>
<!-- Skeleton string format for displaying the time in 24-hour format. -->
<string name="clock_24hr_format">Hm</string>
+
+ <string name="num_pad_key_ratio">1</string>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 72b027af1bf6..871b1c4eb3f6 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -37,6 +37,10 @@
<item name="android:colorControlNormal">@null</item>
<item name="android:colorControlHighlight">?android:attr/colorAccent</item>
<item name="android:background">@drawable/num_pad_key_background</item>
+
+ <!-- Default values for NumPadKey used in a ConstraintLayout. -->
+ <item name="layout_constraintDimensionRatio">@string/num_pad_key_ratio</item>
+ <item name="layout_constraintWidth_max">@dimen/num_pad_key_width</item>
</style>
<style name="Widget.TextView.NumPadKey.Digit"
parent="@android:style/Widget.DeviceDefault.TextView">
@@ -58,8 +62,8 @@
<item name="android:src">@drawable/ic_backspace_24dp</item>
</style>
<style name="NumPadKey.Enter">
- <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
- <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
+ <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
</style>
<style name="Widget.TextView.NumPadKey.Klondike"
parent="@android:style/Widget.DeviceDefault.TextView">
diff --git a/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml b/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml
new file mode 100644
index 000000000000..13133cb451e4
--- /dev/null
+++ b/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.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.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect2 in frameworks/base/core/res -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="2000"
+ android:propertyXName="translateX"
+ android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 "
+ android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex_copy"
+ android:repeatCount="infinite" />
+</set> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_arrow_forward.xml b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
new file mode 100644
index 000000000000..438e4c70dc71
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
@@ -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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:autoMirrored="true"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M6.23,20.23l1.77,1.77l10,-10l-10,-10l-1.77,1.77l8.23,8.23z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
new file mode 100644
index 000000000000..2c34060ccd61
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2H9V6zM18,20H6V10h12V20zM12,17c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2C10,16.1 10.9,17 12,17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
new file mode 100644
index 000000000000..9a69b33fd591
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="?android:attr/textColorPrimary"
+ android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 000000000000..ac4c43bd35b9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
new file mode 100644
index 000000000000..f38a36804bc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
+ android:strokeAlpha="0.3"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,10h2v8h-2z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20,20h2v2h-2z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_background.xml b/packages/SystemUI/res/drawable/internet_dialog_background.xml
new file mode 100644
index 000000000000..3ceb0f6ac06a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_background.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners android:radius="8dp" />
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
new file mode 100644
index 000000000000..50267fda0b25
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_footer_background.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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"/>
+ <corners android:radius="20dp"/>
+ <padding
+ android:left="8dp"
+ android:right="8dp"
+ android:top="4dp"
+ android:bottom="4dp" />
+ <solid android:color="@android:color/transparent" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
new file mode 100644
index 000000000000..14672ef3dcfe
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape android:shape="rectangle">
+ <corners
+ android:topLeftRadius="@dimen/internet_dialog_corner_radius"
+ android:topRightRadius="@dimen/internet_dialog_corner_radius"
+ android:bottomLeftRadius="@dimen/internet_dialog_corner_radius"
+ android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/>
+ <solid android:color="?android:attr/colorBackground" />
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 1535e7276a6f..3a08a7111d9a 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -22,11 +22,12 @@
android:color="?android:attr/textColorPrimary">
<item>
<shape
- android:shape="oval">
+ android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface"/>
<size
android:width="@dimen/keyguard_affordance_width"
android:height="@dimen/keyguard_affordance_height"/>
+ <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 000000000000..95209f8eb8ab
--- /dev/null
+++ b/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.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.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_material_trimmed in frameworks/base/core/res -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+ <target
+ android:name="rect_grp"
+ android:animation="@anim/progress_indeterminate_horizontal_rect" />
+</animated-vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
new file mode 100644
index 000000000000..088e82bb4260
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_state_off_color"/>
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
new file mode 100644
index 000000000000..250188b892f4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/settingslib_state_on_color"/>
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
new file mode 100644
index 000000000000..b41762f7908e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid
+ android:color="@color/settingslib_thumb_off_color"
+ android:alpha="?android:attr/disabledAlpha"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_off.xml b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
new file mode 100644
index 000000000000..87d4aeaac84f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid android:color="@color/settingslib_thumb_off_color"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
new file mode 100644
index 000000000000..5566ea3f62fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:top="@dimen/settingslib_switch_thumb_margin"
+ android:bottom="@dimen/settingslib_switch_thumb_margin">
+ <shape android:shape="oval">
+ <size
+ android:height="@dimen/settingslib_switch_thumb_size"
+ android:width="@dimen/settingslib_switch_thumb_size"/>
+ <solid android:color="@color/settingslib_state_on_color"/>
+ </shape>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
new file mode 100644
index 000000000000..06bb779b91ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_selector.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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
+ <item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
+ <item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
new file mode 100644
index 000000000000..15dfcb70e25e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
@@ -0,0 +1,26 @@
+<?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"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <solid
+ android:color="@color/settingslib_track_off_color"
+ android:alpha="?android:attr/disabledAlpha"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_off_background.xml b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
new file mode 100644
index 000000000000..3a09284d10a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
@@ -0,0 +1,26 @@
+<?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="rectangle"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <padding android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"/>
+ <solid android:color="@color/settingslib_track_off_color"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
new file mode 100644
index 000000000000..1d9dacd6c0f9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -0,0 +1,26 @@
+<?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="rectangle"
+ android:width="@dimen/settingslib_switch_track_width"
+ android:height="@dimen/settingslib_switch_track_height">
+ <padding android:left="@dimen/settingslib_switch_thumb_margin"
+ android:right="@dimen/settingslib_switch_thumb_margin"/>
+ <solid android:color="@color/settingslib_track_on_color"/>
+ <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_selector.xml b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
new file mode 100644
index 000000000000..a38c3b4241a3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_selector.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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
+ <item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
+ <item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 000000000000..aec204f45aa7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+ draws the whole height of the progress bar instead having blank space above and below the
+ bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:height="10dp"
+ android:width="340dp"
+ android:viewportHeight="10"
+ android:viewportWidth="340" >
+ <group
+ android:name="progress_group"
+ android:translateX="180"
+ android:translateY="5" >
+ <path
+ android:name="background_track"
+ android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+ android:fillColor="?androidprv:attr/colorSurfaceVariant"/>
+ <group
+ android:name="rect_grp"
+ android:translateX="-197.60001"
+ android:scaleX="0.5" >
+ <path
+ android:name="rect"
+ android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+ android:fillColor="?androidprv:attr/colorAccentPrimaryVariant" />
+ </group>
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/communal_host_view.xml b/packages/SystemUI/res/layout/communal_host_view.xml
new file mode 100644
index 000000000000..4406a46c3537
--- /dev/null
+++ b/packages/SystemUI/res/layout/communal_host_view.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.
+ -->
+
+<!-- This is a view that shows general status information in Keyguard. -->
+<com.android.systemui.communal.CommunalHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/communal_host"
+ android:orientation="vertical"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ systemui:layout_constraintStart_toStartOf="parent"
+ systemui:layout_constraintTop_toTopOf="parent"
+ systemui:layout_constraintBottom_toBottomOf="parent"
+ systemui:layout_constraintHeight_percent="@dimen/communal_source_height_percentage"
+ android:layout_width="0dp"
+ android:layout_height="0dp"/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_toast.xml b/packages/SystemUI/res/layout/global_actions_toast.xml
new file mode 100644
index 000000000000..1f0899623ddf
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_toast.xml
@@ -0,0 +1,44 @@
+<?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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center|bottom"
+ android:gravity="center"
+ android:layout_marginBottom="@dimen/global_actions_info_margin"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintWidth_max="382dp"
+ android:layout_weight="0"
+ android:background="@drawable/global_actions_lite_background"
+ android:theme="@style/Theme.SystemUI.QuickSettings"
+ android:paddingTop="14dp"
+ android:paddingBottom="14dp"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:text="@string/global_action_smart_lock_disabled" />
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/idle_host_view.xml b/packages/SystemUI/res/layout/idle_host_view.xml
new file mode 100644
index 000000000000..f4078742c916
--- /dev/null
+++ b/packages/SystemUI/res/layout/idle_host_view.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.
+ -->
+
+
+<com.android.systemui.idle.IdleHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/idle_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
new file mode 100644
index 000000000000..3aa2e5aca2a7
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -0,0 +1,374 @@
+<?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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/internet_connectivity_dialog"
+ android:layout_width="@dimen/internet_dialog_list_max_width"
+ android:layout_height="@dimen/internet_dialog_list_max_height"
+ android:background="@drawable/internet_dialog_rounded_top_corner_background"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Widget.SliceView.Panel"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/internet_dialog_title"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="google-sans"
+ android:textSize="24sp"/>
+
+ <TextView
+ android:id="@+id/internet_dialog_subtitle"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="20dp"
+ android:layout_marginTop="4dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:fontFamily="google-sans"
+ android:textSize="14sp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/divider"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:layout_width="340dp"
+ android:layout_height="4dp"
+ android:background="?androidprv:attr/colorSurfaceVariant"/>
+
+ <ProgressBar
+ android:id="@+id/wifi_searching_progress"
+ android:indeterminate="true"
+ android:layout_width="340dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:visibility="gone"
+ style="@style/TrimmedHorizontalProgressBar"/>
+ </LinearLayout>
+
+ <androidx.core.widget.NestedScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/scroll_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/internet_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/mobile_network_layout"
+ android:layout_width="match_parent"
+ android:layout_height="88dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:layout_gravity="center_vertical|start"
+ android:orientation="horizontal"
+ android:layout_marginEnd="@dimen/settingslib_switchbar_margin"
+ android:layout_marginStart="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="22dp"
+ android:paddingEnd="22dp">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/signal_icon"
+ android:autoMirrored="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="1"
+ android:id="@+id/mobile_network_list"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/mobile_title"
+ android:textDirection="locale"
+ android:layout_marginStart="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/mobile_summary"
+ android:textDirection="locale"
+ android:layout_marginStart="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="48dp"
+ android:layout_gravity="end|center_vertical">
+ <Switch
+ android:id="@+id/mobile_toggle"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="@dimen/settingslib_switch_track_height"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/turn_on_wifi_layout"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:layout_marginEnd="@dimen/settingslib_switchbar_margin"
+ android:layout_marginStart="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="22dp"
+ android:paddingEnd="22dp">
+
+ <FrameLayout
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/wifi_toggle_title"
+ android:text="@string/turn_on_wifi"
+ android:textDirection="locale"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:fontFamily="google-sans"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="48dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp">
+ <Switch
+ android:id="@+id/wifi_toggle"
+ android:switchMinWidth="@dimen/settingslib_switch_track_width"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/settingslib_switch_track_width"
+ android:layout_height="@dimen/settingslib_switch_track_height"
+ android:track="@drawable/settingslib_track_selector"
+ android:thumb="@drawable/settingslib_thumb_selector"
+ android:theme="@style/MainSwitch.Settingslib"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/wifi_connected_layout"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="true"
+ android:focusable="true"
+ android:visibility="gone"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:layout_marginEnd="@dimen/settingslib_switchbar_margin"
+ android:layout_marginStart="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="22dp"
+ android:paddingEnd="22dp">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start">
+ <ImageView
+ android:id="@+id/wifi_connected_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="3"
+ android:id="@+id/wifi_connected_list"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="72dp"
+ android:gravity="start|center_vertical">
+ <TextView
+ android:id="@+id/wifi_connected_title"
+ android:textDirection="locale"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/wifi_connected_summary"
+ android:textDirection="locale"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="5dp"
+ android:clickable="false"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/wifi_settings_icon"
+ android:src="@drawable/ic_settings_24dp"
+ android:layout_width="24dp"
+ android:layout_gravity="center"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/wifi_list_layout"
+ android:scrollbars="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:overScrollMode="never"
+ android:nestedScrollingEnabled="false"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/see_all_layout"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical|center_horizontal"
+ android:orientation="horizontal"
+ android:paddingStart="22dp"
+ android:paddingEnd="22dp">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/arrow_forward"
+ android:src="@drawable/ic_arrow_forward"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="16dp">
+ <TextView
+ android:text="@string/see_all_networks"
+ android:textDirection="locale"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginBottom="40dp">
+ <Button
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:id="@+id/done"
+ android:layout_width="67dp"
+ android:layout_height="36dp"
+ android:layout_marginEnd="24dp"
+ android:layout_gravity="end|center_vertical"
+ android:background="@drawable/internet_dialog_footer_background"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/inline_done_button"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </FrameLayout>
+ </LinearLayout>
+ </androidx.core.widget.NestedScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
new file mode 100644
index 000000000000..19b1ef90bdfa
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -0,0 +1,101 @@
+<?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/internet_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/wifi_list"
+ android:layout_width="match_parent"
+ android:layout_height="72dp"
+ android:layout_gravity="center_vertical|start"
+ android:clickable="true"
+ android:focusable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="horizontal"
+ android:paddingStart="22dp"
+ android:paddingEnd="22dp">
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:clickable="false"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="16dp">
+ <ImageView
+ android:id="@+id/wifi_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_weight="3"
+ android:id="@+id/wifi_network_layout"
+ android:orientation="vertical"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="72dp">
+ <TextView
+ android:id="@+id/wifi_title"
+ android:textDirection="locale"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical|start"
+ android:gravity="start|center_vertical"
+ android:layout_marginStart="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ <TextView
+ android:id="@+id/wifi_summary"
+ android:textDirection="locale"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_gravity="center_vertical|start"
+ android:gravity="start|center_vertical"
+ android:layout_marginStart="16dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:fontFamily="google-sans"/>
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/settingslib_switchbar_padding_right"
+ android:clickable="false"
+ android:gravity="center">
+ <ImageView
+ android:id="@+id/wifi_locked_icon"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 8ca1b8e85634..3be99939ba0f 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -19,7 +19,7 @@
<View
android:id="@+id/customizer_transparent_view"
android:layout_width="match_parent"
- android:layout_height="@*android:dimen/quick_qs_offset_height"
+ android:layout_height="@dimen/qs_header_system_icons_area_height"
android:background="@android:color/transparent" />
<com.android.keyguard.AlphaOptimizedLinearLayout
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 59e1a755d7d2..78655c03dd73 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -20,9 +20,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/qs_detail_background"
+ android:layout_marginTop="@dimen/qs_detail_margin_top"
android:clickable="true"
android:orientation="vertical"
- android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
android:paddingBottom="8dp"
android:visibility="invisible"
android:elevation="4dp"
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index da80633f944b..d1ab054dd335 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -28,7 +28,7 @@
<com.android.systemui.ResizingSpace
android:layout_width="match_parent"
- android:layout_height="@dimen/qs_detail_margin_top" />
+ android:layout_height="@dimen/qs_detail_header_margin_top" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 317dbc09eae6..e70084b80308 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -68,93 +68,9 @@
</LinearLayout>
- <LinearLayout
- android:id="@+id/qs_footer_actions_container"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:gravity="center_vertical">
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@android:id/edit"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:contentDescription="@string/accessibility_quick_settings_edit"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/textColorPrimary" />
-
- <com.android.systemui.statusbar.phone.MultiUserSwitch
- android:id="@+id/multi_user_switch"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:focusable="true">
-
- <ImageView
- android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_expanded_size"
- android:layout_height="@dimen/multi_user_avatar_expanded_size"
- android:layout_gravity="center"
- android:scaleType="centerInside" />
- </com.android.systemui.statusbar.phone.MultiUserSwitch>
+ <include layout="@layout/footer_actions"
+ android:id="@+id/qs_footer_actions"/>
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/pm_lite"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
- android:layout_weight="1"
- android:background="@drawable/qs_footer_action_chip_background"
- android:clickable="true"
- android:clipToPadding="false"
- android:focusable="true"
- android:padding="@dimen/qs_footer_icon_padding"
- android:src="@*android:drawable/ic_lock_power_off"
- android:contentDescription="@string/accessibility_quick_settings_power_menu"
- android:tint="?android:attr/textColorPrimary" />
-
- <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@+id/settings_button_container"
- android:layout_width="0dp"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:background="@drawable/qs_footer_action_chip_background"
- android:layout_weight="1"
- android:clipChildren="false"
- android:clipToPadding="false">
-
- <com.android.systemui.statusbar.phone.SettingsButton
- android:id="@+id/settings_button"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_gravity="center"
- android:contentDescription="@string/accessibility_quick_settings_settings"
- android:background="@drawable/qs_footer_action_chip_background_borderless"
- android:padding="@dimen/qs_footer_icon_padding"
- android:scaleType="centerInside"
- android:src="@drawable/ic_settings"
- android:tint="?android:attr/textColorPrimary" />
-
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@+id/tuner_icon"
- android:layout_width="8dp"
- android:layout_height="8dp"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginBottom="@dimen/qs_footer_icon_padding"
- android:src="@drawable/tuner"
- android:tint="?android:attr/textColorTertiary"
- android:visibility="invisible" />
-
- </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
- </LinearLayout>
</LinearLayout>
</com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 4c6418ace3fc..2ac03c262edb 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -13,13 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.systemui.qs.QSContainerImpl
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.qs.QSContainerImpl xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/quick_settings_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
- android:clipChildren="false" >
+ android:clipChildren="false">
<com.android.systemui.qs.NonInterceptingScrollView
android:id="@+id/expanded_qs_scroll_view"
@@ -40,15 +40,33 @@
android:accessibilityTraversalBefore="@android:id/edit"
android:clipToPadding="false"
android:clipChildren="false">
+
<include layout="@layout/qs_footer_impl" />
</com.android.systemui.qs.QSPanel>
</com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/quick_status_bar_expanded_header" />
- <include android:id="@+id/qs_detail" layout="@layout/qs_detail" />
+ <include
+ android:id="@+id/qs_detail"
+ layout="@layout/qs_detail" />
- <include android:id="@+id/qs_customize" layout="@layout/qs_customize_panel"
+ <include
+ android:id="@+id/qs_customize"
+ layout="@layout/qs_customize_panel"
android:visibility="gone" />
+ <ImageView
+ android:id="@+id/qs_drag_handle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="24dp"
+ android:elevation="4dp"
+ android:importantForAccessibility="no"
+ android:scaleType="center"
+ android:src="@drawable/ic_qs_drag_handle"
+ android:tint="@color/qs_detail_button_white"
+ tools:ignore="UseAppTint" />
+
</com.android.systemui.qs.QSContainerImpl>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 5b9ca1b26158..1460cdff7e13 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,29 +21,50 @@
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:minHeight="48dp"
+ android:minHeight="@dimen/qs_header_row_min_height"
android:clickable="false"
android:focusable="true"
android:theme="@style/Theme.SystemUI.QuickSettings.Header">
- <com.android.systemui.statusbar.policy.Clock
- android:id="@+id/clock"
+ <LinearLayout
+ android:id="@+id/clock_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:minWidth="48dp"
- android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical|start"
android:gravity="center_vertical|start"
- android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
- android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.QS.Status" />
+ >
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/min_clickable_item_size"
+ android:minHeight="@dimen/min_clickable_item_size"
+ android:gravity="center_vertical|start"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status" />
+
+ <com.android.systemui.statusbar.policy.VariableDateView
+ android:id="@+id/date_clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|start"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+ />
+ </LinearLayout>
<include layout="@layout/qs_carrier_group"
android:id="@+id/carrier_group"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:minHeight="48dp"
+ android:minHeight="@dimen/qs_header_row_min_height"
android:minWidth="48dp"
android:layout_marginStart="8dp"
android:layout_gravity="end|center_vertical"
@@ -76,7 +97,7 @@
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding" />
- <com.android.systemui.BatteryMeterView
+ <com.android.systemui.battery.BatteryMeterView
android:id="@+id/batteryRemainingIcon"
android:layout_height="match_parent"
android:layout_width="0dp"
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 f3b8b0bfaf36..6b14c96b58e2 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -32,7 +32,7 @@
android:paddingStart="0dp"
android:elevation="4dp" >
- <!-- Date and privacy. Only visible in QS -->
+ <!-- Date and privacy. Only visible in QS when not in split shade -->
<include layout="@layout/quick_status_bar_header_date_privacy"/>
<RelativeLayout
@@ -42,21 +42,32 @@
android:layout_gravity="top"
android:clipChildren="false"
android:clipToPadding="false">
- <!-- Time, icons and Carrier (only in QS) -->
- <include layout="@layout/quick_qs_status_icons"/>
+ <!-- Time, icons and Carrier (only in QS when not in split shade) -->
+ <include layout="@layout/quick_qs_status_icons"/>
- <com.android.systemui.qs.QuickQSPanel
- android:id="@+id/quick_qs_panel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/quick_qs_status_icons"
- android:layout_marginTop="@dimen/qqs_layout_margin_top"
- android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:focusable="true"
- android:paddingBottom="24dp"
- android:importantForAccessibility="yes" />
+ <com.android.systemui.qs.QuickQSPanel
+ android:id="@+id/quick_qs_panel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/quick_qs_status_icons"
+ android:layout_marginTop="@dimen/qqs_layout_margin_top"
+ android:accessibilityTraversalAfter="@id/quick_qs_status_icons"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:paddingBottom="24dp"
+ android:importantForAccessibility="yes">
+
+ <include
+ layout="@layout/footer_actions"
+ android:id="@+id/qqs_footer_actions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="@dimen/qs_footer_margin"
+ android:layout_marginEnd="@dimen/qs_footer_margin"
+ />
+ </com.android.systemui.qs.QuickQSPanel>
</RelativeLayout>
</com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index bff93a99258a..cc44b5e5df38 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -36,7 +36,7 @@
android:layout_weight="1"
android:gravity="center_vertical|start" >
- <com.android.systemui.statusbar.policy.DateView
+ <com.android.systemui.statusbar.policy.VariableDateView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -44,7 +44,9 @@
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.Status"
- systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+ systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+ />
</FrameLayout>
<android.widget.Space
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 194d2e063e97..1c3eedba4f6f 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,16 +14,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-
-<com.android.systemui.navigationbar.buttons.KeyButtonView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/rotate_suggestion"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="0"
- android:scaleType="center"
- android:visibility="invisible"
- android:contentDescription="@string/accessibility_rotate_button"
- android:paddingStart="@dimen/navigation_key_padding"
- android:paddingEnd="@dimen/navigation_key_padding"
-/> \ No newline at end of file
+ >
+
+ <com.android.systemui.navigationbar.buttons.KeyButtonView
+ android:id="@+id/rotate_suggestion"
+ android:layout_width="@dimen/floating_rotation_button_diameter"
+ android:layout_height="@dimen/floating_rotation_button_diameter"
+ android:contentDescription="@string/accessibility_rotate_button"
+ android:paddingStart="@dimen/navigation_key_padding"
+ android:paddingEnd="@dimen/navigation_key_padding"
+ android:layout_gravity="bottom|left"
+ android:scaleType="center"
+ android:visibility="invisible" />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
new file mode 100644
index 000000000000..f2c5b7bd491c
--- /dev/null
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -0,0 +1,89 @@
+<?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"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/split_shade_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/split_shade_header_height"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:clickable="false"
+ android:focusable="true"
+ android:paddingLeft="@dimen/qs_panel_padding"
+ android:paddingRight="@dimen/qs_panel_padding"
+ android:visibility="gone"
+ android:theme="@style/Theme.SystemUI.QuickSettings.Header">
+
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="48dp"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:gravity="start|center_vertical"
+ android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+ android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status" />
+
+ <com.android.systemui.statusbar.policy.DateView
+ android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.QS.Status"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+ <FrameLayout
+ android:id="@+id/rightLayout"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="end">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical">
+
+ <include
+ android:id="@+id/carrier_group"
+ layout="@layout/qs_carrier_group"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginStart="8dp"
+ android:focusable="false"
+ android:minHeight="@dimen/split_shade_header_min_height"
+ android:minWidth="48dp" />
+
+ <com.android.systemui.statusbar.phone.StatusIconContainer
+ android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+
+ <com.android.systemui.battery.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ systemui:textAppearance="@style/TextAppearance.QS.Status" />
+ </LinearLayout>
+ </FrameLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e71ed59d2e04..2b5076bff022 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -80,11 +80,30 @@
android:clipToPadding="false"
android:clipChildren="false">
+ <include layout="@layout/split_shade_header"/>
+
<include
layout="@layout/keyguard_status_view"
android:visibility="gone"/>
- <include layout="@layout/dock_info_overlay" />
+ <include layout="@layout/idle_host_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"/>
+
+ <FrameLayout
+ android:id="@+id/split_shade_smartspace_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/notification_side_paddings"
+ android:paddingEnd="@dimen/notification_side_paddings"
+ systemui:layout_constraintStart_toStartOf="@id/qs_edge_guideline"
+ systemui:layout_constraintEnd_toEndOf="parent"
+ systemui:layout_constraintTop_toTopOf="parent"
+ android:visibility="gone">
+ </FrameLayout>
+
+ <include layout="@layout/dock_info_overlay"/>
<FrameLayout
android:id="@+id/qs_frame"
@@ -117,6 +136,9 @@
systemui:layout_constraintEnd_toEndOf="parent"
/>
+ <include layout="@layout/communal_host_view"
+ android:visibility="gone"/>
+
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index bea50e87a29a..f1288e31fcb8 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -81,9 +81,10 @@
/>
<!-- Keyguard messages -->
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
android:layout_marginTop="@dimen/status_bar_height"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal">
@@ -97,7 +98,11 @@
android:singleLine="true"
android:ellipsize="marquee"
android:focusable="true" />
- </FrameLayout>
+ <FrameLayout android:id="@+id/keyboard_bouncer_container"
+ android:layout_height="0dp"
+ android:layout_width="match_parent"
+ android:layout_weight="1" />
+ </LinearLayout>
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 818d1d77177b..6d5c7d40a5f8 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -29,7 +29,7 @@
android:gravity="center_vertical"
android:orientation="horizontal"/>
- <com.android.systemui.BatteryMeterView android:id="@+id/battery"
+ <com.android.systemui.battery.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/tile_service_request_dialog.xml
index bc9c203b299a..b431d444e15a 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/SystemUI/res/layout/tile_service_request_dialog.xml
@@ -14,18 +14,23 @@
~ 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:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal"
+>
<TextView
- android:id="@+id/global_actions_change_message"
- android:layout_width="wrap_content"
- android:visibility="gone"
+ android:id="@+id/text"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/global_actions_change_description" />
- <ImageView
- android:id="@+id/global_actions_change_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</LinearLayout> \ No newline at end of file
+ android:layout_marginBottom="16dp"
+ android:textDirection="locale"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.PrivacyDialog"
+ android:lineHeight="20sp"
+ />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 8229661fbc24..6f2ee7ce993c 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoem om skerm te vul"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Strek om skerm te vul"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skermkiekie"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock is gedeaktiveer"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Stoor tans skermkiekie..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om oop te maak"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Stawing word vereis. Raak die vingerafdruksensor om te staaf."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Oproep aan die gang"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sal nie outomaties koppel nie"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding nie"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen ander netwerke beskikbaar nie"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerke beskikbaar nie"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkbesonderhede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op \'n netwerk om te koppel"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontsluit om netwerke te bekyk"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Soek tans na netwerke …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kon nie aan netwerk koppel nie"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Moenie teël byvoeg nie"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7f101d73b925..76eace5663fb 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ማያ እንዲሞላ አጉላ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ማያ ለመሙለት ሳብ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገጽ እይታ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ተሰናክሏል"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ለመክፈት የጣት አሻራ ይጠቀሙ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"የአውሮፕላን ሁነታ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"በይነመረብ በራስ-ሰር አይገናኝም"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ግንኙነት የለም"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ሌላ አውታረ መረብ የሉም"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ምንም አውታረ መረቦች የሉም"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"የአውታረ መረብ ዝርዝሮች"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ለመገናኘት አንድ አውታረ መረብ መታ ያድርጉ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"አውታረ መረቦችን ለመመልከት ይክፈቱ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> የሚከተለውን ሰቅ ወደ ፈጣን ቅንብሮች ማከል ይፈልጋል"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ሰቅ አክል"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ሰቅ አታክል"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 8f230246a70e..82e88b504659 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"تكبير/تصغير لملء الشاشة"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"توسيع بملء الشاشة"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"لقطة شاشة"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏تم إيقاف Smart Lock."</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"جارٍ حفظ لقطة الشاشة..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
@@ -1185,4 +1186,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"يمكنك استخدام بصمة الإصبع للفتح"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"المصادقة مطلوبة. المس مستشعر بصمات الإصبع للمصادقة."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"مكالمة هاتفية جارية"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"وضع الطيران"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"لن يتم الاتصال بالإنترنت تلقائيًا."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"لا يتوفّر اتصال بالإنترنت"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"لا تتوفّر شبكات أخرى."</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"لا تتوفّر أي شبكات."</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"تفاصيل الشبكة"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"انقر على إحدى الشبكات للاتصال بالإنترنت"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"فتح القفل لعرض الشبكات"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"يريد تطبيق <xliff:g id="APPNAME">%1$s</xliff:g> إضافة المربّع التالي إلى \"الإعدادات السريعة\""</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"إضافة مربّع"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"عدم إضافة مربّع"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index c4438fca51e3..b63d97c8812d 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্ৰীনশ্বট"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock অক্ষম কৰা হৈছে"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলিবলৈ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ আৱশ্যক। বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিবলৈ ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"চলি থকা ফ’ন কল"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"এয়াৰপ্লেন ম\'ড"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইণ্টাৰনেট স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"সংযোগ নাই"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"কোনো নেটৱৰ্ক উপলব্ধ নহয়"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ৱাই-ফাই"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"নেটৱৰ্কৰ সবিশেষ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"সংযোগ কৰিবলৈ এটা নেটৱৰ্কত টিপক"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটৱর্ক চাবলৈ আনলক কৰক"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>এ ক্ষিপ্ৰ ছেটিঙত এই টাইলটো যোগ দিব বিচাৰিছে"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"টাইল যোগ দিয়ক"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"টাইল যোগ নিদিব"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7e50b194000a..9fdafb160ab3 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ekranı doldurmaq üçün yaxınlaşdır"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ekranı doldurmaq üçün uzat"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skrinşot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktivdir"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"şəkil göndərdi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinşot yadda saxlanılır..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinşot yadda saxlanır..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Doğrulanma tələb olunur. Doğrulamaq üçün barmaq izi sensoruna toxunun."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Davam edən zəng"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Uçuş rejimi"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Qoşulub"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternet avtomatik qoşulmayacaq"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yoxdur"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Heç bir başqa şəbəkə əlçatan deyil"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Əlçatan şəbəkə yoxdur"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Şəbəkə məlumatları"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Qoşulmaq üçün şəbəkəyə toxunun"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Şəbəkələrə baxmaq üçün kilidi açın"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdakı mozaiki Sürətli Ayarlara əlavə etmək istəyir"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik əlavə edin"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Mozaik əlavə etməyin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 701765f4036c..bbd9d4ff7d5e 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj na celom ekranu"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Razvuci na ceo ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Čuvanje snimka ekrana..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je potvrda identiteta. Dodirnite senzor za otisak prsta da biste potvrdili identitet."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktuelni telefonski poziv"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim rada u avionu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Automatsko povezivanje na internet nije moguće"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Veza nije uspostavljena"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da biste videli mreže"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi da doda sledeću pločicu u Brza podešavanja"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj pločicu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 5da6cf8b9c35..bb6c7588133d 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Павял. на ўвесь экран"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Расцягн. на ўвесь экран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Здымак экрана"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцыя \"Smart Lock\" адключана"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"адпраўлены відарыс"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Захаванне скрыншота..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Каб адкрыць, скарыстайце адбітак пальца"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Патрабуецца аўтэнтыфікацыя. Дакраніцеся да сканера адбіткаў пальцаў."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Бягучы тэлефонны выклік"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Рэжым палёту"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аўтаматычнае падключэнне да інтэрнэту адсутнічае"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма падключэння"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Больш няма даступных сетак"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Няма даступных сетак"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Інфармацыя пра сетку"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Каб падключыцца, націсніце на сетку"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблакіраваць для прагляду сетак"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> запытвае дазвол на дадаванне ў Хуткія налады наступнай пліткі"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Дадаць плітку"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не дадаваць плітку"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 742de25d539c..2bca8e39a1ed 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Мащаб – запълва екрана"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Разпъване – запълва екрана"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Екранна снимка"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцията Smart Lock е деактивирана"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"изпратено изображение"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Екранната снимка се запазва..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущо телефонно обаждане"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Самолетен режим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Няма автоматично да се установи връзка с интернет"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Няма връзка"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Няма други налични мрежи"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Няма налични мрежи"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Подробности за мрежата"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Докоснете мрежа, за да се свържете"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Отключване с цел преглед на мрежите"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> иска да добави следния панел към бързите настройки"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавяне на панел"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Отмяна на добавянето"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index b35ae1f1efde..c4ff99ebde17 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট নিন"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ব্যবহারকারী এখন ফোনে কথা বলছেন"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"বিমান মোড"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ইন্টারনেট অটোমেটিক কানেক্ট হবে না"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"কানেকশন নেই"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"অন্য কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"কোনও নেটওয়ার্ক উপলভ্য নেই"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ওয়াই-ফাই"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"নেটওয়ার্কের বিবরণ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"কানেক্ট করতে একটি নেটওয়ার্কে ট্যাপ করুন"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটওয়ার্ক দেখার জন্য আনলক করুন"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index b87739732e4b..d66f1a807588 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Uvećaj prikaz na ekran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Razvuci prikaz na ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spašavanje snimka ekrana..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor za otisak prsta da autentificirate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u toku"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način rada u avionu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nije se moguće automatski povezati s internetom"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani s mrežom"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Druge mreže nisu dostupne"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalji o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da se povežete"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da vidite mreže"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati karticu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a5504f02833e..e4003d37b3ce 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom per omplir pantalla"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estira per omplir pant."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desactivat"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviat una imatge"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"S\'està desant captura de pantalla..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticació necessària. Toca el sensor d\'empremtes digitals per autenticar-te."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Trucada en curs"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode d\'avió"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no es connectarà automàticament"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sense connexió"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hi ha cap altra xarxa disponible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hi ha cap xarxa disponible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalls de la xarxa"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una xarxa per connectar-te"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueja per veure xarxes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vol afegir la icona següent a la configuració ràpida"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Afegeix la icona"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No afegeixis la icona"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f48c9fb1cd77..6df9bdb25ccc 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Přiblížit na celou obrazovku"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Na celou obrazovku"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snímek obrazovky"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkce Smart Lock je deaktivována"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odesílá obrázek"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ukládání snímku obrazovky..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Je vyžadováno ověření. Dotkněte se snímače otisků prstů."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Probíhající hovor"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim Letadlo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Připojeno"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet se nebude automaticky připojovat"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Žádné připojení"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Žádné další sítě nejsou k dispozici"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nejsou k dispozici žádné sítě"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti sítě"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Klepněte na síť, ke které se chcete připojit"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sítě uvidíte po odemknutí"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> chce do Rychlého nastavení přidat následující dlaždici"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Přidat dlaždici"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepřidávat dlaždici"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index cbef46488c5a..f7e18f10f262 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom til fuld skærm"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stræk til fuld skærm"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er deaktiveret"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendte et billede"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gemmer screenshot..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Gemmer screenshot..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykslæseren for at godkende."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Igangværende telefonopkald"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flytilstand"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Der oprettes ikke automatisk internetforbindelse"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Der er ingen forbindelse"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Der er ingen andre tilgængelige netværk"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Der er ingen tilgængelige netværk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netværksoplysninger"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryk på et netværk for at oprette forbindelse"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås op for at se netværk"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil føje følgende handlingsfelt til Kvikmenu"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tilføj handlingsfelt"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tilføj ikke et felt"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 50fcdfb7032a..56a127f82b00 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom auf Bildschirmgröße"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Auf Bildschirmgröße anpassen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktiviert"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"Bild gesendet"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot wird gespeichert..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentifizierung erforderlich. Tippe dazu einfach auf den Fingerabdrucksensor."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktiver Anruf"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flugmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Keine automatische Verbindung mit dem Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Keine Verbindung"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Keine anderen Netzwerke verfügbar"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Keine Netzwerke verfügbar"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netzwerkdetails"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tippe auf ein Netzwerk, um eine Verbindung herzustellen"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Entsperren, um Netzwerke anzuzeigen"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> möchte die folgende Kachel den Schnelleinstellungen hinzufügen"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kachel hinzufügen"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kachel nicht hinzu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index b8d357f3ff95..0ebcd70a34fe 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ζουμ σε πλήρη οθόνη"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Προβoλή σε πλήρη οθ."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Στιγμιότυπο οθόνης"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Το Smart Lock έχει απενεργοποιηθεί"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"έστειλε μια εικόνα"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Αποθήκ. στιγμιότυπου οθόνης..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Τηλεφωνική κλήση σε εξέλιξη"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Λειτουργία πτήσης"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Χωρίς αυτόματη σύνδεση στο διαδίκτυο"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Χωρίς σύνδεση"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Δεν υπάρχουν άλλα διαθέσιμα δίκτυα"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Δεν υπάρχουν διαθέσιμα δίκτυα"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Λεπτομέρειες δικτύου"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Πατήστε ένα δίκτυο για να συνδεθείτε"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ξεκλειδώστε για προβολή δικτύων"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> θέλει να προσθέσει το παρακάτω πλακίδιο στις Γρήγορες ρυθμίσεις"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Προσθήκη πλακιδίου"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Χωρίς προσθ. πλακιδ."</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index d69a95bba878..3424cf764c95 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3666069ee54b..7f0745a0235f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d69a95bba878..3424cf764c95 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d69a95bba878..3424cf764c95 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Aeroplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet won\'t auto‑connect"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"No connection"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No other networks available"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No networks available"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Network details"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tap a network to connect"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Add tile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Do not add tile"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 030c5884d3e3..dc239eef2abc 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎Zoom to fill screen‎‏‎‎‏‎"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎Stretch to fill screen‎‏‎‎‏‎"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎Screenshot‎‏‎‎‏‎"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎Smart Lock disabled‎‏‎‎‏‎"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎sent an image‎‏‎‎‏‎"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Saving screenshot…‎‏‎‎‏‎"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎Saving screenshot…‎‏‎‎‏‎"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎Use fingerprint to open‎‏‎‎‏‎"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎Authentication required. Touch the fingerprint sensor to authenticate.‎‏‎‎‏‎"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎Ongoing phone call‎‏‎‎‏‎"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‎‎Airplane mode‎‏‎‎‏‎"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎Mobile data‎‏‎‎‏‎"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="STATE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ / ‎‏‎‎‏‏‎<xliff:g id="NETWORKMODE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎Connected‎‏‎‎‏‎"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎Internet won\'t auto‑connect‎‏‎‎‏‎"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎No connection‎‏‎‎‏‎"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎No other networks available‎‏‎‎‏‎"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎No networks available‎‏‎‎‏‎"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎Wi‑Fi‎‏‎‎‏‎"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎Network details‎‏‎‎‏‎"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎Tap a network to connect‎‏‎‎‏‎"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎Unlock to view networks‎‏‎‎‏‎"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‎Searching for networks…‎‏‎‎‏‎"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎Failed to connect to network‎‏‎‎‏‎"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‎‎See all‎‏‎‎‏‎"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ wants to add the following tile to Quick Settings‎‏‎‎‏‎"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎Add tile‎‏‎‎‏‎"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‎Do not add tile‎‏‎‎‏‎"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index bb43d81596be..6d11fa015f2a 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ocupar la pantalla"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estirar p/ ocupar la pantalla"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Se inhabilitó Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"envió una imagen"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura de pantalla"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo de avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conexión establecida"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"No se conectará automáticamente a Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Presiona una red para conectarte a ella"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver las redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Se produjo un error al establecer conexión con la red"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere agregar el siguiente azulejo a la Configuración rápida"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Agregar azulejo"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No agregar azulejo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index fd0f8651b221..65b736cceae5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ajustar"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Expandir para ajustar"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock inhabilitado"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviado una imagen"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticación obligatoria. Toca el sensor de huellas digitales para autenticarte."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet no se conecta automáticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sin conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"No hay otras redes disponibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"No hay redes disponibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles de la red"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca una red para conectarte"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere añadir el siguiente recuadro a ajustes rápidos"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Añadir recuadro"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"No añadir recuadro"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6cd609fa6151..63a62446b9a3 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Suumi ekraani täitmiseks"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Venita ekraani täitmiseks"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekraanipilt"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock on keelatud"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"saatis kujutise"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Kuvatõmmise salvestamine ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Kuvatõmmise salvestamine ..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Kasutage avamiseks sõrmejälge"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vajalik on autentimine. Puudutage autentimiseks sõrmejäljeandurit."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Käimasolev telefonikõne"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lennukirežiim"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetiühendust ei looda automaatselt"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ühendus puudub"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ühtegi muud võrku pole saadaval"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ühtegi võrku pole saadaval"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Võrgu üksikasjad"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Puudutage ühendamiseks võrku"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Võrkude vaatamiseks avage"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> soovib kiirseadetesse lisada järgmise paani"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisa paan"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ära lisa paani"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b6162a830186..1f0afbf8fc9a 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Handiagotu pantaila betetzeko"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Luzatu pantaila betetzeko"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Pantaila-argazkia"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Desgaitu da Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"erabiltzaileak irudi bat bidali du"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Pantaila-argazkia gordetzen…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Pantaila-argazkia gordetzen…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentifikazioa behar da. Autentifikatzeko, ukitu hatz-marken sentsorea."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefono-dei bat abian da"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Hegaldi modua"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> (<xliff:g id="NETWORKMODE">%2$s</xliff:g>)"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Ez da automatikoki konektatuko Internetera"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Konexiorik gabe"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ez dago beste sare erabilgarririk"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ez dago sare erabilgarririk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifia"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Sarearen xehetasunak"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Sakatu sare bat hartara konektatzeko"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sareak ikusteko, desblokeatu pantaila"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Sareak bilatzen…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ezin izan da konektatu sarera"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak lauza hau gehitu nahi du Ezarpen bizkorrak menuan:"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Gehitu lauza"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ez gehitu lauza"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d1ddd8c042f4..bb1be5862173 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"بزرگ‌نمایی برای پر کردن صفحه"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"گسترده کردن برای پر کردن صفحه"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"نماگرفت"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏Smart Lock غیرفعال شد"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"تصویری ارسال کرد"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"در حال ذخیره نماگرفت..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره نماگرفت…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالت‌سنجی لازم است. برای اصالت‌سنجی، حسگر اثر انگشت را لمس کنید."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"حالت هواپیما"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"اینترنت به‌طور خودکار متصل نخواهد شد"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"اتصال برقرار نیست"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"شبکه دیگری وجود ندارد"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"شبکه‌ای در دسترس نیست"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"جزئیات شبکه"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"برای اتصال به شبکه روی آن ضربه بزنید"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"برای مشاهده شبکه‌ها، قفل صفحه را باز کنید"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> می‌خواهد کاشی زیر را به «تنظیمات فوری» اضافه کند"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"افزودن کاشی"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"کاشی اضافه نشود"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 652acf7ddd89..0bb2b9101e24 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomaa koko näyttöön"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Venytä koko näyttöön"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Kuvakaappaus"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock poistettu käytöstä"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"lähetti kuvan"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Tallennetaan kuvakaappausta..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Tallennetaan kuvakaappausta..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Todennus vaaditaan. Todenna koskettamalla sormenjälkitunnistinta."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Puhelu käynnissä"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lentokonetila"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetyhteyttä ei muodosteta automaattisesti"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ei yhteyttä"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ei muita verkkoja käytettävissä"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ei verkkoja käytettävissä"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Verkon tiedot"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Muodosta yhteys napauttamalla verkkoa"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Avaa lukitus nähdäksesi verkot"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> haluaa lisätä seuraavan laatan pika-asetuksiin"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lisää laatta"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Älä lisää laattaa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 1ec1f9190a22..619d690a0756 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Touchez le capteur d\'empreintes digitales pour vous authentifier."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours…"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connexion active"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Connexion automatique à Internet impossible"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau n\'est accessible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau n\'est accessible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Touchez un réseau pour vous y connecter"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouillez l\'écran pour afficher les réseaux Wi-Fi"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 6a08d8716677..f7c1728a8145 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock désactivé"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Appuyez sur le lecteur d\'empreintes digitales pour vous authentifier."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Pas de connexion automatique à Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Aucune connexion"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Aucun autre réseau disponible"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Aucun réseau disponible"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Détails du réseau"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Appuyez sur un réseau pour vous connecter"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouiller pour afficher les réseaux"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter le bloc suivant aux Réglages rapides"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ajouter un bloc"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne pas ajouter bloc"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 15678e4cc907..9b6aa53eaa66 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ampliar ata ocupar todo"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estirar ata ocupar todo"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Facer captura"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock está desactivado"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou unha imaxe"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gardando captura de pantalla…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Requírese autenticación. Para autenticarte, toca o sensor de impresión dixital."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica en curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avión"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non se conectará automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sen conexión"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Non hai outras redes dispoñibles"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Non hai redes dispoñibles"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalles da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toca unha rede para conectarte a ela"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea a pantalla para ver as redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> solicita a túa aprobación para engadir o seguinte atallo a Configuración rápida"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engadir atallo"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non engadir atallo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 9255827aa64e..dba4abab1951 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"સ્ક્રીનશૉટ"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ફોન કૉલ ચાલુ છે"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"એરપ્લેન મોડ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ઇન્ટરનેટ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"કોઈ કનેક્શન નથી"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"બીજાં કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"કોઈ નેટવર્ક ઉપલબ્ધ નથી"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"વાઇ-ફાઇ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"નેટવર્કની વિગતો"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"કનેક્ટ કરવા માટે નેટવર્ક પર ટૅપ કરો"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"વાઇ-ફાઇ નેટવર્ક જોવા માટે અનલૉક કરો"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1ebb517dc316..e1235445d7c4 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्‍क्रीन भरने के लिए ज़ूम करें"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्‍क्रीन भरने के लिए खींचें"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock की सुविधा बंद कर दी गई है"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"एक इमेज भेजी गई"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"खोलने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि करना ज़रूरी है. पुष्टि करने के लिए, फ़िंगरप्रिंट सेंसर को छुएं."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फ़ोन कॉल चल रहा है"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"हवाई जहाज़ मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट अपने-आप कनेक्ट नहीं होगा"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"इंटरनेट कनेक्शन नहीं है"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"कोई दूसरा नेटवर्क उपलब्ध नहीं है"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कोई नेटवर्क उपलब्ध नहीं है"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"वाई-फ़ाई"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्क की जानकारी"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इंटरनेट से कनेक्ट करने के लिए, किसी नेटवर्क पर टैप करें"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"वाई-फ़ाई नेटवर्क देखने के लिए, स्क्रीन को अनलॉक करें"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क खोजे जा रहे हैं…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> इस टाइल को \'फटाफट सेटिंग\' में जोड़ने के लिए अनुमति चाहता है"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"टाइल जोड़ें"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"टाइल न जोड़ें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9ec657d8d19d..ff77b678ba92 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj i ispuni zaslon"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rastegni i ispuni zaslon"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snimka zaslona"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock onemogućen"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"šalje sliku"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spremanje snimke zaslona..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor otiska prsta da biste se autentificirali."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u tijeku"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način rada u zrakoplovu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Neće biti automatskog povezivanja s internetom"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Niste povezani"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nije dostupna nijedna druga mreža"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nema dostupnih mreža"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Pojedinosti o mreži"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Dodirnite mrežu da biste se povezali"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte za prikaz mreža"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću pločicu u Brze postavke"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj pločicu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nemoj dodati pločicu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d9d8148938de..172bec8db690 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Nagyítás a kitöltéshez"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Nyújtás kitöltéshez"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Képernyőkép"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock kikapcsolva"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"képet küldött"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Képernyőkép mentése..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ujjlenyomat használata a megnyitáshoz"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Hitelesítés szükséges. Érintse meg az ujjlenyomat-érzékelőt a hitelesítéshez."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Folyamatban lévő telefonhívás"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Repülős üzemmód"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g>/<xliff:g id="STATE">%1$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Az internetre történő csatlakozás nem automatikus"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nincs kapcsolat"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nincs több rendelkezésre álló hálózat"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nincs rendelkezésre álló hálózat"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Hálózati információk"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"A kapcsolódáshoz koppintson a kívánt hálózatra"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Zárolás feloldása a hálózatok megtekintéséhez"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> a következő mozaikot szeretné hozzáadni a Gyorsbeállításokhoz"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Mozaik hozzáadása"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne legyen hozzáadva"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 1d8094a4f61c..e875df8ce39d 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Խոշորացնել` էկրանը լցնելու համար"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ձգել` էկրանը լցնելու համար"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Սքրինշոթ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock-ն անջատված է"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"պատկեր է ուղարկվել"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Սքրինշոթը պահվում է…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Բացելու համար օգտագործեք մատնահետքը"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Պահանջվում է նույնականացում։ Դրա համար մատը հպեք մատնահետքի սկաներին։"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ընթացիկ հեռախոսազանգ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Ավիառեժիմ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Չհաջողվեց ավտոմատ միանալ համացանցին"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Կապ չկա"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Այլ հասանելի ցանցեր չկան"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Հասանելի ցանցեր չկան"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Ցանցի տվյալներ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Հպեք ցանցին՝ միանալու համար"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ապակողպեք՝ ցանցերը դիտելու համար"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածն ուզում է ավելացնել հետևյալ սալիկը Արագ կարգավորումներում"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ավելացնել սալիկ"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Չավելացնել սալիկ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 76ee7224933c..aa07fed14f0f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Perbesar utk mengisi layar"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rentangkn utk mngisi layar"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dinonaktifkan"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"mengirim gambar"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan screenshot..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan sidik jari untuk membuka"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Perlu autentikasi. Sentuh sensor sidik jari untuk melakukan autentikasi."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telepon sedang berlangsung"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mode pesawat"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan terhubung otomatis"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tidak ada koneksi"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Jaringan lain tidak tersedia"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Jaringan tidak tersedia"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detail jaringan"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketuk jaringan untuk menghubungkan"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat jaringan"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingin menambahkan kartu berikut ke Setelan Cepat"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan kartu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah kartu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 01905542684d..4ace5248e0c2 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Fylla skjá með aðdrætti"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Teygja yfir allan skjáinn"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skjámynd"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Slökkt á Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendi mynd"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Vistar skjámynd…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Vistar skjámynd…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Auðkenningar krafist. Auðkenndu með því að snerta fingrafaralesarann."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Símtal í gangi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flugstilling"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internetið tengist ekki sjálfkrafa"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Engin tenging"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Engin önnur net í boði"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ekkert net í boði"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Upplýsingar um net"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ýttu á net til að tengjast"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Taktu úr lás til að skoða netkerfi"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill bæta eftirfarandi reit við flýtistillingar"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Bæta reit við"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ekki bæta reit við"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0efab5ceb84e..29fd0cf29dbf 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom per riempire schermo"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Estendi per riemp. schermo"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funzionalità Smart Lock disattivata"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa l\'impronta per aprire"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticazione obbligatoria. Eseguila toccando il sensore di impronte digitali."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonata in corso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modalità aereo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Connessione attiva"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet non si connetterà automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nessuna connessione"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nessun\'altra rete disponibile"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nessuna rete disponibile"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Dettagli rete"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tocca una rete per connetterti"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Sblocca per visualizzare le reti"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vuole aggiungere il seguente riquadro alle Impostazioni rapide"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Aggiungi riquadro"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Non aggiungerlo"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 1e4c9d66ed9c..c1f13059b937 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"הגדלת התצוגה למילוי המסך"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"מתיחה למילוי של המסך"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"צילום מסך"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"צילום המסך נשמר..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string>
@@ -1173,4 +1175,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"מצב טיסה"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"לא ניתן להתחבר לאינטרנט באופן אוטומטי"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"אין חיבור"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"אין רשתות זמינות אחרות"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"אין רשתות זמינות"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"פרטי הרשת"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"צריך להקיש על רשת כדי להתחבר"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"צריך לבטל את הנעילה כדי להציג את הרשתות"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 27259791f664..77fa5aac2388 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"画面サイズに合わせて拡大"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"画面サイズに合わせて拡大"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"スクリーンショット"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock は無効です"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"画像を送信しました"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"スクリーンショットを保存中..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"指紋を使って開いてください"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"認証が必要です。指紋認証センサーをタッチして認証してください。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"機内モード"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"インターネットに自動的に接続されません"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"接続なし"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"利用できるネットワークはありません"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ネットワークを利用できません"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ネットワークの詳細"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ネットワークをタップして接続"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ネットワークを表示するにはロック解除してください"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> が以下のタイルをクイック設定に追加しようとしています"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"タイルを追加"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"タイルを追加しない"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 21e1c52ae136..bad49d43f02e 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"გაწიეთ ეკრანის შესავსებად."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ეკრანის ანაბეჭდი"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock გათიშულია"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"გაიგზავნა სურათი"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"სკრინშოტის შენახვა…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"გასახსნელად გამოიყენეთ თითის ანაბეჭდი"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"თვითმფრინავის რეჟიმი"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ინტერნეტს ავტომატურად არ დაუკავშირდება"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"კავშირი არ არის"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"სხვა ქსელები მიუწვდომელია"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ქსელები მიუწვდომელია"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ქსელის დეტალები"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"დასაკავშირებლად შეეხეთ ქსელს"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"განბლოკვა ქსელების სანახავად"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>-ს სურს, დაამატოს შემდეგი მოზაიკა სწრაფ პარამეტრებში"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"მოზაიკის დამატება"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"არ დაემატოს მოზაიკა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 6a9a9b5d8d11..41e93b1c7636 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Экранды толтыру үшін ұлғайту"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтыру үшін созу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өшірілді"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншотты сақтауда…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Телефон қоңырауы бар"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Ұшақ режимі"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматты түрде қосылмайды."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыс жоқ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Басқа қолжетімді желі жоқ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Қолжетімді желілер жоқ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Желі деректері"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Желіге қосылу үшін оны түртіңіз."</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Желілерді көру үшін құлыпты ашыңыз."</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> Жылдам параметрлерге келесі бөлшекті қосқысы келеді."</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Бөлшек қосу"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Бөлшек қоспау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 080ba1958e24..c9ab49b4ab9a 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ពង្រីក​​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ទាញ​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"រូបថតអេក្រង់"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"បានបិទ Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"បាន​ផ្ញើរូបភាព"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ប្រើស្នាមម្រាមដៃ ដើម្បីបើក"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"តម្រូវឱ្យ​មាន​ការផ្ទៀងផ្ទាត់។ សូមចុច​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ ដើម្បី​ផ្ទៀងផ្ទាត់​។"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ការហៅទូរសព្ទ​ដែលកំពុង​ដំណើរការ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ពេលជិះ​យន្តហោះ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"អ៊ីនធឺណិតនឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"មិនមាន​ការតភ្ជាប់ទេ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"មិន​មាន​បណ្ដាញផ្សេងទៀតដែល​អាច​ប្រើ​បានទេ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"មិន​មាន​បណ្ដាញដែល​អាច​ប្រើ​បានទេ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ព័ត៌មាន​លម្អិត​អំពីបណ្ដាញ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ចុចលើបណ្ដាញណាមួយ ដើម្បីភ្ជាប់"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ដោះសោ​ដើម្បីមើល​បណ្ដាញ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិន​អាច​ភ្ជាប់​បណ្ដាញ​បានទេ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូល​ប្រអប់​ខាងក្រោម​ទៅក្នុង​ការកំណត់រហ័ស"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូល​ប្រអប់"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"កុំ​បញ្ចូល​ប្រអប់"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index f63d02f6682a..235fc727be20 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ತೆರೆಯುವುದಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ದೃಢೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ. ದೃಢೀಕರಿಸಲು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಫೋನ್ ಕರೆ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ಇಂಟರ್ನೆಟ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ಇತರ ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ಯಾವುದೇ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ವೈ‑ಫೈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ನೆಟ್‌ವರ್ಕ್ ವಿವರಗಳು"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ಕನೆಕ್ಟ್ ಮಾಡಲು ಒಂದು ನೆಟ್‌ವರ್ಕ್ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 371b7de67806..ad5c7e7a2f6f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"전체화면 모드로 확대"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"전체화면 모드로 확대"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"스크린샷"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 사용 중지됨"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"이미지 보냄"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"캡쳐화면 저장 중..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"캡쳐화면 저장 중..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"지문으로 열기"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"인증이 필요합니다. 지문 센서를 터치하여 인증하세요."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"진행 중인 전화 통화"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"비행기 모드"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"인터넷에 자동으로 연결되지 않음"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"연결되지 않음"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"사용 가능한 다른 네트워크가 없음"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"사용 가능한 네트워크가 없음"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"네트워크 세부정보"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"연결하려면 네트워크를 탭하세요"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"네트워크를 보려면 잠금 해제하세요"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>에서 빠른 설정에 다음 타일을 추가하려고 합니다."</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"타일 추가"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"타일 추가 안함"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 404b9ac45179..39a6f594720b 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Экрнд тлтр ү. чен өлч өзг"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтуруу ү-н чоюу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өчүрүлдү"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сүрөт жөнөттү"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншот сакталууда…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншот сакталууда..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Учурдагы телефон чалуу"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Учак режими"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернет автоматтык түрдө туташпайт"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Байланыш жок"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Башка тармактар жеткиликсиз"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Тармактар жеткиликтүү эмес"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Тармактын чоо-жайы"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Кайсы тармакка туташасыз?"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Тармактарды көрүү үчүн кулпусун ачыңыз"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> төмөнкү ыкчам баскычты Ыкчам жөндөөлөргө кошкону жатат"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ыкчам баскыч кошуу"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ыкчам баскыч кошулбасын"</string>
</resources>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index ea456d81aa25..ac4dfd212bb8 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -28,9 +28,6 @@
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">2</integer>
- <!-- We have only space for one notification on phone landscape layouts. -->
- <integer name="keyguard_max_notification_count">1</integer>
-
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">1</integer>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 34bf28aa741e..fc5edf3ade8f 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -32,16 +32,16 @@
-->
<dimen name="qs_customize_header_min_height">48dp</dimen>
+ <!-- In landscape the security footer is actually part of the header,
+ and needs to be as short as the header -->
<dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
<dimen name="qs_footer_padding">14dp</dimen>
- <dimen name="qs_footers_margin_bottom">0dp</dimen>
<dimen name="qs_security_footer_background_inset">12dp</dimen>
- <dimen name="qs_security_footer_corner_radius">28dp</dimen>
<dimen name="battery_detail_graph_space_top">9dp</dimen>
<dimen name="battery_detail_graph_space_bottom">9dp</dimen>
- <dimen name="qs_detail_margin_top">14dp</dimen>
+ <dimen name="qs_detail_header_margin_top">14dp</dimen>
<dimen name="volume_tool_tip_top_margin">12dp</dimen>
<dimen name="volume_row_slider_height">128dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 33fddef3efb3..2e728b23abed 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ພາບໜ້າຈໍ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ປິດການນຳໃຊ້ Smart Lock ແລ້ວ"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ສົ່ງຮູບແລ້ວ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ກຳລັງບັນທຶກຮູບໜ້າຈໍ"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ໃຊ້ລາຍນິ້ວມືເພື່ອເປີດ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ຕ້ອງພິສູດຢືນຢັນ. ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມືເພື່ອພິສູດຢືນຢັນ."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ສາຍໂທອອກ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ໂໝດຢູ່ໃນຍົນ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ຈະບໍ່ເຊື່ອມຕໍ່ອິນເຕີເນັດອັດຕະໂນມັດ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ບໍ່ມີການເຊື່ອມຕໍ່"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ບໍ່ມີເຄືອຂ່າຍອື່ນທີ່ສາມາດໃຊ້ໄດ້"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ບໍ່​ມ​ີ​ເຄືອ​ຂ່າຍ​ທີ່​ສາ​ມາດ​ໃຊ້​ໄດ້"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ລາຍລະອຽດເຄືອຂ່າຍ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ແຕະເຄືອຂ່າຍໃດໜຶ່ງເພື່ອເຊື່ອມຕໍ່"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ປົດລັອກເພື່ອເບິ່ງເຄືອຂ່າຍ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ຕ້ອງການເພີ່ມແຜ່ນຕໍ່ໄປນີ້ໃສ່ການຕັ້ງຄ່າດ່ວນ"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ເພີ່ມແຜ່ນ"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ຢ່າເພີ່ມແຜ່ນ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 94faaf42d1b9..52aa2397c358 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Keisti mast., kad atit. ekr."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ištempti, kad atit. ekr."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekrano kopija"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"„Smart Lock“ išjungta"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"išsiuntė vaizdą"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Išsaugoma ekrano kopija..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Reikia nustatyti tapatybę. Nustatykite tapatybę palietę kontrolinio kodo jutiklį."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Vykstantis telefono skambutis"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lėktuvo režimas"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Prie interneto nebus jungiamasi automatiškai"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nėra ryšio"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nėra kitų pasiekiamų tinklų"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nėra pasiekiamų tinklų"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Išsami tinklo informacija"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Palieskite tinklą, kad prisijungtumėte"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Atrakinkite, kad peržiūrėtumėte visus tinklus"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ nori prie sparčiųjų nustatymų pridėti toliau pateiktą išklotinės elementą"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridėti išklotinės elementą"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridėti išklotinės elemento"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0d14405ea4fb..ac75d9e78460 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Tālumm., lai aizp. ekr."</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Stiepiet, lai aizp. ekr."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekrānuzņēmums"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Sistēma Smart Lock ir atspējota"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nosūtīts attēls"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saglabā ekrānuzņēmumu…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Nepieciešama autentifikācija. Pieskarieties pirksta nospieduma sensoram, lai veiktu autentificēšanu."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Notiekošs tālruņa zvans"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Lidojuma režīms"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ir izveidots savienojums"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneta savienojums netiks izveidots automātiski"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nav savienojuma"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nav pieejams neviens cits tīkls"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nav pieejams neviens tīkls"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Dati par tīklu"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Pieskarieties tīklam, lai izveidotu savienojumu"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lai skatītu tīklus, atbloķējiet"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> pieprasa atļauju pievienot tālāk norādīto elementu ātrajiem iestatījumiem"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pievienot elementu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepievienot elementu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5fda8b211ab1..166e861d526e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Зумирај да се исполни екранот"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Растегни да се исполни екранот"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Слика од екранот"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Оневозможено е Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"испрати слика"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сликата на екранот се зачувува..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Тековен телефонски повик"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Авионски режим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Не може автоматски да се поврзе на интернет"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нема интернет-врска"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нема други достапни мрежи"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нема достапни мрежи"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Детали за мрежата"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Допрете на мрежа за да се поврзете"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Отклучете за да се прикажат мрежите"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> сака да ја додаде следнава плочка на „Брзите поставки“"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додајте плочка"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавајте плочка"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 6956934d7af7..9c6a698a17aa 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"സ്ക്രീൻഷോട്ട്"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ചിത്രം അയച്ചു"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"തുറക്കുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. പരിശോധിച്ചുറപ്പിക്കാൻ, വിരലടയാള സെൻസറിൽ സ്‌പർശിക്കുക."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"സജീവമായ ഫോൺ കോൾ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ഫ്ലൈറ്റ് മോഡ്"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ഇന്റർനെറ്റ് സ്വയമേവ കണക്‌റ്റ് ചെയ്യില്ല"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"കണക്ഷനില്ല"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"മറ്റ് നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"വൈഫൈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"നെറ്റ്‌വർക്ക് വിശദാംശങ്ങൾ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"കണക്‌റ്റ് ചെയ്യാൻ ഒരു നെറ്റ്‌വർക്കിൽ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"നെറ്റ്‌വർക്കുകൾ കാണാൻ അൺ‌ലോക്ക് ചെയ്യുക"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്‌വർക്കുകൾ തിരയുന്നു…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്‌വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ദ്രുത ക്രമീകരണത്തിലേക്ക് ഇനിപ്പറയുന്ന ടൈൽ ചേർക്കാൻ <xliff:g id="APPNAME">%1$s</xliff:g> ആവശ്യപ്പെടുന്നു"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ടൈൽ ചേർക്കുക"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ടൈൽ ചേർക്കരുത്"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0d1ad4bebce6..5fb26e02bb30 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Дэлгэц дүүргэх бол өсгөнө үү"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Дэлгэц дүүргэх бол татна уу"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Дэлгэцийн зураг дарах"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ухаалаг түгжээг идэвхгүй болгосон"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"зураг илгээсэн"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Дэлгэцийн агшинг хадгалж байна…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Дэлгэцийн агшинг хадгалж байна…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Нээхийн тулд хурууны хээг ашиглана уу"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Баталгаажуулалт шаардлагатай. Баталгаажуулахын тулд хурууны хээ мэдрэгчид хүрнэ үү."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Үргэлжилж буй утасны дуудлага"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Нислэгийн горим"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Интернэт автоматаар холбогдохгүй"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Холболт алга"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Өөр боломжтой сүлжээ байхгүй байна"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Боломжтой сүлжээ байхгүй байна"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Сүлжээний дэлгэрэнгүй мэдээлэл"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Холбогдохын тулд сүлжээг товшино уу"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Сүлжээг харахын тулд түгжээг тайлах"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүмэлтэй байна"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Хавтан нэмэх"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Хавтанг бүү нэм"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1fa1a3e0480f..87d7b40a963c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरण्यासाठी झूम करा"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरण्यासाठी ताणा"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"उघडण्यासाठी फिंगरप्रिंट वापरा"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ऑथेंटिकेशन आवश्यक आहे. ऑथेंटिकेट करण्यासाठी फिंगरप्रिंट सेन्सरला स्पर्श करा."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फोन कॉल सुरू आहे"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"विमान मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इंटरनेट ऑटो-कनेक्ट होणार नाही"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"कोणतेही कनेक्शन नाही"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"इतर कोणतेही नेटवर्क उपलब्ध नाहीत"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कोणतेही नेटवर्क उपलब्‍ध नाही"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"वाय-फाय"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कचे तपशील"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"कनेक्ट करण्यासाठी नेटवर्कवर टॅप करा"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्क पाहण्यासाठी स्क्रीन अनलॉक करा"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्‍ट करता आले नाही"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b7d0246c4095..d90ea35750fd 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zum untuk memenuhi skrin"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Regang utk memenuhi skrin"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Tangkapan skrin"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dilumpuhkan"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"menghantar imej"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan tangkapan skrin..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan cap jari untuk membuka"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Pengesahan diperlukan. Sentuh penderia cap jari untuk pengesahan."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telefon yang sedang berjalan"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Mod pesawat"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Disambungkan"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet tidak akan bersambung secara automatik"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Tiada sambungan"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Tiada rangkaian lain yang tersedia"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Tiada rangkaian tersedia"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Butiran rangkaian"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ketik rangkaian untuk membuat sambungan"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat rangkaian"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin berikut kepada Tetapan Pantas"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tambahkan jubin"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Jangan tambah jubin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 81d168b79065..5dcb09e51270 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ပိတ်ထားသည်"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ပုံပို့ထားသည်"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ဖွင့်ရန် လက်ဗွေကို သုံးပါ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"အထောက်အထားစိစစ်ခြင်း လိုအပ်သည်။ အထောက်အထားစိစစ်ရန် လက်ဗွေ အာရုံခံကိရိယာကို ထိပါ။"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"လက်ရှိ ဖုန်းခေါ်ဆိုမှု"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"လေယာဉ်ပျံမုဒ်"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"အင်တာနက်က အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ချိတ်ဆက်မှုမရှိပါ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"အခြားကွန်ရက်များ မရှိပါ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ကွန်ရက်များ မရှိပါ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ကွန်ရက် အသေးစိတ်များ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ချိတ်ဆက်ရန် ကွန်ရက်ကို တို့ပါ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ကွန်ရက်များကြည့်ရန် ဖွင့်ပါ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> က ‘အမြန် ဆက်တင်များ’ တွင် အောက်ပါအကွက်ငယ်ကို ထည့်လိုသည်"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"အကွက်ငယ် ထည့်ရန်"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"အကွက်ငယ် မထည့်ပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 360c58b9e2dc..e789d10eacb8 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom for å fylle skjermen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Strekk for å fylle skjerm"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skjermdump"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er slått av"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Lagrer skjermdumpen …"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående telefonsamtale"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flymodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internett kobles ikke til automatisk"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen tilkobling"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Ingen andre nettverk er tilgjengelige"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Ingen nettverk er tilgjengelige"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Nettverksdetaljer"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trykk på et nettverk for å koble til"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås opp for å se nettverk"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil legge til denne brikken i Hurtiginnstillinger"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Legg til brikke"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ikke legg til brikke"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e5787b578430..f19f8de39ec3 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"स्क्रिन भर्न तन्काउनुहोस्"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रिनसट"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"कुनै छवि पठाइयो"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रिनसट बचत गर्दै…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"फिंगरप्रिन्ट प्रयोग गरी खोल्नुहोस्"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि गर्नु पर्ने हुन्छ। पुष्टि गर्न फिंगरप्रिन्ट सेन्सर छुनुहोस्।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"जारी फोन कल"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"हवाइजहाज मोड"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"इन्टरनेट स्वतः कनेक्ट हुँदैन"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"इन्टरनेट छैन"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"अन्य नेटवर्क उपलब्ध छैनन्"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"कुनै पनि नेटवर्क उपलब्ध छैन"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"नेटवर्कसम्बन्धी विवरण"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"इन्टरनेट कनेक्ट गर्न कुनै नेटवर्कमा ट्याप गर्नुहोस्"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्कहरू हेर्न आफ्नो स्क्रिन अनलक गर्नुहोस्"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 7d75b6e20a1e..765d222157f8 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom om scherm te vullen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rek uit v. schermvulling"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock staat uit"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"heeft een afbeelding gestuurd"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot opslaan..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot opslaan..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om te openen"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Verificatie vereist. Raak de vingerafdruksensor aan om de verificatie uit te voeren."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Actief telefoongesprek"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Vliegtuigmodus"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet maakt niet automatisch verbinding"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Geen verbinding"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Geen andere netwerken beschikbaar"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Geen netwerken beschikbaar"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Netwerkgegevens"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tik op een netwerk om verbinding te maken"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontgrendel het scherm om netwerken te bekijken"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil de volgende tegel toevoegen aan Snelle instellingen"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tegel toevoegen"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tegel niet toevoegen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index ee524bf2e3c8..10b3c4ca5925 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ସ୍କ୍ରୀନ୍‌କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ସ୍କ୍ରିନ୍‌ସଟ୍ ନିଅନ୍ତୁ"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ଚାଲୁଥିବା ଫୋନ୍ କଲ୍"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ଏୟାରପ୍ଲେନ୍ ମୋଡ୍"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ଇଣ୍ଟରନେଟ୍ ସ୍ଵତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ସଂଯୋଗ ନାହିଁ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ଅନ୍ୟ କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"କୌଣସି ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ୱାଇ-ଫାଇ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ନେଟୱାର୍କ ବିବରଣୀ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ସଂଯୋଗ କରିବାକୁ ଏକ ନେଟୱାର୍କରେ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ନେଟୱାର୍କଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index d68ab4fe347a..f4ed0086b466 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ਖੋਲ੍ਹਣ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ਪ੍ਰਮਾਣੀਕਰਨ ਲੋੜੀਂਦਾ ਹੈ। ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ।"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ਜਾਰੀ ਫ਼ੋਨ ਕਾਲ"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ਇੰਟਰਨੈੱਟ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ਕੋਈ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ਕੋਈ ਹੋਰ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ਕੋਈ ਨੈੱਟਵਰਕ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"ਵਾਈ-ਫਾਈ"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ਨੈੱਟਵਰਕ ਵੇਰਵੇ"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"ਕਨੈਕਟ ਕਰਨ ਲਈ ਕਿਸੇ ਨੈੱਟਵਰਕ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9aade18169ad..7b165b227fa0 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Powiększ, aby wypełnić ekran"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Rozciągnij, aby wypełnić ekran"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Zrzut ekranu"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Wyłączono Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"wysłano obraz"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Zapisywanie zrzutu ekranu..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Wymagane uwierzytelnienie. Dotknij czytnika liniii papilarnych, by uwierzytelnić."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktywne połączenie"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Tryb samolotowy"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Połączono"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nie połączy się automatycznie z internetem"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Brak połączenia"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Brak innych dostępnych sieci"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Brak dostępnych sieci"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Szczegóły sieci"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Kliknij sieć, aby połączyć"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odblokuj, by wyświetlić sieci"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> chce dodać do Szybkich ustawień ten kafelek"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj kafelek"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nie dodawaj kafelka"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index cab482ae56fd..8496c926fa5e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index eec34684ff4a..6da3120dfa01 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom para preencher o ecrã"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Esticar p. caber em ec. int."</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captura de ecrã"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desativado"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"A guardar captura de ecrã..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"A guardar captura de ecrã..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilize a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação necessária. Toque no sensor de impressões digitais para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica em curso"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo de avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não estabelece ligação automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem ligação"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Sem redes disponíveis"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque numa rede para estabelecer ligação"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"A procurar redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Não foi possível estabelecer ligação à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A app <xliff:g id="APPNAME">%1$s</xliff:g> pretende adicionar o seguinte mosaico às Definições rápidas"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar mosaico"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicion. mosaico"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index cab482ae56fd..8496c926fa5e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modo avião"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"A Internet não será conectada automaticamente"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Sem conexão"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nenhuma outra rede disponível"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nenhuma rede disponível"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalhes da rede"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Toque em uma rede para se conectar"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueie para ver as redes"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Procurando redes…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Falha ao conectar à rede"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"O app <xliff:g id="APPNAME">%1$s</xliff:g> quer adicionar o bloco a seguir às Configurações rápidas"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adicionar bloco"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Não adicionar bloco"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 1e30ea713210..61a857791eaf 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zoom pt. a umple ecranul"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Înt. pt. a umple ecranul"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Captură de ecran"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dezactivat"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a trimis o imagine"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Se salv. captura de ecran..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosiți amprenta ca să deschideți"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atingeți senzorul de amprentă pentru a vă autentifica."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Apel telefonic în desfășurare"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modul Avion"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Nu se conectează automat la internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nicio conexiune"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nu sunt disponibile alte rețele"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nicio rețea disponibilă"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detalii despre rețea"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Atingeți o rețea pentru a vă conecta"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblocați pentru a vedea rețelele"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vrea să adauge următorul card la Setări rapide"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Adăugați un card"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 99465d54e124..deb9a42f206d 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Подогнать по размерам экрана"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Растянуть на весь экран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функция Smart Lock отключена."</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"отправлено изображение"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сохранение..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущий вызов"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим полета"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Без автоподключения к интернету"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Нет подключения к интернету"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Нет других доступных сетей"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нет доступных сетей"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Сведения о сети"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Выберите сеть, чтобы подключиться"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблокируйте, чтобы посмотреть сети Wi-Fi."</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" хочет добавить в меню \"Быстрые настройки\" указанный параметр."</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Добавить параметр"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не добавлять"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 6ea2a0dd5d7a..5660534b7b88 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"තිරය පිරවීමට විශාලනය කරන්න"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"තිරය පිරවීමට අදින්න"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"තිර රුව"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock අබලයි"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"රූපයක් එවන ලදී"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"තිර රුව සුරකිමින්…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"විවෘත කිරීමට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්‍යාපනය අවශ්‍යයි. සත්‍යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්‍රියාත්මක වන දුරකථන ඇමතුම"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ගුවන් යානා ප්‍රකාරය"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"අන්තර්ජාලය ස්වයංක්‍රියව සම්බන්ධ නොවනු ඇත"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"සම්බන්ධතාවයක් නැත"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ලබා ගත හැකි වෙනත් ජාල නැත"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ජාලය නොතිබේ"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"ජාල විස්තර"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"සම්බන්ධ වීමට ජාලයක් තට්ටු කරන්න"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ජාල බැලීමට අගුලු හරින්න"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> හට ක්ෂණික සැකසීම් වෙත පහත ටයිල් එක් කිරීමට අවශ්‍යයි"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ටයිල් එක් කරන්න"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ටයිල් එක් නොකරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 399b9797d21f..4f7e4b565787 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Priblížiť na celú obrazovku"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Na celú obrazovku"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Snímka obrazovky"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkcia Smart Lock je deaktivovaná"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odoslal(a) obrázok"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Prebieha ukladanie snímky obrazovky..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorte odtlačkom prsta"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vyžaduje sa overenie. Dotknite sa senzora odtlačkov prstov."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Prebiehajúci telefonický hovor"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Režim v lietadle"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet sa nepripojí automaticky"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bez pripojenia"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nie sú k dispozícii žiadne ďalšie siete"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nie sú k dispozícii žiadne siete"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podrobnosti siete"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ak sa chcete pripojiť, klepnite na sieť"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odomknutím si zobrazte siete"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> chce pridať do rýchlych nastavení túto kartu"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Pridať kartu"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nepridať kartu"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 0c93bced9f41..bffccffa84d7 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Povečava čez cel zaslon"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Raztegnitev čez zaslon"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Posnetek zaslona"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Storitev Smart Lock je onemogočena."</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslal(-a) sliko"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Shranjev. posnetka zaslona ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Shranjevanje posnetka zaslona ..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Odprite s prstnim odtisom"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Zahtevano je preverjanje pristnosti. Za preverjanje pristnosti se dotaknite tipala prstnih odtisov."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Poteka klic"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Način za letalo"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Samodejna povezava z internetom ni mogoča."</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ni povezave"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nobeno drugo omrežje ni na voljo"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Na voljo ni nobeno omrežje"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Podatki o omrežju"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Za vzpostavitev povezave se dotaknite omrežja."</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Odklenite za ogled omrežij"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> želi dodati to ploščico v hitre nastavitve."</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj ploščico"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ne dodaj ploščice"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7f101dbd3f57..f89d50da791d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zmadho për të mbushur ekranin"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Shtrije për të mbushur ekranin"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Pamja e ekranit"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Po ruan pamjen e ekranit..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kërkohet vërtetimi. Prek sensorin e gjurmës së gishtit për t\'u vërtetuar."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonatë në vazhdim"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Modaliteti i aeroplanit"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Lidhur"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Interneti nuk do të lidhet automatikisht"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Nuk ka lidhje"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Nuk ofrohet asnjë rrjet tjetër"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Nuk ofrohet asnjë rrjet"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Detajet e rrjetit"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Trokit te një rrjet për t\'u lidhur"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Shkyçe për të parë rrjetet"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1da42cfb2d3d..bc65fe7e1ef6 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Зумирај на целом екрану"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Развуци на цео екран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Снимак екрана"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock је онемогућен"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Чување снимка екрана..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string>
@@ -1167,4 +1168,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Отворите помоћу отиска прста"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна је потврда идентитета. Додирните сензор за отисак прста да бисте потврдили идентитет."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Актуелни телефонски позив"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим рада у авиону"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Аутоматско повезивање на интернет није могуће"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Веза није успостављена"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Није доступна ниједна друга мрежа"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Нема доступних мрежа"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WiFi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Детаљи о мрежи"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Додирните мрежу да бисте се повезали"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Откључајте да бисте видели мреже"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> жели да дода следећу плочицу у Брза подешавања"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додај плочицу"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додај плочицу"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 05aeffbc300d..022d8f7efa34 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Zooma för att fylla skärm"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Dra för att fylla skärmen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skärmbild"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock har inaktiverats"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har skickat en bild"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skärmbilden sparas ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmbilden sparas ..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering krävs. Identifiera dig genom att trycka på fingeravtryckssensorn."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående samtal"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Flygplansläge"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Du ansluts inte automatiskt till internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Ingen anslutning"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Inga andra nätverk är tillgängliga"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Det finns inga tillgängliga nätverk"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wifi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Nätverksinformation"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Tryck på ett nätverk för att ansluta"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås upp för att visa nätverk"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill lägga till följande ruta i snabbinställningarna"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Lägg till ruta"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Lägg inte till ruta"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7f51e82683b7..aef8cba5ad72 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Kuza ili kujaza skrini"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Tanua ili kujaza skrini"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Picha ya skrini"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Kipengele cha Smart Lock kimezimwa"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"imetuma picha"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Simu inayoendelea"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Hali ya ndegeni"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Intaneti haitaunganishwa kiotomatiki"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Hakuna muunganisho"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Hakuna mitandao mingine inayopatikana"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Hakuna mitandao inayopatikana"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Maelezo ya mtandao"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Gusa mtandao ili uunganishe"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Fungua ili uangalie mitandao"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingependa kuongeza kigae kifuatacho kwenye Mipangilio ya Haraka"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Ongeza kigae"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Usiongeze kigae"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index e2b2e2590b23..ab159e1c4814 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -18,6 +18,18 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
+ <!-- The maximum number of rows in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_rows">4</integer>
+
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">8</integer>
+
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
+
+ <!-- The number of columns in the QuickSettings -->
+ <integer name="quick_settings_num_columns">2</integer>
+
+ <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
+ <bool name="config_skinnyNotifsInLandscape">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 2f5e8eaa2263..45b5afa467ac 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -38,4 +38,10 @@
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">4</integer>
+ <!-- How many lines to show in the security footer -->
+ <integer name="qs_security_footer_maxLines">1</integer>
+
+ <!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
+ <bool name="allow_force_nav_bar_handle_opaque">false</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index da80b85b38bf..c2aad2d11c1d 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -95,4 +95,12 @@
<dimen name="controls_top_margin">24dp</dimen>
<dimen name="global_actions_grid_item_layout_height">80dp</dimen>
+
+ <!-- For large screens the security footer appears below the footer,
+ same as phones in portrait -->
+ <dimen name="qs_security_footer_single_line_height">48dp</dimen>
+ <dimen name="qs_security_footer_background_inset">0dp</dimen>
+
+ <!-- When split shade is used, this panel should be aligned to the top -->
+ <dimen name="qs_detail_margin_top">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index 64e2760e7778..436f8d0998f5 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,8 +22,5 @@
<resources>
<integer name="status_bar_config_maxNotificationIcons">5</integer>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">5</integer>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a16355e8dab8..93ac01d7e570 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"திரையை நிரப்ப அளவை மாற்று"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"திரையை நிரப்ப இழு"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ஸ்கிரீன்ஷாட்"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock முடக்கப்பட்டது"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"படம் அனுப்பப்பட்டது"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"செயலில் உள்ள மொபைல் அழைப்பு"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"விமானப் பயன்முறை"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"இணையத்துடன் தானாகவே இணைக்காது"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"இணைப்பு இல்லை"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"வேறு நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"நெட்வொர்க்குகள் எதுவும் கிடைக்கவில்லை"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"வைஃபை"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"நெட்வொர்க் விவரங்கள்"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"இணையத்துடன் இணைய நெட்வொர்க்கைத் தட்டுங்கள்"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"நெட்வொர்க்குகளைப் பார்க்க அன்லாக் செய்யுங்கள்"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"விரைவு அமைப்புகளில் பின்வரும் கட்டத்தைச் சேர்க்க <xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸ் விரும்புகிறது"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"கட்டத்தைச் சேர்"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"கட்டத்தை சேர்க்காதே"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index eeeaf0dd72d1..3f0d65d5738f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -77,6 +77,8 @@
<string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్‌కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్‌కు నింపేలా విస్తరించండి"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్‌షాట్"</string>
+ <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+ <skip />
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్‌ను పంపారు"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
@@ -1161,4 +1163,25 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"తెరవడానికి వేలిముద్రను ఉపయోగించండి"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ప్రామాణీకరణ అవసరం. ప్రామాణీకరించడానికి వేలిముద్ర సెన్సార్‌ను తాకండి."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ఫోన్ కాల్ జరుగుతోంది"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"విమానం మోడ్"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"ఇంటర్నెట్ ఆటోమెటిక్‌గా కనెక్ట్ అవ్వదు"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"కనెక్షన్ లేదు"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ఇతర నెట్‌వర్క్‌లేవీ అందుబాటులో లేవు"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"నెట్‌వర్క్‌లు అందుబాటులో లేవు"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"నెట్‌వర్క్ వివరాలు"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"కనెక్ట్ చేయడానికి నెట్‌వర్క్‌ను ట్యాప్ చేయండి"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"నెట్‌వర్క్‌లను చూడటానికి అన్‌లాక్ చేయండి"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్‌వర్క్‌ల కోసం సెర్చ్ చేస్తోంది…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్‌వర్క్‌కు కనెక్ట్ చేయడం విఫలమైంది"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
+ <!-- no translation found for qs_tile_request_dialog_text (3501359944139877694) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_add (4888460910694986304) -->
+ <skip />
+ <!-- no translation found for qs_tile_request_dialog_not_add (4168716573114067296) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1fd1313719dc..0a76977468bb 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"ขยายจนเต็มหน้าจอ"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"ยืดจนเต็มหน้าจอ"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"ภาพหน้าจอ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ปิดใช้ Smart Lock แล้ว"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ส่งรูปภาพ"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"กำลังบันทึกภาพหน้าจอ..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ใช้ลายนิ้วมือเพื่อเปิด"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ต้องมีการตรวจสอบสิทธิ์ แตะเซ็นเซอร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"กำลังโทรอยู่"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"โหมดบนเครื่องบิน"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"อินเทอร์เน็ตจะไม่เชื่อมต่ออัตโนมัติ"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"ไม่มีการเชื่อมต่อ"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"ไม่มีเครือข่ายอื่นๆ ที่พร้อมใช้งาน"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"ไม่มีเครือข่ายที่พร้อมใช้งาน"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"รายละเอียดเครือข่าย"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"แตะเครือข่ายเพื่อเชื่อมต่อ"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"ปลดล็อกเพื่อดูเครือข่าย"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ต้องการเพิ่มชิ้นส่วนต่อไปนี้ในการตั้งค่าด่วน"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"เพิ่มชิ้นส่วน"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ไม่ต้องเพิ่มชิ้นส่วน"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 3837364887cf..c5c4cf3792bb 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"I-zoom upang punan screen"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"I-stretch upang mapuno screen"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Naka-disable ang Smart Lock"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nagpadala ng larawan"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Sine-save ang screenshot…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Sine-save ang screenshot…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gamitin ang fingerprint para buksan"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kailangan ng pag-authenticate. Pindutin ang sensor para sa fingerprint para mag-authenticate."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Kasalukuyang may tawag sa telepono"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Airplane mode"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Hindi awtomatikong kokonekta ang Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Walang koneksyon"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Walang available na iba pang network"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Walang available na network"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Mga detalye ng network"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Mag-tap ng network para kumonekta"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"I-unlock para tingnan ang mga network"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Gustong idagdag ng <xliff:g id="APPNAME">%1$s</xliff:g> ang sumusunod na tile sa Mga Mabilisang Setting"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Idagdag ang tile"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Huwag idagdag"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1cd547ca7793..d22472eee1c1 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Yakınlaştır (ekranı kaplasın)"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Genişlet (ekran kapansın)"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Ekran görüntüsü"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock devre dışı"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"bir resim gönderildi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ekran görüntüsü kaydediliyor..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Devam eden telefon görüşmesi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Uçak modu"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Bağlı"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"İnternete otomatik olarak bağlanmaz"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Bağlantı yok"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Kullanılabilir başka ağ yok"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Kullanılabilir ağ yok"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Kablosuz"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Ağ bilgileri"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Bağlanmak için bir ağa dokunun"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Ağları görmek için kilidi açın"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdaki kartı Hızlı Ayarlar\'a eklemek istiyor"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Kart ekle"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Kart ekleme"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 70922aa77db3..c364429eb837 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Масштабув. на весь екран"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Розтягнути на весь екран"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Знімок екрана"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock вимкнено"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"надіслане зображення"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Збереження знімка екрана..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string>
@@ -1173,4 +1174,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Активний телефонний виклик"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Режим польоту"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Автоматичне інтернет-з’єднання вимкнено"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Немає з\'єднання"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Інші мережі недоступні"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Немає доступних мереж"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Деталі мережі"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Натисніть мережу, до якої потрібно підключитися"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Розблокувати, щоб переглянути мережі"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Додаток <xliff:g id="APPNAME">%1$s</xliff:g> хоче додати такий параметр у меню швидких налаштувань:"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Додати параметр"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Не додавати параметр"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 7ef014923960..bbaaf20f3ece 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"پوری سکرین پر زوم کریں"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"پوری سکرین پر پھیلائیں"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"اسکرین شاٹ"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏Smart Lock کو غیر فعال کیا گیا"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ایک تصویر بھیجی"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"کھولنے کے لیے فنگر پرنٹ کا استعمال کریں"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"توثیق مطلوب ہے۔ توثیق کرنے کے لیے فنگر پرنٹ سینسر کو ٹچ کریں۔"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"جاری فون کال"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"ہوائی جہاز وضع"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="NETWORKMODE">%2$s</xliff:g> / <xliff:g id="STATE">%1$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"انٹرنیٹ خود کار طور پر منسلک نہیں ہوگا"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"کوئی کنکشن نہیں"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"کوئی دوسرا نیٹ ورک دستیاب نہیں ہے"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"کوئی نیٹ ورکس دستیاب نہیں ہیں"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"نیٹ ورک کی تفصیلات"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"منسلک کرنے کے لیے نیٹ ورک پر تھپتھپائیں"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"نیٹ ورکس کو دیکھنے کے لیے غیر مقفل کریں"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتا ہے"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ٹائل شامل کریں"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ٹائل شامل نہ کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 08ac980d3bee..b7a6448b4472 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Ekranga moslashtirish"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Ekran hajmida cho‘zish"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Skrinshot"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock faolsizlantirildi"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"rasm yuborildi"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinshot saqlanmoqda…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinshot saqlanmoqda…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ochish uchun barmoq izidan foydalaning"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Haqiqiylikni tekshirish talab etiladi. Autentifikatsiya uchun barmoq izi skaneriga tegining."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Joriy telefon chaqiruvi"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Parvoz rejimi"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ulandi"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Internet avtomatik ravishda ulanmaydi"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Internetga ulanmagansiz"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Boshqa tarmoqlar mavjud emas"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Hech qanday tarmoq mavjud emas"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Tarmoq tafsilotlari"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Ulanish uchun tarmoq ustiga bosing"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Tarmoqlarni koʻrish uchun qulfdan chiqaring"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Tarmoqlar qidirilmoqda…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Tarmoqqa ulanmadi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ilovasi Tezkor sozlamalarga quyidagi tugmani kiritmoqchi"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Tugma kiritish"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Tugma kiritilmasin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 60b1232cd8a3..48b3e4c7fc01 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"T.phóng để lấp đầy m.hình"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Giãn ra để lấp đầy m.hình"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Chụp ảnh màn hình"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Tính năng Smart Lock đã tắt"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"đã gửi hình ảnh"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Đang lưu ảnh chụp màn hình..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Đang lưu ảnh chụp màn hình..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Bạn cần phải xác thực. Hãy chạm vào cảm biến vân tay để xác thực."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Đang gọi điện thoại"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Chế độ trên máy bay"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Đã kết nối"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"Sẽ không tự động kết nối Internet"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Không có kết nối mạng"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Không có mạng nào khác"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Không có mạng"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Thông tin chi tiết về mạng"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Nhấn vào một mạng để kết nối"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Mở khóa để xem mạng"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> muốn thêm ô bên dưới vào trình đơn Cài đặt nhanh"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Thêm ô"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Không thêm ô"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 357e59e7ece5..aa3895b2b4cb 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"缩放以填满屏幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"拉伸以填满屏幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"屏幕截图"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"发送了一张图片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在保存屏幕截图..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string>
@@ -1058,7 +1059,7 @@
<string name="accessibility_control_change_favorite" msgid="2943178027582253261">"收藏"</string>
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"取消收藏"</string>
<string name="accessibility_control_move" msgid="8980344493796647792">"移至位置 <xliff:g id="NUMBER">%d</xliff:g>"</string>
- <string name="controls_favorite_default_title" msgid="967742178688938137">"控件"</string>
+ <string name="controls_favorite_default_title" msgid="967742178688938137">"控制"</string>
<string name="controls_favorite_subtitle" msgid="6481675111056961083">"选择要从“快捷设置”菜单访问的控制项"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住并拖动即可重新排列控制器"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"已移除所有控制器"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"正在进行通话"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飞行模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不会自动连接到互联网"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"无网络连接"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"没有其他可用网络"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"没有可用网络"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"WLAN"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"网络详情"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"点按要连接的网络"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解锁即可查看网络"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”希望将以下图块添加到“快捷设置”"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加图块"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加图块"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f8ede3a55b88..3a46bd40689a 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"已傳送圖片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕擷取畫面..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕擷取畫面..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飛行模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連線至互聯網"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有連線"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網絡"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網絡"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"網絡詳細資料"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕按網絡以連線"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖即可查看網絡"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在「快速設定」選單新增以下圖塊"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增圖塊"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增圖塊"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index df84fb0699c9..2a04ca27e9ca 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"傳送了一張圖片"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕截圖…"</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕截圖…"</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"飛航模式"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"不會自動連上網際網路"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"沒有網路連線"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"沒有可用的其他網路"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"沒有可用的網路"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"網路詳細資料"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"輕觸要連線的網路"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖螢幕即可查看網路"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在快速設定選單新增以下設定方塊"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"新增設定方塊"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不要新增設定方塊"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index fe97daa7682e..649b3cd9b0f4 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -77,6 +77,7 @@
<string name="compat_mode_on" msgid="4963711187149440884">"Sondeza ukugcwalisa isikrini"</string>
<string name="compat_mode_off" msgid="7682459748279487945">"Nweba ukugcwalisa isikrini"</string>
<string name="global_action_screenshot" msgid="2760267567509131654">"Isithombe-skrini"</string>
+ <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ukhiye oSmathi ukhutshaziwe"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"uthumele isithombe"</string>
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ilondoloz umfanekiso weskrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string>
@@ -1161,4 +1162,22 @@
<string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
<string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
<string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ikholi yefoni eqhubekayo"</string>
+ <string name="airplane_mode" msgid="2536350001462130668">"Imodi yendiza"</string>
+ <string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
+ <string name="preference_summary_default_combination" msgid="8453246369903749670">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="NETWORKMODE">%2$s</xliff:g>"</string>
+ <string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
+ <string name="mobile_data_off_summary" msgid="5621158216585822679">"I-inthanethi ngeke ixhumeke ngokuzenzakalelayo"</string>
+ <string name="mobile_data_no_connection" msgid="1713872434869947377">"Alukho uxhumano"</string>
+ <string name="non_carrier_network_unavailable" msgid="770049357024492372">"Awekho amanye amanethiwekhi atholakalayo"</string>
+ <string name="all_network_unavailable" msgid="4112774339909373349">"Awekho amanethiwekhi atholakalayo"</string>
+ <string name="turn_on_wifi" msgid="1308379840799281023">"Wi‑Fi"</string>
+ <string name="pref_title_network_details" msgid="1639455355897668883">"Imininingwane yenethiwekhi"</string>
+ <string name="tap_a_network_to_connect" msgid="1565073330852369558">"Thepha inethiwekhi ukuze uxhume"</string>
+ <string name="unlock_to_view_networks" msgid="5072880496312015676">"Vula ukuze ubuke amanethiwekhi"</string>
+ <string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
+ <string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
+ <string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
+ <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"I-<xliff:g id="APPNAME">%1$s</xliff:g> ifuna ukwengeza ithayela elilandelayo Kumasethingi Asheshayo"</string>
+ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Engeza ithayela"</string>
+ <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Ungafaki ithayela"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index b5337d363e12..3121ce37490a 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -69,6 +69,10 @@
<declare-styleable name="DateView">
<attr name="datePattern" format="string" />
</declare-styleable>
+ <declare-styleable name="VariableDateView">
+ <attr name="longDatePattern" format="string" />
+ <attr name="shortDatePattern" format="string" />
+ </declare-styleable>
<declare-styleable name="PseudoGridView">
<attr name="numColumns" format="integer" />
<attr name="verticalSpacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2260d2175268..38af659388d5 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<drawable name="notification_number_text_color">#ffffffff</drawable>
<drawable name="ticker_background_color">#ff1d1d1d</drawable>
<drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
@@ -69,6 +69,9 @@
<!-- Shadows under the clock, date and other keyguard text fields -->
<color name="keyguard_shadow_color">#B2000000</color>
+ <!-- Color for the images in keyguard number pad buttons -->
+ <color name="keyguard_keypad_image_color">@android:color/background_light</color>
+
<!-- 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 -->
@@ -281,4 +284,16 @@
<color name="wallet_card_border">#33FFFFFF</color>
<color name="people_tile_background">@android:color/system_accent2_50</color>
+
+ <!-- Internet Dialog -->
+ <!-- Material next state on color-->
+ <color name="settingslib_state_on_color">?androidprv:attr/colorAccentPrimary</color>
+ <!-- Material next state off color-->
+ <color name="settingslib_state_off_color">?androidprv:attr/colorAccentSecondary</color>
+ <!-- Material next track on color-->
+ <color name="settingslib_track_on_color">?androidprv:attr/colorAccentPrimaryVariant</color>
+ <!-- Material next track off color-->
+ <color name="settingslib_track_off_color">?androidprv:attr/colorAccentSecondaryVariant</color>
+ <color name="connected_network_primary_color">#191C18</color>
+ <color name="connected_network_secondary_color">#41493D</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d274c917c26d..1d30273762e7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -86,7 +86,10 @@
<bool name="config_navigation_bar_enable_auto_dim_no_visible_wallpaper">true</bool>
<!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_columns">4</integer>
+ <integer name="quick_qs_panel_max_tiles">4</integer>
+
+ <!-- The maximum number of rows in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_rows">2</integer>
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">2</integer>
@@ -159,7 +162,7 @@
<!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
card. -->
- <integer name="keyguard_max_notification_count">3</integer>
+ <integer name="keyguard_max_notification_count">-1</integer>
<!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
@@ -194,6 +197,11 @@
low powered state yet. -->
<bool name="doze_long_press_uses_prox">true</bool>
+ <!-- Doze: whether the brightness sensor uses the proximity sensor.
+ If both this parameter and doze_selectively_register_prox are true, registration for the
+ brightness sensor won't occur when the display state is ON. -->
+ <bool name="doze_brightness_uses_prox">true</bool>
+
<!-- Doze: should notifications be used as a pulse signal? -->
<bool name="doze_pulse_on_notifications">true</bool>
@@ -310,7 +318,8 @@
<!-- SystemUIFactory component -->
<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>
- <!-- SystemUI Services: The classes of the stuff to start. -->
+ <!-- SystemUI Services: The classes of base stuff to start by default for all
+ configurations. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
@@ -337,6 +346,12 @@
<item>com.android.systemui.wmshell.WMShell</item>
</string-array>
+ <!-- SystemUI Services: The classes of the additional stuff to start. Services here are
+ specified as an overlay to provide configuration-specific services that
+ supplement those listed in config_systemUIServiceComponents. -->
+ <string-array name="config_additionalSystemUIServiceComponents" translatable="false">
+ </string-array>
+
<!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
<dimen name="config_qsTileStrokeWidthActive">-1dp</dimen>
<dimen name="config_qsTileStrokeWidthInactive">-1dp</dimen>
@@ -608,8 +623,6 @@
<!-- Whether wallet view is shown in landscape / seascape orientations -->
<bool name="global_actions_show_landscape_wallet_view">false</bool>
- <!-- Whether global actions should show an informational message about changes in S -->
- <bool name="global_actions_show_change_info">false</bool>
<!-- Package name of the preferred system app to perform eSOS action -->
<string name="config_preferredEmergencySosPackage" translatable="false"></string>
@@ -670,4 +683,29 @@
1 - Override the setting to always bypass keyguard
2 - Override the setting to never bypass keyguard -->
<integer name="config_face_unlock_bypass_override">0</integer>
+
+ <!-- Whether the communal service should be enabled -->
+ <bool name="config_communalServiceEnabled">false</bool>
+
+ <!-- Component name of communal source service -->
+ <string name="config_communalSourceComponent" translatable="false">@null</string>
+
+ <!-- Whether idle mode should be enabled. When enabled, the lock screen will timeout to an idle
+ screen on inactivity. -->
+ <bool name="config_enableIdleMode">false</bool>
+
+ <!-- Timeout to idle mode duration in milliseconds. -->
+ <integer name="config_idleModeTimeout">10000</integer>
+
+ <!-- The maximum number of attempts to reconnect to the communal source target after failing
+ to connect -->
+ <integer name="config_communalSourceMaxReconnectAttempts">10</integer>
+
+ <!-- The initial amount of time (in milliseconds) before attempting to reconnect to a communal
+ source. This value is used as the base value in an exponential backoff in subsequent
+ attempts. -->
+ <integer name="config_communalSourceReconnectBaseDelay">1000</integer>
+
+ <!-- Flag to activate notification to contents feature -->
+ <bool name="config_notificationToContents">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 78db2a8a3485..4eb3ccc8f408 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -56,13 +56,10 @@
<!-- The amount by which the arrow is shifted to avoid the finger-->
<dimen name="navigation_edge_finger_offset">48dp</dimen>
- <!-- Luminance threshold to determine black/white contrast for the navigation affordances -->
- <item name="navigation_luminance_threshold" type="dimen" format="float">0.5</item>
- <!-- Luminance change threshold that allows applying new value if difference was exceeded -->
- <item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
-
<dimen name="floating_rotation_button_diameter">40dp</dimen>
- <dimen name="floating_rotation_button_min_margin">4dp</dimen>
+ <dimen name="floating_rotation_button_min_margin">20dp</dimen>
+ <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
+ <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
<!-- Height of notification icons in the status bar -->
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
@@ -402,8 +399,11 @@
<dimen name="status_bar_header_padding_bottom">48dp</dimen>
<!-- The height of the container that holds the battery and time in the quick settings header.
+ Preferred over using "@*android:dimen/quick_qs_offset_height" as system icons are not always
+ present in quick settings (e.g. in split shade) and it's useful to be able to override this
+ value in such cases.
-->
- <dimen name="qs_header_system_icons_area_height">48dp</dimen>
+ <dimen name="qs_header_system_icons_area_height">@*android:dimen/quick_qs_offset_height</dimen>
<!-- How far the quick-quick settings panel extends below the status bar -->
<dimen name="qs_quick_header_panel_height">128dp</dimen>
@@ -455,6 +455,10 @@
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
+ <!-- Height of status bar in split shade mode - visible only on large screens -->
+ <dimen name="split_shade_header_height">@*android:dimen/quick_qs_offset_height</dimen>
+ <dimen name="split_shade_header_min_height">@dimen/qs_header_row_min_height</dimen>
+
<!-- The top margin of the panel that holds the list of notifications. -->
<dimen name="notification_panel_margin_top">0dp</dimen>
@@ -604,7 +608,7 @@
<dimen name="qs_detail_item_primary_text_size">16sp</dimen>
<dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
<dimen name="qs_detail_empty_text_size">14sp</dimen>
- <dimen name="qs_detail_margin_top">28dp</dimen>
+ <dimen name="qs_detail_header_margin_top">28dp</dimen>
<dimen name="qs_detail_back_margin_end">16dp</dimen>
<dimen name="qs_detail_header_text_padding">16dp</dimen>
<dimen name="qs_data_usage_text_size">14sp</dimen>
@@ -626,6 +630,7 @@
<dimen name="qs_footer_icon_size">20dp</dimen>
<dimen name="qs_header_top_padding">15dp</dimen>
<dimen name="qs_header_bottom_padding">14dp</dimen>
+ <dimen name="qs_header_row_min_height">48dp</dimen>
<dimen name="qs_footer_padding">20dp</dimen>
<dimen name="qs_security_footer_height">88dp</dimen>
@@ -654,6 +659,8 @@
<!-- Padding between subtitles and the following text in the QSFooter dialog -->
<dimen name="qs_footer_dialog_subtitle_padding">20dp</dimen>
+ <dimen name="qs_detail_margin_top">@*android:dimen/quick_qs_offset_height</dimen>
+
<dimen name="seek_bar_height">3dp</dimen>
<dimen name="seek_bar_corner_radius">3dp</dimen>
@@ -911,6 +918,7 @@
<dimen name="keyguard_affordance_fixed_height">48dp</dimen>
<dimen name="keyguard_affordance_fixed_width">48dp</dimen>
+ <dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
<dimen name="keyguard_affordance_horizontal_offset">32dp</dimen>
<dimen name="keyguard_affordance_vertical_offset">32dp</dimen>
@@ -1590,4 +1598,45 @@
<!-- The padding between the icon and the text. -->
<dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
<dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+
+ <!-- Internet panel related dimensions -->
+ <dimen name="internet_dialog_list_margin">12dp</dimen>
+ <dimen name="internet_dialog_list_max_height">646dp</dimen>
+ <dimen name="internet_dialog_list_max_width">412dp</dimen>
+
+ <!-- Signal icon in internet dialog -->
+ <dimen name="signal_strength_icon_size">24dp</dimen>
+
+ <!-- Internet dialog related dimensions -->
+ <dimen name="internet_dialog_corner_radius">24dp</dimen>
+
+ <!-- Size of internet dialog -->
+ <dimen name="settingslib_switchbar_margin">16dp</dimen>
+ <!-- Minimum width of switch -->
+ <dimen name="settingslib_min_switch_width">52dp</dimen>
+ <!-- Size of layout margin left -->
+ <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
+ <!-- Size of layout margin right -->
+ <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
+ <!-- Radius of switch bar -->
+ <dimen name="settingslib_switch_bar_radius">35dp</dimen>
+ <!-- Margin of switch thumb -->
+ <dimen name="settingslib_switch_thumb_margin">4dp</dimen>
+ <!-- Size of switch thumb -->
+ <dimen name="settingslib_switch_thumb_size">20dp</dimen>
+ <!-- Width of switch track -->
+ <dimen name="settingslib_switch_track_width">52dp</dimen>
+ <!-- Height of switch track -->
+ <dimen name="settingslib_switch_track_height">28dp</dimen>
+ <!-- Radius of switch track -->
+ <dimen name="settingslib_switch_track_radius">35dp</dimen>
+
+ <!-- Height percentage of the parent container occupied by the communal view -->
+ <item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
+
+ <dimen name="drag_and_drop_icon_size">70dp</dimen>
+
+ <dimen name="qs_tile_service_request_dialog_width">304dp</dimen>
+ <dimen name="qs_tile_service_request_tile_width">192dp</dimen>
+ <dimen name="qs_tile_service_request_content_space">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index efa87548af32..c2b87a55f366 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -24,9 +24,6 @@
<bool name="flag_monet">false</bool>
- <!-- b/171917882 -->
- <bool name="flag_notification_twocolumn">false</bool>
-
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">true</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a67021611812..6df34c4dad7a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -211,6 +211,8 @@
<!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
<string name="global_action_screenshot">Screenshot</string>
+ <!-- Message shown in power menu when smart lock has been disabled [CHAR_LIMIT=NONE] -->
+ <string name="global_action_smart_lock_disabled">Smart Lock disabled</string>
<!-- text to show in place of RemoteInput images when they cannot be shown.
[CHAR LIMIT=50] -->
@@ -2988,8 +2990,46 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
- <!-- Placeholder for string describing changes in global actions -->
- <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
- <!-- URL for more information about changes in global actions -->
- <string name="global_actions_change_url" translatable="false"></string>
+ <!-- Provider Model: Title of the airplane mode in the internet dialog. [CHAR LIMIT=50] -->
+ <string name="airplane_mode">Airplane mode</string>
+ <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
+ <string name="mobile_data_settings_title">Mobile data</string>
+ <!-- Provider Model: Summary text separator for preferences including a short description
+ (eg. "Connected / 5G"). [CHAR LIMIT=50] -->
+ <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
+ <!-- Provider Model:
+ Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+ <string name="mobile_data_connection_active">Connected</string>
+ <!-- Provider Model:
+ Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
+ <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
+ <!-- Provider Model:
+ Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+ <string name="mobile_data_no_connection">No connection</string>
+ <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
+ <string name="non_carrier_network_unavailable">No other networks available</string>
+ <!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
+ <string name="all_network_unavailable">No networks available</string>
+ <!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
+ <string name="turn_on_wifi">Wi\u2011Fi</string>
+ <!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
+ <string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
+ <!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
+ <string name="tap_a_network_to_connect">Tap a network to connect</string>
+ <!-- Provider Model: Panel subtitle for unlocking screen to view networks. [CHAR LIMIT=60] -->
+ <string name="unlock_to_view_networks">Unlock to view networks</string>
+ <!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
+ <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
+ <!-- Provider Model: Failure notification for connect -->
+ <string name="wifi_failed_connect_message">Failed to connect to network</string>
+ <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
+ <string name="see_all_networks">See all</string>
+
+ <!-- Text for TileService request dialog. This is shown to the user that an app is requesting
+ user approval to add the shown tile to Quick Settings [CHAR LIMIT=NONE] -->
+ <string name="qs_tile_request_dialog_text"><xliff:g id="appName" example="Fake App">%1$s</xliff:g> wants to add the following tile to Quick Settings</string>
+ <!-- Text for TileService request dialog. Text for button for user to approve adding the tile [CHAR LIMIT=20] -->
+ <string name="qs_tile_request_dialog_add">Add tile</string>
+ <!-- Text for TileService request dialog. Text for button for user to reject adding the tile [CHAR LIMIT=20] -->
+ <string name="qs_tile_request_dialog_not_add">Do not add tile</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 51eabf60385e..7f4866f5a586 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -727,6 +727,16 @@
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
+ <!-- TileService request dialog -->
+ <style name="TileRequestDialog" parent="Theme.SystemUI.QuickSettings.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@drawable/qs_dialog_bg</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowCloseOnTouchOutside">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ </style>
+
<!-- USB Contaminant dialog -->
<style name ="USBContaminant" />
@@ -903,4 +913,52 @@
<!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
</style>
+
+ <style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
+ </style>
+
+ <style name="Widget.SliceView.Panel">
+ <item name="titleSize">16sp</item>
+ <item name="rowStyle">@style/SliceRow</item>
+ <item name="android:background">?android:attr/colorBackgroundFloating</item>
+ </style>
+
+ <style name="SliceRow">
+ <!-- 2dp start padding for the start icon -->
+ <item name="titleItemStartPadding">2dp</item>
+ <item name="titleItemEndPadding">0dp</item>
+
+ <!-- Padding between content and the start icon is 14dp -->
+ <item name="contentStartPadding">14dp</item>
+ <!-- Padding between content and end items is 16dp -->
+ <item name="contentEndPadding">16dp</item>
+
+ <!-- Both side margins of end item are 16dp -->
+ <item name="endItemStartPadding">16dp</item>
+ <item name="endItemEndPadding">16dp</item>
+
+ <!-- Both side margins of bottom divider are 12dp -->
+ <item name="bottomDividerStartPadding">12dp</item>
+ <item name="bottomDividerEndPadding">12dp</item>
+
+ <item name="actionDividerHeight">32dp</item>
+ </style>
+
+ <style name="Theme.SystemUI.Dialog.Internet">
+ <item name="android:windowBackground">@drawable/internet_dialog_background</item>
+ </style>
+
+ <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
+ </style>
+
+ <style name="TrimmedHorizontalProgressBar"
+ parent="android:Widget.Material.ProgressBar.Horizontal">
+ <item name="android:indeterminateDrawable">
+ @drawable/progress_indeterminate_horizontal_material_trimmed
+ </item>
+ <item name="android:minHeight">4dp</item>
+ <item name="android:maxHeight">4dp</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index b2ae2a0ca8ad..3a23094d67da 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -47,6 +47,7 @@ android_library {
static_libs: [
"PluginCoreLib",
+ "androidx.dynamicanimation_dynamicanimation",
],
java_version: "1.8",
min_sdk_version: "26",
diff --git a/packages/SystemUI/shared/lint-baseline.xml b/packages/SystemUI/shared/lint-baseline.xml
index ec9d8fd468a5..4bd6729227e8 100644
--- a/packages/SystemUI/shared/lint-baseline.xml
+++ b/packages/SystemUI/shared/lint-baseline.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev">
<issue
id="NewApi"
@@ -300,6 +300,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+ errorLine1=" taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+ line="192"
+ column="49"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Field requires API level 29 (current min is 26): `android.app.ActivityManager.RunningTaskInfo#isRunning`"
errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
@@ -311,6 +322,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#isRunning`"
+ errorLine1=" isNotInRecents = !change.getTaskInfo().isRunning;"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java"
+ line="210"
+ column="31"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Call requires API level 29 (current min is 26): `android.view.SurfaceControl#release`"
errorLine1=" leash.mSurfaceControl.release();"
errorLine2=" ~~~~~~~">
@@ -575,6 +597,17 @@
<issue
id="NewApi"
+ message="Field requires API level 29 (current min is 26): `android.app.TaskInfo#taskId`"
+ errorLine1=" onTaskMovedToFront(taskInfo.taskId);"
+ errorLine2=" ~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java"
+ line="70"
+ column="28"/>
+ </issue>
+
+ <issue
+ id="NewApi"
message="Call requires API level 29 (current min is 26): `android.graphics.Bitmap#wrapHardwareBuffer`"
errorLine1=" thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
new file mode 100644
index 000000000000..915e7f63ffb2
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.animation
+
+import android.graphics.Point
+import android.util.MathUtils.lerp
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import java.lang.ref.WeakReference
+
+/**
+ * Creates an animation where all registered views are moved into their final location
+ * by moving from the center of the screen to the sides
+ */
+class UnfoldMoveFromCenterAnimator(
+ private val windowManager: WindowManager,
+ /**
+ * Allows to set custom translation applier
+ * Could be useful when a view could be translated from
+ * several sources and we want to set the translation
+ * using custom methods instead of [View.setTranslationX] or
+ * [View.setTranslationY]
+ */
+ var translationApplier: TranslationApplier = object : TranslationApplier {}
+) : UnfoldTransitionProgressProvider.TransitionProgressListener {
+
+ private val screenSize = Point()
+ private var isVerticalFold = false
+
+ private val animatedViews: MutableList<AnimatedView> = arrayListOf()
+ private val tmpArray = IntArray(2)
+
+ /**
+ * Updates display properties in order to calculate the initial position for the views
+ * Must be called before [registerViewForAnimation]
+ */
+ fun updateDisplayProperties() {
+ windowManager.defaultDisplay.getSize(screenSize)
+
+ // Simple implementation to get current fold orientation,
+ // this might not be correct on all devices
+ // TODO: use JetPack WindowManager library to get the fold orientation
+ isVerticalFold = windowManager.defaultDisplay.rotation == Surface.ROTATION_0 ||
+ windowManager.defaultDisplay.rotation == Surface.ROTATION_180
+ }
+
+ /**
+ * Registers a view to be animated, the view should be measured and layouted
+ * After finishing the animation it is necessary to clear
+ * the views using [clearRegisteredViews]
+ */
+ fun registerViewForAnimation(view: View) {
+ val animatedView = createAnimatedView(view)
+ animatedViews.add(animatedView)
+ }
+
+ /**
+ * Unregisters all registered views and resets their translation
+ */
+ fun clearRegisteredViews() {
+ onTransitionProgress(1f)
+ animatedViews.clear()
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ animatedViews.forEach {
+ it.view.get()?.let { view ->
+ translationApplier.apply(
+ view = view,
+ x = lerp(it.startTranslationX, it.finishTranslationX, progress),
+ y = lerp(it.startTranslationY, it.finishTranslationY, progress)
+ )
+ }
+ }
+ }
+
+ private fun createAnimatedView(view: View): AnimatedView {
+ val viewLocation = tmpArray
+ view.getLocationOnScreen(viewLocation)
+
+ val viewX = viewLocation[0].toFloat()
+ val viewY = viewLocation[1].toFloat()
+
+ val viewCenterX = viewX + view.width / 2
+ val viewCenterY = viewY + view.height / 2
+
+ val translationXDiff: Float
+ val translationYDiff: Float
+
+ if (isVerticalFold) {
+ val distanceFromScreenCenterToViewCenter = screenSize.x / 2 - viewCenterX
+ translationXDiff = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ translationYDiff = 0f
+ } else {
+ val distanceFromScreenCenterToViewCenter = screenSize.y / 2 - viewCenterY
+ translationXDiff = 0f
+ translationYDiff = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ }
+
+ return AnimatedView(
+ view = WeakReference(view),
+ startTranslationX = view.translationX + translationXDiff,
+ startTranslationY = view.translationY + translationYDiff,
+ finishTranslationX = view.translationX,
+ finishTranslationY = view.translationY
+ )
+ }
+
+ /**
+ * Interface that allows to use custom logic to apply translation to view
+ */
+ interface TranslationApplier {
+ /**
+ * Called when we need to apply [x] and [y] translation to [view]
+ */
+ fun apply(view: View, x: Float, y: Float) {
+ view.translationX = x
+ view.translationY = y
+ }
+ }
+
+ private class AnimatedView(
+ val view: WeakReference<View>,
+ val startTranslationX: Float,
+ val startTranslationY: Float,
+ val finishTranslationX: Float,
+ val finishTranslationY: Float
+ )
+}
+
+private const val TRANSLATION_PERCENTAGE = 0.3f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.aidl
new file mode 100644
index 000000000000..b76be4fc719a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalHost.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 com.android.systemui.shared.communal;
+
+import com.android.systemui.shared.communal.ICommunalSource;
+
+/**
+* An interface, implemented by SystemUI, for hosting a shared, communal surface on the lock
+* screen. Clients declare themselves sources (as defined by ICommunalSource). ICommunalHost is
+* meant only for the input of said sources. The lifetime scope and interactions that follow after
+* are bound to source.
+*/
+oneway interface ICommunalHost {
+ /**
+ * Invoked to specify the CommunalSource that should be consulted for communal surfaces to be
+ * displayed.
+ */
+ void setSource(in ICommunalSource source) = 1;
+} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl
new file mode 100644
index 000000000000..7ef403b414be
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSource.aidl
@@ -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.
+ */
+
+package com.android.systemui.shared.communal;
+
+import com.android.systemui.shared.communal.ICommunalSurfaceCallback;
+
+/**
+ * An interface, implemented by clients of CommunalHost, to provide communal surfaces for SystemUI.
+ * The associated binder proxy will be retained by SystemUI and called on-demand when a communal
+ * surface is needed (either new instantiation or update).
+ */
+oneway interface ICommunalSource {
+ /**
+ * Called by the CommunalHost when a new communal surface is needed. The provided arguments
+ * match the arguments necessary to construct a SurfaceControlViewHost for producing a
+ * SurfacePackage to return.
+ */
+ void getCommunalSurface(in IBinder hostToken, in int width, in int height, in int displayId,
+ in ICommunalSurfaceCallback callback) = 1;
+} \ No newline at end of file
diff --git a/core/java/android/uwb/AdapterState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
index 991f64a0c0ae..58acce00f6a6 100644
--- a/core/java/android/uwb/AdapterState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/communal/ICommunalSurfaceCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,25 +14,17 @@
* limitations under the License.
*/
-package android.uwb;
+package com.android.systemui.shared.communal;
+
+import android.view.SurfaceControlViewHost;
/**
- * @hide
- */
-@Backing(type="int")
-enum AdapterState {
+* An interface for receiving the result of a surface request. ICommunalSurfaceCallback is
+* implemented by the CommunalHost (SystemUI) to process the results of a new communal surface.
+*/
+interface ICommunalSurfaceCallback {
/**
- * The state when UWB is disabled.
- */
- STATE_DISABLED,
-
- /**
- * The state when UWB is enabled but has no active sessions.
- */
- STATE_ENABLED_INACTIVE,
-
- /**
- * The state when UWB is enabled and has active sessions.
- */
- STATE_ENABLED_ACTIVE,
+ * Invoked when the CommunalSurface has generated the SurfacePackage to be displayed.
+ */
+ void onSurface(in SurfaceControlViewHost.SurfacePackage surfacePackage) = 1;
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 560d89af8e92..bcfb77461307 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.navigationbar.gestural;
+package com.android.systemui.shared.navigationbar;
import static android.view.Display.DEFAULT_DISPLAY;
-import android.content.res.Resources;
+import android.annotation.TargetApi;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Handler;
import android.view.CompositionSamplingListener;
import android.view.SurfaceControl;
@@ -27,16 +28,22 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
-import com.android.systemui.R;
-
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
/**
* A helper class to sample regions on the screen and inspect its luminosity.
*/
+@TargetApi(Build.VERSION_CODES.Q)
public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
View.OnLayoutChangeListener {
+ // Luminance threshold to determine black/white contrast for the navigation affordances.
+ // Passing the threshold of this luminance value will make the button black otherwise white
+ private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f;
+ // Luminance change threshold that allows applying new value if difference was exceeded
+ private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f;
+
private final Handler mHandler = new Handler();
private final View mSampledView;
@@ -52,6 +59,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
*/
private final Rect mRegisteredSamplingBounds = new Rect();
private final SamplingCallback mCallback;
+ private final Executor mBackgroundExecutor;
private boolean mSamplingEnabled = false;
private boolean mSamplingListenerRegistered = false;
@@ -60,9 +68,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
private boolean mWaitingOnDraw;
private boolean mIsDestroyed;
- // Passing the threshold of this luminance value will make the button black otherwise white
- private final float mLuminanceThreshold;
- private final float mLuminanceChangeThreshold;
private boolean mFirstSamplingAfterStart;
private boolean mWindowVisible;
private boolean mWindowHasBlurs;
@@ -82,7 +87,9 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
}
};
- public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback) {
+ public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+ Executor backgroundExecutor) {
+ mBackgroundExecutor = backgroundExecutor;
mSamplingListener = new CompositionSamplingListener(
sampledView.getContext().getMainExecutor()) {
@Override
@@ -96,9 +103,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
mSampledView.addOnAttachStateChangeListener(this);
mSampledView.addOnLayoutChangeListener(this);
- final Resources res = sampledView.getResources();
- mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold);
- mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold);
mCallback = samplingCallback;
}
@@ -183,10 +187,13 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
// We only want to reregister if something actually changed
unregisterSamplingListener();
mSamplingListenerRegistered = true;
- CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
- stopLayerControl, mSamplingRequestBounds);
+ SurfaceControl registeredStopLayer = stopLayerControl;
+ mBackgroundExecutor.execute(() -> {
+ CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
+ registeredStopLayer, mSamplingRequestBounds);
+ });
mRegisteredSamplingBounds.set(mSamplingRequestBounds);
- mRegisteredStopLayer = stopLayerControl;
+ mRegisteredStopLayer = registeredStopLayer;
}
mFirstSamplingAfterStart = false;
} else {
@@ -199,7 +206,9 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
mSamplingListenerRegistered = false;
mRegisteredStopLayer = null;
mRegisteredSamplingBounds.setEmpty();
- CompositionSamplingListener.unregister(mSamplingListener);
+ mBackgroundExecutor.execute(() -> {
+ CompositionSamplingListener.unregister(mSamplingListener);
+ });
}
}
@@ -208,8 +217,10 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
// If the difference between the new luma and the current luma is larger than threshold
// then apply the current luma, this is to prevent small changes causing colors to flicker
- if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) {
- mCallback.onRegionDarknessChanged(medianLuma < mLuminanceThreshold /* isRegionDark */);
+ if (Math.abs(mCurrentMedianLuma - mLastMedianLuma)
+ > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) {
+ mCallback.onRegionDarknessChanged(
+ medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */);
mLastMedianLuma = medianLuma;
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
index 42bc1d0ea0ff..895b6cd96d6f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -15,31 +15,20 @@
package com.android.systemui.shared.plugins;
import android.content.Context;
-import android.os.Looper;
/**
* Provides necessary components for initializing {@link PluginManagerImpl}.
*/
public interface PluginInitializer {
- Looper getBgLooper();
-
/**
- * Called from the bg looper during initialization of {@link PluginManagerImpl}.
+ * Return a list of plugins that don't get disabled when an exception occurs.
*/
- void onPluginManagerInit();
-
- String[] getWhitelistedPlugins(Context context);
+ String[] getPrivilegedPlugins(Context context);
- PluginEnabler getPluginEnabler(Context context);
/**
- * Called from {@link PluginManagerImpl#handleWtfs()}.
+ * Called from {@link PluginInstanceManager}.
*/
void handleWtfs();
-
- /**
- * Returns if pluging manager should run in debug mode.
- */
- boolean isDebuggable();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 2b35bcd9a3ea..dcd3b3eb5dd4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -14,6 +14,7 @@
package com.android.systemui.shared.plugins;
+import android.app.LoadedApk;
import android.app.Notification;
import android.app.Notification.Action;
import android.app.NotificationManager;
@@ -28,9 +29,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -42,9 +42,13 @@ import com.android.systemui.plugins.PluginFragment;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
public class PluginInstanceManager<T extends Plugin> {
@@ -58,81 +62,70 @@ public class PluginInstanceManager<T extends Plugin> {
private final String mAction;
private final boolean mAllowMultiple;
private final VersionInfo mVersion;
+ private final NotificationManager mNotificationManager;
+ private final PluginEnabler mPluginEnabler;
+ private final InstanceFactory<T> mInstanceFactory;
+ private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
+ private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
@VisibleForTesting
- final MainHandler mMainHandler;
- @VisibleForTesting
- final PluginHandler mPluginHandler;
- private final boolean isDebuggable;
+ private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
+ private final boolean mIsDebuggable;
private final PackageManager mPm;
- private final PluginManagerImpl mManager;
- private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
-
- PluginInstanceManager(Context context, String action, PluginListener<T> listener,
- boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
- this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
- manager, manager.isDebuggable(), manager.getWhitelistedPlugins());
- }
-
- @VisibleForTesting
- PluginInstanceManager(Context context, PackageManager pm, String action,
- PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
- PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
- mMainHandler = new MainHandler(Looper.getMainLooper());
- mPluginHandler = new PluginHandler(looper);
- mManager = manager;
+ private final PluginInitializer mInitializer;
+ private final Executor mMainExecutor;
+ private final Executor mBgExecutor;
+
+ private PluginManagerImpl.ClassLoaderFilter mParentClassLoader;
+
+ private PluginInstanceManager(Context context, PackageManager pm, String action,
+ PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor,
+ Executor bgExecutor, VersionInfo version, boolean debuggable,
+ PluginInitializer initializer, NotificationManager notificationManager,
+ PluginEnabler pluginEnabler, List<String> privilegedPlugins,
+ InstanceFactory<T> instanceFactory) {
+ mInitializer = initializer;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
mContext = context;
mPm = pm;
mAction = action;
mListener = listener;
mAllowMultiple = allowMultiple;
mVersion = version;
- mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist));
- isDebuggable = debuggable;
- }
-
- public PluginInfo<T> getPlugin() {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new RuntimeException("Must be called from UI thread");
- }
- mPluginHandler.handleQueryPlugins(null /* All packages */);
- if (mPluginHandler.mPlugins.size() > 0) {
- mMainHandler.removeMessages(MainHandler.PLUGIN_CONNECTED);
- PluginInfo<T> info = mPluginHandler.mPlugins.get(0);
- PluginPrefs.setHasPlugins(mContext);
- info.mPlugin.onCreate(mContext, info.mPluginContext);
- return info;
- }
- return null;
+ mNotificationManager = notificationManager;
+ mPluginEnabler = pluginEnabler;
+ mInstanceFactory = instanceFactory;
+ mPrivilegedPlugins.addAll(privilegedPlugins);
+ mIsDebuggable = debuggable;
}
public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
- mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
+ mBgExecutor.execute(this::queryAll);
}
public void destroy() {
if (DEBUG) Log.d(TAG, "stopListening");
- ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
- for (PluginInfo plugin : plugins) {
- mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
- plugin.mPlugin).sendToTarget();
+ ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+ for (PluginInfo<T> pluginInfo : plugins) {
+ mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
}
}
public void onPackageRemoved(String pkg) {
- mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+ mBgExecutor.execute(() -> removePkg(pkg));
}
public void onPackageChange(String pkg) {
- mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
- mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
+ mBgExecutor.execute(() -> removePkg(pkg));
+ mBgExecutor.execute(() -> queryPkg(pkg));
}
public boolean checkAndDisable(String className) {
boolean disableAny = false;
- ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
- for (PluginInfo info : plugins) {
+ ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+ for (PluginInfo<T> info : plugins) {
if (className.startsWith(info.mPackage)) {
disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
}
@@ -141,7 +134,7 @@ public class PluginInstanceManager<T extends Plugin> {
}
public boolean disableAll() {
- ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
boolean disabledAny = false;
for (int i = 0; i < plugins.size(); i++) {
disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
@@ -149,8 +142,22 @@ public class PluginInstanceManager<T extends Plugin> {
return disabledAny;
}
- private boolean isPluginWhitelisted(ComponentName pluginName) {
- for (String componentNameOrPackage : mWhitelistedPlugins) {
+ private boolean isPluginPackagePrivileged(String packageName) {
+ for (String componentNameOrPackage : mPrivilegedPlugins) {
+ ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
+ if (componentName != null) {
+ if (componentName.getPackageName().equals(packageName)) {
+ return true;
+ }
+ } else if (componentNameOrPackage.equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isPluginPrivileged(ComponentName pluginName) {
+ for (String componentNameOrPackage : mPrivilegedPlugins) {
ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
if (componentName == null) {
if (componentNameOrPackage.equals(pluginName.getPackageName())) {
@@ -165,7 +172,7 @@ public class PluginInstanceManager<T extends Plugin> {
return false;
}
- private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
+ private boolean disable(PluginInfo<T> info, @PluginEnabler.DisableReason int reason) {
// Live by the sword, die by the sword.
// Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
@@ -173,19 +180,19 @@ public class PluginInstanceManager<T extends Plugin> {
// If a plugin is detected in the stack of a crash then this will be called for that
// plugin, if the plugin causing a crash cannot be identified, they are all disabled
// assuming one of them must be bad.
- if (isPluginWhitelisted(pluginComponent)) {
+ if (isPluginPrivileged(pluginComponent)) {
// Don't disable whitelisted plugins as they are a part of the OS.
return false;
}
Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
- mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
+ mPluginEnabler.setDisabled(pluginComponent, reason);
return true;
}
- public <T> boolean dependsOn(Plugin p, Class<T> cls) {
- ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
- for (PluginInfo info : plugins) {
+ <C> boolean dependsOn(Plugin p, Class<C> cls) {
+ ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+ for (PluginInfo<T> info : plugins) {
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
return info.mVersion != null && info.mVersion.hasClass(cls);
}
@@ -199,221 +206,259 @@ public class PluginInstanceManager<T extends Plugin> {
getClass().getSimpleName(), hashCode(), mAction);
}
- private class MainHandler extends Handler {
- private static final int PLUGIN_CONNECTED = 1;
- private static final int PLUGIN_DISCONNECTED = 2;
-
- public MainHandler(Looper looper) {
- super(looper);
+ private void onPluginConnected(PluginInfo<T> pluginInfo) {
+ if (DEBUG) Log.d(TAG, "onPluginConnected");
+ PluginPrefs.setHasPlugins(mContext);
+ mInitializer.handleWtfs();
+ if (!(pluginInfo.mPlugin instanceof PluginFragment)) {
+ // Only call onCreate for plugins that aren't fragments, as fragments
+ // will get the onCreate as part of the fragment lifecycle.
+ pluginInfo.mPlugin.onCreate(mContext, pluginInfo.mPluginContext);
}
+ mListener.onPluginConnected(pluginInfo.mPlugin, pluginInfo.mPluginContext);
+ }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case PLUGIN_CONNECTED:
- if (DEBUG) Log.d(TAG, "onPluginConnected");
- PluginPrefs.setHasPlugins(mContext);
- PluginInfo<T> info = (PluginInfo<T>) msg.obj;
- mManager.handleWtfs();
- if (!(msg.obj instanceof PluginFragment)) {
- // Only call onDestroy for plugins that aren't fragments, as fragments
- // will get the onCreate as part of the fragment lifecycle.
- info.mPlugin.onCreate(mContext, info.mPluginContext);
- }
- mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
- break;
- case PLUGIN_DISCONNECTED:
- if (DEBUG) Log.d(TAG, "onPluginDisconnected");
- mListener.onPluginDisconnected((T) msg.obj);
- if (!(msg.obj instanceof PluginFragment)) {
- // Only call onDestroy for plugins that aren't fragments, as fragments
- // will get the onDestroy as part of the fragment lifecycle.
- ((T) msg.obj).onDestroy();
- }
- break;
- default:
- super.handleMessage(msg);
- break;
- }
+ private void onPluginDisconnected(T plugin) {
+ if (DEBUG) Log.d(TAG, "onPluginDisconnected");
+ mListener.onPluginDisconnected(plugin);
+ if (!(plugin instanceof PluginFragment)) {
+ // Only call onDestroy for plugins that aren't fragments, as fragments
+ // will get the onDestroy as part of the fragment lifecycle.
+ plugin.onDestroy();
}
}
- private class PluginHandler extends Handler {
- private static final int QUERY_ALL = 1;
- private static final int QUERY_PKG = 2;
- private static final int REMOVE_PKG = 3;
-
- private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
-
- public PluginHandler(Looper looper) {
- super(looper);
+ private void queryAll() {
+ if (DEBUG) Log.d(TAG, "queryAll " + mAction);
+ for (int i = mPlugins.size() - 1; i >= 0; i--) {
+ PluginInfo<T> pluginInfo = mPlugins.get(i);
+ mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
}
+ mPlugins.clear();
+ handleQueryPlugins(null);
+ }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case QUERY_ALL:
- if (DEBUG) Log.d(TAG, "queryAll " + mAction);
- for (int i = mPlugins.size() - 1; i >= 0; i--) {
- PluginInfo<T> pluginInfo = mPlugins.get(i);
- mMainHandler.obtainMessage(
- MainHandler.PLUGIN_DISCONNECTED, pluginInfo.mPlugin).sendToTarget();
- }
- mPlugins.clear();
- handleQueryPlugins(null);
- break;
- case REMOVE_PKG:
- String pkg = (String) msg.obj;
- for (int i = mPlugins.size() - 1; i >= 0; i--) {
- final PluginInfo<T> plugin = mPlugins.get(i);
- if (plugin.mPackage.equals(pkg)) {
- mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
- plugin.mPlugin).sendToTarget();
- mPlugins.remove(i);
- }
- }
- break;
- case QUERY_PKG:
- String p = (String) msg.obj;
- if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction);
- if (mAllowMultiple || (mPlugins.size() == 0)) {
- handleQueryPlugins(p);
- } else {
- if (DEBUG) Log.d(TAG, "Too many of " + mAction);
- }
- break;
- default:
- super.handleMessage(msg);
+ private void removePkg(String pkg) {
+ for (int i = mPlugins.size() - 1; i >= 0; i--) {
+ final PluginInfo<T> pluginInfo = mPlugins.get(i);
+ if (pluginInfo.mPackage.equals(pkg)) {
+ mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
+ mPlugins.remove(i);
}
}
+ }
- private void handleQueryPlugins(String pkgName) {
- // This isn't actually a service and shouldn't ever be started, but is
- // a convenient PM based way to manage our plugins.
- Intent intent = new Intent(mAction);
- if (pkgName != null) {
- intent.setPackage(pkgName);
- }
- List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
- if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
- if (result.size() > 1 && !mAllowMultiple) {
- // TODO: Show warning.
- Log.w(TAG, "Multiple plugins found for " + mAction);
- if (DEBUG) {
- for (ResolveInfo info : result) {
- ComponentName name = new ComponentName(info.serviceInfo.packageName,
- info.serviceInfo.name);
- Log.w(TAG, " " + name);
- }
+ private void queryPkg(String pkg) {
+ if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction);
+ if (mAllowMultiple || (mPlugins.size() == 0)) {
+ handleQueryPlugins(pkg);
+ } else {
+ if (DEBUG) Log.d(TAG, "Too many of " + mAction);
+ }
+ }
+
+ private void handleQueryPlugins(String pkgName) {
+ // This isn't actually a service and shouldn't ever be started, but is
+ // a convenient PM based way to manage our plugins.
+ Intent intent = new Intent(mAction);
+ if (pkgName != null) {
+ intent.setPackage(pkgName);
+ }
+ List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
+ if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
+ if (result.size() > 1 && !mAllowMultiple) {
+ // TODO: Show warning.
+ Log.w(TAG, "Multiple plugins found for " + mAction);
+ if (DEBUG) {
+ for (ResolveInfo info : result) {
+ ComponentName name = new ComponentName(info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ Log.w(TAG, " " + name);
}
- return;
- }
- for (ResolveInfo info : result) {
- ComponentName name = new ComponentName(info.serviceInfo.packageName,
- info.serviceInfo.name);
- PluginInfo<T> t = handleLoadPlugin(name);
- if (t == null) continue;
-
- // add plugin before sending PLUGIN_CONNECTED message
- mPlugins.add(t);
- mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
}
+ return;
+ }
+ for (ResolveInfo info : result) {
+ ComponentName name = new ComponentName(info.serviceInfo.packageName,
+ info.serviceInfo.name);
+ PluginInfo<T> pluginInfo = handleLoadPlugin(name);
+ if (pluginInfo == null) continue;
+
+ // add plugin before sending PLUGIN_CONNECTED message
+ mPlugins.add(pluginInfo);
+ mMainExecutor.execute(() -> onPluginConnected(pluginInfo));
}
+ }
- protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
- // This was already checked, but do it again here to make extra extra sure, we don't
- // use these on production builds.
- if (!isDebuggable && !isPluginWhitelisted(component)) {
- // Never ever ever allow these on production builds, they are only for prototyping.
- Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
- return null;
- }
- if (!mManager.getPluginEnabler().isEnabled(component)) {
- if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
+ protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
+ // This was already checked, but do it again here to make extra extra sure, we don't
+ // use these on production builds.
+ if (!mIsDebuggable && !isPluginPrivileged(component)) {
+ // Never ever ever allow these on production builds, they are only for prototyping.
+ Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
+ return null;
+ }
+ if (!mPluginEnabler.isEnabled(component)) {
+ if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
+ return null;
+ }
+ String pkg = component.getPackageName();
+ String cls = component.getClassName();
+ try {
+ ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
+ // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
+ if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.d(TAG, "Plugin doesn't have permission: " + pkg);
return null;
}
- String pkg = component.getPackageName();
- String cls = component.getClassName();
+ // Create our own ClassLoader so we can use our own code as the parent.
+ ClassLoader classLoader = getClassLoader(info);
+ Context pluginContext = new PluginContextWrapper(
+ mContext.createApplicationContext(info, 0), classLoader);
+ Class<?> pluginClass = Class.forName(cls, true, classLoader);
+ // TODO: Only create the plugin before version check if we need it for
+ // legacy version check.
+ T plugin = mInstanceFactory.create(pluginClass);
try {
- ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
- // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
- if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
- != PackageManager.PERMISSION_GRANTED) {
- Log.d(TAG, "Plugin doesn't have permission: " + pkg);
- return null;
- }
- // Create our own ClassLoader so we can use our own code as the parent.
- ClassLoader classLoader = mManager.getClassLoader(info);
- Context pluginContext = new PluginContextWrapper(
- mContext.createApplicationContext(info, 0), classLoader);
- Class<?> pluginClass = Class.forName(cls, true, classLoader);
- // TODO: Only create the plugin before version check if we need it for
- // legacy version check.
- T plugin = (T) pluginClass.newInstance();
+ VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
+ if (DEBUG) Log.d(TAG, "createPlugin");
+ return new PluginInfo<>(pkg, cls, plugin, pluginContext, version);
+ } catch (InvalidVersionException e) {
+ final int icon = Resources.getSystem().getIdentifier(
+ "stat_sys_warning", "drawable", "android");
+ final int color = Resources.getSystem().getIdentifier(
+ "system_notification_accent_color", "color", "android");
+ final Notification.Builder nb = new Notification.Builder(mContext,
+ PluginManager.NOTIFICATION_CHANNEL_ID)
+ .setStyle(new Notification.BigTextStyle())
+ .setSmallIcon(icon)
+ .setWhen(0)
+ .setShowWhen(false)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setColor(mContext.getColor(color));
+ String label = cls;
try {
- VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
- if (DEBUG) Log.d(TAG, "createPlugin");
- return new PluginInfo(pkg, cls, plugin, pluginContext, version);
- } catch (InvalidVersionException e) {
- final int icon = Resources.getSystem().getIdentifier(
- "stat_sys_warning", "drawable", "android");
- final int color = Resources.getSystem().getIdentifier(
- "system_notification_accent_color", "color", "android");
- final Notification.Builder nb = new Notification.Builder(mContext,
- PluginManager.NOTIFICATION_CHANNEL_ID)
- .setStyle(new Notification.BigTextStyle())
- .setSmallIcon(icon)
- .setWhen(0)
- .setShowWhen(false)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(mContext.getColor(color));
- String label = cls;
- try {
- label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
- } catch (NameNotFoundException e2) {
- }
- if (!e.isTooNew()) {
- // Localization not required as this will never ever appear in a user build.
- nb.setContentTitle("Plugin \"" + label + "\" is too old")
- .setContentText("Contact plugin developer to get an updated"
- + " version.\n" + e.getMessage());
- } else {
- // Localization not required as this will never ever appear in a user build.
- nb.setContentTitle("Plugin \"" + label + "\" is too new")
- .setContentText("Check to see if an OTA is available.\n"
- + e.getMessage());
- }
- Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
- Uri.parse("package://" + component.flattenToString()));
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i,
- PendingIntent.FLAG_IMMUTABLE);
- nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
- mContext.getSystemService(NotificationManager.class)
- .notify(SystemMessage.NOTE_PLUGIN, nb.build());
- // TODO: Warn user.
- Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
- + ", expected " + mVersion);
- return null;
+ label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
+ } catch (NameNotFoundException e2) {
}
- } catch (Throwable e) {
- Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+ if (!e.isTooNew()) {
+ // Localization not required as this will never ever appear in a user build.
+ nb.setContentTitle("Plugin \"" + label + "\" is too old")
+ .setContentText("Contact plugin developer to get an updated"
+ + " version.\n" + e.getMessage());
+ } else {
+ // Localization not required as this will never ever appear in a user build.
+ nb.setContentTitle("Plugin \"" + label + "\" is too new")
+ .setContentText("Check to see if an OTA is available.\n"
+ + e.getMessage());
+ }
+ Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
+ Uri.parse("package://" + component.flattenToString()));
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i,
+ PendingIntent.FLAG_IMMUTABLE);
+ nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
+ mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build());
+ // TODO: Warn user.
+ Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
+ + ", expected " + mVersion);
return null;
}
+ } catch (Throwable e) {
+ Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+ return null;
}
+ }
- private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version)
- throws InvalidVersionException {
- VersionInfo pv = new VersionInfo().addClass(pluginClass);
- if (pv.hasVersionInfo()) {
- version.checkVersion(pv);
- } else {
- int fallbackVersion = plugin.getVersion();
- if (fallbackVersion != version.getDefaultVersion()) {
- throw new InvalidVersionException("Invalid legacy version", false);
- }
- return null;
+ private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version)
+ throws InvalidVersionException {
+ VersionInfo pv = new VersionInfo().addClass(pluginClass);
+ if (pv.hasVersionInfo()) {
+ version.checkVersion(pv);
+ } else {
+ int fallbackVersion = plugin.getVersion();
+ if (fallbackVersion != version.getDefaultVersion()) {
+ throw new InvalidVersionException("Invalid legacy version", false);
}
- return pv;
+ return null;
+ }
+ return pv;
+ }
+
+ /** Returns class loader specific for the given plugin. */
+ public ClassLoader getClassLoader(ApplicationInfo appInfo) {
+ if (!mIsDebuggable && !isPluginPackagePrivileged(appInfo.packageName)) {
+ Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:"
+ + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
+ return null;
+ }
+ if (mClassLoaders.containsKey(appInfo.packageName)) {
+ return mClassLoaders.get(appInfo.packageName);
+ }
+
+ List<String> zipPaths = new ArrayList<>();
+ List<String> libPaths = new ArrayList<>();
+ LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
+ ClassLoader classLoader = new PathClassLoader(
+ TextUtils.join(File.pathSeparator, zipPaths),
+ TextUtils.join(File.pathSeparator, libPaths),
+ getParentClassLoader());
+ mClassLoaders.put(appInfo.packageName, classLoader);
+ return classLoader;
+ }
+
+ private ClassLoader getParentClassLoader() {
+ if (mParentClassLoader == null) {
+ // Lazily load this so it doesn't have any effect on devices without plugins.
+ mParentClassLoader = new PluginManagerImpl.ClassLoaderFilter(
+ getClass().getClassLoader(), "com.android.systemui.plugin");
+ }
+ return mParentClassLoader;
+ }
+
+ /**
+ * Construct a {@link PluginInstanceManager}
+ */
+ public static class Factory {
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final Executor mMainExecutor;
+ private final Executor mBgExecutor;
+ private final PluginInitializer mInitializer;
+ private final NotificationManager mNotificationManager;
+ private final PluginEnabler mPluginEnabler;
+ private final List<String> mPrivilegedPlugins;
+ private InstanceFactory<?> mInstanceFactory;
+
+ public Factory(Context context, PackageManager packageManager,
+ Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer,
+ NotificationManager notificationManager, PluginEnabler pluginEnabler,
+ List<String> privilegedPlugins) {
+ mContext = context;
+ mPackageManager = packageManager;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
+ mInitializer = initializer;
+ mNotificationManager = notificationManager;
+ mPluginEnabler = pluginEnabler;
+ mPrivilegedPlugins = privilegedPlugins;
+
+ mInstanceFactory = new InstanceFactory<>();
+ }
+
+ @VisibleForTesting
+ <T extends Plugin> Factory setInstanceFactory(InstanceFactory<T> instanceFactory) {
+ mInstanceFactory = instanceFactory;
+ return this;
+ }
+
+ <T extends Plugin> PluginInstanceManager<T> create(
+ String action, PluginListener<T> listener, boolean allowMultiple,
+ VersionInfo version, boolean debuggable) {
+ return new PluginInstanceManager<T>(mContext, mPackageManager, action, listener,
+ allowMultiple, mMainExecutor, mBgExecutor, version, debuggable,
+ mInitializer, mNotificationManager, mPluginEnabler,
+ mPrivilegedPlugins, (InstanceFactory<T>) mInstanceFactory);
}
}
@@ -443,10 +488,10 @@ public class PluginInstanceManager<T extends Plugin> {
}
}
- static class PluginInfo<T> {
+ static class PluginInfo<T extends Plugin> {
private final Context mPluginContext;
private final VersionInfo mVersion;
- private String mClass;
+ private final String mClass;
T mPlugin;
String mPackage;
@@ -459,4 +504,10 @@ public class PluginInstanceManager<T extends Plugin> {
mVersion = info;
}
}
+
+ static class InstanceFactory<T extends Plugin> {
+ T create(Class cls) throws IllegalAccessException, InstantiationException {
+ return (T) cls.newInstance();
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 3f907a8aa348..d264bf2fae52 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -27,10 +27,8 @@ public interface PluginManager {
// must be one of the channels created in NotificationChannels.java
String NOTIFICATION_CHANNEL_ID = "ALR";
- String[] getWhitelistedPlugins();
-
- <T extends Plugin> T getOneShotPlugin(Class<T> cls);
- <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
+ /** Returns plugins that don't get disabled when an exceptoin occurs. */
+ String[] getPrivilegedPlugins();
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
<T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
@@ -38,7 +36,7 @@ public interface PluginManager {
<T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class<?> cls);
<T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
- Class cls, boolean allowMultiple);
+ Class<?> cls, boolean allowMultiple);
void removePluginListener(PluginListener<?> listener);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 2b4cdd6cf575..ea7b0c34136a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -14,48 +14,31 @@
package com.android.systemui.shared.plugins;
-import android.app.LoadedApk;
-import android.app.Notification;
-import android.app.Notification.Action;
import android.app.NotificationManager;
-import android.app.PendingIntent;
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.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.os.SystemProperties;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.widget.Toast;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import dalvik.system.PathClassLoader;
-
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+
/**
* @see Plugin
*/
@@ -64,92 +47,42 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
private static final String TAG = PluginManagerImpl.class.getSimpleName();
static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
- private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+ private final ArrayMap<PluginListener<?>, PluginInstanceManager<?>> mPluginMap
= new ArrayMap<>();
private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
- private final ArraySet<String> mOneShotPackages = new ArraySet<>();
- private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
+ private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
private final Context mContext;
- private final PluginInstanceManagerFactory mFactory;
+ private final PluginInstanceManager.Factory mInstanceManagerFactory;
private final boolean mIsDebuggable;
private final PluginPrefs mPluginPrefs;
private final PluginEnabler mPluginEnabler;
- private final PluginInitializer mPluginInitializer;
- private ClassLoaderFilter mParentClassLoader;
private boolean mListening;
- private boolean mHasOneShot;
- private Looper mLooper;
-
- public PluginManagerImpl(Context context, PluginInitializer initializer) {
- this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(),
- Thread.getUncaughtExceptionPreHandler(), initializer);
- }
- @VisibleForTesting
- PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
- UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) {
+ public PluginManagerImpl(Context context,
+ PluginInstanceManager.Factory instanceManagerFactory,
+ boolean debuggable,
+ Optional<UncaughtExceptionHandler> defaultHandlerOptional,
+ PluginEnabler pluginEnabler,
+ PluginPrefs pluginPrefs,
+ List<String> privilegedPlugins) {
mContext = context;
- mFactory = factory;
- mLooper = initializer.getBgLooper();
+ mInstanceManagerFactory = instanceManagerFactory;
mIsDebuggable = debuggable;
- mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
- mPluginPrefs = new PluginPrefs(mContext);
- mPluginEnabler = initializer.getPluginEnabler(mContext);
- mPluginInitializer = initializer;
+ mPrivilegedPlugins.addAll(privilegedPlugins);
+ mPluginPrefs = pluginPrefs;
+ mPluginEnabler = pluginEnabler;
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
- defaultHandler);
+ defaultHandlerOptional);
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
-
- new Handler(mLooper).post(new Runnable() {
- @Override
- public void run() {
- initializer.onPluginManagerInit();
- }
- });
}
public boolean isDebuggable() {
return mIsDebuggable;
}
- public String[] getWhitelistedPlugins() {
- return mWhitelistedPlugins.toArray(new String[0]);
- }
-
- public PluginEnabler getPluginEnabler() {
- return mPluginEnabler;
- }
-
- // TODO(mankoff): This appears to be only called from tests. Remove?
- public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
- ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
- if (info == null) {
- throw new RuntimeException(cls + " doesn't provide an interface");
- }
- if (TextUtils.isEmpty(info.action())) {
- throw new RuntimeException(cls + " doesn't provide an action");
- }
- return getOneShotPlugin(info.action(), cls);
- }
-
- public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
- if (Looper.myLooper() != Looper.getMainLooper()) {
- throw new RuntimeException("Must be called from UI thread");
- }
- // Passing null causes compiler to complain about incompatible (generic) types.
- PluginListener<Plugin> dummy = null;
- PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
- false, mLooper, cls, this);
- mPluginPrefs.addAction(action);
- PluginInfo<T> info = p.getPlugin();
- if (info != null) {
- mOneShotPackages.add(info.mPackage);
- mHasOneShot = true;
- startListening();
- return info.mPlugin;
- }
- return null;
+ public String[] getPrivilegedPlugins() {
+ return mPrivilegedPlugins.toArray(new String[0]);
}
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
@@ -167,10 +100,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
- Class cls, boolean allowMultiple) {
+ Class<?> cls, boolean allowMultiple) {
mPluginPrefs.addAction(action);
- PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
- allowMultiple, mLooper, cls, this);
+ PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple,
+ new VersionInfo().addClass(cls), isDebuggable());
p.loadAll();
synchronized (this) {
mPluginMap.put(listener, p);
@@ -208,8 +141,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
private void stopListening() {
- // Never stop listening if a one-shot is present.
- if (!mListening || mHasOneShot) return;
+ if (!mListening) return;
mListening = false;
mContext.unregisterReceiver(this);
}
@@ -218,7 +150,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
synchronized (this) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
+ for (PluginInstanceManager<?> manager : mPluginMap.values()) {
manager.loadAll();
}
}
@@ -226,46 +158,17 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
Uri uri = intent.getData();
ComponentName component = ComponentName.unflattenFromString(
uri.toString().substring(10));
- if (isPluginWhitelisted(component)) {
- // Don't disable whitelisted plugins as they are a part of the OS.
+ if (isPluginPrivileged(component)) {
+ // Don't disable privileged plugins as they are a part of the OS.
return;
}
- getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
+ mPluginEnabler.setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
SystemMessage.NOTE_PLUGIN);
} else {
Uri data = intent.getData();
String pkg = data.getEncodedSchemeSpecificPart();
ComponentName componentName = ComponentName.unflattenFromString(pkg);
- if (mOneShotPackages.contains(pkg)) {
- int icon = Resources.getSystem().getIdentifier(
- "stat_sys_warning", "drawable", "android");
- int color = Resources.getSystem().getIdentifier(
- "system_notification_accent_color", "color", "android");
- String label = pkg;
- try {
- PackageManager pm = mContext.getPackageManager();
- label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
- } catch (NameNotFoundException e) {
- }
- // Localization not required as this will never ever appear in a user build.
- final Notification.Builder nb =
- new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(icon)
- .setWhen(0)
- .setShowWhen(false)
- .setPriority(Notification.PRIORITY_MAX)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(mContext.getColor(color))
- .setContentTitle("Plugin \"" + label + "\" has updated")
- .setContentText("Restart SysUI for changes to take effect.");
- Intent i = new Intent("com.android.systemui.action.RESTART").setData(
- Uri.parse("package://" + pkg));
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_MUTABLE_UNAUDITED);
- nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
- mContext.getSystemService(NotificationManager.class)
- .notify(SystemMessage.NOTE_PLUGIN, nb.build());
- }
if (clearClassLoader(pkg)) {
if (Build.IS_ENG) {
Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
@@ -276,22 +179,22 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())
&& componentName != null) {
@PluginEnabler.DisableReason int disableReason =
- getPluginEnabler().getDisableReason(componentName);
+ mPluginEnabler.getDisableReason(componentName);
if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
|| disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
|| disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
Log.i(TAG, "Re-enabling previously disabled plugin that has been "
+ "updated: " + componentName.flattenToShortString());
- getPluginEnabler().setEnabled(componentName);
+ mPluginEnabler.setEnabled(componentName);
}
}
synchronized (this) {
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
+ for (PluginInstanceManager<?> manager : mPluginMap.values()) {
manager.onPackageChange(pkg);
}
} else {
- for (PluginInstanceManager manager : mPluginMap.values()) {
+ for (PluginInstanceManager<?> manager : mPluginMap.values()) {
manager.onPackageRemoved(pkg);
}
}
@@ -299,41 +202,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
}
- /** Returns class loader specific for the given plugin. */
- public ClassLoader getClassLoader(ApplicationInfo appInfo) {
- if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) {
- Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
- + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
- return null;
- }
- if (mClassLoaders.containsKey(appInfo.packageName)) {
- return mClassLoaders.get(appInfo.packageName);
- }
-
- List<String> zipPaths = new ArrayList<>();
- List<String> libPaths = new ArrayList<>();
- LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
- ClassLoader classLoader = new PathClassLoader(
- TextUtils.join(File.pathSeparator, zipPaths),
- TextUtils.join(File.pathSeparator, libPaths),
- getParentClassLoader());
- mClassLoaders.put(appInfo.packageName, classLoader);
- return classLoader;
- }
-
private boolean clearClassLoader(String pkg) {
return mClassLoaders.remove(pkg) != null;
}
- ClassLoader getParentClassLoader() {
- if (mParentClassLoader == null) {
- // Lazily load this so it doesn't have any effect on devices without plugins.
- mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
- "com.android.systemui.plugin");
- }
- return mParentClassLoader;
- }
-
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
synchronized (this) {
for (int i = 0; i < mPluginMap.size(); i++) {
@@ -345,46 +217,18 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
return false;
}
- public void handleWtfs() {
- mPluginInitializer.handleWtfs();
- }
-
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (this) {
pw.println(String.format(" plugin map (%d):", mPluginMap.size()));
- for (PluginListener listener : mPluginMap.keySet()) {
+ for (PluginListener<?> listener : mPluginMap.keySet()) {
pw.println(String.format(" %s -> %s",
listener, mPluginMap.get(listener)));
}
}
}
- @VisibleForTesting
- public static class PluginInstanceManagerFactory {
- public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
- String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
- Class<?> cls, PluginManagerImpl manager) {
- return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
- new VersionInfo().addClass(cls), manager);
- }
- }
-
- private boolean isPluginPackageWhitelisted(String packageName) {
- for (String componentNameOrPackage : mWhitelistedPlugins) {
- ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
- if (componentName != null) {
- if (componentName.getPackageName().equals(packageName)) {
- return true;
- }
- } else if (componentNameOrPackage.equals(packageName)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isPluginWhitelisted(ComponentName pluginName) {
- for (String componentNameOrPackage : mWhitelistedPlugins) {
+ private boolean isPluginPrivileged(ComponentName pluginName) {
+ for (String componentNameOrPackage : mPrivilegedPlugins) {
ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
if (componentName != null) {
if (componentName.equals(pluginName)) {
@@ -399,7 +243,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
// This allows plugins to include any libraries or copied code they want by only including
// classes from the plugin library.
- private static class ClassLoaderFilter extends ClassLoader {
+ static class ClassLoaderFilter extends ClassLoader {
private final String mPackage;
private final ClassLoader mBase;
@@ -417,16 +261,20 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
private class PluginExceptionHandler implements UncaughtExceptionHandler {
- private final UncaughtExceptionHandler mHandler;
+ private final Optional<UncaughtExceptionHandler> mExceptionHandlerOptional;
- private PluginExceptionHandler(UncaughtExceptionHandler handler) {
- mHandler = handler;
+ private PluginExceptionHandler(
+ Optional<UncaughtExceptionHandler> exceptionHandlerOptional) {
+ mExceptionHandlerOptional = exceptionHandlerOptional;
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (SystemProperties.getBoolean("plugin.debugging", false)) {
- mHandler.uncaughtException(thread, throwable);
+ Throwable finalThrowable = throwable;
+ mExceptionHandlerOptional.ifPresent(
+ handler -> handler.uncaughtException(thread, finalThrowable));
+
return;
}
// Search for and disable plugins that may have been involved in this crash.
@@ -436,7 +284,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
// disable all the plugins, so we can be sure that SysUI is running as
// best as possible.
synchronized (this) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
+ for (PluginInstanceManager<?> manager : mPluginMap.values()) {
disabledAny |= manager.disableAll();
}
}
@@ -446,7 +294,9 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
}
// Run the normal exception handler so we can crash and cleanup our state.
- mHandler.uncaughtException(thread, throwable);
+ Throwable finalThrowable = throwable;
+ mExceptionHandlerOptional.ifPresent(
+ handler -> handler.uncaughtException(thread, finalThrowable));
}
private boolean checkStack(Throwable throwable) {
@@ -454,7 +304,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
boolean disabledAny = false;
synchronized (this) {
for (StackTraceElement element : throwable.getStackTrace()) {
- for (PluginInstanceManager manager : mPluginMap.values()) {
+ for (PluginInstanceManager<?> manager : mPluginMap.values()) {
disabledAny |= manager.checkAndDisable(element.getClassName());
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 277b2e31f7be..8bd0f910dac3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -77,8 +77,22 @@ oneway interface IOverviewProxy {
void onSplitScreenSecondaryBoundsChanged(in Rect bounds, in Rect insets) = 17;
/**
- * Sent IME status changes
+ * Sent when suggested rotation button could be shown
*/
- void onImeWindowStatusChanged(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) = 18;
+ void onRotationProposal(int rotation, boolean isValid) = 18;
+
+ /**
+ * Sent when disable flags change
+ */
+ void disable(int displayId, int state1, int state2, boolean animate) = 19;
+
+ /**
+ * Sent when behavior changes. See WindowInsetsController#@Behavior
+ */
+ void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
+
+ /**
+ * Sent when screen turned on and ready to use (blocker scrim is hidden)
+ */
+ void onScreenTurnedOn() = 21;
}
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 f72245b9b252..11557ad87929 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
@@ -140,5 +140,8 @@ interface ISystemUiProxy {
/** Notifies that a swipe-up gesture has started */
oneway void notifySwipeUpGestureStarted() = 46;
- // Next id = 47
+ /** Notifies when taskbar status updated */
+ oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
+
+ // Next id = 48
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 7dffc2613956..e33985dc4288 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -16,13 +16,24 @@
package com.android.systemui.shared.recents.utilities;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
import android.os.Handler;
import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.Surface;
/* Common code */
public class Utilities {
+ private static final float TABLET_MIN_DPS = 600;
+
/**
* Posts a runnable on a handler at the front of the queue ignoring any sync barriers.
*/
@@ -31,6 +42,23 @@ public class Utilities {
h.sendMessageAtFrontOfQueue(msg);
}
+ public static boolean isRotationAnimationCCW(int from, int to) {
+ // All 180deg WM rotation animations are CCW, match that
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+ if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+ if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+ if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+ if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+ return false; // Default
+ }
+
/** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
public static float computeContrastBetweenColors(int bg, int fg) {
float bgR = Color.red(bg) / 255f;
@@ -58,4 +86,52 @@ public class Utilities {
public static float clamp(float value, float min, float max) {
return Math.max(min, Math.min(max, value));
}
+
+ /**
+ * @return updated set of flags from InputMethodService based off {@param oldHints}
+ * Leaves original hints unmodified
+ */
+ public static int calculateBackDispositionHints(int oldHints, int backDisposition,
+ boolean imeShown, boolean showImeSwitcher) {
+ int hints = oldHints;
+ switch (backDisposition) {
+ case InputMethodService.BACK_DISPOSITION_DEFAULT:
+ case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+ case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+ if (imeShown) {
+ hints |= NAVIGATION_HINT_BACK_ALT;
+ } else {
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ }
+ break;
+ case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
+ hints &= ~NAVIGATION_HINT_BACK_ALT;
+ break;
+ }
+ if (showImeSwitcher) {
+ hints |= NAVIGATION_HINT_IME_SHOWN;
+ } else {
+ hints &= ~NAVIGATION_HINT_IME_SHOWN;
+ }
+
+ return hints;
+ }
+
+ /** See {@link #isTablet(Configuration, Context)} */
+ public static boolean isTablet(Context context) {
+ Configuration newConfig = context.getResources().getConfiguration();
+ return isTablet(newConfig, context);
+ }
+
+ /**
+ * @return whether or not {@param newConfig} represents that of a large screen device or not
+ */
+ public static boolean isTablet(Configuration newConfig, Context context) {
+ float density = Resources.getSystem().getDisplayMetrics().density;
+ int size = Math.min((int) (density * newConfig.screenWidthDp),
+ (int) (density* newConfig.screenHeightDp));
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ return (size / densityRatio) >= TABLET_MIN_DPS;
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
new file mode 100644
index 000000000000..5581a1c90527
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/ViewRippler.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.android.systemui.shared.recents.utilities;
+
+import android.view.View;
+
+/**
+ * Shows view ripples by toggling the provided Views "pressed" state.
+ * Ripples 4 times.
+ */
+public class ViewRippler {
+ private static final int RIPPLE_OFFSET_MS = 50;
+ private static final int RIPPLE_INTERVAL_MS = 2000;
+ private View mRoot;
+
+ public void start(View root) {
+ stop(); // Stop any pending ripple animations
+
+ mRoot = root;
+
+ // Schedule pending ripples, offset the 1st to avoid problems with visibility change
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
+ mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
+ mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
+ }
+
+ public void stop() {
+ if (mRoot != null) mRoot.removeCallbacks(mRipple);
+ }
+
+ private final Runnable mRipple = new Runnable() {
+ @Override
+ public void run() { // Cause the ripple to fire via false presses
+ if (!mRoot.isAttachedToWindow()) return;
+ mRoot.setPressed(true /* pressed */);
+ mRoot.setPressed(false /* pressed */);
+ }
+ };
+} \ No newline at end of file
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 442716878af4..b82d896e6bea 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
@@ -80,8 +80,7 @@ public final class InteractionJankMonitorWrapper {
public static void begin(View v, @CujType int cujType, long timeout) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
Configuration.Builder builder =
- new Configuration.Builder(cujType)
- .setView(v)
+ Configuration.Builder.withView(cujType, v)
.setTimeout(timeout);
InteractionJankMonitor.getInstance().begin(builder);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c468e416f8a5..b827356e952c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -112,6 +112,12 @@ public class QuickStepContract {
public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+ // ImeSwitcher is showing
+ public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
+ // Device dozing/AOD state
+ public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
+ // The home feature is disabled (either by SUW/SysUI/device policy)
+ public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
@Retention(RetentionPolicy.SOURCE)
@IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -133,7 +139,10 @@ public class QuickStepContract {
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
SYSUI_STATE_IME_SHOWING,
- SYSUI_STATE_MAGNIFICATION_OVERLAP
+ SYSUI_STATE_MAGNIFICATION_OVERLAP,
+ SYSUI_STATE_IME_SWITCHER_SHOWING,
+ SYSUI_STATE_DEVICE_DOZING,
+ SYSUI_STATE_BACK_DISABLED
})
public @interface SystemUiStateFlags {}
@@ -162,6 +171,9 @@ public class QuickStepContract {
? "allow_gesture" : "");
str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
+ str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : "");
+ str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : "");
+ str.add((flags & SYSUI_STATE_BACK_DISABLED) != 0 ? "back_disabled" : "");
return str.toString();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index ee55bf0aa8b7..025d7ef48096 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -69,7 +69,8 @@ public class RemoteAnimationAdapterCompat {
return mRemoteTransition;
}
- private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
+ /** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */
+ public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
@@ -260,7 +261,7 @@ public class RemoteAnimationAdapterCompat {
t.remove(leashMap.valueAt(i));
}
t.apply();
- finishCallback.onTransitionFinished(null /* wct */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 2407d216b4ab..0a1465708edb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -68,11 +68,16 @@ public class RemoteAnimationTargetCompat {
public final boolean isNotInRecents;
public final Rect contentInsets;
public final ActivityManager.RunningTaskInfo taskInfo;
+ public final boolean allowEnterPip;
public final int rotationChange;
public final int windowType;
private final SurfaceControl mStartLeash;
+ // Fields used only to unrap into RemoteAnimationTarget
+ private final WindowConfiguration windowConfiguration;
+ private final Rect startBounds;
+
public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
taskId = app.taskId;
mode = app.mode;
@@ -88,10 +93,13 @@ public class RemoteAnimationTargetCompat {
contentInsets = app.contentInsets;
activityType = app.windowConfiguration.getActivityType();
taskInfo = app.taskInfo;
+ allowEnterPip = app.allowEnterPip;
rotationChange = 0;
mStartLeash = app.startLeash;
windowType = app.windowType;
+ windowConfiguration = app.windowConfiguration;
+ startBounds = app.startBounds;
}
private static int newModeToLegacyMode(int newMode) {
@@ -107,6 +115,14 @@ public class RemoteAnimationTargetCompat {
}
}
+ public RemoteAnimationTarget unwrap() {
+ return new RemoteAnimationTarget(
+ taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets,
+ prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration,
+ isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType
+ );
+ }
+
/**
* Almost a copy of Transitions#setupStartState.
@@ -214,9 +230,14 @@ public class RemoteAnimationTargetCompat {
activityType = ACTIVITY_TYPE_UNDEFINED;
}
taskInfo = change.getTaskInfo();
+ allowEnterPip = change.getAllowEnterPip();
mStartLeash = null;
rotationChange = change.getEndRotation() - change.getStartRotation();
windowType = INVALID_WINDOW_TYPE;
+
+ // TODO this probably isn't right but it's unused for now /shrug
+ windowConfiguration = new WindowConfiguration();
+ startBounds = change.getStartAbsBounds();
}
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 653d73020c4f..aac52357d008 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -17,14 +17,20 @@
package com.android.systemui.shared.system;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.Parcelable;
@@ -73,7 +79,7 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -87,7 +93,7 @@ public class RemoteTransitionCompat implements Parcelable {
IRemoteTransitionFinishedCallback finishedCallback) {
final Runnable finishAdapter = () -> {
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call transition finished callback", e);
}
@@ -119,13 +125,20 @@ public class RemoteTransitionCompat implements Parcelable {
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
WindowContainerToken pausingTask = null;
+ WindowContainerToken pipTask = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
t.setLayer(leashMap.get(change.getLeash()),
info.getChanges().size() * 3 - i);
- if (change.getTaskInfo() != null) {
- pausingTask = change.getTaskInfo().token;
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null) {
+ continue;
+ }
+ pausingTask = taskInfo.token;
+ if (taskInfo.pictureInPictureParams != null
+ && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
+ pipTask = taskInfo.token;
}
}
}
@@ -134,8 +147,8 @@ public class RemoteTransitionCompat implements Parcelable {
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- mRecentsSession.setup(controller, info, finishedCallback, pausingTask,
- leashMap);
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTask, pipTask,
+ leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -147,7 +160,7 @@ public class RemoteTransitionCompat implements Parcelable {
if (!mergeTarget.equals(mToken)) return;
if (!mRecentsSession.merge(info, t, recents)) return;
try {
- finishedCallback.onTransitionFinished(null /* wct */);
+ finishedCallback.onTransitionFinished(null /* wct */, null /* sct */);
} catch (RemoteException e) {
Log.e(TAG, "Error merging transition.", e);
}
@@ -156,14 +169,21 @@ public class RemoteTransitionCompat implements Parcelable {
}
/** Adds a filter check that restricts this remote transition to home open transitions. */
- public void addHomeOpenCheck() {
+ public void addHomeOpenCheck(ComponentName homeActivity) {
if (mFilter == null) {
mFilter = new TransitionFilter();
}
+ // No need to handle the transition that also dismisses keyguard.
+ mFilter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
mFilter.mRequirements =
- new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement()};
mFilter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+ mFilter.mRequirements[0].mTopActivity = homeActivity;
mFilter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ mFilter.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+ mFilter.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+ mFilter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
}
/**
@@ -175,13 +195,17 @@ public class RemoteTransitionCompat implements Parcelable {
private RecentsAnimationControllerCompat mWrapped = null;
private IRemoteTransitionFinishedCallback mFinishCB = null;
private WindowContainerToken mPausingTask = null;
+ private WindowContainerToken mPipTask = null;
private TransitionInfo mInfo = null;
private SurfaceControl mOpeningLeash = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
+ private PictureInPictureSurfaceTransaction mPipTransaction = null;
+ private IBinder mTransition = null;
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask,
- ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
+ WindowContainerToken pipTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
+ IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -190,7 +214,9 @@ public class RemoteTransitionCompat implements Parcelable {
mInfo = info;
mFinishCB = finishCB;
mPausingTask = pausingTask;
+ mPipTask = pipTask;
mLeashMap = leashMap;
+ mTransition = transition;
}
@SuppressLint("NewApi")
@@ -247,6 +273,7 @@ public class RemoteTransitionCompat implements Parcelable {
@Override public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
+ mPipTransaction = finishTransaction;
if (mWrapped != null) {
mWrapped.setFinishTaskTransaction(taskId, finishTransaction, overlay);
}
@@ -263,10 +290,13 @@ public class RemoteTransitionCompat implements Parcelable {
try {
if (!toHome && mPausingTask != null && mOpeningLeash == null) {
// The gesture went back to opening the app rather than continuing with
- // recents, so end the transition by moving the app back to the top.
+ // recents, so end the transition by moving the app back to the top (and also
+ // re-showing it's task).
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mPausingTask, true /* onTop */);
- mFinishCB.onTransitionFinished(wct);
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mInfo.getChange(mPausingTask).getLeash());
+ mFinishCB.onTransitionFinished(wct, t);
} else {
if (mOpeningLeash != null) {
// TODO: the launcher animation should handle this
@@ -275,7 +305,18 @@ public class RemoteTransitionCompat implements Parcelable {
t.setAlpha(mOpeningLeash, 1.f);
t.apply();
}
- mFinishCB.onTransitionFinished(null /* wct */);
+ if (mPipTask != null && mPipTransaction != null) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mInfo.getChange(mPipTask).getLeash());
+ PictureInPictureSurfaceTransaction.apply(mPipTransaction,
+ mInfo.getChange(mPipTask).getLeash(), t);
+ mPipTask = null;
+ mPipTransaction = null;
+ mFinishCB.onTransitionFinished(null /* wct */, t);
+ } else {
+ mFinishCB.onTransitionFinished(null /* wct */, null /* sct */);
+ }
+
}
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
@@ -298,6 +339,7 @@ public class RemoteTransitionCompat implements Parcelable {
mInfo = null;
mOpeningLeash = null;
mLeashMap = null;
+ mTransition = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
@@ -318,6 +360,23 @@ public class RemoteTransitionCompat implements Parcelable {
@Override public boolean removeTask(int taskId) {
return mWrapped != null ? mWrapped.removeTask(taskId) : false;
}
+
+ /**
+ * @see IRecentsAnimationController#detachNavigationBarFromApp
+ */
+ @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+ try {
+ ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to detach the navigation bar from app", e);
+ }
+ }
+
+ /**
+ * @see IRecentsAnimationController#animateNavigationBarToApp(long)
+ */
+ @Override public void animateNavigationBarToApp(long duration) {
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
index 2d01d6af63c5..e49d9f92267e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
@@ -16,6 +16,4 @@
package com.android.systemui.shared.system.smartspace;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-
-parcelable SmartspaceState; \ No newline at end of file
+parcelable SmartspaceState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
index 047ff75468ed..047ff75468ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/statusbar/policy/CallbackController.java
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
new file mode 100644
index 000000000000..9b5eae8f6c26
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.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.
+ */
+@file:JvmName("UnfoldTransitionFactory")
+
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.config.ANIMATION_MODE_HINGE_ANGLE
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
+import java.lang.IllegalStateException
+import java.util.concurrent.Executor
+
+fun createUnfoldTransitionProgressProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: ScreenStatusProvider,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ mainHandler: Handler,
+ mainExecutor: Executor
+): UnfoldTransitionProgressProvider {
+
+ if (!config.isEnabled) {
+ throw IllegalStateException("Trying to create " +
+ "UnfoldTransitionProgressProvider when the transition is disabled")
+ }
+
+ val hingeAngleProvider =
+ if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+ RotationSensorHingeAngleProvider(sensorManager)
+ } else {
+ EmptyHingeAngleProvider()
+ }
+
+ val foldStateProvider = DeviceFoldStateProvider(
+ context,
+ hingeAngleProvider,
+ screenStatusProvider,
+ deviceStateManager,
+ mainExecutor
+ )
+
+ return if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+ PhysicsBasedUnfoldTransitionProgressProvider(
+ mainHandler,
+ foldStateProvider
+ )
+ } else {
+ FixedTimingTransitionProgressProvider(foldStateProvider)
+ }
+}
+
+fun createConfig(context: Context): UnfoldTransitionConfig =
+ ResourceUnfoldTransitionConfig(context)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
new file mode 100644
index 000000000000..e17f43e64b21
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -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.systemui.unfold
+
+import android.annotation.FloatRange
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.statusbar.policy.CallbackController
+
+/**
+ * Interface that allows to receive unfold transition progress updates.
+ * It can be used to update view properties based on the current animation progress.
+ * onTransitionProgress callback could be called on each frame.
+ *
+ * Use [createUnfoldTransitionProgressProvider] to create instances of this interface
+ */
+interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgressListener> {
+
+ fun destroy()
+
+ interface TransitionProgressListener {
+ fun onTransitionStarted() {}
+ fun onTransitionFinished() {}
+ fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
new file mode 100644
index 000000000000..fa6b5de0b1ee
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.unfold.config
+
+import android.content.Context
+import android.os.SystemProperties
+
+internal class ResourceUnfoldTransitionConfig(
+ private val context: Context
+) : UnfoldTransitionConfig {
+
+ override val isEnabled: Boolean
+ get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
+
+ @AnimationMode
+ override val mode: Int
+ get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
+ ANIMATION_MODE_FIXED_TIMING)
+
+ private fun readIsEnabled(): Boolean = context.resources
+ .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+}
+
+/**
+ * Temporary persistent property to control unfold transition mode
+ * See [com.android.unfold.config.AnimationMode]
+ */
+private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_mode"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
new file mode 100644
index 000000000000..75d9dc32339f
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -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.systemui.unfold.config
+
+import android.annotation.IntDef
+
+interface UnfoldTransitionConfig {
+ val isEnabled: Boolean
+
+ @AnimationMode
+ val mode: Int
+}
+
+@IntDef(prefix = ["ANIMATION_MODE_"], value = [
+ ANIMATION_MODE_DISABLED,
+ ANIMATION_MODE_FIXED_TIMING,
+ ANIMATION_MODE_HINGE_ANGLE
+])
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class AnimationMode
+
+const val ANIMATION_MODE_DISABLED = 0
+const val ANIMATION_MODE_FIXED_TIMING = 1
+const val ANIMATION_MODE_HINGE_ANGLE = 2
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
new file mode 100644
index 000000000000..732882e99038
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.progress
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.util.FloatProperty
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+
+/**
+ * Emits animation progress with fixed timing after unfolding
+ */
+internal class FixedTimingTransitionProgressProvider(
+ private val foldStateProvider: FoldStateProvider
+) : UnfoldTransitionProgressProvider, FoldStateProvider.FoldUpdatesListener {
+
+ private val animatorListener = AnimatorListener()
+ private val animator =
+ ObjectAnimator.ofFloat(this, AnimationProgressProperty, 0f, 1f)
+ .apply {
+ duration = TRANSITION_TIME_MILLIS
+ addListener(animatorListener)
+ }
+
+
+ private var transitionProgress: Float = 0.0f
+ set(value) {
+ listeners.forEach { it.onTransitionProgress(value) }
+ field = value
+ }
+
+ private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+
+ init {
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
+
+ override fun destroy() {
+ animator.cancel()
+ foldStateProvider.removeCallback(this)
+ foldStateProvider.stop()
+ }
+
+ override fun onFoldUpdate(@FoldUpdate update: Int) {
+ when (update) {
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE ->
+ animator.start()
+ FOLD_UPDATE_FINISH_CLOSED ->
+ animator.cancel()
+ }
+ }
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ listeners.remove(listener)
+ }
+
+ override fun onHingeAngleUpdate(angle: Float) {
+ }
+
+ private object AnimationProgressProperty :
+ FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") {
+
+ override fun setValue(
+ provider: FixedTimingTransitionProgressProvider,
+ value: Float
+ ) {
+ provider.transitionProgress = value
+ }
+
+ override fun get(provider: FixedTimingTransitionProgressProvider): Float =
+ provider.transitionProgress
+ }
+
+ private inner class AnimatorListener : Animator.AnimatorListener {
+
+ override fun onAnimationStart(animator: Animator) {
+ listeners.forEach { it.onTransitionStarted() }
+ }
+
+ override fun onAnimationEnd(animator: Animator) {
+ listeners.forEach { it.onTransitionFinished() }
+ }
+
+ override fun onAnimationRepeat(animator: Animator) {
+ }
+
+ override fun onAnimationCancel(animator: Animator) {
+ }
+ }
+
+ private companion object {
+ private const val TRANSITION_TIME_MILLIS = 400L
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
new file mode 100644
index 000000000000..b111892ceb6e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.progress
+
+import android.os.Handler
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+
+/**
+ * Maps fold updates to unfold transition progress using DynamicAnimation.
+ *
+ * TODO(b/193793338) Current limitations:
+ * - doesn't handle folding transition
+ * - doesn't handle postures
+ */
+internal class PhysicsBasedUnfoldTransitionProgressProvider(
+ private val handler: Handler,
+ private val foldStateProvider: FoldStateProvider
+) :
+ UnfoldTransitionProgressProvider,
+ FoldUpdatesListener,
+ DynamicAnimation.OnAnimationEndListener {
+
+ private val springAnimation = SpringAnimation(this, AnimationProgressProperty)
+ .apply {
+ addEndListener(this@PhysicsBasedUnfoldTransitionProgressProvider)
+ }
+
+ private val timeoutRunnable = TimeoutRunnable()
+
+ private var isTransitionRunning = false
+ private var isAnimatedCancelRunning = false
+
+ private var transitionProgress: Float = 0.0f
+ set(value) {
+ if (isTransitionRunning) {
+ listeners.forEach { it.onTransitionProgress(value) }
+ }
+ field = value
+ }
+
+ private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+
+ init {
+ foldStateProvider.addCallback(this)
+ foldStateProvider.start()
+ }
+
+ override fun destroy() {
+ foldStateProvider.stop()
+ }
+
+ override fun onHingeAngleUpdate(angle: Float) {
+ if (!isTransitionRunning || isAnimatedCancelRunning) return
+ springAnimation.animateToFinalPosition(angle / 180f)
+ }
+
+ override fun onFoldUpdate(@FoldUpdate update: Int) {
+ when (update) {
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
+ onStartTransition()
+ startTransition(startValue = 0f)
+ }
+ FOLD_UPDATE_FINISH_FULL_OPEN -> {
+ cancelTransition(endValue = 1f, animate = true)
+ }
+ FOLD_UPDATE_FINISH_CLOSED -> {
+ cancelTransition(endValue = 0f, animate = false)
+ }
+ }
+ }
+
+ private fun cancelTransition(endValue: Float, animate: Boolean) {
+ handler.removeCallbacks(timeoutRunnable)
+
+ if (animate) {
+ isAnimatedCancelRunning = true
+ springAnimation.animateToFinalPosition(endValue)
+ } else {
+ transitionProgress = endValue
+ isAnimatedCancelRunning = false
+ isTransitionRunning = false
+ springAnimation.cancel()
+
+ listeners.forEach {
+ it.onTransitionFinished()
+ }
+ }
+ }
+
+ override fun onAnimationEnd(
+ animation: DynamicAnimation<out DynamicAnimation<*>>,
+ canceled: Boolean,
+ value: Float,
+ velocity: Float
+ ) {
+ if (isAnimatedCancelRunning) {
+ cancelTransition(value, animate = false)
+ }
+ }
+
+ private fun onStartTransition() {
+ listeners.forEach {
+ it.onTransitionStarted()
+ }
+ isTransitionRunning = true
+ }
+
+ private fun startTransition(startValue: Float) {
+ if (!isTransitionRunning) onStartTransition()
+
+ springAnimation.apply {
+ spring = SpringForce().apply {
+ finalPosition = startValue
+ dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ stiffness = SPRING_STIFFNESS
+ }
+ minimumVisibleChange = MINIMAL_VISIBLE_CHANGE
+ setStartValue(startValue)
+ setMinValue(0f)
+ setMaxValue(1f)
+ }
+
+ springAnimation.start()
+
+ handler.postDelayed(timeoutRunnable, TRANSITION_TIMEOUT_MILLIS)
+ }
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ listeners.remove(listener)
+ }
+
+ private inner class TimeoutRunnable : Runnable {
+
+ override fun run() {
+ cancelTransition(endValue = 1f, animate = true)
+ }
+ }
+
+ private object AnimationProgressProperty :
+ FloatPropertyCompat<PhysicsBasedUnfoldTransitionProgressProvider>("animation_progress") {
+
+ override fun setValue(
+ provider: PhysicsBasedUnfoldTransitionProgressProvider,
+ value: Float
+ ) {
+ provider.transitionProgress = value
+ }
+
+ override fun getValue(provider: PhysicsBasedUnfoldTransitionProgressProvider): Float =
+ provider.transitionProgress
+ }
+}
+
+private const val TRANSITION_TIMEOUT_MILLIS = 2000L
+private const val SPRING_STIFFNESS = 200.0f
+private const val MINIMAL_VISIBLE_CHANGE = 0.001f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
new file mode 100644
index 000000000000..949652bc9137
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.updates
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager
+import androidx.core.util.Consumer
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
+import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
+import java.util.concurrent.Executor
+
+internal class DeviceFoldStateProvider(
+ context: Context,
+ private val hingeAngleProvider: HingeAngleProvider,
+ private val screenStatusProvider: ScreenStatusProvider,
+ private val deviceStateManager: DeviceStateManager,
+ private val mainExecutor: Executor
+) : FoldStateProvider {
+
+ private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
+
+ @FoldUpdate
+ private var lastFoldUpdate: Int? = null
+
+ private val hingeAngleListener = HingeAngleListener()
+ private val screenListener = ScreenStatusListener()
+ private val foldStateListener = FoldStateListener(context)
+
+ private var isFolded = false
+
+ override fun start() {
+ deviceStateManager.registerCallback(
+ mainExecutor,
+ foldStateListener
+ )
+ screenStatusProvider.addCallback(screenListener)
+ hingeAngleProvider.addCallback(hingeAngleListener)
+ }
+
+ override fun stop() {
+ screenStatusProvider.removeCallback(screenListener)
+ deviceStateManager.unregisterCallback(foldStateListener)
+ hingeAngleProvider.removeCallback(hingeAngleListener)
+ hingeAngleProvider.stop()
+ }
+
+ override fun addCallback(listener: FoldUpdatesListener) {
+ outputListeners.add(listener)
+ }
+
+ override fun removeCallback(listener: FoldUpdatesListener) {
+ outputListeners.remove(listener)
+ }
+
+ private fun onHingeAngle(angle: Float) {
+ when (lastFoldUpdate) {
+ FOLD_UPDATE_FINISH_FULL_OPEN -> {
+ if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
+ lastFoldUpdate = FOLD_UPDATE_START_CLOSING
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
+ }
+ }
+ FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
+ if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
+ lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
+ }
+ }
+ }
+
+ outputListeners.forEach { it.onHingeAngleUpdate(angle) }
+ }
+
+ private inner class FoldStateListener(context: Context) :
+ DeviceStateManager.FoldStateListener(context, { folded: Boolean ->
+ isFolded = folded
+
+ if (folded) {
+ lastFoldUpdate = FOLD_UPDATE_FINISH_CLOSED
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) }
+ hingeAngleProvider.stop()
+ } else {
+ lastFoldUpdate = FOLD_UPDATE_START_OPENING
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_OPENING) }
+ hingeAngleProvider.start()
+ }
+ })
+
+ private inner class ScreenStatusListener :
+ ScreenStatusProvider.ScreenListener {
+
+ override fun onScreenTurnedOn() {
+ if (!isFolded) {
+ outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }
+ }
+ }
+ }
+
+ private inner class HingeAngleListener : Consumer<Float> {
+
+ override fun accept(angle: Float) {
+ onHingeAngle(angle)
+ }
+ }
+}
+
+private const val MOVEMENT_THRESHOLD_DEGREES = 10f
+private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
new file mode 100644
index 000000000000..4c6d241b1456
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.unfold.updates
+
+import android.annotation.FloatRange
+import android.annotation.IntDef
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.statusbar.policy.CallbackController
+
+/**
+ * Allows to subscribe to main events related to fold/unfold process such as hinge angle update,
+ * start folding/unfolding, screen availability
+ */
+internal interface FoldStateProvider : CallbackController<FoldUpdatesListener> {
+ fun start()
+ fun stop()
+
+ interface FoldUpdatesListener {
+ fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float)
+ fun onFoldUpdate(@FoldUpdate update: Int)
+ }
+
+ @IntDef(prefix = ["FOLD_UPDATE_"], value = [
+ FOLD_UPDATE_START_OPENING,
+ FOLD_UPDATE_HALF_OPEN,
+ FOLD_UPDATE_START_CLOSING,
+ FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE,
+ FOLD_UPDATE_FINISH_HALF_OPEN,
+ FOLD_UPDATE_FINISH_FULL_OPEN,
+ FOLD_UPDATE_FINISH_CLOSED
+ ])
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class FoldUpdate
+}
+
+const val FOLD_UPDATE_START_OPENING = 0
+const val FOLD_UPDATE_HALF_OPEN = 1
+const val FOLD_UPDATE_START_CLOSING = 2
+const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 3
+const val FOLD_UPDATE_FINISH_HALF_OPEN = 4
+const val FOLD_UPDATE_FINISH_FULL_OPEN = 5
+const val FOLD_UPDATE_FINISH_CLOSED = 6
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
new file mode 100644
index 000000000000..9b58b1fcad46
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -0,0 +1,17 @@
+package com.android.systemui.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+
+internal class EmptyHingeAngleProvider : HingeAngleProvider {
+ override fun start() {
+ }
+
+ override fun stop() {
+ }
+
+ override fun removeCallback(listener: Consumer<Float>) {
+ }
+
+ override fun addCallback(listener: Consumer<Float>) {
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
new file mode 100644
index 000000000000..854991383a8c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -0,0 +1,12 @@
+package com.android.systemui.unfold.updates.hinge
+
+import androidx.core.util.Consumer
+import com.android.systemui.statusbar.policy.CallbackController
+
+internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
+ fun start()
+ fun stop()
+}
+
+const val FULLY_OPEN_DEGREES = 180f
+const val FULLY_CLOSED_DEGREES = 0f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
new file mode 100644
index 000000000000..8b6eecfdfde4
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
@@ -0,0 +1,67 @@
+package com.android.systemui.unfold.updates.hinge
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import androidx.core.util.Consumer
+import com.android.systemui.shared.recents.utilities.Utilities
+
+/**
+ * Temporary hinge angle provider that uses rotation sensor instead.
+ * It requires to have the device in a certain position to work correctly
+ * (flat to the ground)
+ */
+internal class RotationSensorHingeAngleProvider(
+ private val sensorManager: SensorManager
+) : HingeAngleProvider {
+
+ private val sensorListener = HingeAngleSensorListener()
+ private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+
+ override fun start() {
+ val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
+ sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
+ }
+
+ override fun stop() {
+ sensorManager.unregisterListener(sensorListener)
+ }
+
+ override fun removeCallback(listener: Consumer<Float>) {
+ listeners.remove(listener)
+ }
+
+ override fun addCallback(listener: Consumer<Float>) {
+ listeners.add(listener)
+ }
+
+ private fun onHingeAngle(angle: Float) {
+ listeners.forEach { it.accept(angle) }
+ }
+
+ private inner class HingeAngleSensorListener : SensorEventListener {
+
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+ }
+
+ override fun onSensorChanged(event: SensorEvent) {
+ // Jumbojack sends incorrect sensor reading 1.0f event in the beginning, let's ignore it
+ if (event.values[3] == 1.0f) return
+
+ val angleRadians = event.values.convertToAngle()
+ val hingeAngleDegrees = Math.toDegrees(angleRadians).toFloat()
+ val angle = Utilities.clamp(hingeAngleDegrees, FULLY_CLOSED_DEGREES, FULLY_OPEN_DEGREES)
+ onHingeAngle(angle)
+ }
+
+ private val rotationMatrix = FloatArray(9)
+ private val resultOrientation = FloatArray(9)
+
+ private fun FloatArray.convertToAngle(): Double {
+ SensorManager.getRotationMatrixFromVector(rotationMatrix, this)
+ SensorManager.getOrientation(rotationMatrix, resultOrientation)
+ return resultOrientation[2] + Math.PI
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index 01e34d9f8f97..1eec8033ac5d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -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.
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.systemui.unfold.updates.screen
-package com.android.server.wm.flicker.launch
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.statusbar.policy.CallbackController
-import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
+interface ScreenStatusProvider : CallbackController<ScreenListener> {
-fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
- .then()
- .showsAppWindowOnTop("Snapshot", testApp.getPackage())
+ interface ScreenListener {
+ /**
+ * Called when the screen is on and ready (windows are drawn and screen blocker is removed)
+ */
+ fun onScreenTurnedOn()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a5b25097a56e..8f14cd858f1e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,6 +14,9 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
@@ -22,6 +25,8 @@ import com.android.systemui.plugins.ClockPlugin;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.TimeZone;
@@ -37,6 +42,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
private static final long CLOCK_IN_MILLIS = 200;
private static final long SMARTSPACE_MOVE_MILLIS = 350;
+ @IntDef({LARGE, SMALL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ClockSize { }
+
+ public static final int LARGE = 0;
+ public static final int SMALL = 1;
+
/**
* Optional/alternative clock injected via plugin.
*/
@@ -65,13 +77,13 @@ public class KeyguardClockSwitch extends RelativeLayout {
private float mDarkAmount;
/**
- * Boolean value indicating if notifications are visible on lock screen. Use null to signify
- * it is uninitialized.
+ * Indicates which clock is currently displayed - should be one of {@link ClockSize}.
+ * Use null to signify it is uninitialized.
*/
- private Boolean mHasVisibleNotifications = null;
+ @ClockSize private Integer mDisplayedClockSize = null;
- private AnimatorSet mClockInAnim = null;
- private AnimatorSet mClockOutAnim = null;
+ @VisibleForTesting AnimatorSet mClockInAnim = null;
+ @VisibleForTesting AnimatorSet mClockOutAnim = null;
private ObjectAnimator mSmartspaceAnim = null;
/**
@@ -264,19 +276,17 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
/**
- * Based upon whether notifications are showing or not, display/hide the large clock and
- * the smaller version.
+ * Display the desired clock and hide the other one
+ *
+ * @return true if desired clock appeared and false if it was already visible
*/
- boolean willSwitchToLargeClock(boolean hasVisibleNotifications) {
- if (mHasVisibleNotifications != null
- && hasVisibleNotifications == mHasVisibleNotifications) {
+ boolean switchToClock(@ClockSize int clockSize) {
+ if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
- boolean useLargeClock = !hasVisibleNotifications;
- animateClockChange(useLargeClock);
-
- mHasVisibleNotifications = hasVisibleNotifications;
- return useLargeClock;
+ animateClockChange(clockSize == LARGE);
+ mDisplayedClockSize = clockSize;
+ return true;
}
public Paint getPaint() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f4a3fb236ffa..0b78ddb2229c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -17,11 +17,13 @@
package com.android.keyguard;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import android.app.WallpaperManager;
import android.text.TextUtils;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -91,8 +93,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
- // If set, will replace keyguard_status_area
- private View mSmartspaceView;
+ private ViewGroup mSmartspaceContainer;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private SmartspaceTransitionController mSmartspaceTransitionController;
@@ -146,6 +147,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
mClockFrame = mView.findViewById(R.id.lockscreen_clock_view);
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
+ mSmartspaceContainer = mView.findViewById(R.id.keyguard_smartspace_container);
+ mSmartspaceController.setKeyguardStatusContainer(mSmartspaceContainer);
mClockViewController =
new AnimatableClockController(
@@ -187,35 +190,25 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
updateAodIcons();
- if (mSmartspaceController.isEnabled()) {
- mSmartspaceView = mSmartspaceController.buildAndConnectView(mView);
+ if (mSmartspaceController.isSmartspaceEnabled()) {
+ // "Enabled" doesn't mean smartspace is displayed here - inside mSmartspaceContainer -
+ // it might be a part of another view when in split shade. But it means that it CAN be
+ // displayed here, so we want to hide keyguard_status_area and set views relations
+ // accordingly.
View ksa = mView.findViewById(R.id.keyguard_status_area);
- int ksaIndex = mView.indexOfChild(ksa);
+ // we show either keyguard_status_area or smartspace, so when smartspace can be visible,
+ // keyguard_status_area should be hidden
ksa.setVisibility(View.GONE);
- // Place smartspace view below normal clock...
- RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
- MATCH_PARENT, WRAP_CONTENT);
- lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view);
-
- mView.addView(mSmartspaceView, ksaIndex, lp);
- int startPadding = getContext().getResources()
- .getDimensionPixelSize(R.dimen.below_clock_padding_start);
- int endPadding = getContext().getResources()
- .getDimensionPixelSize(R.dimen.below_clock_padding_end);
- mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
-
updateClockLayout();
- View nic = mView.findViewById(
- R.id.left_aligned_notification_icon_container);
- lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
- lp.addRule(RelativeLayout.BELOW, mSmartspaceView.getId());
+ View nic = mView.findViewById(R.id.left_aligned_notification_icon_container);
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+ lp.addRule(RelativeLayout.BELOW, mSmartspaceContainer.getId());
nic.setLayoutParams(lp);
-
- mView.setSmartspaceView(mSmartspaceView);
- mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
+ mView.setSmartspaceView(mSmartspaceContainer);
+ mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceContainer);
}
}
@@ -238,10 +231,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
// instance of this class. In order to fix this, we need to modify the plugin so that
// (a) we get a new view each time and (b) we can properly clean up an old view by making
// it unregister itself as a plugin listener.
- if (mSmartspaceView != null) {
- mView.removeView(mSmartspaceView);
- mSmartspaceView = null;
- }
+ mSmartspaceContainer.removeAllViews();
}
/**
@@ -254,7 +244,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
private void updateClockLayout() {
- if (mSmartspaceController.isEnabled()) {
+ if (mSmartspaceController.isSmartspaceEnabled()) {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
lp.topMargin = getContext().getResources().getDimensionPixelSize(
@@ -264,10 +254,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- if (mView.willSwitchToLargeClock(hasVisibleNotifications)) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ boolean appeared = mView.switchToClock(clockSize);
+ if (appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
@@ -317,8 +309,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
PropertyAnimator.setProperty(mLargeClockFrame, AnimatableProperty.SCALE_Y,
scale, props, animate);
- if (mSmartspaceView != null) {
- PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+ if (mSmartspaceContainer != null) {
+ PropertyAnimator.setProperty(mSmartspaceContainer, AnimatableProperty.TRANSLATION_X,
x, props, animate);
// If we're unlocking with the SmartSpace shared element transition, let the controller
@@ -336,8 +328,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
public void setChildrenAlphaExcludingSmartspace(float alpha) {
final Set<View> excludedViews = new HashSet<>();
- if (mSmartspaceView != null) {
- excludedViews.add(mSmartspaceView);
+ if (mSmartspaceContainer != null) {
+ excludedViews.add(mSmartspaceContainer);
}
setChildrenAlphaExcluding(alpha, excludedViews);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 97d3a5a4cd18..75425e1e6ca3 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.policy.DevicePostureController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -166,6 +167,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
private final TelephonyManager mTelephonyManager;
private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
private final FalsingCollector mFalsingCollector;
+ private final DevicePostureController mDevicePostureController;
@Inject
public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -175,7 +177,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
@Main Resources resources, LiftToActivateListener liftToActivateListener,
TelephonyManager telephonyManager, FalsingCollector falsingCollector,
- EmergencyButtonController.Factory emergencyButtonControllerFactory) {
+ EmergencyButtonController.Factory emergencyButtonControllerFactory,
+ DevicePostureController devicePostureController) {
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mLatencyTracker = latencyTracker;
@@ -187,6 +190,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
mTelephonyManager = telephonyManager;
mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
mFalsingCollector = falsingCollector;
+ mDevicePostureController = devicePostureController;
}
/** Create a new {@link KeyguardInputViewController}. */
@@ -200,7 +204,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
- emergencyButtonController, mMessageAreaControllerFactory);
+ emergencyButtonController, mMessageAreaControllerFactory,
+ mDevicePostureController);
} else if (keyguardInputView instanceof KeyguardPasswordView) {
return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -212,7 +217,8 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView>
return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, emergencyButtonController, mFalsingCollector);
+ mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
+ mDevicePostureController);
} else if (keyguardInputView instanceof KeyguardSimPinView) {
return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 9286175cc2ea..e960e81cb4c1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -1,20 +1,15 @@
package com.android.keyguard
import android.annotation.CurrentTimeMillisLong
-import android.hardware.biometrics.BiometricAuthenticator.Modality
-import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
-import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
/** Verbose logging for various keyguard listening states. */
sealed class KeyguardListenModel {
/** Timestamp of the state change. */
abstract val timeMillis: Long
- /** Current user */
+ /** Current user. */
abstract val userId: Int
- /** If keyguard is listening for the given [modality]. */
+ /** If keyguard is listening for the modality represented by this model. */
abstract val listening: Boolean
- /** Sensor type */
- @Modality abstract val modality: Int
}
/**
@@ -45,9 +40,7 @@ data class KeyguardFingerprintListenModel(
val udfps: Boolean,
val userDoesNotHaveTrust: Boolean,
val userNeedsStrongAuth: Boolean
-) : KeyguardListenModel() {
- override val modality: Int = TYPE_FACE
-}
+) : KeyguardListenModel()
/**
* Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFace].
@@ -72,6 +65,4 @@ data class KeyguardFaceListenModel(
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
val switchingUser: Boolean
-) : KeyguardListenModel() {
- override val modality: Int = TYPE_FINGERPRINT
-}
+) : KeyguardListenModel()
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 8fc4240e1054..1efda7edee2f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -16,20 +16,23 @@
package com.android.keyguard;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
-import android.widget.LinearLayout;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
-
-import java.util.List;
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
* Displays a PIN pad for unlocking.
@@ -39,13 +42,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
- private ViewGroup mContainer;
- private ViewGroup mRow0;
- private ViewGroup mRow1;
- private ViewGroup mRow2;
- private ViewGroup mRow3;
+ private ConstraintLayout mContainer;
private int mDisappearYTranslation;
private View[][] mViews;
+ @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPINView(Context context) {
this(context, null);
@@ -72,6 +72,11 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
updateMargins();
}
+ void onDevicePostureChanged(@DevicePostureInt int posture) {
+ mLastDevicePosture = posture;
+ updateMargins();
+ }
+
@Override
protected void resetState() {
}
@@ -82,30 +87,48 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
}
private void updateMargins() {
+ // Re-apply everything to the keys...
int bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.num_pad_row_margin_bottom);
-
- for (ViewGroup vg : List.of(mRow1, mRow2, mRow3)) {
- ((LinearLayout.LayoutParams) vg.getLayoutParams()).setMargins(0, 0, 0, bottomMargin);
- }
-
- bottomMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.num_pad_entry_row_margin_bottom);
- ((LinearLayout.LayoutParams) mRow0.getLayoutParams()).setMargins(0, 0, 0, bottomMargin);
-
- if (mEcaView != null) {
- int ecaTopMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_eca_top_margin);
- int ecaBottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_eca_bottom_margin);
- ((LinearLayout.LayoutParams) mEcaView.getLayoutParams()).setMargins(0, ecaTopMargin,
- 0, ecaBottomMargin);
+ int rightMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.num_pad_key_margin_end);
+ String ratio = mContext.getResources().getString(R.string.num_pad_key_ratio);
+
+ // mView contains all Views that make up the PIN pad; row0 = the entry test field, then
+ // rows 1-4 contain the buttons. Iterate over all views that make up the buttons in the pad,
+ // and re-set all the margins.
+ for (int row = 1; row < 5; row++) {
+ for (int column = 0; column < 3; column++) {
+ View key = mViews[row][column];
+
+ ConstraintLayout.LayoutParams lp =
+ (ConstraintLayout.LayoutParams) key.getLayoutParams();
+
+ lp.dimensionRatio = ratio;
+
+ // Don't set any margins on the last row of buttons.
+ if (row != 4) {
+ lp.bottomMargin = bottomMargin;
+ }
+
+ // Don't set margins on the rightmost buttons.
+ if (column != 2) {
+ lp.rightMargin = rightMargin;
+ }
+
+ key.setLayoutParams(lp);
+ }
}
- View entryView = findViewById(R.id.pinEntry);
- ViewGroup.LayoutParams lp = entryView.getLayoutParams();
- lp.height = mContext.getResources().getDimensionPixelSize(R.dimen.keyguard_password_height);
- entryView.setLayoutParams(lp);
+ // Update the guideline based on the device posture...
+ float halfOpenPercentage =
+ mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
+
+ ConstraintSet cs = new ConstraintSet();
+ cs.clone(mContainer);
+ cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
+ mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainer);
}
@Override
@@ -113,13 +136,9 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
super.onFinishInflate();
mContainer = findViewById(R.id.pin_container);
- mRow0 = findViewById(R.id.row0);
- mRow1 = findViewById(R.id.row1);
- mRow2 = findViewById(R.id.row2);
- mRow3 = findViewById(R.id.row3);
mViews = new View[][]{
new View[]{
- mRow0, null, null
+ findViewById(R.id.row0), null, null
},
new View[]{
findViewById(R.id.key1), findViewById(R.id.key2),
@@ -188,9 +207,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private void enableClipping(boolean enable) {
mContainer.setClipToPadding(enable);
mContainer.setClipChildren(enable);
- mRow1.setClipToPadding(enable);
- mRow2.setClipToPadding(enable);
- mRow3.setClipToPadding(enable);
setClipChildren(enable);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 98e7fb48b7a6..a35aedf6f503 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
+
import android.content.Context;
import android.graphics.Rect;
import android.os.SystemClock;
@@ -22,16 +24,19 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
+
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternView;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
public class KeyguardPatternView extends KeyguardInputView
implements AppearAnimationCreator<LockPatternView.CellState> {
@@ -68,7 +73,7 @@ public class KeyguardPatternView extends KeyguardInputView
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
- private ViewGroup mContainer;
+ private ConstraintLayout mContainer;
public KeyguardPatternView(Context context) {
this(context, null);
@@ -90,6 +95,18 @@ public class KeyguardPatternView extends KeyguardInputView
mContext, android.R.interpolator.fast_out_linear_in));
}
+ void onDevicePostureChanged(@DevicePostureInt int posture) {
+ // Update the guideline based on the device posture...
+ float halfOpenPercentage =
+ mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
+
+ ConstraintSet cs = new ConstraintSet();
+ cs.clone(mContainer);
+ cs.setGuidelinePercent(R.id.pin_pad_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
+ ? halfOpenPercentage : 0.0f);
+ cs.applyTo(mContainer);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index d5be7bacaadc..94e07b713915 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -38,6 +38,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingClassifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import java.util.List;
@@ -56,6 +57,9 @@ public class KeyguardPatternViewController
private final FalsingCollector mFalsingCollector;
private final EmergencyButtonController mEmergencyButtonController;
private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+ private final DevicePostureController mPostureController;
+ private final DevicePostureController.Callback mPostureCallback =
+ posture -> mView.onDevicePostureChanged(posture);
private KeyguardMessageAreaController mMessageAreaController;
private LockPatternView mLockPatternView;
@@ -192,7 +196,8 @@ public class KeyguardPatternViewController
LatencyTracker latencyTracker,
FalsingCollector falsingCollector,
EmergencyButtonController emergencyButtonController,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+ KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+ DevicePostureController postureController) {
super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
@@ -203,6 +208,7 @@ public class KeyguardPatternViewController
KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
mMessageAreaController = mMessageAreaControllerFactory.create(kma);
mLockPatternView = mView.findViewById(R.id.lockPatternView);
+ mPostureController = postureController;
}
@Override
@@ -235,6 +241,7 @@ public class KeyguardPatternViewController
getKeyguardSecurityCallback().onCancelClicked();
});
}
+ mPostureController.addCallback(mPostureCallback);
}
@Override
@@ -247,6 +254,7 @@ public class KeyguardPatternViewController
if (cancelBtn != null) {
cancelBtn.setOnClickListener(null);
}
+ mPostureController.removeCallback(mPostureCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 262bed3f695c..9f4585fb1a92 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -23,10 +23,14 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.statusbar.policy.DevicePostureController;
public class KeyguardPinViewController
extends KeyguardPinBasedInputViewController<KeyguardPINView> {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DevicePostureController mPostureController;
+ private final DevicePostureController.Callback mPostureCallback = posture ->
+ mView.onDevicePostureChanged(posture);
protected KeyguardPinViewController(KeyguardPINView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -35,11 +39,13 @@ public class KeyguardPinViewController
KeyguardMessageAreaController.Factory messageAreaControllerFactory,
LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
EmergencyButtonController emergencyButtonController,
- FalsingCollector falsingCollector) {
+ FalsingCollector falsingCollector,
+ DevicePostureController postureController) {
super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
messageAreaControllerFactory, latencyTracker, liftToActivateListener,
emergencyButtonController, falsingCollector);
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mPostureController = postureController;
}
@Override
@@ -53,6 +59,14 @@ public class KeyguardPinViewController
getKeyguardSecurityCallback().onCancelClicked();
});
}
+
+ mPostureController.addCallback(mPostureCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ super.onViewDetached();
+ mPostureController.removeCallback(mPostureCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ca4d73b6de5d..840e8c8cba58 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -42,7 +42,6 @@ import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
import android.widget.FrameLayout;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -104,7 +103,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
private boolean mIsSecurityViewLeftAligned = true;
private boolean mOneHandedMode = false;
- private SecurityMode mSecurityMode = SecurityMode.Invalid;
private ViewPropertyAnimator mRunningOneHandedAnimator;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -248,66 +246,47 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
-
- updateLayoutForSecurityMode(securityMode);
}
- void updateLayoutForSecurityMode(SecurityMode securityMode) {
- mSecurityMode = securityMode;
- mOneHandedMode = canUseOneHandedBouncer();
-
- if (mOneHandedMode) {
- mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
- }
-
+ /**
+ * Sets whether this security container is in one handed mode. If so, it will measure its
+ * child SecurityViewFlipper in one half of the screen, and move it when tapping on the opposite
+ * side of the screen.
+ */
+ public void setOneHandedMode(boolean oneHandedMode) {
+ mOneHandedMode = oneHandedMode;
updateSecurityViewGravity();
updateSecurityViewLocation(false);
}
- /** Update keyguard position based on a tapped X coordinate. */
- public void updateKeyguardPosition(float x) {
- if (mOneHandedMode) {
- moveBouncerForXCoordinate(x, /* animate= */false);
- }
+ /** Returns whether this security container is in one-handed mode. */
+ public boolean isOneHandedMode() {
+ return mOneHandedMode;
}
- /** Return whether the one-handed keyguard should be enabled. */
- private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
- return false;
- }
-
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ /**
+ * When in one-handed mode, sets if the inner SecurityViewFlipper should be aligned to the
+ * left-hand side of the screen or not, and whether to animate when moving between the two.
+ */
+ public void setOneHandedModeLeftAligned(boolean leftAligned, boolean animate) {
+ mIsSecurityViewLeftAligned = leftAligned;
+ updateSecurityViewLocation(animate);
}
- /** 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;
- }
+ /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
+ public boolean isOneHandedModeLeftAligned() {
+ return mIsSecurityViewLeftAligned;
}
private void updateSecurityViewGravity() {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) mSecurityViewFlipper.getLayoutParams();
if (mOneHandedMode) {
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
@@ -315,7 +294,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
lp.gravity = Gravity.CENTER_HORIZONTAL;
}
- securityView.setLayoutParams(lp);
+ mSecurityViewFlipper.setLayoutParams(lp);
}
/**
@@ -324,14 +303,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
* by the security view .
*/
private void updateSecurityViewLocation(boolean animate) {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
+ if (mSecurityViewFlipper == null) {
return;
}
if (!mOneHandedMode) {
- securityView.setTranslationX(0);
+ mSecurityViewFlipper.setTranslationX(0);
return;
}
@@ -343,7 +320,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
if (animate) {
- mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
+ mRunningOneHandedAnimator =
+ mSecurityViewFlipper.animate().translationX(targetTranslation);
mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
@Override
@@ -355,27 +333,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mRunningOneHandedAnimator.start();
} else {
- securityView.setTranslationX(targetTranslation);
+ mSecurityViewFlipper.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() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
@@ -635,7 +596,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
for (int i = 0; i < getChildCount(); i++) {
final View view = getChildAt(i);
if (view.getVisibility() != GONE) {
- if (mOneHandedMode && isKeyguardSecurityView(view)) {
+ if (mOneHandedMode && view == mSecurityViewFlipper) {
measureChildWithMargins(view, halfWidthMeasureSpec, 0,
heightMeasureSpec, 0);
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index dd7c7ea6b5a5..0df2d65d2acb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -32,6 +32,7 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.metrics.LogMaker;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
@@ -49,6 +50,8 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -74,12 +77,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final SecurityCallback mSecurityCallback;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
- private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+ @VisibleForTesting
+ final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
private MotionEvent mTouchDown;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -91,6 +96,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// Do just a bit of our own falsing. People should only be tapping on the input, not
// swiping.
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // If we're in one handed mode, the user can tap on the opposite side of the screen
+ // to move the bouncer across. In that case, inhibit the falsing (otherwise the taps
+ // to move the bouncer to each screen side can end up closing it instead).
+ if (mView.isOneHandedMode()) {
+ if ((mView.isOneHandedModeLeftAligned() && ev.getX() > mView.getWidth() / 2f)
+ || (!mView.isOneHandedModeLeftAligned()
+ && ev.getX() <= mView.getWidth() / 2f)) {
+ mFalsingCollector.avoidGesture();
+ }
+ }
+
if (mTouchDown != null) {
mTouchDown.recycle();
mTouchDown = null;
@@ -204,7 +220,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
KeyguardStateController keyguardStateController,
SecurityCallback securityCallback,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
@@ -218,6 +235,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardSecurityCallback);
mConfigurationController = configurationController;
mLastOrientation = getResources().getConfiguration().orientation;
+ mFalsingCollector = falsingCollector;
}
@Override
@@ -442,13 +460,49 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- mView.updateLayoutForSecurityMode(securityMode);
+ configureOneHandedMode();
}
mSecurityCallback.onSecurityModeChanged(
securityMode, newView != null && newView.needsInput());
}
+ /** Read whether the one-handed keyguard should be on the left/right from settings. */
+ private boolean isOneHandedKeyguardLeftAligned() {
+ try {
+ return Settings.Global.getInt(mView.getContext().getContentResolver(),
+ Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
+ == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
+ } catch (Settings.SettingNotFoundException ex) {
+ return true;
+ }
+ }
+
+ private boolean canUseOneHandedBouncer() {
+ // Is it enabled?
+ if (!getResources().getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning)) {
+ return false;
+ }
+
+ if (!KeyguardSecurityModel.isSecurityViewOneHanded(mCurrentSecurityMode)) {
+ return false;
+ }
+
+ return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
+ }
+
+ private void configureOneHandedMode() {
+ boolean oneHandedMode = canUseOneHandedBouncer();
+
+ mView.setOneHandedMode(oneHandedMode);
+
+ if (oneHandedMode) {
+ mView.setOneHandedModeLeftAligned(
+ isOneHandedKeyguardLeftAligned(), /* animate= */false);
+ }
+ }
+
public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
// +1 for this time
final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
@@ -513,13 +567,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
int newOrientation = getResources().getConfiguration().orientation;
if (newOrientation != mLastOrientation) {
mLastOrientation = newOrientation;
- mView.updateLayoutForSecurityMode(mCurrentSecurityMode);
+ configureOneHandedMode();
}
}
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
- mView.updateKeyguardPosition(x);
+ if (mView.isOneHandedMode()) {
+ mView.setOneHandedModeLeftAligned(x <= mView.getWidth() / 2f, false);
+ }
}
static class Factory {
@@ -535,6 +591,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private final KeyguardStateController mKeyguardStateController;
private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
private final ConfigurationController mConfigurationController;
+ private final FalsingCollector mFalsingCollector;
@Inject
Factory(KeyguardSecurityContainer view,
@@ -547,7 +604,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
KeyguardSecurityViewFlipperController securityViewFlipperController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingCollector falsingCollector) {
mView = view;
mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
mLockPatternUtils = lockPatternUtils;
@@ -558,6 +616,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mKeyguardStateController = keyguardStateController;
mSecurityViewFlipperController = securityViewFlipperController;
mConfigurationController = configurationController;
+ mFalsingCollector = falsingCollector;
}
public KeyguardSecurityContainerController create(
@@ -566,7 +625,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
- mConfigurationController);
+ mConfigurationController, mFalsingCollector);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 72e502816534..867f1178c3f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -19,6 +19,8 @@ package com.android.keyguard;
import android.graphics.Rect;
import android.util.Slog;
+import com.android.keyguard.KeyguardClockSwitch.ClockSize;
+import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -63,6 +65,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
KeyguardClockSwitchController keyguardClockSwitchController,
KeyguardStateController keyguardStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
+ CommunalStateController communalStateController,
ConfigurationController configurationController,
DozeParameters dozeParameters,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -75,8 +78,9 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mConfigurationController = configurationController;
mDozeParameters = dozeParameters;
mKeyguardStateController = keyguardStateController;
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
- dozeParameters, unlockedScreenOffAnimationController, /* animateYPos= */ true);
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
+ keyguardStateController, dozeParameters, unlockedScreenOffAnimationController,
+ /* animateYPos= */ true);
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mSmartspaceTransitionController = smartspaceTransitionController;
}
@@ -116,10 +120,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
- * Set whether or not the lock screen is showing notifications.
+ * Set which clock should be displayed on the keyguard. The other one will be automatically
+ * hidden.
*/
- public void setHasVisibleNotifications(boolean hasVisibleNotifications) {
- mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications);
+ public void displayClock(@ClockSize int clockSize) {
+ mKeyguardClockSwitchController.displayClock(clockSize);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 877e76480b1e..92d1bc4173d8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -103,10 +103,10 @@ 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.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -2908,6 +2908,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
updateBiometricListeningState();
}
+ /** Notifies that the occluded state changed. */
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ Assert.isMainThread();
+ if (DEBUG) {
+ Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")");
+ }
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onKeyguardOccludedChanged(occluded);
+ }
+ }
+ }
+
/**
* Handle {@link #MSG_KEYGUARD_RESET}
*/
@@ -3090,6 +3104,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
callback.onClockVisibilityChanged();
+ callback.onKeyguardOccludedChanged(mKeyguardOccluded);
callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
callback.onTelephonyCapable(mTelephonyCapable);
callback.onLockScreenModeChanged(mLockScreenMode);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 9849a7efe837..6aa7aaa4d488 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -88,6 +88,12 @@ public class KeyguardUpdateMonitorCallback {
*/
public void onKeyguardVisibilityChanged(boolean showing) { }
+ /**
+ * Called when the keyguard occluded state changes.
+ * @param occluded Indicates if the keyguard is now occluded.
+ */
+ public void onKeyguardOccludedChanged(boolean occluded) { }
+
public void onKeyguardVisibilityChangedRaw(boolean showing) {
final long now = SystemClock.elapsedRealtime();
if (showing == mShowing
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 28a54d56b071..a71b4f47640b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -22,6 +22,7 @@ import android.view.View;
import android.view.ViewPropertyAnimator;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -38,6 +39,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
public class KeyguardVisibilityHelper {
private View mView;
+ private final CommunalStateController mCommunalStateController;
private final KeyguardStateController mKeyguardStateController;
private final DozeParameters mDozeParameters;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -47,11 +49,13 @@ public class KeyguardVisibilityHelper {
private final AnimationProperties mAnimationProperties = new AnimationProperties();
public KeyguardVisibilityHelper(View view,
+ CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
DozeParameters dozeParameters,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
boolean animateYPos) {
mView = view;
+ mCommunalStateController = communalStateController;
mKeyguardStateController = keyguardStateController;
mDozeParameters = dozeParameters;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
@@ -73,6 +77,14 @@ public class KeyguardVisibilityHelper {
mView.animate().cancel();
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
+
+ // If the communal view is showing, hide immediately
+ if (mCommunalStateController.getCommunalViewShowing()) {
+ mView.setVisibility(View.GONE);
+ mView.setAlpha(1f);
+ return;
+ }
+
if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD
&& statusBarState != KEYGUARD) || goingToFullShade) {
mKeyguardViewVisibilityAnimating = true;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 509ac8a6d9fe..aa8cbd710e90 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -46,6 +46,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -99,6 +100,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull private CharSequence mUnlockedLabel;
@NonNull private CharSequence mLockedLabel;
@Nullable private final Vibrator mVibrator;
+ @Nullable private final AuthRippleController mAuthRippleController;
private boolean mIsDozing;
private boolean mIsBouncerShowing;
@@ -135,7 +137,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull AccessibilityManager accessibilityManager,
@NonNull ConfigurationController configurationController,
@NonNull @Main DelayableExecutor executor,
- @Nullable Vibrator vibrator
+ @Nullable Vibrator vibrator,
+ @Nullable AuthRippleController authRippleController
) {
super(view);
mStatusBarStateController = statusBarStateController;
@@ -148,6 +151,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mConfigurationController = configurationController;
mExecutor = executor;
mVibrator = vibrator;
+ mAuthRippleController = authRippleController;
final Context context = view.getContext();
mUnlockIcon = mView.getContext().getResources().getDrawable(
@@ -538,6 +542,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
// pre-emptively set to true to hide view
mIsBouncerShowing = true;
+ if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+ mAuthRippleController.showRipple(FINGERPRINT);
+ }
updateVisibility();
mKeyguardViewController.showBouncer(/* scrim */ true);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 57407f1f34c0..c659bf786a45 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -26,7 +26,7 @@ import android.view.MotionEvent;
import androidx.annotation.Nullable;
-import com.android.settingslib.Utils;
+import com.android.systemui.R;
/**
* Similar to the {@link NumPadKey}, but displays an image.
@@ -59,7 +59,9 @@ public class NumPadButton extends AlphaOptimizedImageButton {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 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
+ // the height to match the old pin bouncer.
+ // This is only used for PIN/PUK; the main PIN pad now uses ConstraintLayout, which will
+ // force our width/height to conform to the ratio in the layout.
int width = getMeasuredWidth();
boolean shortenHeight = mAnimator == null
@@ -90,8 +92,7 @@ public class NumPadButton extends AlphaOptimizedImageButton {
public void reloadColors() {
if (mAnimator != null) mAnimator.reloadColors(getContext());
- int textColor = Utils.getColorAttrDefaultColor(getContext(),
- android.R.attr.colorBackground);
- ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
+ int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color);
+ ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 232c6fc99068..e79ea9a44843 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -175,7 +175,9 @@ public class NumPadKey extends ViewGroup {
measureChildren(widthMeasureSpec, heightMeasureSpec);
// 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
+ // the height to match the old pin bouncer.
+ // This is only used for PIN/PUK; the main PIN pad now uses ConstraintLayout, which will
+ // force our width/height to conform to the ratio in the layout.
int width = getMeasuredWidth();
boolean shortenHeight = mAnimator == null
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index a6725234e4af..fc14b6a99008 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -18,6 +18,7 @@ package com.android.keyguard.dagger;
import com.android.keyguard.CarrierText;
import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
import dagger.Module;
@@ -31,4 +32,11 @@ public abstract class KeyguardStatusBarViewModule {
static CarrierText getCarrierText(KeyguardStatusBarView view) {
return view.findViewById(R.id.keyguard_carrier_text);
}
+
+ /** */
+ @Provides
+ @KeyguardStatusBarViewScope
+ static BatteryMeterView getBatteryMeterView(KeyguardStatusBarView view) {
+ return view.findViewById(R.id.battery);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 62d5a458d51d..cc166c210078 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -39,113 +39,116 @@ import dagger.Lazy;
@SysUISingleton
public class ActivityStarterDelegate implements ActivityStarter {
- private Optional<Lazy<StatusBar>> mActualStarter;
+ private Lazy<Optional<StatusBar>> mActualStarterOptionalLazy;
@Inject
- public ActivityStarterDelegate(Optional<Lazy<StatusBar>> statusBar) {
- mActualStarter = statusBar;
+ public ActivityStarterDelegate(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ mActualStarterOptionalLazy = statusBarOptionalLazy;
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(intent));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback, View associatedView) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback, associatedView));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback, associatedView));
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent,
Runnable intentSentUiThreadCallback,
ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
- intentSentUiThreadCallback, animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startPendingIntentDismissingKeyguard(
+ intent, intentSentUiThreadCallback, animationController));
}
@Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
int flags) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade,
- flags));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, onlyProvisioned, dismissShade, flags));
}
@Override
public void startActivity(Intent intent, boolean dismissShade) {
- mActualStarter.ifPresent(starter -> starter.get().startActivity(intent, dismissShade));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade));
}
@Override
public void startActivity(Intent intent, boolean dismissShade,
- @Nullable ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, dismissShade, animationController));
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked) {
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade, animationController,
+ showOverLockscreenWhenLocked));
}
@Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, onlyProvisioned, dismissShade));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, onlyProvisioned, dismissShade));
}
@Override
public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
- mActualStarter.ifPresent(
- starter -> starter.get().startActivity(intent, dismissShade, callback));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.startActivity(intent, dismissShade, callback));
}
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(intent, delay));
}
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
- animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(
+ intent, delay, animationController));
}
@Override
public void postStartActivityDismissingKeyguard(PendingIntent intent) {
- mActualStarter.ifPresent(
- starter -> starter.get().postStartActivityDismissingKeyguard(intent));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(intent));
}
@Override
public void postStartActivityDismissingKeyguard(PendingIntent intent,
ActivityLaunchAnimator.Controller animationController) {
- mActualStarter.ifPresent(starter ->
- starter.get().postStartActivityDismissingKeyguard(intent, animationController));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postStartActivityDismissingKeyguard(
+ intent, animationController));
}
@Override
public void postQSRunnableDismissingKeyguard(Runnable runnable) {
- mActualStarter.ifPresent(
- starter -> starter.get().postQSRunnableDismissingKeyguard(runnable));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.postQSRunnableDismissingKeyguard(runnable));
}
@Override
public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancel,
boolean afterKeyguardGone) {
- mActualStarter.ifPresent(starter -> starter.get().dismissKeyguardThenExecute(action, cancel,
- afterKeyguardGone));
+ mActualStarterOptionalLazy.get().ifPresent(
+ starter -> starter.dismissKeyguardThenExecute(action, cancel, afterKeyguardGone));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 76f30a80114a..1214ecda362a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -65,15 +66,14 @@ import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.recents.Recents;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -99,7 +99,6 @@ import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -116,7 +115,6 @@ import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -246,7 +244,6 @@ public class Dependency {
@Inject Lazy<BluetoothController> mBluetoothController;
@Inject Lazy<LocationController> mLocationController;
@Inject Lazy<RotationLockController> mRotationLockController;
- @Inject Lazy<NetworkController> mNetworkController;
@Inject Lazy<ZenModeController> mZenModeController;
@Inject Lazy<HotspotController> mHotspotController;
@Inject Lazy<CastController> mCastController;
@@ -350,8 +347,6 @@ public class Dependency {
@Inject Lazy<DozeParameters> mDozeParameters;
@Inject Lazy<IWallpaperManager> mWallpaperManager;
@Inject Lazy<CommandQueue> mCommandQueue;
- @Inject Lazy<Recents> mRecents;
- @Inject Lazy<StatusBar> mStatusBar;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
@Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
@@ -362,8 +357,9 @@ public class Dependency {
@Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
@Inject Lazy<EdgeBackGestureHandler.Factory> mEdgeBackGestureHandlerFactoryLazy;
@Inject Lazy<UiEventLogger> mUiEventLogger;
- @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
+ @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
+ @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
@Inject
public Dependency() {
@@ -393,8 +389,6 @@ public class Dependency {
mProviders.put(RotationLockController.class, mRotationLockController::get);
- mProviders.put(NetworkController.class, mNetworkController::get);
-
mProviders.put(ZenModeController.class, mZenModeController::get);
mProviders.put(HotspotController.class, mHotspotController::get);
@@ -555,8 +549,6 @@ public class Dependency {
mProviders.put(DozeParameters.class, mDozeParameters::get);
mProviders.put(IWallpaperManager.class, mWallpaperManager::get);
mProviders.put(CommandQueue.class, mCommandQueue::get);
- mProviders.put(Recents.class, mRecents::get);
- mProviders.put(StatusBar.class, mStatusBar::get);
mProviders.put(ProtoTracer.class, mProtoTracer::get);
mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get);
mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get);
@@ -576,6 +568,7 @@ public class Dependency {
mProviders.put(SystemStatusAnimationScheduler.class,
mSystemStatusAnimationSchedulerLazy::get);
mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+ mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
mProviders.put(EdgeBackGestureHandler.Factory.class,
mEdgeBackGestureHandlerFactoryLazy::get);
mProviders.put(UiEventLogger.class, mUiEventLogger::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a68f79604b25..c8f8404ff0d6 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,8 @@ import android.app.WallpaperColors;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
@@ -29,7 +31,6 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
-import android.view.DisplayInfo;
import android.view.SurfaceHolder;
import android.view.WindowManager;
@@ -90,7 +91,7 @@ public class ImageWallpaper extends WallpaperService {
mMiniBitmap = null;
}
- class GLEngine extends Engine {
+ class GLEngine extends Engine implements DisplayListener {
// Surface is rejected if size below a threshold on some devices (ie. 8px on elfin)
// set min to 64 px (CTS covers this), please refer to ag/4867989 for detail.
@VisibleForTesting
@@ -102,15 +103,15 @@ public class ImageWallpaper extends WallpaperService {
private EglHelper mEglHelper;
private final Runnable mFinishRenderingTask = this::finishRendering;
private boolean mNeedRedraw;
- private int mWidth = 1;
- private int mHeight = 1;
+
+ private boolean mDisplaySizeValid = false;
+ private int mDisplayWidth = 1;
+ private int mDisplayHeight = 1;
+
private int mImgWidth = 1;
private int mImgHeight = 1;
- private float mPageWidth = 1.f;
- private float mPageOffset = 1.f;
- GLEngine() {
- }
+ GLEngine() { }
@VisibleForTesting
GLEngine(Handler handler) {
@@ -124,13 +125,23 @@ public class ImageWallpaper extends WallpaperService {
mRenderer = getRendererInstance();
setFixedSizeAllowed(true);
updateSurfaceSize();
- Rect window = getDisplayContext()
- .getSystemService(WindowManager.class)
- .getCurrentWindowMetrics()
- .getBounds();
- mHeight = window.height();
- mWidth = window.width();
+
mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
+ getDisplayContext().getSystemService(DisplayManager.class)
+ .registerDisplayListener(this, mWorker.getThreadHandler());
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) { }
+
+ @Override
+ public void onDisplayRemoved(int displayId) { }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == getDisplayContext().getDisplayId()) {
+ mDisplaySizeValid = false;
+ }
}
EglHelper getEglHelperInstance() {
@@ -154,26 +165,10 @@ public class ImageWallpaper extends WallpaperService {
if (pages == mPages) return;
mPages = pages;
if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
- updateShift();
mWorker.getThreadHandler().post(() ->
computeAndNotifyLocalColors(new ArrayList<>(mColorAreas), mMiniBitmap));
}
- private void updateShift() {
- if (mImgHeight == 0) {
- mPageOffset = 0;
- mPageWidth = 1;
- return;
- }
- // calculate shift
- DisplayInfo displayInfo = new DisplayInfo();
- getDisplayContext().getDisplay().getDisplayInfo(displayInfo);
- int screenWidth = displayInfo.getNaturalWidth();
- float imgWidth = Math.min(mImgWidth > 0 ? screenWidth / (float) mImgWidth : 1.f, 1.f);
- mPageWidth = imgWidth;
- mPageOffset = (1 - imgWidth) / (float) (mPages - 1);
- }
-
private void updateMiniBitmap(Bitmap b) {
if (b == null) return;
int size = Math.min(b.getWidth(), b.getHeight());
@@ -204,6 +199,8 @@ public class ImageWallpaper extends WallpaperService {
@Override
public void onDestroy() {
+ getDisplayContext().getSystemService(DisplayManager.class)
+ .unregisterDisplayListener(this);
mMiniBitmap = null;
mWorker.getThreadHandler().post(() -> {
mRenderer.finish();
@@ -268,6 +265,16 @@ public class ImageWallpaper extends WallpaperService {
* (1-Wr)].
*/
private RectF pageToImgRect(RectF area) {
+ if (!mDisplaySizeValid) {
+ Rect window = getDisplayContext()
+ .getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics()
+ .getBounds();
+ mDisplayWidth = window.width();
+ mDisplayHeight = window.height();
+ mDisplaySizeValid = true;
+ }
+
// Width of a page for the caller of this API.
float virtualPageWidth = 1f / (float) mPages;
float leftPosOnPage = (area.left % virtualPageWidth) / virtualPageWidth;
@@ -275,12 +282,24 @@ public class ImageWallpaper extends WallpaperService {
int currentPage = (int) Math.floor(area.centerX() / virtualPageWidth);
RectF imgArea = new RectF();
+
+ if (mImgWidth == 0 || mImgHeight == 0 || mDisplayWidth <= 0 || mDisplayHeight <= 0) {
+ return imgArea;
+ }
+
imgArea.bottom = area.bottom;
imgArea.top = area.top;
+
+ float imageScale = Math.min(((float) mImgHeight) / mDisplayHeight, 1);
+ float mappedScreenWidth = mDisplayWidth * imageScale;
+ float pageWidth = Math.min(1.0f,
+ mImgWidth > 0 ? mappedScreenWidth / (float) mImgWidth : 1.f);
+ float pageOffset = (1 - pageWidth) / (float) (mPages - 1);
+
imgArea.left = MathUtils.constrain(
- leftPosOnPage * mPageWidth + currentPage * mPageOffset, 0, 1);
+ leftPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
imgArea.right = MathUtils.constrain(
- rightPosOnPage * mPageWidth + currentPage * mPageOffset, 0, 1);
+ rightPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
if (imgArea.left > imgArea.right) {
// take full page
imgArea.left = 0;
@@ -293,7 +312,6 @@ public class ImageWallpaper extends WallpaperService {
private List<WallpaperColors> getLocalWallpaperColors(@NonNull List<RectF> areas,
Bitmap b) {
List<WallpaperColors> colors = new ArrayList<>(areas.size());
- updateShift();
for (int i = 0; i < areas.size(); i++) {
RectF area = pageToImgRect(areas.get(i));
if (area == null || !LOCAL_COLOR_BOUNDS.contains(area)) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e9c565377530..f653088e552a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -797,8 +797,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
}
static boolean shouldDrawCutout(Context context) {
- return context.getResources().getBoolean(
- com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
+ return DisplayCutout.getFillBuiltInDisplayCutout(
+ context.getResources(), context.getDisplay().getUniqueId());
}
private void updateLayoutParams() {
@@ -1085,7 +1085,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
int dw = flipped ? lh : lw;
int dh = flipped ? lw : lh;
- Path path = DisplayCutout.pathFromResources(getResources(), dw, dh);
+ Path path = DisplayCutout.pathFromResources(
+ getResources(), getDisplay().getUniqueId(), dw, dh);
if (path != null) {
mBoundingPath.set(path);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index affad7a57d86..3555e8d8e193 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -25,6 +25,8 @@ import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.PendingIntent;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Handler;
@@ -73,6 +75,9 @@ public class SwipeHelper implements Gefingerpoken {
private final FlingAnimationUtils mFlingAnimationUtils;
private float mPagingTouchSlop;
private final float mSlopMultiplier;
+ private int mTouchSlop;
+ private float mTouchSlopMultiplier;
+
private final Callback mCallback;
private final int mSwipeDirection;
private final VelocityTracker mVelocityTracker;
@@ -105,6 +110,10 @@ public class SwipeHelper implements Gefingerpoken {
final int y = (int) mDownLocation[1] - mViewOffset[1];
mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
+
+ if (isAvailableToDragAndDrop(mTouchedView)) {
+ mCallback.onLongPressSent(mTouchedView);
+ }
}
}
}
@@ -126,6 +135,8 @@ public class SwipeHelper implements Gefingerpoken {
mVelocityTracker = VelocityTracker.obtain();
mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
+ mTouchSlop = viewConfiguration.getScaledTouchSlop();
+ mTouchSlopMultiplier = viewConfiguration.getAmbiguousGestureMultiplier();
// Extra long-press!
mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
@@ -297,7 +308,9 @@ public class SwipeHelper implements Gefingerpoken {
mIsSwiping = false;
mSnappingChild = false;
mLongPressSent = false;
+ mCallback.onLongPressSent(null);
mVelocityTracker.clear();
+ cancelLongPress();
mTouchedView = mCallback.getChildAtPosition(ev);
if (mTouchedView != null) {
@@ -349,6 +362,7 @@ public class SwipeHelper implements Gefingerpoken {
mIsSwiping = false;
mTouchedView = null;
mLongPressSent = false;
+ mCallback.onLongPressSent(null);
mMenuRowIntercepting = false;
cancelLongPress();
if (captured) return true;
@@ -438,6 +452,12 @@ public class SwipeHelper implements Gefingerpoken {
private boolean mCancelled;
@Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mCallback.onBeginDrag(animView);
+ }
+
+ @Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
@@ -593,11 +613,7 @@ public class SwipeHelper implements Gefingerpoken {
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mLongPressSent && !mMenuRowIntercepting) {
- return true;
- }
-
- if (!mIsSwiping && !mMenuRowIntercepting) {
+ if (!mIsSwiping && !mMenuRowIntercepting && !mLongPressSent) {
if (mCallback.getChildAtPosition(ev) != null) {
// We are dragging directly over a card, make sure that we also catch the gesture
// even if nobody else wants the touch event.
@@ -623,30 +639,40 @@ public class SwipeHelper implements Gefingerpoken {
if (absDelta >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
}
- // don't let items that can't be dismissed be dragged more than
- // maxScrollDistance
- if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
- mTouchedView,
- delta > 0)) {
- float size = getSize(mTouchedView);
- float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
- if (absDelta >= size) {
- delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
- } else {
- int startPosition = mCallback.getConstrainSwipeStartPosition();
- if (absDelta > startPosition) {
- int signedStartPosition =
- (int) (startPosition * Math.signum(delta));
- delta = signedStartPosition
- + maxScrollDistance * (float) Math.sin(
- ((delta - signedStartPosition) / size) * (Math.PI / 2));
+
+ if (mLongPressSent) {
+ if (absDelta >= getTouchSlop(ev)) {
+ if (mTouchedView instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) mTouchedView)
+ .doDragCallback(ev.getX(), ev.getY());
+ }
+ }
+ } else {
+ // don't let items that can't be dismissed be dragged more than
+ // maxScrollDistance
+ if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
+ mTouchedView,
+ delta > 0)) {
+ float size = getSize(mTouchedView);
+ float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
+ if (absDelta >= size) {
+ delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
+ } else {
+ int startPosition = mCallback.getConstrainSwipeStartPosition();
+ if (absDelta > startPosition) {
+ int signedStartPosition =
+ (int) (startPosition * Math.signum(delta));
+ delta = signedStartPosition
+ + maxScrollDistance * (float) Math.sin(
+ ((delta - signedStartPosition) / size) * (Math.PI / 2));
+ }
}
}
- }
- setTranslation(mTouchedView, mTranslation + delta);
- updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
- onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
+ setTranslation(mTouchedView, mTranslation + delta);
+ updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
+ onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
+ }
}
break;
case MotionEvent.ACTION_UP:
@@ -747,6 +773,29 @@ public class SwipeHelper implements Gefingerpoken {
mIsSwiping = false;
}
+ private float getTouchSlop(MotionEvent event) {
+ // Adjust the touch slop if another gesture may be being performed.
+ return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+ ? mTouchSlop * mTouchSlopMultiplier
+ : mTouchSlop;
+ }
+
+ private boolean isAvailableToDragAndDrop(View v) {
+ if (v.getResources().getBoolean(R.bool.config_notificationToContents)) {
+ if (v instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
+ boolean canBubble = enr.getEntry().canBubble();
+ Notification notif = enr.getEntry().getSbn().getNotification();
+ PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+ : notif.fullScreenIntent;
+ if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public interface Callback {
View getChildAtPosition(MotionEvent ev);
@@ -771,6 +820,13 @@ public class SwipeHelper implements Gefingerpoken {
void onDragCancelled(View v);
/**
+ * Called when the child is long pressed and available to start drag and drop.
+ *
+ * @param v the view that was long pressed.
+ */
+ void onLongPressSent(View v);
+
+ /**
* Called when the child is snapped to a position.
*
* @param animView the view that was snapped.
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index e880cc8fd10a..c6a750af862c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -23,6 +23,8 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -48,6 +50,7 @@ public abstract class SystemUI implements Dumpable {
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
}
+ @VisibleForTesting
protected void onBootCompleted() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 865ca40b1f4c..4fd270131466 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -42,6 +42,8 @@ import com.android.systemui.util.NotificationChannels;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
/**
* Application class for SystemUI.
@@ -159,8 +161,17 @@ public class SystemUIApplication extends Application implements
*/
public void startServicesIfNeeded() {
- String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
- startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
+ final String[] names = SystemUIFactory.getInstance()
+ .getSystemUIServiceComponents(getResources());
+ final String[] additionalNames = SystemUIFactory.getInstance()
+ .getAdditionalSystemUIServiceComponents(getResources());
+
+ final ArrayList<String> serviceComponents = new ArrayList<>();
+ Collections.addAll(serviceComponents, names);
+ Collections.addAll(serviceComponents, additionalNames);
+
+ startServicesIfNeeded(/* metricsPrefix= */ "StartServices",
+ serviceComponents.toArray(new String[serviceComponents.size()]));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index a28223de2bf1..d31301aacbcb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -187,6 +187,13 @@ public class SystemUIFactory {
}
/**
+ * Returns the list of additional system UI components that should be started.
+ */
+ public String[] getAdditionalSystemUIServiceComponents(Resources resources) {
+ return resources.getStringArray(R.array.config_additionalSystemUIServiceComponents);
+ }
+
+ /**
* Returns the list of system UI components that should be started per user.
*/
public String[] getSystemUIServiceComponentsPerUser(Resources resources) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
index 9bedb1ea7563..fbb909f30121 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityButtonModeObserver.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import android.annotation.IntDef;
@@ -49,7 +50,8 @@ public class AccessibilityButtonModeObserver extends
@Retention(RetentionPolicy.SOURCE)
@IntDef({
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR,
- ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
+ ACCESSIBILITY_BUTTON_MODE_GESTURE
})
public @interface AccessibilityButtonMode {}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 17178fa8e606..e521c90961fb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -310,7 +310,8 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
}
void onConfigurationChanged(int configDiff) {
- if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ if ((configDiff & (ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE))
+ != 0) {
final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds);
mDraggableWindowBounds.set(getDraggableWindowBounds());
// Keep the Y position with the same height ratio before the window bounds and
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index ca2c034c5d32..2f88291d1994 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.util.Assert;
import java.util.Locale;
+import java.util.Optional;
import javax.inject.Inject;
@@ -139,10 +140,10 @@ public class SystemActions extends SystemUI {
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private final SystemActionsBroadcastReceiver mReceiver;
- private final Recents mRecents;
+ private final Optional<Recents> mRecentsOptional;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
- private final Lazy<StatusBar> mStatusBar;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final NotificationShadeWindowController mNotificationShadeController;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@@ -150,10 +151,10 @@ public class SystemActions extends SystemUI {
@Inject
public SystemActions(Context context,
NotificationShadeWindowController notificationShadeController,
- Lazy<StatusBar> statusBar,
- Recents recents) {
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Optional<Recents> recentsOptional) {
super(context);
- mRecents = recents;
+ mRecentsOptional = recentsOptional;
mReceiver = new SystemActionsBroadcastReceiver();
mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
mA11yManager = (AccessibilityManager) mContext.getSystemService(
@@ -161,9 +162,9 @@ public class SystemActions extends SystemUI {
mNotificationShadeController = notificationShadeController;
// Saving in instance variable since to prevent GC since
// NotificationShadeWindowController.registerCallback() only keeps weak references.
- mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing) ->
+ mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
registerOrUnregisterDismissNotificationShadeAction();
- mStatusBar = statusBar;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
@Override
@@ -242,8 +243,9 @@ public class SystemActions extends SystemUI {
// Saving state in instance variable since this callback is called quite often to avoid
// binder calls
- StatusBar statusBar = mStatusBar.get();
- if (statusBar.isPanelExpanded() && !statusBar.isKeyguardShowing()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (statusBarOptional.map(StatusBar::isPanelExpanded).orElse(false)
+ && !statusBarOptional.get().isKeyguardShowing()) {
if (!mDismissNotificationShadeActionRegistered) {
mA11yManager.registerSystemAction(
createRemoteAction(
@@ -368,15 +370,16 @@ public class SystemActions extends SystemUI {
}
private void handleRecents() {
- mRecents.toggleRecentApps();
+ mRecentsOptional.ifPresent(Recents::toggleRecentApps);
}
private void handleNotifications() {
- mStatusBar.get().animateExpandNotificationsPanel();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::animateExpandNotificationsPanel);
}
private void handleQuickSettings() {
- mStatusBar.get().animateExpandSettingsPanel(null);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.animateExpandSettingsPanel(null));
}
private void handlePowerDialog() {
@@ -425,7 +428,9 @@ public class SystemActions extends SystemUI {
}
private void handleAccessibilityDismissNotificationShade() {
- mStatusBar.get().animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, false /* force */);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
}
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index cee395bd9a6a..32813479dc0a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -91,7 +91,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
final WindowMagnificationController controller = new WindowMagnificationController(
- mContext,
+ windowContext,
mHandler, new SfVsyncFrameCallbackProvider(), null,
new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
return new WindowMagnificationAnimationController(windowContext, controller);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 717c7156f917..a51e3fcfd50b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.annotation.UiContext;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -35,6 +36,7 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
@@ -76,6 +78,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
private static final String TAG = "WindowMagnificationController";
+ @SuppressWarnings("isloggabletaglength")
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Build.IS_DEBUGGABLE;
// Delay to avoid updating state description too frequently.
private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100;
// It should be consistent with the value defined in WindowMagnificationGestureHandler.
@@ -158,14 +162,15 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mRotation = display.getRotation();
mWm = context.getSystemService(WindowManager.class);
- mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+ mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
mResources = mContext.getResources();
mScale = mResources.getInteger(R.integer.magnification_default_scale);
mBounceEffectDuration = mResources.getInteger(
com.android.internal.R.integer.config_shortAnimTime);
updateDimensions();
- setInitialStartBounds();
+ setMagnificationFrameWith(mWindowBounds, mWindowBounds.width() / 2,
+ mWindowBounds.height() / 2);
computeBounceAnimationScale();
mMirrorWindowControl = mirrorWindowControl;
@@ -286,18 +291,59 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
* @param configDiff a bit mask of the differences between the configurations
*/
void onConfigurationChanged(int configDiff) {
+ if (DEBUG) {
+ Log.d(TAG, "onConfigurationChanged = " + Configuration.configurationDiffToString(
+ configDiff));
+ }
+ if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ onRotate();
+ }
+
+ if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+ updateAccessibilityWindowTitleIfNeeded();
+ }
+
+ boolean reCreateWindow = false;
if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
updateDimensions();
computeBounceAnimationScale();
- if (isWindowVisible()) {
- deleteWindowMagnification();
- enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+ reCreateWindow = true;
+ }
+
+ if ((configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+ reCreateWindow |= handleScreenSizeChanged();
+ }
+
+ // Recreate the window again to correct the window appearance due to density or
+ // window size changed not caused by rotation.
+ if (isWindowVisible() && reCreateWindow) {
+ deleteWindowMagnification();
+ enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
+ }
+ }
+
+ /**
+ * Calculates the magnification frame if the window bounds is changed.
+ * Note that the orientation also changes the wind bounds, so it should be handled first.
+ *
+ * @return {@code true} if the magnification frame is changed with the new window bounds.
+ */
+ private boolean handleScreenSizeChanged() {
+ final Rect oldWindowBounds = new Rect(mWindowBounds);
+ final Rect currentWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
+
+ if (currentWindowBounds.equals(oldWindowBounds)) {
+ if (DEBUG) {
+ Log.d(TAG, "updateMagnificationFrame -- window bounds is not changed");
}
- } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
- onRotate();
- } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
- updateAccessibilityWindowTitleIfNeeded();
+ return false;
}
+ mWindowBounds.set(currentWindowBounds);
+ final float newCenterX = (getCenterX()) * mWindowBounds.width() / oldWindowBounds.width();
+ final float newCenterY = (getCenterY()) * mWindowBounds.height() / oldWindowBounds.height();
+ setMagnificationFrameWith(mWindowBounds, (int) newCenterX, (int) newCenterY);
+ calculateMagnificationFrameBoundary();
+ return true;
}
private void updateSystemUIStateIfNeeded() {
@@ -311,30 +357,42 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mWm.updateViewLayout(mMirrorView, params);
}
- /** Handles MirrorWindow position when the device rotation changed. */
+ /**
+ * Keep MirrorWindow position on the screen unchanged when device rotates 90° clockwise or
+ * anti-clockwise.
+ */
private void onRotate() {
final Display display = mContext.getDisplay();
final int oldRotation = mRotation;
- mWindowBounds = mWm.getCurrentWindowMetrics().getBounds();
-
- setMagnificationFrameBoundary();
mRotation = display.getRotation();
+ final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+ if (rotationDegree == 0 || rotationDegree == 180) {
+ Log.w(TAG, "onRotate -- rotate with the device. skip it");
+ return;
+ }
+ final Rect currentWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
+ if (currentWindowBounds.width() != mWindowBounds.height()
+ || currentWindowBounds.height() != mWindowBounds.width()) {
+ Log.w(TAG, "onRotate -- unexpected window height/width");
+ return;
+ }
+
+ mWindowBounds.set(currentWindowBounds);
+
+ calculateMagnificationFrameBoundary();
if (!isWindowVisible()) {
return;
}
// Keep MirrorWindow position on the screen unchanged when device rotates 90°
// clockwise or anti-clockwise.
- final int rotationDegree = getDegreeFromRotation(mRotation, oldRotation);
+
final Matrix matrix = new Matrix();
matrix.setRotate(rotationDegree);
if (rotationDegree == 90) {
matrix.postTranslate(mWindowBounds.width(), 0);
} else if (rotationDegree == 270) {
matrix.postTranslate(0, mWindowBounds.height());
- } else {
- Log.w(TAG, "Invalid rotation change. " + rotationDegree);
- return;
}
// The rect of MirrorView is going to be transformed.
LayoutParams params =
@@ -440,12 +498,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
}
- private void setInitialStartBounds() {
+ private void setMagnificationFrameWith(Rect windowBounds, int centerX, int centerY) {
// Sets the initial frame area for the mirror and places it in the center of the display.
- final int initSize = Math.min(mWindowBounds.width(), mWindowBounds.height()) / 2
+ final int initSize = Math.min(windowBounds.width(), windowBounds.height()) / 2
+ 2 * mMirrorSurfaceMargin;
- final int initX = mWindowBounds.width() / 2 - initSize / 2;
- final int initY = mWindowBounds.height() / 2 - initSize / 2;
+ final int initX = centerX - initSize / 2;
+ final int initY = centerY - initSize / 2;
mMagnificationFrame.set(initX, initY, initX + initSize, initY + initSize);
}
@@ -553,7 +611,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mSourceBounds.set(left, top, right, bottom);
}
- private void setMagnificationFrameBoundary() {
+ private void calculateMagnificationFrameBoundary() {
// Calculates width and height for magnification frame could exceed out the screen.
// TODO : re-calculating again when scale is changed.
// The half width of magnification frame.
@@ -644,7 +702,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
: centerY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
- setMagnificationFrameBoundary();
+ calculateMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isWindowVisible()) {
createMirrorWindow();
@@ -764,6 +822,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
pw.println(" mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
pw.println(" mScale:" + mScale);
pw.println(" mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+ pw.println(" mSourceBounds:"
+ + (isWindowVisible() ? mSourceBounds : "empty"));
pw.println(" mSystemGestureTop:" + mSystemGestureTop);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
index 81a13a236685..408201558a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
@@ -57,7 +57,9 @@ public class AssistOrbController {
public void run() {
mView.removeCallbacks(this);
mView.show(false /* show */, true /* animate */, () -> {
- mWindowManager.removeView(mView);
+ if (mView.isAttachedToWindow()) {
+ mWindowManager.removeView(mView);
+ }
});
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 169a9c0c6eac..f13730e602a0 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -70,7 +70,7 @@ public final class PhoneStateMonitor {
};
private final Context mContext;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final StatusBarStateController mStatusBarStateController;
private boolean mLauncherShowing;
@@ -78,7 +78,7 @@ public final class PhoneStateMonitor {
@Inject
PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache) {
mContext = context;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
@@ -180,8 +180,7 @@ public final class PhoneStateMonitor {
}
private boolean isBouncerShowing() {
- return mStatusBarOptionalLazy.map(
- statusBarLazy -> statusBarLazy.get().isBouncerShowing()).orElse(false);
+ return mStatusBarOptionalLazy.get().map(StatusBar::isBouncerShowing).orElse(false);
}
private boolean isKeyguardLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index c9e67715decb..5616a00592f2 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -40,10 +40,10 @@ import com.android.systemui.people.widget.PeopleBackupHelper
* After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
* indicating that restoring is finished for a given user.
*/
-class BackupHelper : BackupAgentHelper() {
+open class BackupHelper : BackupAgentHelper() {
companion object {
- private const val TAG = "BackupHelper"
+ const val TAG = "BackupHelper"
internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index deceb951c2bb..66085ac0a915 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 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.
@@ -13,64 +13,50 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui;
+package com.android.systemui.battery;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.database.ContentObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
+import android.os.UserHandle;
import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.StyleRes;
+import androidx.annotation.VisibleForTesting;
import com.android.settingslib.graph.ThemedBatteryDrawable;
+import com.android.systemui.DualToneHandler;
+import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
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.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.text.NumberFormat;
-public class BatteryMeterView extends LinearLayout implements
- BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
-
+public class BatteryMeterView extends LinearLayout implements DarkReceiver {
@Retention(SOURCE)
@IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF, MODE_ESTIMATE})
@@ -81,21 +67,14 @@ public class BatteryMeterView extends LinearLayout implements
public static final int MODE_ESTIMATE = 3;
private final ThemedBatteryDrawable mDrawable;
- private final String mSlotBattery;
private final ImageView mBatteryIconView;
- private final CurrentUserTracker mUserTracker;
private TextView mBatteryPercentView;
- private BatteryController mBatteryController;
- private SettingObserver mSettingObserver;
private final @StyleRes int mPercentageStyleId;
private int mTextColor;
private int mLevel;
private int mShowPercentMode = MODE_DEFAULT;
private boolean mShowPercentAvailable;
- // Some places may need to show the battery conditionally, and not obey the tuner
- private boolean mIgnoreTunerUpdates;
- private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
// Error state where we know nothing about the current battery state
private boolean mBatteryStateUnknown;
@@ -103,19 +82,19 @@ public class BatteryMeterView extends LinearLayout implements
private Drawable mUnknownStateDrawable;
private DualToneHandler mDualToneHandler;
- private int mUser;
private int mNonAdaptedSingleToneColor;
private int mNonAdaptedForegroundColor;
private int mNonAdaptedBackgroundColor;
+ private BatteryEstimateFetcher mBatteryEstimateFetcher;
+
public BatteryMeterView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
@@ -128,14 +107,11 @@ public class BatteryMeterView extends LinearLayout implements
mDrawable = new ThemedBatteryDrawable(context, frameColor);
atts.recycle();
- mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
mShowPercentAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_battery_percentage_setting_available);
setupLayoutTransition();
- mSlotBattery = context.getString(
- com.android.internal.R.string.status_bar_battery);
mBatteryIconView = new ImageView(context);
mBatteryIconView.setImageDrawable(mDrawable);
final MarginLayoutParams mlp = new MarginLayoutParams(
@@ -150,21 +126,8 @@ public class BatteryMeterView extends LinearLayout implements
// Init to not dark at all.
onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
- mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
- @Override
- public void onUserSwitched(int newUserId) {
- mUser = newUserId;
- getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
- getContext().getContentResolver().registerContentObserver(
- Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
- newUserId);
- updateShowPercent();
- }
- };
-
setClipChildren(false);
setClipToPadding(false);
- Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
}
private void setupLayoutTransition() {
@@ -192,6 +155,7 @@ public class BatteryMeterView extends LinearLayout implements
* 0 - No preference
* 1 - Force on
* 2 - Force off
+ * 3 - Estimate
* @param mode desired mode (none, on, off)
*/
public void setPercentShowMode(@BatteryPercentMode int mode) {
@@ -200,44 +164,6 @@ public class BatteryMeterView extends LinearLayout implements
updateShowPercent();
}
- /**
- * Set {@code true} to turn off BatteryMeterView's subscribing to the tuner for updates, and
- * thus avoid it controlling its own visibility
- *
- * @param ignore whether to ignore the tuner or not
- */
- public void setIgnoreTunerUpdates(boolean ignore) {
- mIgnoreTunerUpdates = ignore;
- updateTunerSubscription();
- }
-
- private void updateTunerSubscription() {
- if (mIgnoreTunerUpdates) {
- unsubscribeFromTunerUpdates();
- } else {
- subscribeForTunerUpdates();
- }
- }
-
- private void subscribeForTunerUpdates() {
- if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
- return;
- }
-
- Dependency.get(TunerService.class)
- .addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
- mIsSubscribedForTunerUpdates = true;
- }
-
- private void unsubscribeFromTunerUpdates() {
- if (!mIsSubscribedForTunerUpdates) {
- return;
- }
-
- Dependency.get(TunerService.class).removeTunable(this);
- mIsSubscribedForTunerUpdates = false;
- }
-
public void setColorsFromContext(Context context) {
if (context == null) {
return;
@@ -251,42 +177,7 @@ public class BatteryMeterView extends LinearLayout implements
return false;
}
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
- ArraySet<String> icons = StatusBarIconController.getIconHideList(
- getContext(), newValue);
- setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
- }
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- mBatteryController = Dependency.get(BatteryController.class);
- mBatteryController.addCallback(this);
- mUser = ActivityManager.getCurrentUser();
- getContext().getContentResolver().registerContentObserver(
- Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
- getContext().getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
- false, mSettingObserver);
- updateShowPercent();
- subscribeForTunerUpdates();
- mUserTracker.startTracking();
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mUserTracker.stopTracking();
- mBatteryController.removeCallback(this);
- getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
- unsubscribeFromTunerUpdates();
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ void onBatteryLevelChanged(int level, boolean pluggedIn) {
mDrawable.setCharging(pluggedIn);
mDrawable.setBatteryLevel(level);
mCharging = pluggedIn;
@@ -294,8 +185,7 @@ public class BatteryMeterView extends LinearLayout implements
updatePercentText();
}
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
+ void onPowerSaveChanged(boolean isPowerSave) {
mDrawable.setPowerSaveEnabled(isPowerSave);
}
@@ -315,19 +205,28 @@ public class BatteryMeterView extends LinearLayout implements
updateShowPercent();
}
- private void updatePercentText() {
+ /**
+ * Sets the fetcher that should be used to get the estimated time remaining for the user's
+ * battery.
+ */
+ void setBatteryEstimateFetcher(BatteryEstimateFetcher fetcher) {
+ mBatteryEstimateFetcher = fetcher;
+ }
+
+ void updatePercentText() {
if (mBatteryStateUnknown) {
setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
return;
}
- if (mBatteryController == null) {
+ if (mBatteryEstimateFetcher == null) {
return;
}
if (mBatteryPercentView != null) {
if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
- mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
+ mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate(
+ (String estimate) -> {
if (mBatteryPercentView == null) {
return;
}
@@ -361,12 +260,12 @@ public class BatteryMeterView extends LinearLayout implements
: R.string.accessibility_battery_level, mLevel));
}
- private void updateShowPercent() {
+ void updateShowPercent() {
final boolean showing = mBatteryPercentView != null;
// TODO(b/140051051)
final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System
.getIntForUser(getContext().getContentResolver(),
- SHOW_BATTERY_PERCENT, 0, mUser));
+ SHOW_BATTERY_PERCENT, 0, UserHandle.USER_CURRENT));
boolean shouldShow =
(mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
|| mShowPercentMode == MODE_ON
@@ -394,11 +293,6 @@ public class BatteryMeterView extends LinearLayout implements
}
}
- @Override
- public void onDensityOrFontScaleChanged() {
- scaleBatteryMeterViews();
- }
-
private Drawable getUnknownStateDrawable() {
if (mUnknownStateDrawable == null) {
mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown);
@@ -408,8 +302,7 @@ public class BatteryMeterView extends LinearLayout implements
return mUnknownStateDrawable;
}
- @Override
- public void onBatteryUnknownStateChanged(boolean isUnknown) {
+ void onBatteryUnknownStateChanged(boolean isUnknown) {
if (mBatteryStateUnknown == isUnknown) {
return;
}
@@ -428,7 +321,7 @@ public class BatteryMeterView extends LinearLayout implements
/**
* Looks up the scale factor for status bar icons and scales the battery view by that amount.
*/
- private void scaleBatteryMeterViews() {
+ void scaleBatteryMeterViews() {
Resources res = getContext().getResources();
TypedValue typedValue = new TypedValue();
@@ -489,20 +382,15 @@ public class BatteryMeterView extends LinearLayout implements
pw.println(" mMode: " + mShowPercentMode);
}
- private final class SettingObserver extends ContentObserver {
- public SettingObserver(Handler handler) {
- super(handler);
- }
+ @VisibleForTesting
+ CharSequence getBatteryPercentViewText() {
+ return mBatteryPercentView.getText();
+ }
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
- updateShowPercent();
- if (TextUtils.equals(uri.getLastPathSegment(),
- Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
- // update the text for sure if the estimate in the cache was updated
- updatePercentText();
- }
- }
+ /** An interface that will fetch the estimated time remaining for the user's battery. */
+ public interface BatteryEstimateFetcher {
+ void fetchBatteryTimeRemainingEstimate(
+ BatteryController.EstimateFetchCompletion completion);
}
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
new file mode 100644
index 000000000000..ae9a32309d45
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.battery;
+
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.View;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/** Controller for {@link BatteryMeterView}. **/
+public class BatteryMeterViewController extends ViewController<BatteryMeterView> {
+ private final ConfigurationController mConfigurationController;
+ private final TunerService mTunerService;
+ private final ContentResolver mContentResolver;
+ private final BatteryController mBatteryController;
+
+ private final String mSlotBattery;
+ private final SettingObserver mSettingObserver;
+ private final CurrentUserTracker mCurrentUserTracker;
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mView.scaleBatteryMeterViews();
+ }
+ };
+
+ private final TunerService.Tunable mTunable = new TunerService.Tunable() {
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ ArraySet<String> icons = StatusBarIconController.getIconHideList(
+ getContext(), newValue);
+ mView.setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
+ }
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ mView.onBatteryLevelChanged(level, pluggedIn);
+ }
+
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ mView.onPowerSaveChanged(isPowerSave);
+ }
+
+ @Override
+ public void onBatteryUnknownStateChanged(boolean isUnknown) {
+ mView.onBatteryUnknownStateChanged(isUnknown);
+ }
+ };
+
+ // Some places may need to show the battery conditionally, and not obey the tuner
+ private boolean mIgnoreTunerUpdates;
+ private boolean mIsSubscribedForTunerUpdates;
+
+ @Inject
+ public BatteryMeterViewController(
+ BatteryMeterView view,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController) {
+ super(view);
+ mConfigurationController = configurationController;
+ mTunerService = tunerService;
+ mContentResolver = contentResolver;
+ mBatteryController = batteryController;
+
+ mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString);
+
+ mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery);
+ mSettingObserver = new SettingObserver(mainHandler);
+ mCurrentUserTracker = new CurrentUserTracker(broadcastDispatcher) {
+ @Override
+ public void onUserSwitched(int newUserId) {
+ contentResolver.unregisterContentObserver(mSettingObserver);
+ registerShowBatteryPercentObserver(newUserId);
+ mView.updateShowPercent();
+ }
+ };
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mConfigurationController.addCallback(mConfigurationListener);
+ subscribeForTunerUpdates();
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+
+ registerShowBatteryPercentObserver(ActivityManager.getCurrentUser());
+ registerGlobalBatteryUpdateObserver();
+ mCurrentUserTracker.startTracking();
+
+ mView.updateShowPercent();
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
+ unsubscribeFromTunerUpdates();
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+
+ mCurrentUserTracker.stopTracking();
+ mContentResolver.unregisterContentObserver(mSettingObserver);
+ }
+
+ /**
+ * Turn off {@link BatteryMeterView}'s subscribing to the tuner for updates, and thus avoid it
+ * controlling its own visibility.
+ */
+ public void ignoreTunerUpdates() {
+ mIgnoreTunerUpdates = true;
+ unsubscribeFromTunerUpdates();
+ }
+
+ private void subscribeForTunerUpdates() {
+ if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
+ return;
+ }
+
+ mTunerService.addTunable(mTunable, StatusBarIconController.ICON_HIDE_LIST);
+ mIsSubscribedForTunerUpdates = true;
+ }
+
+ private void unsubscribeFromTunerUpdates() {
+ if (!mIsSubscribedForTunerUpdates) {
+ return;
+ }
+
+ mTunerService.removeTunable(mTunable);
+ mIsSubscribedForTunerUpdates = false;
+ }
+
+ private void registerShowBatteryPercentObserver(int user) {
+ mContentResolver.registerContentObserver(
+ Settings.System.getUriFor(SHOW_BATTERY_PERCENT),
+ false,
+ mSettingObserver,
+ user);
+ }
+
+ private void registerGlobalBatteryUpdateObserver() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
+ false,
+ mSettingObserver);
+ }
+
+ private final class SettingObserver extends ContentObserver {
+ public SettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ mView.updateShowPercent();
+ if (TextUtils.equals(uri.getLastPathSegment(),
+ Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
+ // update the text for sure if the estimate in the cache was updated
+ mView.updatePercentText();
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index b7344fbc6dda..a2e55c0f76e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -28,6 +28,7 @@ import com.android.systemui.util.ViewController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* Handles:
@@ -42,7 +43,7 @@ import java.io.PrintWriter;
abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
extends ViewController<T> implements Dumpable {
@NonNull final StatusBarStateController mStatusBarStateController;
- @NonNull final StatusBar mStatusBar;
+ @NonNull final Optional<StatusBar> mStatusBarOptional;
@NonNull final DumpManager mDumpManger;
boolean mNotificationShadeExpanded;
@@ -50,11 +51,11 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
protected UdfpsAnimationViewController(
T view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
super(view);
mStatusBarStateController = statusBarStateController;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
mDumpManger = dumpManager;
}
@@ -62,13 +63,17 @@ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
@Override
protected void onViewAttached() {
- mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.addExpansionChangedListener(
+ mStatusBarExpansionChangedListener));
mDumpManger.registerDumpable(getDumpTag(), this);
}
@Override
protected void onViewDetached() {
- mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.removeExpansionChangedListener(
+ mStatusBarExpansionChangedListener));
mDumpManger.unregisterDumpable(getDumpTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
index 93d80e29aded..85955e1b5d56 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -22,6 +22,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations for biometric prompt.
*/
@@ -29,9 +31,9 @@ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
protected UdfpsBpViewController(
@NonNull UdfpsBpView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index e612fb4712fc..b5378cd22904 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -43,7 +43,6 @@ import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -76,6 +75,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.time.SystemClock;
import java.util.Optional;
@@ -109,7 +109,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
- @NonNull private final StatusBar mStatusBar;
+ @NonNull private final Optional<StatusBar> mStatusBarOptional;
@NonNull private final StatusBarStateController mStatusBarStateController;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@@ -125,6 +125,7 @@ public class UdfpsController implements DozeReceiver {
@Nullable private final UdfpsHbmProvider mHbmProvider;
@NonNull private final KeyguardBypassController mKeyguardBypassController;
@NonNull private final ConfigurationController mConfigurationController;
+ @NonNull private final SystemClock mSystemClock;
@VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -449,19 +450,19 @@ public class UdfpsController implements DozeReceiver {
final String touchInfo = String.format(
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
- final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
+ final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
if (!isIlluminationRequested && !mGoodCaptureReceived &&
!exceedsVelocityThreshold) {
onFingerDown((int) event.getRawX(), (int) event.getRawY(), minor,
major);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
- mTouchLogTime = SystemClock.elapsedRealtime();
- mPowerManager.userActivity(SystemClock.uptimeMillis(),
+ mTouchLogTime = mSystemClock.elapsedRealtime();
+ mPowerManager.userActivity(mSystemClock.uptimeMillis(),
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
- mTouchLogTime = SystemClock.elapsedRealtime();
+ mTouchLogTime = mSystemClock.elapsedRealtime();
}
} else {
Log.v(TAG, "onTouch | finger outside");
@@ -508,7 +509,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -525,7 +526,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull KeyguardBypassController keyguardBypassController,
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
- @NonNull ConfigurationController configurationController) {
+ @NonNull ConfigurationController configurationController,
+ @NonNull SystemClock systemClock) {
mContext = context;
mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
@@ -537,7 +539,7 @@ public class UdfpsController implements DozeReceiver {
mFingerprintManager = checkNotNull(fingerprintManager);
mWindowManager = windowManager;
mFgExecutor = fgExecutor;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -561,6 +563,7 @@ public class UdfpsController implements DozeReceiver {
mainHandler);
mKeyguardBypassController = keyguardBypassController;
mConfigurationController = configurationController;
+ mSystemClock = systemClock;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
@@ -763,7 +766,7 @@ public class UdfpsController implements DozeReceiver {
enrollView,
mServerRequest.mEnrollHelper,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
@@ -773,7 +776,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsKeyguardViewController(
keyguardView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mKeyguardViewManager,
mKeyguardUpdateMonitor,
mFgExecutor,
@@ -781,6 +784,7 @@ public class UdfpsController implements DozeReceiver {
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
+ mSystemClock,
this
);
case IUdfpsOverlayController.REASON_AUTH_BP:
@@ -790,7 +794,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsBpViewController(
bpView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
@@ -800,7 +804,7 @@ public class UdfpsController implements DozeReceiver {
return new UdfpsFpmOtherViewController(
authOtherView,
mStatusBarStateController,
- mStatusBar,
+ mStatusBarOptional,
mDumpManager
);
default:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
index 3dab010d917c..54244a16ed78 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -24,6 +24,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations during enrollment.
*/
@@ -48,9 +50,9 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
@NonNull UdfpsEnrollView view,
@NonNull UdfpsEnrollHelper enrollHelper,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
mEnrollProgressBarRadius = getContext().getResources()
.getInteger(R.integer.config_udfpsEnrollProgressBar);
mEnrollHelper = enrollHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
index 6e2e4baf492b..dcb5aefc8aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -22,6 +22,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Optional;
+
/**
* Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
* states.
@@ -32,9 +34,9 @@ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmO
protected UdfpsFpmOtherViewController(
@NonNull UdfpsFpmOtherView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull DumpManager dumpManager) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 4896305daa2e..b81b54720fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -35,9 +35,11 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
/**
@@ -50,6 +52,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final ConfigurationController mConfigurationController;
+ @NonNull private final SystemClock mSystemClock;
@NonNull private final UdfpsController mUdfpsController;
private boolean mShowingUdfpsBouncer;
@@ -59,6 +62,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
private int mStatusBarState;
private float mTransitionToFullShadeProgress;
private float mLastDozeAmount;
+ private long mLastUdfpsBouncerShowTime = -1;
/**
* hidden amount of pin/pattern/password bouncer
@@ -71,7 +75,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
protected UdfpsKeyguardViewController(
@NonNull UdfpsKeyguardView view,
@NonNull StatusBarStateController statusBarStateController,
- @NonNull StatusBar statusBar,
+ @NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull DelayableExecutor mainDelayableExecutor,
@@ -79,14 +83,16 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
@NonNull ConfigurationController configurationController,
+ @NonNull SystemClock systemClock,
@NonNull UdfpsController udfpsController) {
- super(view, statusBarStateController, statusBar, dumpManager);
+ super(view, statusBarStateController, statusBarOptional, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mExecutor = mainDelayableExecutor;
mKeyguardViewMediator = keyguardViewMediator;
mLockScreenShadeTransitionController = transitionController;
mConfigurationController = configurationController;
+ mSystemClock = systemClock;
mUdfpsController = udfpsController;
}
@@ -155,6 +161,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
}
mShowingUdfpsBouncer = show;
+ if (mShowingUdfpsBouncer) {
+ mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
+ }
updatePauseAuth();
if (mShowingUdfpsBouncer) {
if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
@@ -218,16 +227,25 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
* If we were previously showing the udfps bouncer, hide it and instead show the regular
* (pin/pattern/password) bouncer.
*
- * Does nothing if we weren't previously showing the udfps bouncer.
+ * Does nothing if we weren't previously showing the UDFPS bouncer.
*/
private void maybeShowInputBouncer() {
- if (mShowingUdfpsBouncer) {
+ if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
mKeyguardViewManager.resetAlternateAuth(false);
}
}
/**
+ * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
+ * of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
+ * bouncer.
+ */
+ private boolean hasUdfpsBouncerShownWithMinTime() {
+ return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200;
+ }
+
+ /**
* Set the progress we're currently transitioning to the full shade. 0.0f means we're not
* transitioning yet, while 1.0f means we've fully dragged down.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
new file mode 100644
index 000000000000..6e2fcd11f643
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostView.java
@@ -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.
+ */
+
+package com.android.systemui.communal;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Container for communal presentation. Containing communal-related view to this parent view allows
+ * for aggregate measurement/layout adjustments and capturing said values before the communal views
+ * might be available.
+ */
+public class CommunalHostView extends FrameLayout {
+ public CommunalHostView(@NonNull Context context) {
+ this(context, null, 0);
+ }
+
+ public CommunalHostView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CommunalHostView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
new file mode 100644
index 000000000000..320c2ea5ba78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalHostViewController.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Injectable controller for {@link CommunalHostView}.
+ */
+public class CommunalHostViewController extends ViewController<CommunalHostView> {
+ private static final String TAG = "CommunalController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String STATE_LIST_FORMAT = "[%s]";
+
+ private final Executor mMainExecutor;
+ private final CommunalStateController mCommunalStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardStateController mKeyguardStateController;
+ private final StatusBarStateController mStatusBarStateController;
+ private WeakReference<CommunalSource> mLastSource;
+ private int mState;
+ private float mQsExpansion;
+ private float mShadeExpansion;
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef({STATE_KEYGUARD_SHOWING, STATE_DOZING, STATE_BOUNCER_SHOWING, STATE_KEYGUARD_OCCLUDED})
+ public @interface State {}
+
+ private static final int STATE_KEYGUARD_SHOWING = 1 << 0;
+ private static final int STATE_DOZING = 1 << 1;
+ private static final int STATE_BOUNCER_SHOWING = 1 << 2;
+ private static final int STATE_KEYGUARD_OCCLUDED = 1 << 3;
+
+ // Only show communal view when keyguard is showing and not dozing.
+ private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
+ private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
+ STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
+
+ private ViewController<? extends View> mCommunalViewController;
+
+ private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ if (DEBUG) {
+ Log.d(TAG, "onKeyguardBouncerChanged:" + bouncer);
+ }
+
+ setState(STATE_BOUNCER_SHOWING, bouncer);
+ }
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ if (DEBUG) {
+ Log.d(TAG, "onKeyguardOccludedChanged" + occluded);
+ }
+
+ setState(STATE_KEYGUARD_OCCLUDED, occluded);
+ }
+ };
+
+ private KeyguardStateController.Callback mKeyguardCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ final boolean isShowing = mKeyguardStateController.isShowing();
+ if (DEBUG) {
+ Log.d(TAG, "setKeyguardShowing:" + isShowing);
+ }
+
+ setState(STATE_KEYGUARD_SHOWING, isShowing);
+ }
+ };
+
+ private StatusBarStateController.StateListener mDozeCallback =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (DEBUG) {
+ Log.d(TAG, "setDozing:" + isDozing);
+ }
+
+ setState(STATE_DOZING, isDozing);
+ }
+ };
+
+ @Inject
+ protected CommunalHostViewController(@Main Executor mainExecutor,
+ CommunalStateController communalStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController, CommunalHostView view) {
+ super(view);
+ mCommunalStateController = communalStateController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mMainExecutor = mainExecutor;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+
+ setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
+ setState(STATE_DOZING, mStatusBarStateController.isDozing());
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mKeyguardStateController.addCallback(mKeyguardCallback);
+ mStatusBarStateController.addCallback(mDozeCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
+ mStatusBarStateController.removeCallback(mDozeCallback);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ }
+
+ private void setState(@State int stateFlag, boolean enabled) {
+ final int existingState = mState;
+ if (DEBUG) {
+ Log.d(TAG, "setState flag:" + describeState(stateFlag) + " enabled:" + enabled);
+ }
+
+ if (enabled) {
+ mState |= stateFlag;
+ } else {
+ mState &= ~stateFlag;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "updated state:" + describeState());
+ }
+
+ if (existingState != mState) {
+ showSource();
+ }
+ }
+
+ private String describeState(@State int stateFlag) {
+ switch(stateFlag) {
+ case STATE_DOZING:
+ return "dozing";
+ case STATE_BOUNCER_SHOWING:
+ return "bouncer_showing";
+ case STATE_KEYGUARD_SHOWING:
+ return "keyguard_showing";
+ default:
+ return "UNDEFINED_STATE";
+ }
+ }
+
+ private String describeState() {
+ StringBuilder stringBuilder = new StringBuilder();
+
+ if ((mState & STATE_KEYGUARD_SHOWING) == STATE_KEYGUARD_SHOWING) {
+ stringBuilder.append(String.format(STATE_LIST_FORMAT,
+ describeState(STATE_KEYGUARD_SHOWING)));
+ }
+ if ((mState & STATE_DOZING) == STATE_DOZING) {
+ stringBuilder.append(String.format(STATE_LIST_FORMAT,
+ describeState(STATE_DOZING)));
+ }
+ if ((mState & STATE_BOUNCER_SHOWING) == STATE_BOUNCER_SHOWING) {
+ stringBuilder.append(String.format(STATE_LIST_FORMAT,
+ describeState(STATE_BOUNCER_SHOWING)));
+ }
+
+ return stringBuilder.toString();
+ }
+
+ private void showSource() {
+ // Make sure all necessary states are present for showing communal and all invalid states
+ // are absent
+ mMainExecutor.execute(() -> {
+ final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null;
+
+ if (DEBUG) {
+ Log.d(TAG, "showSource. currentSource:" + currentSource);
+ }
+
+ if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
+ && (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
+ && currentSource != null) {
+ mView.removeAllViews();
+
+ // Make view visible.
+ mView.setVisibility(View.VISIBLE);
+
+ final Context context = mView.getContext();
+
+ final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
+ currentSource.requestCommunalView(context);
+
+ if (listenableFuture == null) {
+ Log.e(TAG, "could not request communal view");
+ return;
+ }
+
+ listenableFuture.addListener(() -> {
+ try {
+ final CommunalSource.CommunalViewResult result = listenableFuture.get();
+ result.view.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mView.addView(result.view);
+
+ mCommunalViewController = result.viewController;
+ mCommunalViewController.init();
+ } catch (Exception e) {
+ Log.e(TAG, "could not obtain communal view through callback:" + e);
+ }
+ }, mMainExecutor);
+ } else {
+ mView.removeAllViews();
+ mView.setVisibility(View.INVISIBLE);
+ mCommunalStateController.setCommunalViewShowing(false);
+ }
+ });
+ }
+
+ /**
+ * Instructs {@link CommunalHostViewController} to display provided source.
+ *
+ * @param source The new {@link CommunalSource}, {@code null} if not set.
+ */
+ public void show(WeakReference<CommunalSource> source) {
+ mLastSource = source;
+ showSource();
+ }
+
+ /**
+ * Invoked when the quick settings is expanded.
+ * @param expansionFraction the percentage the QS shade has been expanded.
+ */
+ public void updateQsExpansion(float expansionFraction) {
+ mQsExpansion = expansionFraction;
+ updateCommunalViewOccluded();
+ }
+
+ /**
+ * Invoked when the main shade is expanded.
+ * @param shadeExpansion the percentage the main shade has expanded.
+ */
+ public void updateShadeExpansion(float shadeExpansion) {
+ mShadeExpansion = shadeExpansion;
+ updateCommunalViewOccluded();
+ }
+
+ private void updateCommunalViewOccluded() {
+ mCommunalStateController.setCommunalViewOccluded(
+ mQsExpansion > 0.0f || mShadeExpansion > 0.0f);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.java
new file mode 100644
index 000000000000..99c311e37217
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSource.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.systemui.communal;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.util.ViewController;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * {@link CommunalSource} defines an interface for working with a source for communal data. Clients
+ * may request a communal surface that can be shown within a {@link android.view.SurfaceView}.
+ * Callbacks may also be registered to listen to state changes.
+ */
+public interface CommunalSource {
+ /**
+ * {@link CommunalViewResult} is handed back from {@link #requestCommunalView(Context)} and
+ * contains the view to be displayed and its associated controller.
+ */
+ class CommunalViewResult {
+ /**
+ * The resulting communal view.
+ */
+ public final View view;
+ /**
+ * The controller for the communal view.
+ */
+ public final ViewController<? extends View> viewController;
+
+ /**
+ * The default constructor for {@link CommunalViewResult}.
+ * @param view The communal view.
+ * @param viewController The communal view's controller.
+ */
+ public CommunalViewResult(View view, ViewController<? extends View> viewController) {
+ this.view = view;
+ this.viewController = viewController;
+ }
+ }
+
+ /**
+ * Requests a communal surface that can be displayed inside {@link CommunalHostView}.
+ *
+ * @param context The {@link View} {@link Context} to build the resulting view from
+ * @return A future that can be listened upon for the resulting {@link CommunalViewResult}. The
+ * value will be {@code null} in case of a failure.
+ */
+ ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
+
+ /**
+ * Adds a {@link Callback} to receive future status updates regarding this
+ * {@link CommunalSource}.
+ *
+ * @param callback The {@link Callback} to be added.
+ */
+ void addCallback(Callback callback);
+
+ /**
+ * Removes a {@link Callback} from receiving future updates.
+ *
+ * @param callback The {@link Callback} to be removed.
+ */
+ void removeCallback(Callback callback);
+
+ /**
+ * An interface for receiving updates on the state of the {@link CommunalSource}.
+ */
+ interface Callback {
+ /**
+ * Invoked when the {@link CommunalSource} is no longer available for use.
+ */
+ void onDisconnected();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
new file mode 100644
index 000000000000..2244532cca94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.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.communal;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.MainThread;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.util.settings.SecureSettings;
+
+import com.google.android.collect.Lists;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.inject.Inject;
+
+/**
+ * A Monitor for reporting a {@link CommunalSource} presence.
+ */
+@SysUISingleton
+public class CommunalSourceMonitor {
+ private static final String TAG = "CommunalSourceMonitor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ // A list of {@link Callback} that have registered to receive updates.
+ private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
+ private final SecureSettings mSecureSettings;
+
+ private CommunalSource mCurrentSource;
+ private boolean mCommunalEnabled;
+
+ private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
+ @Override
+ public void onDisconnected() {
+ // Clear source reference.
+ setSource(null /* source */);
+ }
+ };
+
+ @VisibleForTesting
+ @Inject
+ public CommunalSourceMonitor(
+ @MainThread Handler mainThreadHandler,
+ SecureSettings secureSettings) {
+ mSecureSettings = secureSettings;
+
+ ContentObserver settingsObserver = new ContentObserver(mainThreadHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ reloadSettings();
+ }
+ };
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.COMMUNAL_MODE_ENABLED,
+ /* notifyForDescendants= */false,
+ settingsObserver, UserHandle.USER_SYSTEM);
+ reloadSettings();
+ }
+
+ /**
+ * Sets the current {@link CommunalSource}, informing any callbacks. Any existing
+ * {@link CommunalSource} will be disconnected.
+ *
+ * @param source The new {@link CommunalSource}.
+ */
+ public void setSource(CommunalSource source) {
+ if (mCurrentSource != null) {
+ mCurrentSource.removeCallback(mSourceCallback);
+ }
+
+ mCurrentSource = source;
+
+ if (mCommunalEnabled) {
+ executeOnSourceAvailableCallbacks();
+ }
+
+ // Add callback to be informed when the source disconnects.
+ if (mCurrentSource != null) {
+ mCurrentSource.addCallback(mSourceCallback);
+ }
+ }
+
+ private void executeOnSourceAvailableCallbacks() {
+ // If the new source is valid, inform registered Callbacks of its presence.
+ Iterator<WeakReference<Callback>> itr = mCallbacks.iterator();
+ while (itr.hasNext()) {
+ Callback cb = itr.next().get();
+ if (cb == null) {
+ itr.remove();
+ } else {
+ cb.onSourceAvailable(
+ (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>(
+ mCurrentSource) : null);
+ }
+ }
+ }
+
+ /**
+ * Adds a {@link Callback} to receive {@link CommunalSource} updates.
+ *
+ * @param callback The {@link Callback} to add.
+ */
+ public void addCallback(Callback callback) {
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // Inform the callback of any already present CommunalSource.
+ if (mCommunalEnabled && mCurrentSource != null) {
+ callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
+ }
+ }
+
+ /**
+ * Removes the specified {@link Callback} from receive future updates if present.
+ *
+ * @param callback The {@link Callback} to add.
+ */
+ public void removeCallback(Callback callback) {
+ mCallbacks.removeIf(el -> el.get() == callback);
+ }
+
+ private void reloadSettings() {
+ boolean newCommunalEnabled = mSecureSettings.getIntForUser(
+ Settings.Secure.COMMUNAL_MODE_ENABLED,
+ 1,
+ UserHandle.USER_SYSTEM) == 1;
+
+ if (DEBUG) {
+ Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled);
+ }
+
+ if (mCommunalEnabled != newCommunalEnabled) {
+ mCommunalEnabled = newCommunalEnabled;
+ executeOnSourceAvailableCallbacks();
+ }
+ }
+
+ /**
+ * Interface implemented to be notified when new {@link CommunalSource} become available.
+ */
+ public interface Callback {
+ /**
+ * Called when a new {@link CommunalSource} has been registered. This will also be invoked
+ * when a {@link Callback} is first registered and a {@link CommunalSource} is already
+ * registered.
+ *
+ * @param source The new {@link CommunalSource}.
+ */
+ void onSourceAvailable(WeakReference<CommunalSource> source);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
new file mode 100644
index 000000000000..c72f5422b1f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.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 com.android.systemui.communal;
+
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+import javax.inject.Inject;
+
+/**
+ * CommunalStateController enables publishing and listening to communal-related state changes.
+ */
+@SysUISingleton
+public class CommunalStateController implements
+ CallbackController<CommunalStateController.Callback> {
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private boolean mCommunalViewOccluded;
+ private boolean mCommunalViewShowing;
+
+ /**
+ * Callback for communal events.
+ */
+ public interface Callback {
+ /**
+ * Called when the visibility of the communal view changes.
+ */
+ default void onCommunalViewShowingChanged() {
+ }
+
+ /**
+ * Called when the occlusion of the communal view changes.
+ */
+ default void onCommunalViewOccludedChanged() {
+ }
+ }
+
+ @VisibleForTesting
+ @Inject
+ public CommunalStateController() {
+ }
+
+ /**
+ * Sets whether the communal view is showing.
+ * @param communalViewShowing {@code true} if the view is showing, {@code false} otherwise.
+ */
+ public void setCommunalViewShowing(boolean communalViewShowing) {
+ if (mCommunalViewShowing == communalViewShowing) {
+ return;
+ }
+
+ mCommunalViewShowing = communalViewShowing;
+
+ final ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
+ for (Callback callback : callbacks) {
+ callback.onCommunalViewShowingChanged();
+ }
+ }
+
+ /**
+ * Sets whether the communal view is occluded (but otherwise still showing).
+ * @param communalViewOccluded {@code true} if the view is occluded, {@code false} otherwise.
+ */
+ public void setCommunalViewOccluded(boolean communalViewOccluded) {
+ if (mCommunalViewOccluded == communalViewOccluded) {
+ return;
+ }
+
+ mCommunalViewOccluded = communalViewOccluded;
+
+ ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
+ for (int i = 0; i < callbacks.size(); i++) {
+ callbacks.get(i).onCommunalViewOccludedChanged();
+ }
+ }
+
+ /**
+ * Returns whether the communal view is showing.
+ * @return {@code true} if the view is showing, {@code false} otherwise.
+ */
+ public boolean getCommunalViewShowing() {
+ return mCommunalViewShowing;
+ }
+
+ /**
+ * Returns whether the communal view is occluded.
+ * @return {@code true} if the view is occluded, {@code false} otherwise.
+ */
+ public boolean getCommunalViewOccluded() {
+ return mCommunalViewOccluded;
+ }
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
+ mCallbacks.remove(callback);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
new file mode 100644
index 000000000000..5f75d86a9463
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal.dagger;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.idle.dagger.IdleViewComponent;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module providing Communal-related functionality.
+ */
+@Module(subcomponents = {
+ CommunalViewComponent.class,
+ IdleViewComponent.class,
+})
+public interface CommunalModule {
+ String IDLE_VIEW = "idle_view";
+
+ /** */
+ @Provides
+ @Named(IDLE_VIEW)
+ static View provideIdleView(Context context) {
+ FrameLayout view = new FrameLayout(context);
+ return view;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.java
new file mode 100644
index 000000000000..3a80a03aecb2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalViewComponent.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.systemui.communal.dagger;
+
+import com.android.systemui.communal.CommunalHostView;
+import com.android.systemui.communal.CommunalHostViewController;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for working with {@link CommunalHostView}.
+ */
+@Subcomponent
+public interface CommunalViewComponent {
+ /** Simple factory for {@link CommunalViewComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ CommunalViewComponent build(@BindsInstance CommunalHostView view);
+ }
+
+ /** Builds a {@link CommunalHostViewController}. */
+ CommunalHostViewController getCommunalHostViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalService.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalService.java
new file mode 100644
index 000000000000..1612670f6fa4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalService.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 com.android.systemui.communal.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.communal.CommunalSourceMonitor;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.communal.ICommunalHost;
+import com.android.systemui.shared.communal.ICommunalSource;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * CommunalService services requests to {@link ICommunalHost}, allowing clients to declare
+ * themselves as the source of communal surfaces.
+ */
+public class CommunalService extends Service {
+ final Executor mMainExecutor;
+ final CommunalSourceMonitor mMonitor;
+ private final CommunalSourceImpl.Factory mSourceFactory;
+
+ private ICommunalHost.Stub mBinder = new ICommunalHost.Stub() {
+ @Override
+ public void setSource(ICommunalSource source) {
+ mMonitor.setSource(
+ source != null ? mSourceFactory.create(source) : null);
+ }
+ };
+
+ @Inject
+ CommunalService(@Main Executor mainExecutor, CommunalSourceImpl.Factory sourceFactory,
+ CommunalSourceMonitor monitor) {
+ mMainExecutor = mainExecutor;
+ mSourceFactory = sourceFactory;
+ mMonitor = monitor;
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ // The service does not expect requests outside ICommunalHost.
+ return mBinder;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.java
new file mode 100644
index 000000000000..df368c337ce4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourceImpl.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.communal.service;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceView;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+
+import com.android.systemui.communal.CommunalSource;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.communal.ICommunalSource;
+import com.android.systemui.shared.communal.ICommunalSurfaceCallback;
+
+import com.google.android.collect.Lists;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * {@link CommunalSourceImpl} provides a wrapper around {@link ICommunalSource} proxies as an
+ * implementation of {@link CommunalSource}. Requests and responses for communal surfaces are
+ * translated into the proper binder calls.
+ */
+public class CommunalSourceImpl implements CommunalSource {
+ private static final String TAG = "CommunalSourceImpl";
+ private static final boolean DEBUG = false;
+ private final ICommunalSource mSourceProxy;
+ private final Executor mMainExecutor;
+ private final CommunalStateController mCommunalStateController;
+
+ static class Factory {
+ private final Executor mExecutor;
+ private final CommunalStateController mCommunalStateController;
+
+ @Inject
+ Factory(@Main Executor executor, CommunalStateController communalStateController) {
+ mExecutor = executor;
+ mCommunalStateController = communalStateController;
+ }
+
+ public CommunalSource create(ICommunalSource source) {
+ return new CommunalSourceImpl(mExecutor, mCommunalStateController, source);
+ }
+ }
+
+ // mConnected is initialized to true as it is presumed instances are constructed with valid
+ // proxies. The source can never be reconnected once the proxy has died. Once this value
+ // becomes false, the source will always report disconnected to registering callbacks.
+ private boolean mConnected = true;
+
+ // A list of {@link Callback} that have registered to receive updates.
+ private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
+
+ public CommunalSourceImpl(Executor mainExecutor,
+ CommunalStateController communalStateController, ICommunalSource sourceProxy) {
+ mMainExecutor = mainExecutor;
+ mCommunalStateController = communalStateController;
+ mSourceProxy = sourceProxy;
+
+ try {
+ // Track connection status based on proxy lifetime.
+ mSourceProxy.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ if (DEBUG) {
+ Log.d(TAG, "Source lost. Clearing reporting disconnect.");
+ }
+
+ // Set connection state and inform callbacks.
+ onDisconnected();
+ }
+ }, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not link to the source proxy death:" + e);
+ }
+ }
+
+ private void onDisconnected() {
+ mConnected = false;
+ for (WeakReference<Callback> cbRef : mCallbacks) {
+ final Callback cb = cbRef.get();
+ if (cb != null) {
+ cb.onDisconnected();
+ }
+ }
+
+ mCallbacks.clear();
+ }
+
+ @Override
+ public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
+ if (DEBUG) {
+ Log.d(TAG, "Received request for communal view");
+ }
+ ListenableFuture<CommunalViewResult> packageFuture =
+ CallbackToFutureAdapter.getFuture(completer -> {
+ final SurfaceView view = new SurfaceView(context);
+ completer.set(new CommunalViewResult(view,
+ new CommunalSurfaceViewController(view, mMainExecutor,
+ mCommunalStateController, this)));
+ return "CommunalSourceImpl::requestCommunalSurface::getCommunalSurface";
+ });
+
+ return packageFuture;
+ }
+
+ /**
+ * Called internally to request a new {@link android.view.SurfaceControlViewHost.SurfacePackage}
+ * for showing communal content.
+ *
+ * @param hostToken The HostToken necessary to generate a {@link SurfaceControlViewHost}.
+ * @param displayId The id of the display the surface will be shown on.
+ * @param width The width of the surface.
+ * @param height The height of the surface.
+ * @return A future that returns the resulting
+ * {@link android.view.SurfaceControlViewHost.SurfacePackage}.
+ */
+ protected ListenableFuture<SurfaceControlViewHost.SurfacePackage> requestCommunalSurface(
+ IBinder hostToken, int displayId, int width, int height) {
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ mSourceProxy.getCommunalSurface(hostToken, width, height, displayId,
+ new ICommunalSurfaceCallback.Stub() {
+ @Override
+ public void onSurface(
+ SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ completer.set(surfacePackage);
+ }
+ });
+ return "CommunalSourceImpl::requestCommunalSurface::getCommunalSurface";
+ });
+
+ }
+
+ @Override
+ public void addCallback(Callback callback) {
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // If not connected anymore, immediately inform new callback of disconnection and remove.
+ if (!mConnected) {
+ onDisconnected();
+ }
+ }
+
+ @Override
+ public void removeCallback(Callback callback) {
+ mCallbacks.removeIf(el -> el.get() == callback);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourcePrimer.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourcePrimer.java
new file mode 100644
index 000000000000..0f013ff73eda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSourcePrimer.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.systemui.communal.service;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.os.PatternMatcher;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.communal.CommunalSourceMonitor;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.communal.ICommunalSource;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link CommunalSourcePrimer} is responsible for priming SystemUI with a pre-configured
+ * Communal source. The SystemUI service binds to the component to retrieve the
+ * {@link com.android.systemui.communal.CommunalSource}. {@link CommunalSourcePrimer} has no effect
+ * if there is no pre-defined value.
+ */
+@SysUISingleton
+public class CommunalSourcePrimer extends SystemUI {
+ private static final String TAG = "CommunalSourcePrimer";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String ACTION_COMMUNAL_SOURCE = "android.intent.action.COMMUNAL_SOURCE";
+
+ private final Context mContext;
+ private final DelayableExecutor mMainExecutor;
+ private final CommunalSourceMonitor mMonitor;
+ private final CommunalSourceImpl.Factory mSourceFactory;
+ private final ComponentName mComponentName;
+ private final int mBaseReconnectDelayMs;
+ private final int mMaxReconnectAttempts;
+
+ private int mReconnectAttempts = 0;
+ private Runnable mCurrentReconnectCancelable;
+
+ private final Runnable mConnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCurrentReconnectCancelable = null;
+ bindToService();
+ }
+ };
+
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "package added receiver - onReceive");
+ }
+
+ initiateConnectionAttempt();
+ }
+ };
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ final ICommunalSource source = ICommunalSource.Stub.asInterface(service);
+ if (DEBUG) {
+ Log.d(TAG, "onServiceConnected. source:" + source);
+ }
+
+ if (source == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onServiceConnected. invalid source");
+ // Since the service could just repeatedly return null, the primer chooses
+ // to schedule rather than initiate a new connection attempt sequence.
+ scheduleConnectionAttempt();
+ }
+ return;
+ }
+
+ mMonitor.setSource(mSourceFactory.create(source));
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ if (DEBUG) {
+ Log.d(TAG, "onBindingDied. lost communal source. initiating reconnect");
+ }
+
+ initiateConnectionAttempt();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "onServiceDisconnected. lost communal source. initiating reconnect");
+ }
+
+ initiateConnectionAttempt();
+ }
+ };
+
+ @Inject
+ public CommunalSourcePrimer(Context context, @Main Resources resources,
+ DelayableExecutor mainExecutor,
+ CommunalSourceMonitor monitor,
+ CommunalSourceImpl.Factory sourceFactory) {
+ super(context);
+ mContext = context;
+ mMainExecutor = mainExecutor;
+ mMonitor = monitor;
+ mSourceFactory = sourceFactory;
+ mMaxReconnectAttempts = resources.getInteger(
+ R.integer.config_communalSourceMaxReconnectAttempts);
+ mBaseReconnectDelayMs = resources.getInteger(
+ R.integer.config_communalSourceReconnectBaseDelay);
+
+ final String component = resources.getString(R.string.config_communalSourceComponent);
+ mComponentName = component != null && !component.isEmpty()
+ ? ComponentName.unflattenFromString(component) : null;
+ }
+
+ @Override
+ public void start() {
+ }
+
+ private void initiateConnectionAttempt() {
+ // Reset attempts
+ mReconnectAttempts = 0;
+ mMonitor.setSource(null);
+
+ // The first attempt is always a direct invocation rather than delayed.
+ bindToService();
+ }
+
+ private void registerPackageListening() {
+ if (mComponentName == null) {
+ return;
+ }
+
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mComponentName.getPackageName(),
+ PatternMatcher.PATTERN_LITERAL);
+ // Note that we directly register the receiver here as data schemes are not supported by
+ // BroadcastDispatcher.
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void scheduleConnectionAttempt() {
+ // always clear cancelable if present.
+ if (mCurrentReconnectCancelable != null) {
+ mCurrentReconnectCancelable.run();
+ mCurrentReconnectCancelable = null;
+ }
+
+ if (mReconnectAttempts >= mMaxReconnectAttempts) {
+ if (DEBUG) {
+ Log.d(TAG, "exceeded max connection attempts.");
+ }
+ return;
+ }
+
+ final long reconnectDelayMs =
+ (long) Math.scalb(mBaseReconnectDelayMs, mReconnectAttempts);
+
+ if (DEBUG) {
+ Log.d(TAG,
+ "scheduling connection attempt in " + reconnectDelayMs + "milliseconds");
+ }
+
+ mCurrentReconnectCancelable = mMainExecutor.executeDelayed(mConnectRunnable,
+ reconnectDelayMs);
+
+ mReconnectAttempts++;
+ }
+
+ @Override
+ protected void onBootCompleted() {
+ super.onBootCompleted();
+
+ if (DEBUG) {
+ Log.d(TAG, "onBootCompleted. communal source component:" + mComponentName);
+ }
+
+ registerPackageListening();
+ initiateConnectionAttempt();
+ }
+
+ private void bindToService() {
+ if (mComponentName == null) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "attempting to bind to communal source");
+ }
+
+ final Intent intent = new Intent();
+ intent.setAction(ACTION_COMMUNAL_SOURCE);
+ intent.setComponent(mComponentName);
+
+ final boolean binding = mContext.bindService(intent, Context.BIND_AUTO_CREATE,
+ mMainExecutor, mConnection);
+
+ if (!binding) {
+ if (DEBUG) {
+ Log.d(TAG, "bindService failed, rescheduling");
+ }
+
+ scheduleConnectionAttempt();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.java
new file mode 100644
index 000000000000..8a67744f0325
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/service/CommunalSurfaceViewController.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.communal.service;
+
+import android.annotation.IntDef;
+import android.util.Log;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.util.ViewController;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.Executor;
+
+/**
+ * {@link CommunalSurfaceViewController} coordinates requesting communal surfaces to populate a
+ * {@link SurfaceView} with.
+ */
+public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
+ private static final String TAG = "CommunalSurfaceViewCtlr";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final Executor mMainExecutor;
+ private final CommunalStateController mCommunalStateController;
+ private final CommunalSourceImpl mSource;
+
+ @IntDef({STATE_SURFACE_CREATED, STATE_SURFACE_VIEW_ATTACHED})
+ private @interface State {}
+
+ private static final int STATE_SURFACE_CREATED = 1 << 0;
+ private static final int STATE_SURFACE_VIEW_ATTACHED = 1 << 1;
+
+ private static final int STATE_CAN_SHOW_SURFACE =
+ STATE_SURFACE_CREATED | STATE_SURFACE_VIEW_ATTACHED;
+
+ private int mCurrentState;
+
+ // The current in-flight request for a surface package.
+ private ListenableFuture<SurfaceControlViewHost.SurfacePackage> mCurrentSurfaceFuture;
+
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ setState(STATE_SURFACE_CREATED, true);
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ setState(STATE_SURFACE_CREATED, false);
+ }
+ };
+
+ protected CommunalSurfaceViewController(SurfaceView view, Executor executor,
+ CommunalStateController communalStateController, CommunalSourceImpl source) {
+ super(view);
+ mCommunalStateController = communalStateController;
+ mSource = source;
+ mMainExecutor = executor;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ mView.getHolder().addCallback(mSurfaceHolderCallback);
+ }
+
+ private void setState(@State int state, boolean enabled) {
+ if (DEBUG) {
+ Log.d(TAG, "setState. state:" + state + " enable:" + enabled);
+ }
+
+ final int newState = enabled ? mCurrentState | state : mCurrentState & ~state;
+
+ // no new state is available
+ if (newState == mCurrentState) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "setState. new state:" + mCurrentState);
+ }
+
+ mCurrentState = newState;
+
+ showSurface(newState == STATE_CAN_SHOW_SURFACE);
+ }
+
+ private void showSurface(boolean show) {
+ mView.setWillNotDraw(false);
+
+ if (!show) {
+ // If the surface is no longer showing, cancel any in-flight requests.
+ if (mCurrentSurfaceFuture != null) {
+ mCurrentSurfaceFuture.cancel(true);
+ mCurrentSurfaceFuture = null;
+ }
+
+ mView.setWillNotDraw(true);
+ return;
+ }
+
+ // Since this method is only called when the state has changed, mCurrentSurfaceFuture should
+ // be null here.
+ mCurrentSurfaceFuture = mSource.requestCommunalSurface(mView.getHostToken(),
+ mView.getDisplay().getDisplayId(), mView.getMeasuredWidth(),
+ mView.getMeasuredHeight());
+
+ mCurrentSurfaceFuture.addListener(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // If the request is received after detached, ignore.
+ if (!mView.isAttachedToWindow()) {
+ return;
+ }
+
+ SurfaceControlViewHost.SurfacePackage surfacePackage =
+ mCurrentSurfaceFuture.get();
+ mCurrentSurfaceFuture = null;
+
+ if (DEBUG) {
+ Log.d(TAG, "Received surface package:" + surfacePackage);
+ }
+
+ if (surfacePackage != null) {
+ mView.setChildSurfacePackage(surfacePackage);
+ mView.setZOrderOnTop(true);
+ mView.postInvalidate();
+ mCommunalStateController.setCommunalViewShowing(true);
+ } else {
+ Log.e(TAG, "couldn't get the surface package");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "An error occurred retrieving the future result:" + e);
+ }
+ }
+ }, mMainExecutor);
+ }
+
+ @Override
+ protected void onViewAttached() {
+ setState(STATE_SURFACE_VIEW_ATTACHED, true);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ setState(STATE_SURFACE_VIEW_ATTACHED, false);
+ }
+}
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 567d0cb3e6cb..557bca830f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@ class ControlsUiControllerImpl @Inject constructor (
private val onSeedingComplete = Consumer<Boolean> {
accepted ->
if (accepted) {
- selectedStructure = controlsController.get().getFavorites().maxBy {
+ selectedStructure = controlsController.get().getFavorites().maxByOrNull {
it.controls.size
} ?: EMPTY_STRUCTURE
updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index 596e440c3f4a..17bd14c3e877 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -20,11 +20,11 @@ import android.app.Service;
import com.android.systemui.ImageWallpaper;
import com.android.systemui.SystemUIService;
+import com.android.systemui.communal.service.CommunalService;
import com.android.systemui.doze.DozeService;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
import com.android.systemui.keyguard.KeyguardService;
import com.android.systemui.screenrecord.RecordingService;
-import com.android.systemui.screenshot.TakeScreenshotService;
import dagger.Binds;
import dagger.Module;
@@ -39,6 +39,12 @@ public abstract class DefaultServiceBinder {
/** */
@Binds
@IntoMap
+ @ClassKey(CommunalService.class)
+ public abstract Service bindCommunalService(CommunalService service);
+
+ /** */
+ @Binds
+ @IntoMap
@ClassKey(DozeService.class)
public abstract Service bindDozeService(DozeService service);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c97a30e6e13e..c093219de59b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -24,6 +24,8 @@ import android.app.INotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.om.OverlayManager;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.ColorDisplayManager;
import android.os.Handler;
@@ -59,18 +61,18 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.LifecycleScreenStatusProvider;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.NavigationBarA11yHelper;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarOverlayController;
import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.navigationbar.TaskbarDelegate;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -90,6 +92,9 @@ import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.theme.ThemeOverlayApplier;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.unfold.UnfoldTransitionFactory;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -188,13 +193,6 @@ public class DependencyProvider {
}
/** */
- @Provides
- @SysUISingleton
- public PluginManager providePluginManager(Context context) {
- return new PluginManagerImpl(context, new PluginInitializerImpl());
- }
-
- /** */
@SysUISingleton
@Provides
static ThemeOverlayApplier provideThemeOverlayManager(Context context,
@@ -225,7 +223,7 @@ public class DependencyProvider {
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<Recents> recentsOptional,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -234,6 +232,8 @@ public class DependencyProvider {
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
@@ -252,7 +252,7 @@ public class DependencyProvider {
pipOptional,
splitScreenOptional,
recentsOptional,
- statusBarLazy,
+ statusBarOptionalLazy,
shadeController,
notificationRemoteInputManager,
notificationShadeDepthController,
@@ -261,6 +261,8 @@ public class DependencyProvider {
uiEventLogger,
navBarOverlayController,
configurationController,
+ navigationBarA11yHelper,
+ taskbarDelegate,
userTracker);
}
@@ -372,6 +374,37 @@ public class DependencyProvider {
/** */
@Provides
@SysUISingleton
+ public UnfoldTransitionProgressProvider provideUnfoldTransitionProgressProvider(
+ Context context,
+ UnfoldTransitionConfig config,
+ LifecycleScreenStatusProvider screenStatusProvider,
+ DeviceStateManager deviceStateManager,
+ SensorManager sensorManager,
+ @Main Executor executor,
+ @Main Handler handler
+ ) {
+ return UnfoldTransitionFactory
+ .createUnfoldTransitionProgressProvider(
+ context,
+ config,
+ screenStatusProvider,
+ deviceStateManager,
+ sensorManager,
+ handler,
+ executor
+ );
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
+ public UnfoldTransitionConfig provideUnfoldTransitionConfig(Context context) {
+ return UnfoldTransitionFactory.createConfig(context);
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
public Choreographer providesChoreographer() {
return Choreographer.getInstance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 954ba797e164..4d1608fb445d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -41,6 +41,7 @@ import android.content.pm.ShortcutManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.DisplayManager;
import android.hardware.face.FaceManager;
@@ -159,6 +160,12 @@ public class FrameworkServicesModule {
@Provides
@Singleton
+ static DeviceStateManager provideDeviceStateManager(Context context) {
+ return context.getSystemService(DeviceStateManager.class);
+ }
+
+ @Provides
+ @Singleton
static IActivityManager provideIActivityManager() {
return ActivityManager.getService();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index a89c7acea984..648f345205db 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -23,6 +23,7 @@ import android.util.DisplayMetrics;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.dagger.qualifiers.TestHarness;
+import com.android.systemui.plugins.PluginsModule;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
import javax.inject.Singleton;
@@ -47,7 +48,9 @@ import dagger.Provides;
*/
@Module(includes = {
FrameworkServicesModule.class,
- GlobalConcurrencyModule.class})
+ GlobalConcurrencyModule.class,
+ PluginsModule.class,
+})
public class GlobalModule {
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index d648c949ffc5..a3a45fe7ae40 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,8 +18,6 @@ package com.android.systemui.dagger;
import android.content.Context;
-import com.android.systemui.util.concurrency.ThreadFactory;
-
import javax.inject.Singleton;
import dagger.BindsInstance;
@@ -55,9 +53,4 @@ public interface GlobalRootComponent {
* Builder for a SysUIComponent.
*/
SysUIComponent.Builder getSysUIComponent();
-
- /**
- * Build a {@link ThreadFactory}.
- */
- ThreadFactory createThreadFactory();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 1ed881993800..30844ccc877b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -51,7 +51,11 @@ import dagger.multibindings.IntoMap;
/**
* SystemUI objects that are injectable should go here.
*/
-@Module(includes = {RecentsModule.class, StatusBarModule.class, KeyguardModule.class})
+@Module(includes = {
+ RecentsModule.class,
+ StatusBarModule.class,
+ KeyguardModule.class,
+})
public abstract class SystemUIBinder {
/** Inject into AuthController. */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 46ab5f68abe9..3d90eded20cb 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -31,11 +31,13 @@ import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.UdfpsHbmProvider;
import com.android.systemui.classifier.FalsingModule;
+import com.android.systemui.communal.dagger.CommunalModule;
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
@@ -46,7 +48,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.settings.dagger.SettingsModule;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -96,6 +97,7 @@ import dagger.Provides;
AppOpsModule.class,
AssistModule.class,
ClockModule.class,
+ CommunalModule.class,
ControlsModule.class,
DemoModeModule.class,
FalsingModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 470d2f364c1c..98d2739836a9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -31,6 +31,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.view.Display;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.BrightnessSensor;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
@@ -63,6 +64,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
private final Optional<Sensor> mLightSensorOptional;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private final DozeParameters mDozeParameters;
+ private final DockManager mDockManager;
private final int[] mSensorToBrightness;
private final int[] mSensorToScrimOpacity;
private final int mScreenBrightnessDim;
@@ -87,7 +89,8 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
@BrightnessSensor Optional<Sensor> lightSensorOptional, DozeHost host, Handler handler,
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
WakefulnessLifecycle wakefulnessLifecycle,
- DozeParameters dozeParameters) {
+ DozeParameters dozeParameters,
+ DockManager dockManager) {
mContext = context;
mDozeService = service;
mSensorManager = sensorManager;
@@ -96,6 +99,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
mDozeParameters = dozeParameters;
mDozeHost = host;
mHandler = handler;
+ mDockManager = dockManager;
mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness;
@@ -122,13 +126,20 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
@Override
public void onScreenState(int state) {
- if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
+ boolean isDockedScreenOn = state == Display.STATE_ON && mDockManager.isDocked();
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
+ || (isDockedScreenOn && shouldRegisterLightSensorWhenScreenOnDocked())) {
setLightSensorEnabled(true);
} else {
setLightSensorEnabled(false);
}
}
+ private boolean shouldRegisterLightSensorWhenScreenOnDocked() {
+ return !mDozeParameters.brightnessUsesProx()
+ || !mDozeParameters.getSelectivelyRegisterSensorsUsingProx();
+ }
+
private void onDestroy() {
setLightSensorEnabled(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index bfa478088cad..5b327bd5e4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -34,7 +34,7 @@ import javax.inject.Inject
* See [DumpHandler] for more information on how and when this information is dumped.
*/
@SysUISingleton
-class DumpManager @Inject constructor() {
+open class DumpManager @Inject constructor() {
private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
index 6fbf81c53c90..d4d01c8d97b5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
@@ -16,6 +16,7 @@
package com.android.systemui.flags;
+import android.content.Context;
import android.content.res.Resources;
import android.util.SparseArray;
@@ -25,7 +26,9 @@ import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.plugins.FlagReaderPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.wrapper.BuildInfo;
import javax.inject.Inject;
@@ -55,18 +58,68 @@ import javax.inject.Inject;
public class FeatureFlagReader {
private final Resources mResources;
private final boolean mAreFlagsOverrideable;
+ private final PluginManager mPluginManager;
private final SystemPropertiesHelper mSystemPropertiesHelper;
private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
+ private FlagReaderPlugin mPlugin = new FlagReaderPlugin(){};
+
@Inject
public FeatureFlagReader(
@Main Resources resources,
BuildInfo build,
+ PluginManager pluginManager,
SystemPropertiesHelper systemPropertiesHelper) {
mResources = resources;
+ mPluginManager = pluginManager;
mSystemPropertiesHelper = systemPropertiesHelper;
mAreFlagsOverrideable =
build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
+
+ mPluginManager.addPluginListener(mPluginListener, FlagReaderPlugin.class);
+ }
+
+ private final PluginListener<FlagReaderPlugin> mPluginListener =
+ new PluginListener<FlagReaderPlugin>() {
+ public void onPluginConnected(FlagReaderPlugin plugin, Context context) {
+ mPlugin = plugin;
+ }
+
+ public void onPluginDisconnected(FlagReaderPlugin plugin) {
+ mPlugin = new FlagReaderPlugin() {};
+ }
+ };
+
+ boolean isEnabled(BooleanFlag flag) {
+ return mPlugin.isEnabled(flag.getId(), flag.getDefault());
+ }
+
+ String getValue(StringFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ int getValue(IntFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ long getValue(LongFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ float getValue(FloatFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ double getValue(DoubleFlag flag) {
+ return mPlugin.getValue(flag.getId(), flag.getDefault());
+ }
+
+ void addListener(FlagReaderPlugin.Listener listener) {
+ mPlugin.addListener(listener);
+ }
+
+ void removeListener(FlagReaderPlugin.Listener listener) {
+ mPlugin.removeListener(listener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index 5a4245853a6f..e51f90f9c73b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 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,14 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar;
+package com.android.systemui.flags;
import android.content.Context;
import android.util.FeatureFlagUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlagReader;
+import com.android.systemui.plugins.FlagReaderPlugin;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import javax.inject.Inject;
@@ -34,11 +40,88 @@ import javax.inject.Inject;
public class FeatureFlags {
private final FeatureFlagReader mFlagReader;
private final Context mContext;
+ private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
+ private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
@Inject
public FeatureFlags(FeatureFlagReader flagReader, Context context) {
mFlagReader = flagReader;
mContext = context;
+
+ flagReader.addListener(mListener);
+ }
+
+ private final FlagReaderPlugin.Listener mListener = id -> {
+ if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
+ mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
+ }
+ };
+
+ @VisibleForTesting
+ void addFlag(Flag flag) {
+ mFlagMap.put(flag.getId(), flag);
+ }
+
+ /**
+ * @param flag The {@link BooleanFlag} of interest.
+ * @return The value of the flag.
+ */
+ public boolean isEnabled(BooleanFlag flag) {
+ return mFlagReader.isEnabled(flag);
+ }
+
+ /**
+ * @param flag The {@link StringFlag} of interest.
+ * @return The value of the flag.
+ */
+ public String getValue(StringFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link IntFlag} of interest.
+ * @return The value of the flag.
+ */
+ public int getValue(IntFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link LongFlag} of interest.
+ * @return The value of the flag.
+ */
+ public long getValue(LongFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link FloatFlag} of interest.
+ * @return The value of the flag.
+ */
+ public float getValue(FloatFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /**
+ * @param flag The {@link DoubleFlag} of interest.
+ * @return The value of the flag.
+ */
+ public double getValue(DoubleFlag flag) {
+ return mFlagReader.getValue(flag);
+ }
+
+ /** Add a listener for a specific flag. */
+ public void addFlagListener(Flag<?> flag, Listener listener) {
+ mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
+ mListeners.get(flag.getId()).add(listener);
+ mFlagMap.putIfAbsent(flag.getId(), flag);
+ }
+
+ /** Remove a listener for a specific flag. */
+ public void removeFlagListener(Flag<?> flag, Listener listener) {
+ if (mListeners.containsKey(flag.getId())) {
+ mListeners.get(flag.getId()).remove(listener);
+ }
}
public boolean isNewNotifPipelineEnabled() {
@@ -49,15 +132,11 @@ public class FeatureFlags {
return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
}
- /** b/171917882 */
- public boolean isTwoColumnNotificationShadeEnabled() {
- return mFlagReader.isEnabled(R.bool.flag_notification_twocolumn);
- }
-
public boolean isKeyguardLayoutEnabled() {
return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
}
+ /** */
public boolean useNewLockscreenAnimations() {
return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
}
@@ -112,4 +191,10 @@ public class FeatureFlags {
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+
+ /** Simple interface for beinga alerted when a specific flag changes value. */
+ public interface Listener {
+ /** */
+ void onFlagChanged(Flag<?> flag);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 28f63b07e584..6561bd5a5323 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -25,8 +25,12 @@ import javax.inject.Inject
* Proxy to make {@link SystemProperties} easily testable.
*/
@SysUISingleton
-class SystemPropertiesHelper @Inject constructor() {
+open class SystemPropertiesHelper @Inject constructor() {
fun getBoolean(name: String, default: Boolean): Boolean {
return SystemProperties.getBoolean(name, default)
}
+
+ fun set(name: String, value: Int) {
+ SystemProperties.set(name, value.toString())
+ }
} \ 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
deleted file mode 100644
index bc4ced452630..000000000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.globalactions;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.annotation.Nullable;
-import android.app.IActivityManager;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.app.trust.TrustManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.UserManager;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.service.dreams.IDreamManager;
-import android.telecom.TelecomManager;
-import android.transition.AutoTransition;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-import androidx.lifecycle.LifecycleOwner;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.view.RotationPolicy;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
-import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.telephony.TelephonyListenerManager;
-import com.android.systemui.util.RingerModeTracker;
-import com.android.systemui.util.leak.RotationUtils;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-/**
- * Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
- * on whether the keyguard is showing, and whether the device is provisioned.
- * This version includes wallet.
- */
-public class GlobalActionsDialog extends GlobalActionsDialogLite
- implements DialogInterface.OnDismissListener,
- DialogInterface.OnShowListener,
- ConfigurationController.ConfigurationListener,
- GlobalActionsPanelPlugin.Callbacks,
- LifecycleOwner {
-
- private static final String TAG = "GlobalActionsDialog";
-
- private final LockPatternUtils mLockPatternUtils;
- private final KeyguardStateController mKeyguardStateController;
- private final SysUiState mSysUiState;
- private final ActivityStarter mActivityStarter;
- private final SysuiColorExtractor mSysuiColorExtractor;
- private final IStatusBarService mStatusBarService;
- private final NotificationShadeWindowController mNotificationShadeWindowController;
- private GlobalActionsPanelPlugin mWalletPlugin;
-
- @VisibleForTesting
- boolean mShowLockScreenCards = false;
-
- private final KeyguardStateController.Callback mKeyguardStateControllerListener =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- if (mDialog != null) {
- ActionsDialog dialog = (ActionsDialog) mDialog;
- boolean unlocked = mKeyguardStateController.isUnlocked();
- if (dialog.mWalletViewController != null) {
- dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
- }
-
- if (unlocked) {
- dialog.hideLockMessage();
- }
- }
- }
- };
-
- private final ContentObserver mSettingsObserver = new ContentObserver(mMainHandler) {
- @Override
- public void onChange(boolean selfChange) {
- onPowerMenuLockScreenSettingsChanged();
- }
- };
-
- /**
- * @param context everything needs a context :(
- */
- @Inject
- public GlobalActionsDialog(
- Context context,
- GlobalActionsManager windowManagerFuncs,
- AudioManager audioManager,
- IDreamManager iDreamManager,
- DevicePolicyManager devicePolicyManager,
- LockPatternUtils lockPatternUtils,
- BroadcastDispatcher broadcastDispatcher,
- TelephonyListenerManager telephonyListenerManager,
- GlobalSettings globalSettings,
- SecureSettings secureSettings,
- @Nullable Vibrator vibrator,
- @Main Resources resources,
- ConfigurationController configurationController,
- ActivityStarter activityStarter,
- KeyguardStateController keyguardStateController,
- UserManager userManager,
- TrustManager trustManager,
- IActivityManager iActivityManager,
- @Nullable TelecomManager telecomManager,
- MetricsLogger metricsLogger,
- SysuiColorExtractor colorExtractor,
- IStatusBarService statusBarService,
- NotificationShadeWindowController notificationShadeWindowController,
- IWindowManager iWindowManager,
- @Background Executor backgroundExecutor,
- UiEventLogger uiEventLogger,
- RingerModeTracker ringerModeTracker,
- SysUiState sysUiState,
- @Main Handler handler,
- PackageManager packageManager,
- StatusBar statusBar) {
-
- super(context,
- windowManagerFuncs,
- audioManager,
- iDreamManager,
- devicePolicyManager,
- lockPatternUtils,
- broadcastDispatcher,
- telephonyListenerManager,
- globalSettings,
- secureSettings,
- vibrator,
- resources,
- configurationController,
- keyguardStateController,
- userManager,
- trustManager,
- iActivityManager,
- telecomManager,
- metricsLogger,
- colorExtractor,
- statusBarService,
- notificationShadeWindowController,
- iWindowManager,
- backgroundExecutor,
- uiEventLogger,
- null,
- ringerModeTracker,
- sysUiState,
- handler,
- packageManager,
- statusBar);
-
- mLockPatternUtils = lockPatternUtils;
- mKeyguardStateController = keyguardStateController;
- mSysuiColorExtractor = colorExtractor;
- mStatusBarService = statusBarService;
- mNotificationShadeWindowController = notificationShadeWindowController;
- mSysUiState = sysUiState;
- mActivityStarter = activityStarter;
-
- mKeyguardStateController.addCallback(mKeyguardStateControllerListener);
-
- // Listen for changes to show pay on the power menu while locked
- onPowerMenuLockScreenSettingsChanged();
- mGlobalSettings.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
- false /* notifyForDescendants */,
- mSettingsObserver);
- }
-
- @Override
- public void destroy() {
- super.destroy();
- mKeyguardStateController.removeCallback(mKeyguardStateControllerListener);
- mGlobalSettings.unregisterContentObserver(mSettingsObserver);
- }
-
- /**
- * Show the global actions dialog (creating if necessary)
- *
- * @param keyguardShowing True if keyguard is showing
- */
- public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
- GlobalActionsPanelPlugin walletPlugin) {
- mWalletPlugin = walletPlugin;
- super.showOrHideDialog(keyguardShowing, isDeviceProvisioned);
- }
-
- /**
- * Returns the maximum number of power menu items to show based on which GlobalActions
- * layout is being used.
- */
- @VisibleForTesting
- @Override
- protected int getMaxShownPowerItems() {
- return getContext().getResources().getInteger(
- com.android.systemui.R.integer.power_menu_max_columns);
- }
-
- /**
- * Create the global actions dialog.
- *
- * @return A new dialog.
- */
- @Override
- protected ActionsDialogLite createDialog() {
- initDialogItems();
-
- ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
- this::getWalletViewController, mSysuiColorExtractor,
- mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(),
- getStatusBar());
-
- if (shouldShowLockMessage(dialog)) {
- dialog.showLockMessage();
- }
- dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
- dialog.setOnDismissListener(this);
- dialog.setOnShowListener(this);
-
- return dialog;
- }
-
- @Nullable
- private GlobalActionsPanelPlugin.PanelViewController getWalletViewController() {
- if (mWalletPlugin == null) {
- return null;
- }
- return mWalletPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());
- }
-
- /**
- * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
- * called when the quick access wallet requests that an intent be started (with lock screen
- * shown first if needed).
- */
- @Override
- public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
- mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
- }
-
- @Override
- protected int getEmergencyTextColor(Context context) {
- return context.getResources().getColor(
- com.android.systemui.R.color.global_actions_emergency_text);
- }
-
- @Override
- protected int getEmergencyIconColor(Context context) {
- return getContext().getResources().getColor(
- com.android.systemui.R.color.global_actions_emergency_text);
- }
-
- @Override
- protected int getEmergencyBackgroundColor(Context context) {
- return getContext().getResources().getColor(
- com.android.systemui.R.color.global_actions_emergency_background);
- }
-
- @Override
- protected int getGridItemLayoutResource() {
- return com.android.systemui.R.layout.global_actions_grid_item_v2;
- }
-
- @VisibleForTesting
- static class ActionsDialog extends ActionsDialogLite {
-
- private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory;
- @Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
- private ResetOrientationData mResetOrientationData;
- @VisibleForTesting ViewGroup mLockMessageContainer;
- private TextView mLockMessage;
-
- ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter,
- Provider<GlobalActionsPanelPlugin.PanelViewController> walletFactory,
- SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
- NotificationShadeWindowController notificationShadeWindowController,
- SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- StatusBar statusBar) {
- super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
- adapter, overflowAdapter, sysuiColorExtractor, statusBarService,
- notificationShadeWindowController, sysuiState, onRotateCallback,
- keyguardShowing, powerAdapter, uiEventLogger, null,
- statusBar);
- mWalletFactory = walletFactory;
-
- // Update window attributes
- Window window = getWindow();
- window.getAttributes().systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- window.setLayout(MATCH_PARENT, MATCH_PARENT);
- window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- window.addFlags(
- WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- setTitle(R.string.global_actions);
- initializeLayout();
- }
-
- private boolean isWalletViewAvailable() {
- return mWalletViewController != null && mWalletViewController.getPanelContent() != null;
- }
-
- private void initializeWalletView() {
- if (mWalletFactory == null) {
- return;
- }
- mWalletViewController = mWalletFactory.get();
- if (!isWalletViewAvailable()) {
- return;
- }
-
- boolean isLandscapeWalletViewShown = mContext.getResources().getBoolean(
- com.android.systemui.R.bool.global_actions_show_landscape_wallet_view);
-
- int rotation = RotationUtils.getRotation(mContext);
- boolean rotationLocked = RotationPolicy.isRotationLocked(mContext);
- if (rotation != RotationUtils.ROTATION_NONE) {
- if (rotationLocked) {
- if (mResetOrientationData == null) {
- mResetOrientationData = new ResetOrientationData();
- mResetOrientationData.locked = true;
- mResetOrientationData.rotation = rotation;
- }
-
- // Unlock rotation, so user can choose to rotate to portrait to see the panel.
- // This call is posted so that the rotation does not change until post-layout,
- // otherwise onConfigurationChanged() may not get invoked.
- mGlobalActionsLayout.post(() ->
- RotationPolicy.setRotationLockAtAngle(
- mContext, false, RotationUtils.ROTATION_NONE));
-
- if (!isLandscapeWalletViewShown) {
- return;
- }
- }
- } else {
- if (!rotationLocked) {
- if (mResetOrientationData == null) {
- mResetOrientationData = new ResetOrientationData();
- mResetOrientationData.locked = false;
- }
- }
-
- boolean shouldLockRotation = !isLandscapeWalletViewShown;
- if (rotationLocked != shouldLockRotation) {
- // Locks the screen to portrait if the landscape / seascape orientation does not
- // show the wallet view, so the user doesn't accidentally hide the panel.
- // This call is posted so that the rotation does not change until post-layout,
- // otherwise onConfigurationChanged() may not get invoked.
- mGlobalActionsLayout.post(() ->
- RotationPolicy.setRotationLockAtAngle(
- mContext, shouldLockRotation, RotationUtils.ROTATION_NONE));
- }
- }
-
- // Disable rotation suggestions, if enabled
- setRotationSuggestionsEnabled(false);
-
- FrameLayout panelContainer =
- findViewById(com.android.systemui.R.id.global_actions_wallet);
- FrameLayout.LayoutParams panelParams =
- new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT);
- panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.global_actions_wallet_top_margin);
- View walletView = mWalletViewController.getPanelContent();
- panelContainer.addView(walletView, panelParams);
- // Smooth transitions when wallet is resized, which can happen when a card is added
- ViewGroup root = findViewById(com.android.systemui.R.id.global_actions_grid_root);
- if (root != null) {
- walletView.addOnLayoutChangeListener((v, l, t, r, b, ol, ot, or, ob) -> {
- int oldHeight = ob - ot;
- int newHeight = b - t;
- if (oldHeight > 0 && oldHeight != newHeight) {
- TransitionSet transition = new AutoTransition()
- .setDuration(250)
- .setOrdering(TransitionSet.ORDERING_TOGETHER);
- TransitionManager.beginDelayedTransition(root, transition);
- }
- });
- }
- }
-
- @Override
- protected int getLayoutResource() {
- return com.android.systemui.R.layout.global_actions_grid_v2;
- }
-
- @Override
- protected void initializeLayout() {
- super.initializeLayout();
- mLockMessageContainer = requireViewById(
- com.android.systemui.R.id.global_actions_lock_message_container);
- mLockMessage = requireViewById(com.android.systemui.R.id.global_actions_lock_message);
- initializeWalletView();
- getWindow().setBackgroundDrawable(mBackgroundDrawable);
- }
-
- @Override
- protected void showDialog() {
- mShowing = true;
- mNotificationShadeWindowController.setRequestTopUi(true, TAG);
- mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, true)
- .commitUpdate(mContext.getDisplayId());
-
- ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
- root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
- root.setPadding(windowInsets.getStableInsetLeft(),
- windowInsets.getStableInsetTop(),
- windowInsets.getStableInsetRight(),
- windowInsets.getStableInsetBottom());
- return WindowInsets.CONSUMED;
- });
-
- mBackgroundDrawable.setAlpha(0);
- float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
- ObjectAnimator alphaAnimator =
- ObjectAnimator.ofFloat(mContainer, "alpha", 0f, 1f);
- alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- alphaAnimator.setDuration(183);
- alphaAnimator.addUpdateListener((animation) -> {
- float animatedValue = animation.getAnimatedFraction();
- int alpha = (int) (animatedValue * mScrimAlpha * 255);
- mBackgroundDrawable.setAlpha(alpha);
- });
-
- ObjectAnimator xAnimator =
- ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
- xAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- xAnimator.setDuration(350);
-
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(alphaAnimator, xAnimator);
- animatorSet.start();
- }
-
- @Override
- protected void dismissInternal() {
- super.dismissInternal();
- }
-
- @Override
- protected void completeDismiss() {
- dismissWallet();
- resetOrientation();
- super.completeDismiss();
- }
-
- private void dismissWallet() {
- if (mWalletViewController != null) {
- mWalletViewController.onDismissed();
- // The wallet controller should not be re-used after being dismissed.
- mWalletViewController = null;
- }
- }
-
- private void resetOrientation() {
- if (mResetOrientationData != null) {
- RotationPolicy.setRotationLockAtAngle(mContext, mResetOrientationData.locked,
- mResetOrientationData.rotation);
- }
- setRotationSuggestionsEnabled(true);
- }
-
- @Override
- public void refreshDialog() {
- // ensure dropdown menus are dismissed before re-initializing the dialog
- dismissWallet();
- super.refreshDialog();
- }
-
- void hideLockMessage() {
- if (mLockMessageContainer.getVisibility() == View.VISIBLE) {
- mLockMessageContainer.animate().alpha(0).setDuration(150).setListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLockMessageContainer.setVisibility(View.GONE);
- }
- }).start();
- }
- }
-
- void showLockMessage() {
- Drawable lockIcon = mContext.getDrawable(com.android.internal.R.drawable.ic_lock);
- lockIcon.setTint(mContext.getColor(com.android.systemui.R.color.control_primary_text));
- mLockMessage.setCompoundDrawablesWithIntrinsicBounds(null, lockIcon, null, null);
- mLockMessageContainer.setVisibility(View.VISIBLE);
- }
-
- private static class ResetOrientationData {
- public boolean locked;
- public int rotation;
- }
- }
-
- /**
- * Determines whether or not debug mode has been activated for the Global Actions Panel.
- */
- private static boolean isPanelDebugModeEnabled(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.GLOBAL_ACTIONS_PANEL_DEBUG_ENABLED, 0) == 1;
- }
-
- /**
- * Determines whether or not the Global Actions menu should be forced to use the newer
- * grid-style layout.
- */
- private static boolean isForceGridEnabled(Context context) {
- return isPanelDebugModeEnabled(context);
- }
-
- private boolean shouldShowLockMessage(ActionsDialog dialog) {
- return 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()
- && (!mShowLockScreenCards || isLockedAfterBoot)
- && dialog.isWalletViewAvailable();
- }
-
- private void onPowerMenuLockScreenSettingsChanged() {
- mShowLockScreenCards = mSecureSettings.getInt(
- Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 06e74821869e..8603eba06048 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -84,6 +84,7 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
@@ -108,6 +109,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.ScreenshotHelper;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.MultiListLayout;
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.animation.Interpolators;
@@ -131,6 +133,7 @@ import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -170,6 +173,11 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+ // See NotificationManagerService#scheduleDurationReachedLocked
+ private static final long TOAST_FADE_TIME = 333;
+ // See NotificationManagerService.LONG_DELAY
+ private static final int TOAST_VISIBLE_TIME = 3500;
+
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
@@ -190,7 +198,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
private final SysUiState mSysUiState;
- private final GlobalActionsInfoProvider mInfoProvider;
// Used for RingerModeTracker
private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
@@ -230,7 +237,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
- private final StatusBar mStatusBar;
+ private final Optional<StatusBar> mStatusBarOptional;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -333,12 +341,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- GlobalActionsInfoProvider infoProvider,
RingerModeTracker ringerModeTracker,
SysUiState sysUiState,
@Main Handler handler,
PackageManager packageManager,
- StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -358,7 +366,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mTelecomManager = telecomManager;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -368,7 +375,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mSysUiState = sysUiState;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -418,8 +426,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
return mUiEventLogger;
}
- protected StatusBar getStatusBar() {
- return mStatusBar;
+ protected Optional<StatusBar> getStatusBar() {
+ return mStatusBarOptional;
+ }
+
+ protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+ return mKeyguardUpdateMonitor;
}
/**
@@ -653,7 +665,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider, mStatusBar);
+ mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -850,7 +862,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
if (mTelecomManager != null) {
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -982,7 +994,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mIActivityManager.requestInteractiveBugReport();
}
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
} catch (RemoteException e) {
}
}
@@ -1002,7 +1014,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
mIActivityManager.requestFullBugReport();
// Close shade so user sees the activity
- mStatusBar.collapseShade();
+ mStatusBarOptional.ifPresent(StatusBar::collapseShade);
} catch (RemoteException e) {
}
return false;
@@ -2119,9 +2131,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
- private GlobalActionsInfoProvider mInfoProvider;
private GestureDetector mGestureDetector;
- private StatusBar mStatusBar;
+ private Optional<StatusBar> mStatusBarOptional;
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private LockPatternUtils mLockPatternUtils;
protected ViewGroup mContainer;
@@ -2146,7 +2159,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceY < 0 && distanceY > distanceX
- && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ && e1.getY() <= mStatusBarOptional.map(
+ StatusBar::getStatusBarHeight).orElse(0)) {
// Downwards scroll from top
openShadeAndDismiss();
return true;
@@ -2158,7 +2172,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
- && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ && e1.getY() <= mStatusBarOptional.map(
+ StatusBar::getStatusBarHeight).orElse(0)) {
// Downwards fling from top
openShadeAndDismiss();
return true;
@@ -2173,7 +2188,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
+ Optional<StatusBar> statusBarOptional,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
@@ -2186,8 +2202,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mOnRotateCallback = onRotateCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mInfoProvider = infoProvider;
- mStatusBar = statusBar;
+ mStatusBarOptional = statusBarOptional;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2218,12 +2235,14 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private void openShadeAndDismiss() {
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- if (mStatusBar.isKeyguardShowing()) {
+ if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
// match existing lockscreen behavior to open QS when swiping from status bar
- mStatusBar.animateExpandSettingsPanel(null);
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.animateExpandSettingsPanel(null));
} else {
// otherwise, swiping down should expand notification shade
- mStatusBar.animateExpandNotificationsPanel();
+ mStatusBarOptional.ifPresent(
+ statusBar -> statusBar.animateExpandNotificationsPanel());
}
dismiss();
}
@@ -2305,8 +2324,12 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mScrimAlpha = 1.0f;
}
- if (mInfoProvider != null && mInfoProvider.shouldShowMessage()) {
- mInfoProvider.addPanel(mContext, mContainer, mAdapter.getCount(), () -> dismiss());
+ // If user entered from the lock screen and smart lock was enabled, disable it
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user);
+ if (mKeyguardShowing && userHasTrust) {
+ mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
+ showSmartLockDisabledMessage();
}
}
@@ -2319,6 +2342,37 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
contentParent.setClipToPadding(false);
}
+ private void showSmartLockDisabledMessage() {
+ // Since power menu is the top window, make a Toast-like view that will show up
+ View message = LayoutInflater.from(mContext)
+ .inflate(com.android.systemui.R.layout.global_actions_toast, mContainer, false);
+
+ // Set up animation
+ AccessibilityManager mAccessibilityManager =
+ (AccessibilityManager) getContext().getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ final int visibleTime = mAccessibilityManager.getRecommendedTimeoutMillis(
+ TOAST_VISIBLE_TIME, AccessibilityManager.FLAG_CONTENT_TEXT);
+ message.setVisibility(View.VISIBLE);
+ message.setAlpha(0f);
+ mContainer.addView(message);
+
+ // Fade in
+ message.animate()
+ .alpha(1f)
+ .setDuration(TOAST_FADE_TIME)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Then fade out
+ message.animate()
+ .alpha(0f)
+ .setDuration(TOAST_FADE_TIME)
+ .setStartDelay(visibleTime);
+ }
+ });
+ }
+
@Override
protected void onStart() {
super.onStart();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
deleted file mode 100644
index 25837e3aacdf..000000000000
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.globalactions
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.content.res.Configuration
-import android.net.Uri
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.plugins.ActivityStarter
-import javax.inject.Inject
-
-private const val TAG = "GlobalActionsInfo"
-
-/** Maximum number of times to show change info message */
-private const val MAX_VIEW_COUNT = 3
-
-/** Maximum number of buttons allowed in landscape before this panel does not fit */
-private const val MAX_BUTTONS_LANDSCAPE = 4
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-class GlobalActionsInfoProvider @Inject constructor(
- private val context: Context,
- private val walletClient: QuickAccessWalletClient,
- private val controlsController: ControlsController,
- private val activityStarter: ActivityStarter
-) {
-
- private var pendingIntent: PendingIntent
-
- init {
- val url = context.resources.getString(R.string.global_actions_change_url)
- val intent = Intent(Intent.ACTION_VIEW).apply {
- setData(Uri.parse(url))
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }
- pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
- }
-
- fun addPanel(context: Context, parent: ViewGroup, nActions: Int, dismissParent: Runnable) {
- // This panel does not fit on landscape with two rows of buttons showing,
- // so skip adding the panel (and incrementing view counT) in that case
- val isLandscape =
- context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- if (isLandscape && nActions > MAX_BUTTONS_LANDSCAPE) {
- return
- }
-
- val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
- parent, false)
-
- val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
- val message = view.findViewById<TextView>(R.id.global_actions_change_message)
- message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
-
- view.setOnClickListener { _ ->
- dismissParent.run()
- activityStarter.postStartActivityDismissingKeyguard(pendingIntent)
- }
- parent.addView(view, 0) // Add to top
- incrementViewCount()
- }
-
- fun shouldShowMessage(): Boolean {
- // This is only relevant for some devices
- val isEligible = context.resources.getBoolean(
- R.bool.global_actions_show_change_info)
- if (!isEligible) {
- return false
- }
-
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
-
- // Only show to users who previously had these items set up
- val viewCount = if (sharedPrefs.contains(KEY_VIEW_COUNT) || hadContent()) {
- sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- } else {
- -1
- }
-
- // Limit number of times this is displayed
- return viewCount > -1 && viewCount < MAX_VIEW_COUNT
- }
-
- private fun hadContent(): Boolean {
- // Check whether user would have seen content in the power menu that has now moved
- val hadControls = controlsController.getFavorites().size > 0
- val hadCards = walletClient.isWalletFeatureAvailable
- Log.d(TAG, "Previously had controls $hadControls, cards $hadCards")
- return hadControls || hadCards
- }
-
- private fun incrementViewCount() {
- val sharedPrefs = context.getSharedPreferences(PREFERENCE, Context.MODE_PRIVATE)
- val count = sharedPrefs.getInt(KEY_VIEW_COUNT, 0)
- sharedPrefs.edit().putInt(KEY_VIEW_COUNT, count + 1).apply()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
new file mode 100644
index 000000000000..7d79279799ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostView.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.idle;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * {@link IdleHostView} houses a surface to be displayed when the device idle.
+ */
+public class IdleHostView extends FrameLayout {
+ public IdleHostView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public IdleHostView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public IdleHostView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
new file mode 100644
index 000000000000..38af02b0f101
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.idle;
+
+import static com.android.systemui.communal.dagger.CommunalModule.IDLE_VIEW;
+
+import android.annotation.IntDef;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.service.dreams.Sandman;
+import android.util.Log;
+import android.view.Choreographer;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+/**
+ * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen.
+ */
+public class IdleHostViewController extends ViewController<IdleHostView> implements
+ SensorEventListener {
+ private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController";
+ private static final String TAG = "IdleHostViewController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef({STATE_IDLE_MODE_ENABLED, STATE_DOZING, STATE_KEYGUARD_SHOWING, STATE_IDLING})
+ public @interface State {}
+
+ // Set at construction to indicate idle mode is available.
+ private static final int STATE_IDLE_MODE_ENABLED = 1 << 0;
+
+ // Set when the device has entered a system level dream.
+ private static final int STATE_DOZING = 1 << 1;
+
+ // Set when the keyguard is showing.
+ private static final int STATE_KEYGUARD_SHOWING = 1 << 2;
+
+ // Set when input monitoring has established the device is now idling.
+ private static final int STATE_IDLING = 1 << 3;
+
+ // Set when the device is in a low light environment.
+ private static final int STATE_LOW_LIGHT = 1 << 4;
+
+ // The state the controller must be in to start recognizing idleness (lack of input
+ // interaction).
+ private static final int CONDITIONS_IDLE_MONITORING =
+ STATE_IDLE_MODE_ENABLED | STATE_KEYGUARD_SHOWING;
+
+ // The state the controller must be in before entering idle mode.
+ private static final int CONDITIONS_IDLING = CONDITIONS_IDLE_MONITORING | STATE_IDLING;
+
+ // The state the controller must be in to start listening for low light signals.
+ private static final int CONDITIONS_LOW_LIGHT_MONITORING =
+ STATE_IDLE_MODE_ENABLED | STATE_KEYGUARD_SHOWING;
+
+ // The aggregate current state.
+ private int mState;
+ private boolean mIdleModeActive;
+ private boolean mLowLightModeActive;
+ private boolean mIsMonitoringLowLight;
+
+ private final Context mContext;
+
+ // Timeout to idle in milliseconds.
+ private final int mIdleTimeout;
+
+ // Factory for generating input listeners.
+ private final InputMonitorFactory mInputMonitorFactory;
+
+ // Delayable executor.
+ private final DelayableExecutor mDelayableExecutor;
+
+ private final BroadcastDispatcher mBroadcastDispatcher;
+
+ private final PowerManager mPowerManager;
+
+ private final AsyncSensorManager mSensorManager;
+
+ // Light sensor used to detect low light condition.
+ private final Sensor mSensor;
+
+ // Runnable for canceling enabling idle.
+ private Runnable mCancelEnableIdling;
+
+ // Keyguard state controller for monitoring keyguard show state.
+ private final KeyguardStateController mKeyguardStateController;
+
+ // Status bar state controller for monitoring when the device is dozing.
+ private final StatusBarStateController mStatusBarStateController;
+
+ // Looper to use for monitoring input.
+ private final Looper mLooper;
+
+ // Choreographer to use for monitoring input.
+ private final Choreographer mChoreographer;
+
+ // Monitor for tracking touches for activity.
+ private InputMonitorCompat mInputMonitor;
+
+ // Delayed callback for enabling idle mode.
+ private final Runnable mEnableIdlingCallback = () -> {
+ if (DEBUG) {
+ Log.d(TAG, "enabling idle");
+ }
+ setState(STATE_IDLING, true);
+ };
+
+ private final KeyguardStateController.Callback mKeyguardCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onKeyguardShowingChanged() {
+ setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStatusBarCallback =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ setState(STATE_DOZING, isDozing);
+ }
+ };
+
+ private final BroadcastReceiver mDreamEndedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
+ setState(STATE_IDLING, false);
+ }
+ }
+ };
+
+ final Provider<View> mIdleViewProvider;
+
+ @Inject
+ protected IdleHostViewController(
+ Context context,
+ BroadcastDispatcher broadcastDispatcher,
+ PowerManager powerManager,
+ AsyncSensorManager sensorManager,
+ IdleHostView view, InputMonitorFactory factory,
+ @Main DelayableExecutor delayableExecutor,
+ @Main Resources resources,
+ @Main Looper looper,
+ @Named(IDLE_VIEW) Provider<View> idleViewProvider,
+ Choreographer choreographer,
+ KeyguardStateController keyguardStateController,
+ StatusBarStateController statusBarStateController) {
+ super(view);
+ mContext = context;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mPowerManager = powerManager;
+ mSensorManager = sensorManager;
+ mIdleViewProvider = idleViewProvider;
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mLooper = looper;
+ mChoreographer = choreographer;
+
+ mState = STATE_KEYGUARD_SHOWING;
+
+ final boolean enabled = resources.getBoolean(R.bool.config_enableIdleMode);
+ if (enabled) {
+ mState |= STATE_IDLE_MODE_ENABLED;
+ }
+
+ setState(mState, true);
+
+ mIdleTimeout = resources.getInteger(R.integer.config_idleModeTimeout);
+ mInputMonitorFactory = factory;
+ mDelayableExecutor = delayableExecutor;
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+
+ if (DEBUG) {
+ Log.d(TAG, "initial state:" + mState + " enabled:" + enabled
+ + " timeout:" + mIdleTimeout);
+ }
+ }
+
+ @Override
+ public void init() {
+ super.init();
+
+ setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
+ setState(STATE_DOZING, mStatusBarStateController.isDozing());
+ }
+
+ private void setState(@State int state, boolean active) {
+ final int oldState = mState;
+
+ if (active) {
+ mState |= state;
+ } else {
+ mState &= ~state;
+ }
+
+ // If we have entered doze or no longer match the preconditions for idling, remove idling.
+ if ((mState & STATE_DOZING) == STATE_DOZING
+ || (mState & CONDITIONS_IDLE_MONITORING) != CONDITIONS_IDLE_MONITORING) {
+ mState &= ~STATE_IDLING;
+ }
+
+ if (oldState == mState) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "state changed from " + oldState + " to " + mState);
+ }
+
+ enableIdleMonitoring(mState == CONDITIONS_IDLE_MONITORING);
+ enableIdleMode(mState == CONDITIONS_IDLING);
+ // Loose matching. Doesn't need to be the exact state to monitor low light, but only
+ // the specified states need to match.
+ enableLowLightMonitoring(
+ (mState & CONDITIONS_LOW_LIGHT_MONITORING) == CONDITIONS_LOW_LIGHT_MONITORING);
+ enableLowLightMode((mState & STATE_LOW_LIGHT) == STATE_LOW_LIGHT);
+ }
+
+ private void enableIdleMonitoring(boolean enable) {
+ if (enable && mInputMonitor == null) {
+ if (DEBUG) {
+ Log.d(TAG, "enable idle monitoring");
+ }
+ // Set initial timeout to idle.
+ mCancelEnableIdling = mDelayableExecutor.executeDelayed(mEnableIdlingCallback,
+ mIdleTimeout);
+
+ // Monitor - any input should reset timer
+ mInputMonitor = mInputMonitorFactory.getInputMonitor(INPUT_MONITOR_IDENTIFIER);
+ mInputMonitor.getInputReceiver(mLooper, mChoreographer,
+ v -> {
+ if (DEBUG) {
+ Log.d(TAG, "touch detected, resetting timeout");
+ }
+ // When input is received, reset timeout.
+ if (mCancelEnableIdling != null) {
+ mCancelEnableIdling.run();
+ mCancelEnableIdling = null;
+ }
+ mCancelEnableIdling = mDelayableExecutor.executeDelayed(
+ mEnableIdlingCallback, mIdleTimeout);
+ });
+ } else if (!enable && mInputMonitor != null) {
+ if (DEBUG) {
+ Log.d(TAG, "disable idle monitoring");
+ }
+ // Clean up idle callback and touch monitoring.
+ if (mCancelEnableIdling != null) {
+ mCancelEnableIdling.run();
+ mCancelEnableIdling = null;
+ }
+
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ private void enableIdleMode(boolean enable) {
+ if (mIdleModeActive == enable) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "enable idle mode:" + enable);
+ }
+
+ mIdleModeActive = enable;
+
+ if (mIdleModeActive) {
+ // Track when the dream ends to cancel any timeouts.
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ mBroadcastDispatcher.registerReceiver(mDreamEndedReceiver, filter);
+
+ // Start dream.
+ Sandman.startDreamByUserRequest(mContext);
+ } else {
+ // Stop tracking dream end.
+ mBroadcastDispatcher.unregisterReceiver(mDreamEndedReceiver);
+ }
+ }
+
+ private void enableLowLightMonitoring(boolean enable) {
+ if (enable == mIsMonitoringLowLight) {
+ return;
+ }
+
+ mIsMonitoringLowLight = enable;
+
+ if (mIsMonitoringLowLight) {
+ if (DEBUG) Log.d(TAG, "Enabling low light monitoring.");
+ mSensorManager.registerListener(this /*listener*/, mSensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ if (DEBUG) Log.d(TAG, "Disabling low light monitoring.");
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ private void enableLowLightMode(boolean enable) {
+ if (mLowLightModeActive == enable) {
+ return;
+ }
+
+ mLowLightModeActive = enable;
+
+ if (mLowLightModeActive) {
+ if (DEBUG) Log.d(TAG, "Entering low light, start dozing.");
+
+ mPowerManager.goToSleep(
+ SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0);
+ } else {
+ if (DEBUG) Log.d(TAG, "Exiting low light, stop dozing.");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_APPLICATION, "Exit low light condition");
+ }
+ }
+
+ @Override
+ protected void onViewAttached() {
+ if (DEBUG) {
+ Log.d(TAG, "onViewAttached");
+ }
+
+ mKeyguardStateController.addCallback(mKeyguardCallback);
+ mStatusBarStateController.addCallback(mStatusBarCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mKeyguardStateController.removeCallback(mKeyguardCallback);
+ mStatusBarStateController.removeCallback(mStatusBarCallback);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.values.length == 0) {
+ if (DEBUG) Log.w(TAG, "SensorEvent doesn't have value");
+ return;
+ }
+
+ final boolean isLowLight = event.values[0] < 10;
+ setState(STATE_LOW_LIGHT, isLowLight);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ if (DEBUG) {
+ Log.d(TAG, "onAccuracyChanged accuracy=" + accuracy);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java b/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
new file mode 100644
index 000000000000..be0a378d10a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/InputMonitorFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.idle;
+
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import javax.inject.Inject;
+
+/**
+ * {@link InputMonitorFactory} generates instances of {@link InputMonitorCompat}.
+ */
+public class InputMonitorFactory {
+ private final int mDisplayId;
+
+ @Inject
+ public InputMonitorFactory(@DisplayId int displayId) {
+ mDisplayId = displayId;
+ }
+
+ /**
+ * Generates a new {@link InputMonitorCompat}.
+ *
+ * @param identifier Identifier to generate monitor with.
+ * @return A {@link InputMonitorCompat} instance.
+ */
+ public InputMonitorCompat getInputMonitor(String identifier) {
+ return new InputMonitorCompat(identifier, mDisplayId);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java b/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.java
new file mode 100644
index 000000000000..9754b1f009f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/idle/dagger/IdleViewComponent.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.systemui.idle.dagger;
+
+import com.android.systemui.idle.IdleHostView;
+import com.android.systemui.idle.IdleHostViewController;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for working with {@link IdleHostView}.
+ */
+@Subcomponent
+public interface IdleViewComponent {
+ /** Simple factory for {@link Factory}. */
+ @Subcomponent.Factory
+ interface Factory {
+ IdleViewComponent build(@BindsInstance IdleHostView idleHostView);
+ }
+
+ /** Builds a {@link IdleHostViewController}. */
+ IdleHostViewController getIdleHostViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 62b92cb33f5c..35773955529f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -18,14 +18,34 @@ package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
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_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
-
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionFlags;
+import static android.view.WindowManager.TransitionOldType;
+import static android.view.WindowManager.TransitionType;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
+
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.Service;
+import android.app.WindowConfiguration;
import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
@@ -42,8 +62,13 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.TransitionFilter;
+import android.window.TransitionInfo;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardDrawnCallback;
@@ -51,8 +76,11 @@ import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
import com.android.internal.policy.IKeyguardStateCallback;
import com.android.systemui.SystemUIApplication;
+import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
+import java.util.ArrayList;
+
import javax.inject.Inject;
public class KeyguardService extends Service {
@@ -79,40 +107,197 @@ public class KeyguardService extends Service {
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
public static boolean sEnableRemoteKeyguardGoingAwayAnimation =
- !Transitions.ENABLE_SHELL_TRANSITIONS && sEnableRemoteKeyguardAnimation >= 1;
+ sEnableRemoteKeyguardAnimation >= 1;
/**
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
public static boolean sEnableRemoteKeyguardOccludeAnimation =
- !Transitions.ENABLE_SHELL_TRANSITIONS && sEnableRemoteKeyguardAnimation >= 2;
+ sEnableRemoteKeyguardAnimation >= 2;
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
+ private static int newModeToLegacyMode(int newMode) {
+ switch (newMode) {
+ case WindowManager.TRANSIT_OPEN:
+ case WindowManager.TRANSIT_TO_FRONT:
+ return MODE_OPENING;
+ case WindowManager.TRANSIT_CLOSE:
+ case WindowManager.TRANSIT_TO_BACK:
+ return MODE_CLOSING;
+ default:
+ return 2; // MODE_CHANGING
+ }
+ }
+
+ private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers) {
+ final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ boolean changeIsWallpaper =
+ (info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
+ if (wallpapers != changeIsWallpaper) continue;
+
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ final int taskId = taskInfo != null ? change.getTaskInfo().taskId : -1;
+ boolean isNotInRecents;
+ WindowConfiguration windowConfiguration = null;
+ if (taskInfo != null) {
+ if (taskInfo.getConfiguration() != null) {
+ windowConfiguration =
+ change.getTaskInfo().getConfiguration().windowConfiguration;
+ }
+ isNotInRecents = !change.getTaskInfo().isRunning;
+ } else {
+ isNotInRecents = true;
+ }
+ Rect localBounds = new Rect(change.getEndAbsBounds());
+ localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
+
+ out.add(new RemoteAnimationTarget(
+ taskId,
+ newModeToLegacyMode(change.getMode()),
+ change.getLeash(),
+ (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
+ || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0,
+ null /* clipRect */,
+ new Rect(0, 0, 0, 0) /* contentInsets */,
+ info.getChanges().size() - i,
+ new Point(), localBounds, new Rect(change.getEndAbsBounds()),
+ windowConfiguration, isNotInRecents, null /* startLeash */,
+ change.getStartAbsBounds(), taskInfo, false /* allowEnterPip */));
+ }
+ return out.toArray(new RemoteAnimationTarget[out.size()]);
+ }
+
+ private static @TransitionOldType int getTransitionOldType(@TransitionType int type,
+ @TransitionFlags int flags, RemoteAnimationTarget[] apps) {
+ if (type == TRANSIT_KEYGUARD_GOING_AWAY
+ || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+ return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+ : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+ } else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
+ return TRANSIT_OLD_KEYGUARD_OCCLUDE;
+ } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
+ return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+ } else {
+ Slog.d(TAG, "Unexpected transit type: " + type);
+ return TRANSIT_OLD_NONE;
+ }
+ }
+
+ private static IRemoteTransition wrap(IRemoteAnimationRunner runner) {
+ return new IRemoteTransition.Stub() {
+ @Override
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
+ Slog.d(TAG, "Starts IRemoteAnimationRunner: info=" + info);
+ final RemoteAnimationTarget[] apps = wrap(info, false /* wallpapers */);
+ final RemoteAnimationTarget[] wallpapers = wrap(info, true /* wallpapers */);
+ final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0];
+
+ // TODO: Remove this, and update alpha value in the IAnimationRunner.
+ for (TransitionInfo.Change change : info.getChanges()) {
+ t.setAlpha(change.getLeash(), 1.0f);
+ }
+ t.apply();
+ runner.onAnimationStart(getTransitionOldType(info.getType(), info.getFlags(), apps),
+ apps, wallpapers, nonApps,
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ Slog.d(TAG, "Finish IRemoteAnimationRunner.");
+ finishCallback.onTransitionFinished(null /* wct */, null /* t */);
+ }
+ }
+ );
+ }
+
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) {
+
+ }
+ };
+ }
+
@Inject
public KeyguardService(KeyguardViewMediator keyguardViewMediator,
- KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher) {
+ KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
+ ShellTransitions shellTransitions) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
- RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
- if (sEnableRemoteKeyguardGoingAwayAnimation) {
- final RemoteAnimationAdapter exitAnimationAdapter =
- new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
- definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
- definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
- exitAnimationAdapter);
- }
- if (sEnableRemoteKeyguardOccludeAnimation) {
- final RemoteAnimationAdapter occludeAnimationAdapter =
- new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
- definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
- definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
- }
- ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
- DEFAULT_DISPLAY, definition);
+ if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
+ if (sEnableRemoteKeyguardGoingAwayAnimation) {
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
+ TransitionFilter f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+ shellTransitions.registerRemote(f, wrap(mExitAnimationRunner));
+ }
+ if (sEnableRemoteKeyguardOccludeAnimation) {
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
+ // Register for occluding
+ TransitionFilter f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+ f.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ // First require at-least one app showing that occludes.
+ f.mRequirements[0].mMustBeIndependent = false;
+ f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ // Then require that we aren't closing any occludes (because this would mean a
+ // regular task->task or activity->activity animation not involving keyguard).
+ f.mRequirements[1].mNot = true;
+ f.mRequirements[1].mMustBeIndependent = false;
+ f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ shellTransitions.registerRemote(f, mOccludeAnimation);
+
+ // Now register for un-occlude.
+ f = new TransitionFilter();
+ f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+ f.mRequirements = new TransitionFilter.Requirement[]{
+ new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+ // First require at-least one app going-away (doesn't need occlude flag
+ // as that is implicit by it having been visible and we don't want to exclude
+ // cases where we are un-occluding because the app removed its showWhenLocked
+ // capability at runtime).
+ f.mRequirements[1].mMustBeIndependent = false;
+ f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ f.mRequirements[1].mMustBeTask = true;
+ // Then require that we aren't opening any occludes (otherwise we'd remain
+ // occluded).
+ f.mRequirements[0].mNot = true;
+ f.mRequirements[0].mMustBeIndependent = false;
+ f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+ f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ shellTransitions.registerRemote(f, mUnoccludeAnimation);
+ }
+ } else {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ if (sEnableRemoteKeyguardGoingAwayAnimation) {
+ final RemoteAnimationAdapter exitAnimationAdapter =
+ new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
+ exitAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ exitAnimationAdapter);
+ }
+ if (sEnableRemoteKeyguardOccludeAnimation) {
+ final RemoteAnimationAdapter occludeAnimationAdapter =
+ new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE,
+ occludeAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE,
+ occludeAnimationAdapter);
+ }
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
+ }
}
@Override
@@ -145,10 +330,10 @@ public class KeyguardService extends Service {
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
- Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+ Trace.beginSection("mExitAnimationRunner.onAnimationStart#startKeyguardExitAnimation");
checkPermission();
mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
- null /* nonApps */, finishedCallback);
+ nonApps, finishedCallback);
Trace.endSection();
}
@@ -166,14 +351,14 @@ public class KeyguardService extends Service {
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
+ Slog.d(TAG, "mOccludeAnimationRunner.onAnimationStart: transit=" + transit);
try {
if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
mBinder.setOccluded(true /* isOccluded */, true /* animate */);
} else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
mBinder.setOccluded(false /* isOccluded */, true /* animate */);
}
- // TODO(bc-unlock): Implement occlude/unocclude animation applied on apps,
- // wallpapers and nonApps.
+ // TODO(bc-unlock): Implement (un)occlude animation.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException");
@@ -185,6 +370,40 @@ public class KeyguardService extends Service {
}
};
+ final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() {
+ @Override
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
+ t.apply();
+ mBinder.setOccluded(true /* isOccluded */, true /* animate */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ }
+ };
+
+ final IRemoteTransition mUnoccludeAnimation = new IRemoteTransition.Stub() {
+ @Override
+ public void startAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+ throws RemoteException {
+ t.apply();
+ mBinder.setOccluded(false /* isOccluded */, true /* animate */);
+ finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+ }
+
+ @Override
+ public void mergeAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction t, IBinder mergeTarget,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ }
+ };
+
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 941f2c6f4282..e51b60213446 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -31,7 +31,7 @@ import com.android.keyguard.KeyguardViewController
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -222,6 +222,11 @@ class KeyguardUnlockAnimationController @Inject constructor(
keyguardViewController.hide(startTime, 350)
surfaceBehindEntryAnimator.start()
}
+
+ // Finish the keyguard remote animation if the dismiss amount has crossed the threshold.
+ // Check it here in case there is no more change to the dismiss amount after the last change
+ // that starts the keyguard animation. @see #updateKeyguardViewMediatorIfThresholdsReached()
+ finishKeyguardExitRemoteAnimationIfReachThreshold()
}
fun notifyCancelKeyguardExitAnimation() {
@@ -353,16 +358,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
val dismissAmount = keyguardStateController.dismissAmount
-
- // Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
- // crossed the threshold to finish the dismissal.
- val reachedHideKeyguardThreshold = (dismissAmount >= 1f ||
- (keyguardStateController.isDismissingFromSwipe &&
- // Don't hide if we're flinging during a swipe, since we need to finish
- // animating it out. This will be called again after the fling ends.
- !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
- dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
-
if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
// We passed the threshold, and we're not yet showing the surface behind the
@@ -375,9 +370,35 @@ class KeyguardUnlockAnimationController @Inject constructor(
// out.
keyguardViewMediator.get().hideSurfaceBehindKeyguard()
fadeOutSurfaceBehind()
- } else if (keyguardViewMediator.get()
- .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
- reachedHideKeyguardThreshold) {
+ } else {
+ finishKeyguardExitRemoteAnimationIfReachThreshold()
+ }
+ }
+
+ /**
+ * Hides the keyguard if we're fully dismissed, or if we're swiping to dismiss and have crossed
+ * the threshold to finish the dismissal.
+ */
+ private fun finishKeyguardExitRemoteAnimationIfReachThreshold() {
+ // no-op if keyguard is not showing or animation is not enabled.
+ if (!KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation ||
+ !keyguardViewController.isShowing) {
+ return
+ }
+
+ // no-op if animation is not requested yet.
+ if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
+ !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
+ return
+ }
+
+ val dismissAmount = keyguardStateController.dismissAmount
+ if (dismissAmount >= 1f ||
+ (keyguardStateController.isDismissingFromSwipe &&
+ // Don't hide if we're flinging during a swipe, since we need to finish
+ // animating it out. This will be called again after the fling ends.
+ !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
+ dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) {
keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished(false /* cancelled */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ee3d40edc2eb..281771860cb7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2347,8 +2347,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
}
private Configuration.Builder createInteractionJankMonitorConf(String tag) {
- return new Configuration.Builder(CUJ_LOCKSCREEN_UNLOCK_ANIMATION)
- .setView(mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
+ return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
.setTag(tag);
}
@@ -2392,7 +2392,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
final boolean wasShowing = mShowing;
onKeyguardExitFinished();
- if (mKeyguardStateController.isDismissingFromSwipe() || !wasShowing) {
+ if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
new file mode 100644
index 000000000000..f25ec5591e3a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.keyguard
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import javax.inject.Inject
+
+@SysUISingleton
+class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenLifecycle) :
+ ScreenStatusProvider, ScreenLifecycle.Observer {
+
+ init {
+ screenLifecycle.addObserver(this)
+ }
+
+ private val listeners: MutableList<ScreenListener> = mutableListOf()
+
+ override fun removeCallback(listener: ScreenListener) {
+ listeners.remove(listener)
+ }
+
+ override fun addCallback(listener: ScreenListener) {
+ listeners.add(listener)
+ }
+
+ override fun onScreenTurnedOn() {
+ listeners.forEach(ScreenListener::onScreenTurnedOn)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 2bf102f724f4..5ff624db33c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -24,7 +24,6 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -45,7 +44,6 @@ class KeyguardMediaController @Inject constructor(
private val bypassController: KeyguardBypassController,
private val statusBarStateController: SysuiStatusBarStateController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
- private val featureFlags: FeatureFlags,
private val context: Context,
configurationController: ConfigurationController
) {
@@ -73,7 +71,7 @@ class KeyguardMediaController @Inject constructor(
}
private fun updateResources() {
- useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+ useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index cb11454e44ee..4a67e9442510 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -23,10 +23,14 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
@@ -40,10 +44,12 @@ import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSE
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_FORCE_OPAQUE;
import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -54,9 +60,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
-import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.IdRes;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -68,6 +72,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -90,6 +95,7 @@ import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -101,7 +107,6 @@ import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.VisibleForTesting;
@@ -130,6 +135,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -151,7 +157,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
-import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Consumer;
@@ -162,8 +167,7 @@ import dagger.Lazy;
* Contains logic for a navigation bar view.
*/
public class NavigationBar implements View.OnAttachStateChangeListener,
- Callbacks, NavigationModeController.ModeChangedListener,
- AccessibilityButtonModeObserver.ModeChangedListener {
+ Callbacks, NavigationModeController.ModeChangedListener {
public static final String TAG = "NavigationBar";
private static final boolean DEBUG = false;
@@ -180,13 +184,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Context mContext;
private final WindowManager mWindowManager;
private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final SysUiState mSysUiFlagsContainer;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
@@ -201,11 +204,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final UserTracker mUserTracker;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
+ private NavigationBarFrame mFrame;
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -287,7 +292,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public boolean shouldHideOnTouch() {
- return !mNotificationRemoteInputManager.getController().isRemoteInputActive();
+ return !mNotificationRemoteInputManager.isRemoteInputActive();
}
@Override
@@ -381,6 +386,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
@Override
+ public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+ mNavigationBarView
+ .getFloatingRotationButton()
+ .onTaskbarStateChanged(visible, stashed);
+ }
+
+ @Override
public void onToggleRecentApps() {
// The same case as onOverviewShown but only for 3-button navigation.
mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
@@ -475,7 +487,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
- Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
+ Optional<Recents> recentsOptional,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -483,17 +496,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
+ NavigationBarA11yHelper navigationBarA11yHelper,
UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
- mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mSysUiFlagsContainer = sysUiFlagsContainer;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
@@ -508,11 +521,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mUserTracker = userTracker;
mNotificationShadeDepthController = notificationShadeDepthController;
mNavBarMode = mNavigationModeController.addListener(this);
- mAccessibilityButtonModeObserver.addListener(this);
}
public NavigationBarView getView() {
@@ -520,34 +533,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
public View createView(Bundle savedState) {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_SLIPPERY,
- PixelFormat.TRANSLUCENT);
- lp.token = new Binder();
- lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- lp.windowAnimations = 0;
- lp.setTitle("NavigationBar" + mContext.getDisplayId());
- lp.setFitInsetsTypes(0 /* types */);
- lp.setTrustedOverlay();
-
- NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
+ mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
R.layout.navigation_bar_window, null);
- View barView = LayoutInflater.from(frame.getContext()).inflate(
- R.layout.navigation_bar, frame);
+ View barView = LayoutInflater.from(mFrame.getContext()).inflate(
+ R.layout.navigation_bar, mFrame);
barView.addOnAttachStateChangeListener(this);
mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
- mContext.getSystemService(WindowManager.class).addView(frame, lp);
+ mContext.getSystemService(WindowManager.class).addView(mFrame,
+ getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
+ .getRotation()));
mDisplayId = mContext.getDisplayId();
mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -595,19 +591,19 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mDeviceProvisionedController.addCallback(mUserSetupListener);
mNotificationShadeDepthController.addListener(mDepthListener);
- setAccessibilityFloatingMenuModeIfNeeded();
+ updateAccessibilityButtonModeIfNeeded();
return barView;
}
public void destroyView() {
+ setAutoHideController(/* autoHideController */ null);
mCommandQueue.removeCallback(this);
mContext.getSystemService(WindowManager.class).removeViewImmediate(
mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
- mAccessibilityButtonModeObserver.removeListener(this);
- mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
mDeviceProvisionedController.removeCallback(mUserSetupListener);
mNotificationShadeDepthController.removeListener(mDepthListener);
@@ -618,7 +614,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onViewAttachedToWindow(View v) {
final Display display = v.getDisplay();
- mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
+ mNavigationBarView.setComponents(mRecentsOptional);
+ mNavigationBarView.setComponents(mStatusBarOptionalLazy.get().get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
@@ -629,7 +626,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
mNavigationBarView.setBehavior(mBehavior);
- mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+ mNavigationBarA11yHelper.registerA11yEventListener(mAccessibilityListener);
mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
@@ -704,6 +701,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
+ mFrame = null;
mNavigationBarView = null;
mOrientationHandle = null;
}
@@ -722,6 +720,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
* Called when a non-reloading configuration change happens and we need to update.
*/
public void onConfigurationChanged(Configuration newConfig) {
+ final int rotation = newConfig.windowConfiguration.getRotation();
final Locale locale = mContext.getResources().getConfiguration().locale;
final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
if (!locale.equals(mLocale) || ld != mLayoutDirection) {
@@ -735,9 +734,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
refreshLayout(ld);
}
- repositionNavigationBar();
+ repositionNavigationBar(rotation);
if (canShowSecondaryHandle()) {
- int rotation = newConfig.windowConfiguration.getRotation();
if (rotation != mCurrentRotation) {
mCurrentRotation = rotation;
orientSecondaryHomeHandle();
@@ -889,30 +887,15 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return;
}
boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int hints = mNavigationIconHints;
- switch (backDisposition) {
- case InputMethodService.BACK_DISPOSITION_DEFAULT:
- case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
- case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
- if (imeShown) {
- hints |= NAVIGATION_HINT_BACK_ALT;
- } else {
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- }
- break;
- case InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING:
- hints &= ~NAVIGATION_HINT_BACK_ALT;
- break;
- }
- if (showImeSwitcher) {
- hints |= NAVIGATION_HINT_IME_SHOWN;
- } else {
- hints &= ~NAVIGATION_HINT_IME_SHOWN;
- }
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
if (hints == mNavigationIconHints) return;
mNavigationIconHints = hints;
- mNavigationBarView.setNavigationIconHints(hints);
+ if (!isTablet(mContext)) {
+ // All IME functions handled by launcher via Sysui flags for large screen
+ mNavigationBarView.setNavigationIconHints(hints);
+ }
checkBarModes();
updateSystemUiStateFlags(-1);
}
@@ -937,6 +920,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onRotationProposal(final int rotation, boolean isValid) {
+ // The CommandQueue callbacks are added when the view is created to ensure we track other
+ // states, but until the view is attached (at the next traversal), the view's display is
+ // not valid. Just ignore the rotation in this case.
+ if (!mNavigationBarView.isAttachedToWindow()) return;
+
final int winRotation = mNavigationBarView.getDisplay().getRotation();
final boolean rotateSuggestionsDisabled = RotationButtonController
.hasDisable2RotateSuggestionFlag(mDisabledFlags2);
@@ -984,7 +972,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
if (displayId != mDisplayId) {
return;
}
@@ -1116,13 +1104,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|| (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0;
}
- private void repositionNavigationBar() {
- if (!mNavigationBarView.isAttachedToWindow()) return;
+ private void repositionNavigationBar(int rotation) {
+ if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
prepareNavigationBarView();
- mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(),
- ((View) mNavigationBarView.getParent()).getLayoutParams());
+ mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation));
}
private void updateScreenPinningGestures() {
@@ -1164,7 +1151,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1180,13 +1167,14 @@ 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 their ONLY options are to answer or reject the call.)
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
TelecomManager telecomManager =
mContext.getSystemService(TelecomManager.class);
if (telecomManager != null && telecomManager.isRinging()) {
- if (mStatusBarLazy.get().isKeyguardShowing()) {
+ if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
mHomeBlockedThisTouch = true;
@@ -1202,14 +1190,15 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
- mStatusBarLazy.get().awakenDreams();
+ statusBarOptional.ifPresent(StatusBar::awakenDreams);
break;
}
return false;
}
private void onVerticalChanged(boolean isVertical) {
- mStatusBarLazy.get().setQsScrimEnabled(!isVertical);
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.setQsScrimEnabled(!isVertical));
}
private boolean onNavigationTouch(View v, MotionEvent event) {
@@ -1235,7 +1224,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
AssistManager.INVOCATION_TYPE_KEY,
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mAssistManagerLazy.get().startAssist(args);
- mStatusBarLazy.get().awakenDreams();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
mNavigationBarView.abortCurrentGesture();
return true;
}
@@ -1261,7 +1250,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
- mStatusBarLazy.get().awakenDreams();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
mCommandQueue.toggleRecentApps();
}
@@ -1366,8 +1355,11 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return false;
}
- return mStatusBarLazy.get().toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
- MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
+ return mStatusBarOptionalLazy.get().map(
+ statusBar -> statusBar.toggleSplitScreenMode(
+ MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
+ MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS))
+ .orElse(false);
}
private void onAccessibilityClick(View v) {
@@ -1385,9 +1377,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
return true;
}
- void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
- boolean[] feedbackEnabled = new boolean[1];
- int a11yFlags = getA11yButtonState(feedbackEnabled);
+ void updateAccessibilityServicesState() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1396,17 +1387,37 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
updateSystemUiStateFlags(a11yFlags);
}
- private void setAccessibilityFloatingMenuModeIfNeeded() {
- if (QuickStepContract.isGesturalMode(mNavBarMode)) {
+ private void updateAccessibilityButtonModeIfNeeded() {
+ final int mode = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
+
+ // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
+ // mode, so we don't need to update it.
+ if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return;
+ }
+
+ // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
+ if (QuickStepContract.isGesturalMode(mNavBarMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
+ Settings.Secure.putIntForUser(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
+ UserHandle.USER_CURRENT);
+ // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
+ // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
+ } else if (!QuickStepContract.isGesturalMode(mNavBarMode)
+ && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
Settings.Secure.putIntForUser(mContentResolver,
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU, UserHandle.USER_CURRENT);
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
}
}
public void updateSystemUiStateFlags(int a11yFlags) {
if (a11yFlags < 0) {
- a11yFlags = getA11yButtonState(null);
+ a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
}
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1416,6 +1427,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
@@ -1431,45 +1444,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
- /**
- * Returns the system UI flags corresponding the the current accessibility button state
- *
- * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
- */
- public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
- boolean feedbackEnabled = false;
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<AccessibilityServiceInfo> services =
- mAccessibilityManager.getEnabledAccessibilityServiceList(
- AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
- for (int i = services.size() - 1; i >= 0; --i) {
- AccessibilityServiceInfo info = services.get(i);
- if (info.feedbackType != 0 && info.feedbackType !=
- AccessibilityServiceInfo.FEEDBACK_GENERIC) {
- feedbackEnabled = true;
- }
- }
-
- if (outFeedbackEnabled != null) {
- outFeedbackEnabled[0] = feedbackEnabled;
- }
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
- }
-
private void updateAssistantEntrypoints() {
mAssistantAvailable = mAssistManagerLazy.get()
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
@@ -1516,7 +1490,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
/** Sets {@link AutoHideController} to the navigation bar. */
- public void setAutoHideController(AutoHideController autoHideController) {
+ private void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
if (mAutoHideController != null) {
mAutoHideController.setNavigationBar(mAutoHideUiElement);
@@ -1531,7 +1505,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
private void checkBarModes() {
// We only have status bar on default display now.
if (mIsOnDefaultDisplay) {
- mStatusBarLazy.get().checkBarModes();
+ mStatusBarOptionalLazy.get().ifPresent(StatusBar::checkBarModes);
} else {
checkNavBarModes();
}
@@ -1549,7 +1523,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
* Checks current navigation bar mode and make transitions.
*/
public void checkNavBarModes() {
- final boolean anim = mStatusBarLazy.get().isDeviceInteractive()
+ final boolean anim =
+ mStatusBarOptionalLazy.get().map(StatusBar::isDeviceInteractive).orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
}
@@ -1564,18 +1539,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
}
updateScreenPinningGestures();
- setAccessibilityFloatingMenuModeIfNeeded();
+ updateAccessibilityButtonModeIfNeeded();
if (!canShowSecondaryHandle()) {
resetSecondaryHandle();
}
}
- @Override
- public void onAccessibilityButtonModeChanged(int mode) {
- updateAccessibilityServicesState(mAccessibilityManager);
- }
-
public void disableAnimationsDuringHide(long delay) {
mNavigationBarView.setLayoutTransitionsEnabled(false);
mHandler.postDelayed(mEnableLayoutTransitions,
@@ -1600,16 +1570,97 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
mNavigationBarView.getBarTransitions().finishAnimations();
}
- private final AccessibilityServicesStateChangeListener mAccessibilityListener =
+ private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener =
this::updateAccessibilityServicesState;
+ private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+ WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+ lp.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+ int width = WindowManager.LayoutParams.MATCH_PARENT;
+ int height = WindowManager.LayoutParams.MATCH_PARENT;
+ int insetsHeight = -1;
+ int gravity = Gravity.BOTTOM;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ boolean navBarCanMove = true;
+ if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) {
+ Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds();
+ navBarCanMove = displaySize.width() != displaySize.height()
+ && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_navBarCanMove);
+ }
+ if (!navBarCanMove) {
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ } else {
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_frame_height);
+ insetsHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_width);
+ break;
+ }
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ width,
+ height,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_SLIPPERY,
+ PixelFormat.TRANSLUCENT);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ lp.gravity = gravity;
+ if (insetsHeight != -1) {
+ lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0);
+ } else {
+ lp.providedInternalInsets = Insets.NONE;
+ }
+ }
+ lp.token = new Binder();
+ lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.windowAnimations = 0;
+ lp.setTitle("NavigationBar" + mContext.getDisplayId());
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.setTrustedOverlay();
+ return lp;
+ }
+
private boolean canShowSecondaryHandle() {
return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null;
}
private final Consumer<Integer> mRotationWatcher = rotation -> {
- if (mNavigationBarView.needsReorient(rotation)) {
- repositionNavigationBar();
+ if (mNavigationBarView != null
+ && mNavigationBarView.needsReorient(rotation)) {
+ repositionNavigationBar(rotation);
}
};
@@ -1629,7 +1680,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAccessibilityServicesState(mAccessibilityManager);
+ updateAccessibilityServicesState();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
new file mode 100644
index 000000000000..13e6d8b410d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarA11yHelper.java
@@ -0,0 +1,90 @@
+package com.android.systemui.navigationbar;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Extracts shared elements of a11y necessary between navbar and taskbar delegate
+ */
+@SysUISingleton
+public final class NavigationBarA11yHelper implements
+ AccessibilityButtonModeObserver.ModeChangedListener {
+ private final AccessibilityManager mAccessibilityManager;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final List<NavA11yEventListener> mA11yEventListeners = new ArrayList<>();
+
+ @Inject
+ public NavigationBarA11yHelper(AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver) {
+ mAccessibilityManager = accessibilityManager;
+ accessibilityManagerWrapper.addCallback(
+ accessibilityManager1 -> NavigationBarA11yHelper.this.dispatchEventUpdate());
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+
+ mAccessibilityButtonModeObserver.addListener(this);
+ }
+
+ public void registerA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.add(listener);
+ }
+
+ public void removeA11yEventListener(NavA11yEventListener listener) {
+ mA11yEventListeners.remove(listener);
+ }
+
+ private void dispatchEventUpdate() {
+ for (NavA11yEventListener listener : mA11yEventListeners) {
+ listener.updateAccessibilityServicesState();
+ }
+ }
+
+ @Override
+ public void onAccessibilityButtonModeChanged(int mode) {
+ dispatchEventUpdate();
+ }
+
+ /**
+ * See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
+ * {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
+ *
+ * @return the a11y button clickable and long_clickable states, or 0 if there is no
+ * a11y button in the navbar
+ */
+ public int getA11yButtonState() {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ return 0;
+ }
+
+ return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ public interface NavA11yEventListener {
+ void updateAccessibilityServicesState();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index b9e9240b354a..de42730fc948 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -19,16 +19,15 @@ package com.android.systemui.navigationbar;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static com.android.systemui.shared.recents.utilities.Utilities.isTablet;
+
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -86,8 +85,6 @@ public class NavigationBarController implements Callbacks,
ConfigurationController.ConfigurationListener,
NavigationModeController.ModeChangedListener, Dumpable {
- private static final float TABLET_MIN_DPS = 600;
-
private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
@@ -107,18 +104,19 @@ public class NavigationBarController implements Callbacks,
private final Optional<Pip> mPipOptional;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<Recents> mRecentsOptional;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final SystemActions mSystemActions;
private final UiEventLogger mUiEventLogger;
private final Handler mHandler;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final DisplayManager mDisplayManager;
private final NavigationBarOverlayController mNavBarOverlayController;
private final TaskbarDelegate mTaskbarDelegate;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private int mNavMode;
- private boolean mIsTablet;
+ @VisibleForTesting boolean mIsTablet;
private final UserTracker mUserTracker;
/** A displayId - nav bar maps. */
@@ -148,7 +146,7 @@ public class NavigationBarController implements Callbacks,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<Recents> recentsOptional,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -157,6 +155,8 @@ public class NavigationBarController implements Callbacks,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ TaskbarDelegate taskbarDelegate,
UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
@@ -175,13 +175,14 @@ public class NavigationBarController implements Callbacks,
mPipOptional = pipOptional;
mSplitScreenOptional = splitScreenOptional;
mRecentsOptional = recentsOptional;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationShadeDepthController = notificationShadeDepthController;
mSystemActions = systemActions;
mUiEventLogger = uiEventLogger;
mHandler = mainHandler;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
@@ -189,15 +190,17 @@ public class NavigationBarController implements Callbacks,
mNavBarOverlayController = navBarOverlayController;
mNavMode = mNavigationModeController.addListener(this);
mNavigationModeController.addListener(this);
- mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mTaskbarDelegate = taskbarDelegate;
+ mTaskbarDelegate.setOverviewProxyService(overviewProxyService,
+ navigationBarA11yHelper, mSysUiFlagsContainer);
+ mIsTablet = isTablet(mContext);
mUserTracker = userTracker;
}
@Override
public void onConfigChanged(Configuration newConfig) {
boolean isOldConfigTablet = mIsTablet;
- mIsTablet = isTablet(newConfig);
+ mIsTablet = isTablet(newConfig, mContext);
boolean largeScreenChanged = mIsTablet != isOldConfigTablet;
// If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded
if (largeScreenChanged && updateNavbarForTaskbar()) {
@@ -237,25 +240,27 @@ public class NavigationBarController implements Callbacks,
});
}
- /**
- * @return {@code true} if navbar was added/removed, false otherwise
- */
- public boolean updateNavbarForTaskbar() {
- if (!isThreeButtonTaskbarFlagEnabled()) {
- return false;
+ /** @see #initializeTaskbarIfNecessary() */
+ private boolean updateNavbarForTaskbar() {
+ boolean taskbarShown = initializeTaskbarIfNecessary();
+ if (!taskbarShown && mNavigationBars.get(mContext.getDisplayId()) == null) {
+ createNavigationBar(mContext.getDisplay(), null, null);
}
+ return taskbarShown;
+ }
- if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
+ /** @return {@code true} if taskbar is enabled, false otherwise */
+ private boolean initializeTaskbarIfNecessary() {
+ if (mIsTablet) {
// Remove navigation bar when taskbar is showing, currently only for 3 button mode
removeNavigationBar(mContext.getDisplayId());
mCommandQueue.addCallback(mTaskbarDelegate);
- } else if (mNavigationBars.get(mContext.getDisplayId()) == null) {
- // Add navigation bar after taskbar goes away
- createNavigationBar(mContext.getDisplay(), null, null);
+ mTaskbarDelegate.init(mContext.getDisplayId());
+ } else {
mCommandQueue.removeCallback(mTaskbarDelegate);
+ mTaskbarDelegate.destroy();
}
-
- return true;
+ return mIsTablet;
}
@Override
@@ -266,7 +271,7 @@ public class NavigationBarController implements Callbacks,
@Override
public void onDisplayReady(int displayId) {
Display display = mDisplayManager.getDisplay(displayId);
- mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mIsTablet = isTablet(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
}
@@ -302,7 +307,7 @@ public class NavigationBarController implements Callbacks,
*/
public void createNavigationBars(final boolean includeDefaultDisplay,
RegisterStatusBarResult result) {
- if (updateNavbarForTaskbar()) {
+ if (initializeTaskbarIfNecessary()) {
return;
}
@@ -326,7 +331,7 @@ public class NavigationBarController implements Callbacks,
return;
}
- if (isThreeButtonTaskbarEnabled()) {
+ if (mIsTablet) {
return;
}
@@ -363,7 +368,7 @@ public class NavigationBarController implements Callbacks,
mPipOptional,
mSplitScreenOptional,
mRecentsOptional,
- mStatusBarLazy,
+ mStatusBarOptionalLazy,
mShadeController,
mNotificationRemoteInputManager,
mNotificationShadeDepthController,
@@ -371,6 +376,7 @@ public class NavigationBarController implements Callbacks,
mHandler,
mNavBarOverlayController,
mUiEventLogger,
+ mNavigationBarA11yHelper,
mUserTracker);
mNavigationBars.put(displayId, navBar);
@@ -395,7 +401,6 @@ public class NavigationBarController implements Callbacks,
void removeNavigationBar(int displayId) {
NavigationBar navBar = mNavigationBars.get(displayId);
if (navBar != null) {
- navBar.setAutoHideController(/* autoHideController */ null);
navBar.destroyView();
mNavigationBars.remove(displayId);
}
@@ -462,24 +467,6 @@ public class NavigationBarController implements Callbacks,
return mNavigationBars.get(DEFAULT_DISPLAY);
}
- private boolean isThreeButtonTaskbarEnabled() {
- return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON &&
- isThreeButtonTaskbarFlagEnabled();
- }
-
- private boolean isThreeButtonTaskbarFlagEnabled() {
- return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
- }
-
- private boolean isTablet(Configuration newConfig) {
- float density = Resources.getSystem().getDisplayMetrics().density;
- int size = Math.min((int) (density * newConfig.screenWidthDp),
- (int) (density* newConfig.screenHeightDp));
- DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
- float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- return (size / densityRatio) >= TABLET_MIN_DPS;
- }
-
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
for (int i = 0; i < mNavigationBars.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 61e803312b55..93388f93991f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -69,6 +69,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.ContextualButton;
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
@@ -78,7 +79,7 @@ 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;
-import com.android.systemui.navigationbar.gestural.RegionSamplingHelper;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -96,6 +97,8 @@ import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
public class NavigationBarView extends FrameLayout implements
@@ -162,6 +165,7 @@ public class NavigationBarView extends FrameLayout implements
private Configuration mTmpLastConfiguration;
private NavigationBarInflaterView mNavigationInflaterView;
+ private Optional<Recents> mRecentsOptional = Optional.empty();
private NotificationPanelViewController mPanelView;
private RotationContextButton mRotationContextButton;
private FloatingRotationButton mFloatingRotationButton;
@@ -250,7 +254,7 @@ public class NavigationBarView extends FrameLayout implements
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == R.id.action_toggle_overview) {
- Dependency.get(Recents.class).toggleRecentApps();
+ mRecentsOptional.ifPresent(Recents::toggleRecentApps);
} else {
return super.performAccessibilityAction(host, action, args);
}
@@ -273,14 +277,23 @@ public class NavigationBarView extends FrameLayout implements
false /* inScreen */, false /* useNearestRegion */));
};
- private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
- if (visible) {
- // If the button will actually become visible and the navbar is about to hide,
- // tell the statusbar to keep it around for longer
- mAutoHideController.touchAutoHide();
- }
- notifyActiveTouchRegions();
- };
+ private final RotationButtonUpdatesCallback mRotationButtonListener =
+ new RotationButtonUpdatesCallback() {
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ if (visible) {
+ // If the button will actually become visible and the navbar is about
+ // to hide, tell the statusbar to keep it around for longer
+ mAutoHideController.touchAutoHide();
+ }
+ notifyActiveTouchRegions();
+ }
+
+ @Override
+ public void onPositionChanged() {
+ notifyActiveTouchRegions();
+ }
+ };
private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
if (visible) {
@@ -342,6 +355,7 @@ public class NavigationBarView extends FrameLayout implements
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(mContext);
mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates);
+ Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR);
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
@@ -364,7 +378,7 @@ public class NavigationBarView extends FrameLayout implements
public boolean isSamplingEnabled() {
return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
}
- });
+ }, backgroundExecutor);
mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
@@ -385,6 +399,10 @@ public class NavigationBarView extends FrameLayout implements
return mBarTransitions.getLightTransitionsController();
}
+ public void setComponents(Optional<Recents> recentsOptional) {
+ mRecentsOptional = recentsOptional;
+ }
+
public void setComponents(NotificationPanelViewController panel) {
mPanelView = panel;
updatePanelSystemUiStateFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
index e48785844347..3486c6e75931 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButton.java
@@ -20,12 +20,10 @@ import android.view.View;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import java.util.function.Consumer;
-
/** Interface of a rotation button that interacts {@link RotationButtonController}. */
public interface RotationButton {
void setRotationButtonController(RotationButtonController rotationButtonController);
- void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback);
+ void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback);
View getCurrentView();
boolean show();
boolean hide();
@@ -39,4 +37,12 @@ public interface RotationButton {
default boolean acceptRotationProposal() {
return getCurrentView() != null;
}
+
+ /**
+ * Callback for updates provided by a rotation button
+ */
+ interface RotationButtonUpdatesCallback {
+ void onVisibilityChanged(boolean isVisible);
+ void onPositionChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 649ac875b4c2..196625b3630e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -46,7 +46,10 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
+import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.recents.utilities.ViewRippler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -138,12 +141,12 @@ public class RotationButtonController {
}
void setRotationButton(RotationButton rotationButton,
- Consumer<Boolean> visibilityChangedCallback) {
+ RotationButtonUpdatesCallback updatesCallback) {
mRotationButton = rotationButton;
mRotationButton.setRotationButtonController(this);
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
- mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
+ mRotationButton.setUpdatesCallback(updatesCallback);
}
void registerListeners() {
@@ -311,7 +314,7 @@ public class RotationButtonController {
// Prepare to show the navbar icon by updating the icon style to change anim params
mLastRotationSuggestion = rotation; // Remember rotation for click
- final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation);
+ final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
mIconResId = rotationCCW
? R.drawable.ic_sysbar_rotate_button_ccw_start_90
@@ -431,23 +434,6 @@ public class RotationButtonController {
return rotation == NATURAL_ROTATION;
}
- private boolean isRotationAnimationCCW(int from, int to) {
- // All 180deg WM rotation animations are CCW, match that
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
- if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
- if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
- if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
- if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
- return false; // Default
- }
-
private void rescheduleRotationTimeout(final boolean reasonHover) {
// May be called due to a new rotation proposal or a change in hover state
if (reasonHover) {
@@ -520,38 +506,6 @@ public class RotationButtonController {
}
}
- private class ViewRippler {
- private static final int RIPPLE_OFFSET_MS = 50;
- private static final int RIPPLE_INTERVAL_MS = 2000;
- private View mRoot;
-
- public void start(View root) {
- stop(); // Stop any pending ripple animations
-
- mRoot = root;
-
- // Schedule pending ripples, offset the 1st to avoid problems with visibility change
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_OFFSET_MS);
- mRoot.postOnAnimationDelayed(mRipple, RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 2 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 3 * RIPPLE_INTERVAL_MS);
- mRoot.postOnAnimationDelayed(mRipple, 4 * RIPPLE_INTERVAL_MS);
- }
-
- public void stop() {
- if (mRoot != null) mRoot.removeCallbacks(mRipple);
- }
-
- private final Runnable mRipple = new Runnable() {
- @Override
- public void run() { // Cause the ripple to fire via false presses
- if (!mRoot.isAttachedToWindow()) return;
- mRoot.setPressed(true /* pressed */);
- mRoot.setPressed(false /* pressed */);
- }
- };
- }
-
enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "The rotation button was shown")
ROTATION_SUGGESTION_SHOWN(206),
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 03147d8f1085..5a04355406e5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -16,23 +16,114 @@
package com.android.systemui.navigationbar;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.InsetsVisibilities;
+import android.view.View;
+import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.CommandQueue;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+@Singleton
public class TaskbarDelegate implements CommandQueue.Callbacks {
- private final OverviewProxyService mOverviewProxyService;
+ private OverviewProxyService mOverviewProxyService;
+ private NavigationBarA11yHelper mNavigationBarA11yHelper;
+ private SysUiState mSysUiState;
+ private int mDisplayId;
+ private int mNavigationIconHints;
+ private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
+ this::updateSysuiFlags;
+ private int mDisabledFlags;
- public TaskbarDelegate(OverviewProxyService overviewProxyService) {
+ @Inject
+ public TaskbarDelegate() { /* no-op */ }
+
+ public void setOverviewProxyService(OverviewProxyService overviewProxyService,
+ NavigationBarA11yHelper navigationBarA11yHelper,
+ SysUiState sysUiState) {
+ // TODO: adding this in the ctor results in a dagger dependency cycle :(
mOverviewProxyService = overviewProxyService;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
+ mSysUiState = sysUiState;
+ }
+
+ public void destroy() {
+ mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
+ }
+
+ public void init(int displayId) {
+ mDisplayId = displayId;
+ mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
+ // Set initial state for any listeners
+ updateSysuiFlags();
+ }
+
+ private void updateSysuiFlags() {
+ int a11yFlags = mNavigationBarA11yHelper.getA11yButtonState();
+ boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
+ boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
+
+ mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
+ .setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
+ .setFlag(SYSUI_STATE_IME_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ .setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
+ (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
+ .setFlag(SYSUI_STATE_HOME_DISABLED,
+ (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
+ .setFlag(SYSUI_STATE_BACK_DISABLED,
+ (mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
+ .commitUpdate(mDisplayId);
}
@Override
public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
- mOverviewProxyService.notifyImeWindowStatus(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
+ int hints = Utilities.calculateBackDispositionHints(mNavigationIconHints, backDisposition,
+ imeShown, showImeSwitcher);
+ if (hints != mNavigationIconHints) {
+ mNavigationIconHints = hints;
+ updateSysuiFlags();
+ }
+ }
+
+ @Override
+ public void onRotationProposal(int rotation, boolean isValid) {
+ mOverviewProxyService.onRotationProposal(rotation, isValid);
+ }
+
+ @Override
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ mDisabledFlags = state1;
+ updateSysuiFlags();
+ mOverviewProxyService.disable(displayId, state1, state2, animate);
+ }
+
+ @Override
+ public void onSystemBarAttributesChanged(int displayId, int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, int behavior,
+ InsetsVisibilities requestedVisibilities, String packageName) {
+ mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
index 6a97a3379939..ebb67af43a37 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/RotationContextButton.java
@@ -23,10 +23,6 @@ import android.view.View;
import com.android.systemui.navigationbar.RotationButton;
import com.android.systemui.navigationbar.RotationButtonController;
-import com.android.systemui.navigationbar.buttons.ContextualButton;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-
-import java.util.function.Consumer;
/** Containing logic for the rotation button in nav bar. */
public class RotationContextButton extends ContextualButton implements RotationButton {
@@ -48,13 +44,10 @@ public class RotationContextButton extends ContextualButton implements RotationB
}
@Override
- public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
- setListener(new ContextButtonListener() {
- @Override
- public void onVisibilityChanged(ContextualButton button, boolean visible) {
- if (visibilityChangedCallback != null) {
- visibilityChangedCallback.accept(visible);
- }
+ public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+ setListener((button, visible) -> {
+ if (updatesCallback != null) {
+ updatesCallback.onVisibilityChanged(visible);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
index 61118c5d26ac..46057952e079 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButton.java
@@ -20,48 +20,72 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.Surface;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.navigationbar.RotationButton;
import com.android.systemui.navigationbar.RotationButtonController;
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position;
-import java.util.function.Consumer;
-
-/** Containing logic for the rotation button on the physical left bottom corner of the screen. */
+/**
+ * Containing logic for the rotation button on the physical left bottom corner of the screen.
+ */
public class FloatingRotationButton implements RotationButton {
private static final float BACKGROUND_ALPHA = 0.92f;
+ private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
- private final Context mContext;
private final WindowManager mWindowManager;
+ private final ViewGroup mKeyButtonContainer;
private final KeyButtonView mKeyButtonView;
- private final int mDiameter;
- private final int mMargin;
+
+ private final int mContainerSize;
+
private KeyButtonDrawable mKeyButtonDrawable;
private boolean mIsShowing;
private boolean mCanShow = true;
+ private int mDisplayRotation;
+
+ private boolean mIsTaskbarVisible = false;
+ private boolean mIsTaskbarStashed = false;
+
+ private final FloatingRotationButtonPositionCalculator mPositionCalculator;
private RotationButtonController mRotationButtonController;
- private Consumer<Boolean> mVisibilityChangedCallback;
+ private RotationButtonUpdatesCallback mUpdatesCallback;
+ private Position mPosition;
public FloatingRotationButton(Context context) {
- mContext = context;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate(
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(
R.layout.rotate_suggestion, null);
+ mKeyButtonView = mKeyButtonContainer.findViewById(R.id.rotate_suggestion);
mKeyButtonView.setVisibility(View.VISIBLE);
- Resources res = mContext.getResources();
- mDiameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
- mMargin = Math.max(res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
+ Resources res = context.getResources();
+
+ int defaultMargin = Math.max(
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
res.getDimensionPixelSize(R.dimen.rounded_corner_content_padding));
+
+ int taskbarMarginLeft =
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_left_margin);
+ int taskbarMarginBottom =
+ res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_bottom_margin);
+
+ mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
+ taskbarMarginLeft, taskbarMarginBottom);
+
+ final int diameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
+ mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
+ taskbarMarginBottom));
}
@Override
@@ -72,8 +96,8 @@ public class FloatingRotationButton implements RotationButton {
}
@Override
- public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
- mVisibilityChangedCallback = visibilityChangedCallback;
+ public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
+ mUpdatesCallback = updatesCallback;
}
@Override
@@ -86,45 +110,39 @@ public class FloatingRotationButton implements RotationButton {
if (!mCanShow || mIsShowing) {
return false;
}
+
mIsShowing = true;
int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
- mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mContainerSize,
+ mContainerSize,
+ 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
PixelFormat.TRANSLUCENT);
+
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
lp.setFitInsetsTypes(0 /*types */);
- switch (mWindowManager.getDefaultDisplay().getRotation()) {
- case Surface.ROTATION_0:
- lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
- break;
- case Surface.ROTATION_90:
- lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
- break;
- case Surface.ROTATION_180:
- lp.gravity = Gravity.TOP | Gravity.RIGHT;
- break;
- case Surface.ROTATION_270:
- lp.gravity = Gravity.TOP | Gravity.LEFT;
- break;
- default:
- break;
- }
- mWindowManager.addView(mKeyButtonView, lp);
+
+ mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
+ mPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ lp.gravity = mPosition.getGravity();
+ ((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
+ mPosition.getGravity();
+
+ updateTranslation(mPosition, /* animate */ false);
+
+ mWindowManager.addView(mKeyButtonContainer, lp);
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
mKeyButtonDrawable.resetAnimation();
mKeyButtonDrawable.startAnimation();
}
- mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5,
- int i6, int i7) {
- if (mIsShowing && mVisibilityChangedCallback != null) {
- mVisibilityChangedCallback.accept(true);
- }
- mKeyButtonView.removeOnLayoutChangeListener(this);
- }
- });
+
+ if (mUpdatesCallback != null) {
+ mUpdatesCallback.onVisibilityChanged(true);
+ }
+
return true;
}
@@ -133,10 +151,10 @@ public class FloatingRotationButton implements RotationButton {
if (!mIsShowing) {
return false;
}
- mWindowManager.removeViewImmediate(mKeyButtonView);
+ mWindowManager.removeViewImmediate(mKeyButtonContainer);
mIsShowing = false;
- if (mVisibilityChangedCallback != null) {
- mVisibilityChangedCallback.accept(false);
+ if (mUpdatesCallback != null) {
+ mUpdatesCallback.onVisibilityChanged(false);
}
return true;
}
@@ -183,4 +201,43 @@ public class FloatingRotationButton implements RotationButton {
hide();
}
}
+
+ public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
+ mIsTaskbarVisible = taskbarVisible;
+ mIsTaskbarStashed = taskbarStashed;
+
+ if (!mIsShowing) return;
+
+ final Position newPosition = mPositionCalculator
+ .calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
+
+ if (newPosition.getTranslationX() != mPosition.getTranslationX()
+ || newPosition.getTranslationY() != mPosition.getTranslationY()) {
+ updateTranslation(newPosition, /* animate */ true);
+ mPosition = newPosition;
+ }
+ }
+
+ private void updateTranslation(Position position, boolean animate) {
+ final int translationX = position.getTranslationX();
+ final int translationY = position.getTranslationY();
+
+ if (animate) {
+ mKeyButtonView
+ .animate()
+ .translationX(translationX)
+ .translationY(translationY)
+ .setDuration(MARGIN_ANIMATION_DURATION_MILLIS)
+ .setInterpolator(new AccelerateDecelerateInterpolator())
+ .withEndAction(() -> {
+ if (mUpdatesCallback != null && mIsShowing) {
+ mUpdatesCallback.onPositionChanged();
+ }
+ })
+ .start();
+ } else {
+ mKeyButtonView.setTranslationX(translationX);
+ mKeyButtonView.setTranslationY(translationY);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
new file mode 100644
index 000000000000..3ce51ad331c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculator.kt
@@ -0,0 +1,65 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+
+/**
+ * Calculates gravity and translation that is necessary to display
+ * the button in the correct position based on the current state
+ */
+internal class FloatingRotationButtonPositionCalculator(
+ private val defaultMargin: Int,
+ private val taskbarMarginLeft: Int,
+ private val taskbarMarginBottom: Int
+) {
+
+ fun calculatePosition(
+ currentRotation: Int,
+ taskbarVisible: Boolean,
+ taskbarStashed: Boolean
+ ): Position {
+
+ val isTaskbarSide = currentRotation == Surface.ROTATION_0
+ || currentRotation == Surface.ROTATION_90
+ val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed
+
+ val gravity = resolveGravity(currentRotation)
+
+ val marginLeft = if (useTaskbarMargin) taskbarMarginLeft else defaultMargin
+ val marginBottom = if (useTaskbarMargin) taskbarMarginBottom else defaultMargin
+
+ val translationX =
+ if (gravity and Gravity.RIGHT == Gravity.RIGHT) {
+ -marginLeft
+ } else {
+ marginLeft
+ }
+ val translationY =
+ if (gravity and Gravity.BOTTOM == Gravity.BOTTOM) {
+ -marginBottom
+ } else {
+ marginBottom
+ }
+
+ return Position(
+ gravity = gravity,
+ translationX = translationX,
+ translationY = translationY
+ )
+ }
+
+ data class Position(
+ val gravity: Int,
+ val translationX: Int,
+ val translationY: Int
+ )
+
+ private fun resolveGravity(rotation: Int): Int =
+ when (rotation) {
+ Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
+ Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
+ Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
+ Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
+ else -> throw IllegalArgumentException("Invalid rotation $rotation")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 7fdb79eae2a9..8d1dfc842fba 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -55,9 +55,11 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
import java.io.PrintWriter;
+import java.util.concurrent.Executor;
public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPlugin {
@@ -349,6 +351,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
setVisibility(GONE);
+ Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR);
boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@@ -366,7 +369,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl
public boolean isSamplingEnabled() {
return isPrimaryDisplay;
}
- });
+ }, backgroundExecutor);
mRegionSamplingHelper.setWindowVisible(true);
mShowProtection = !isPrimaryDisplay;
}
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 985903435b9a..82a5aa04a073 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -450,7 +450,7 @@ public class PeopleSpaceWidgetManager {
PeopleTileKey key = new PeopleTileKey(
sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName());
if (!PeopleTileKey.isValid(key)) {
- Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
+ if (DEBUG) Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
return;
}
int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index ad1e21d7cc45..0b565ea25911 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -17,25 +17,27 @@ package com.android.systemui.plugins;
import android.util.ArrayMap;
import com.android.systemui.Dependency;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
import com.android.systemui.shared.plugins.PluginManager;
import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
/**
*/
-@SysUISingleton
+@Singleton
public class PluginDependencyProvider extends DependencyProvider {
private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>();
- private final PluginManager mManager;
+ private final Lazy<PluginManager> mManagerLazy;
/**
*/
@Inject
- public PluginDependencyProvider(PluginManager manager) {
- mManager = manager;
+ public PluginDependencyProvider(Lazy<PluginManager> managerLazy) {
+ mManagerLazy = managerLazy;
PluginDependency.sProvider = this;
}
@@ -51,7 +53,7 @@ public class PluginDependencyProvider extends DependencyProvider {
@Override
<T> T get(Plugin p, Class<T> cls) {
- if (!mManager.dependsOn(p, cls)) {
+ if (!mManagerLazy.get().dependsOn(p, cls)) {
throw new IllegalArgumentException(p.getClass() + " does not depend on " + cls);
}
synchronized (mDependencies) {
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index 63374150adaa..40f59744e038 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -22,16 +22,22 @@ import android.content.pm.PackageManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.shared.plugins.PluginEnabler;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** */
+@Singleton
public class PluginEnablerImpl implements PluginEnabler {
private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
- private PackageManager mPm;
+ private final PackageManager mPm;
private final SharedPreferences mAutoDisabledPrefs;
public PluginEnablerImpl(Context context) {
this(context, context.getPackageManager());
}
+ @Inject
@VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
mAutoDisabledPrefs = context.getSharedPreferences(
CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
index 7f01d6f1ffa3..654d000ca09e 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -15,16 +15,17 @@
package com.android.systemui.plugins;
import android.content.Context;
-import android.os.Build;
-import android.os.Looper;
import android.util.Log;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.shared.plugins.PluginEnabler;
import com.android.systemui.shared.plugins.PluginInitializer;
import com.android.systemui.shared.plugins.PluginManagerImpl;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** */
+@Singleton
public class PluginInitializerImpl implements PluginInitializer {
/**
@@ -33,44 +34,24 @@ public class PluginInitializerImpl implements PluginInitializer {
private static final boolean WTFS_SHOULD_CRASH = false;
private boolean mWtfsSet;
- @Override
- public Looper getBgLooper() {
- return Dependency.get(Dependency.BG_LOOPER);
- }
-
- @Override
- public void onPluginManagerInit() {
- // Plugin dependencies that don't have another good home can go here, but
- // dependencies that have better places to init can happen elsewhere.
- Dependency.get(PluginDependencyProvider.class)
- .allowPluginDependency(ActivityStarter.class);
+ @Inject
+ public PluginInitializerImpl(PluginDependencyProvider dependencyProvider) {
+ dependencyProvider.allowPluginDependency(ActivityStarter.class);
}
@Override
- public String[] getWhitelistedPlugins(Context context) {
+ public String[] getPrivilegedPlugins(Context context) {
return context.getResources().getStringArray(R.array.config_pluginWhitelist);
}
- public PluginEnabler getPluginEnabler(Context context) {
- return new PluginEnablerImpl(context);
- }
@Override
public void handleWtfs() {
if (WTFS_SHOULD_CRASH && !mWtfsSet) {
mWtfsSet = true;
- Log.setWtfHandler(new Log.TerribleFailureHandler() {
- @Override
- public void onTerribleFailure(String tag, Log.TerribleFailure what,
- boolean system) {
- throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
- }
+ Log.setWtfHandler((tag, what, system) -> {
+ throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
});
}
}
-
- @Override
- public boolean isDebuggable() {
- return Build.IS_DEBUGGABLE;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
new file mode 100644
index 000000000000..1ea9b3c08b1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_HANDLER;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.plugins.PluginEnabler;
+import com.android.systemui.shared.plugins.PluginInitializer;
+import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginPrefs;
+import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
+import com.android.systemui.util.concurrency.ThreadFactory;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for code related to plugins.
+ *
+ * Covers code both in com.android.systemui.plugins and code in
+ * com.android.systemui.shared.plugins.
+ */
+@Module(includes = {GlobalConcurrencyModule.class})
+public abstract class PluginsModule {
+ public static final String PLUGIN_THREAD = "plugin_thread";
+ public static final String PLUGIN_DEBUG = "plugin_debug";
+ public static final String PLUGIN_PRIVILEGED = "plugin_privileged";
+
+ @Provides
+ @Named(PLUGIN_DEBUG)
+ static boolean providesPluginDebug() {
+ return Build.IS_DEBUGGABLE;
+ }
+
+ @Binds
+ abstract PluginEnabler bindsPluginEnablerImpl(PluginEnablerImpl impl);
+
+ @Binds
+ abstract PluginInitializer bindsPluginInitializerImpl(PluginInitializerImpl impl);
+
+ @Provides
+ @Singleton
+ static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
+ PackageManager packageManager, @Main Executor mainExecutor,
+ @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer,
+ NotificationManager notificationManager, PluginEnabler pluginEnabler,
+ @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
+ return new PluginInstanceManager.Factory(
+ context, packageManager, mainExecutor, pluginExecutor, initializer,
+ notificationManager, pluginEnabler, privilegedPlugins);
+ }
+
+ @Provides
+ @Singleton
+ @Named(PLUGIN_THREAD)
+ static Executor providesPluginExecutor(ThreadFactory threadFactory) {
+ return threadFactory.buildExecutorOnNewThread("plugin");
+ }
+
+ @Provides
+ static PluginManager providesPluginManager(
+ Context context,
+ PluginInstanceManager.Factory instanceManagerFactory,
+ @Named(PLUGIN_DEBUG) boolean debug,
+ @Named(PRE_HANDLER)
+ Optional<Thread.UncaughtExceptionHandler> uncaughtExceptionHandlerOptional,
+ PluginEnabler pluginEnabler,
+ PluginPrefs pluginPrefs,
+ @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
+ return new PluginManagerImpl(context, instanceManagerFactory, debug,
+ uncaughtExceptionHandlerOptional, pluginEnabler, pluginPrefs,
+ privilegedPlugins);
+ }
+
+ @Provides
+ static PluginPrefs providesPluginPrefs(Context context) {
+ return new PluginPrefs(context);
+ }
+
+ @Provides
+ @Named(PLUGIN_PRIVILEGED)
+ static List<String> providesPrivilegedPlugins(PluginInitializer initializer, Context context) {
+ return Arrays.asList(initializer.getPrivilegedPlugins(context));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index a888305cc83d..625265485f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -54,6 +54,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.Arrays;
+import java.util.Optional;
import java.util.concurrent.Future;
import javax.inject.Inject;
@@ -108,15 +109,15 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
private IThermalEventListener mUsbThermalEventListener;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue, Lazy<StatusBar> statusBarLazy) {
+ CommandQueue commandQueue, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
public void start() {
@@ -710,7 +711,8 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- if (!mStatusBarLazy.get().isDeviceInVrMode()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (!statusBarOptional.map(StatusBar::isDeviceInVrMode).orElse(false)) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e74703b42..eec69f98b9be 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt
appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
.toList()
.sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+ { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
types = itemsList.map { it.privacyType }.distinct().sorted()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
deleted file mode 100644
index 38b20ee45946..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.systemui.statusbar.phone.AutoTileManager.HOTSPOT;
-import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
-import static com.android.systemui.statusbar.phone.AutoTileManager.NIGHT;
-import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
-import com.android.systemui.util.UserAwareController;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.inject.Inject;
-
-public class AutoAddTracker implements UserAwareController {
-
- private static final String[][] CONVERT_PREFS = {
- {Key.QS_HOTSPOT_ADDED, HOTSPOT},
- {Key.QS_DATA_SAVER_ADDED, SAVER},
- {Key.QS_INVERT_COLORS_ADDED, INVERSION},
- {Key.QS_WORK_ADDED, WORK},
- {Key.QS_NIGHTDISPLAY_ADDED, NIGHT},
- };
-
- private final ArraySet<String> mAutoAdded;
- private final Context mContext;
- private int mUserId;
-
- public AutoAddTracker(Context context, int userId) {
- mContext = context;
- mUserId = userId;
- mAutoAdded = new ArraySet<>(getAdded());
- }
-
- /**
- * Init method must be called after construction to start listening
- */
- public void initialize() {
- // TODO: remove migration code and shared preferences keys after P release
- if (mUserId == UserHandle.USER_SYSTEM) {
- for (String[] convertPref : CONVERT_PREFS) {
- if (Prefs.getBoolean(mContext, convertPref[0], false)) {
- setTileAdded(convertPref[1]);
- Prefs.remove(mContext, convertPref[0]);
- }
- }
- }
- mContext.getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
- UserHandle.USER_ALL);
- }
-
- @Override
- public void changeUser(UserHandle newUser) {
- if (newUser.getIdentifier() == mUserId) {
- return;
- }
- mUserId = newUser.getIdentifier();
- mAutoAdded.clear();
- mAutoAdded.addAll(getAdded());
- }
-
- @Override
- public int getCurrentUserId() {
- return mUserId;
- }
-
- public boolean isAdded(String tile) {
- return mAutoAdded.contains(tile);
- }
-
- public void setTileAdded(String tile) {
- if (mAutoAdded.add(tile)) {
- saveTiles();
- }
- }
-
- public void setTileRemoved(String tile) {
- if (mAutoAdded.remove(tile)) {
- saveTiles();
- }
- }
-
- public void destroy() {
- mContext.getContentResolver().unregisterContentObserver(mObserver);
- }
-
- private void saveTiles() {
- Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
- TextUtils.join(",", mAutoAdded), mUserId);
- }
-
- private Collection<String> getAdded() {
- String current = Secure.getStringForUser(mContext.getContentResolver(),
- Secure.QS_AUTO_ADDED_TILES, mUserId);
- if (current == null) {
- return Collections.emptyList();
- }
- return Arrays.asList(current.split(","));
- }
-
- @VisibleForTesting
- protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- mAutoAdded.clear();
- mAutoAdded.addAll(getAdded());
- }
- };
-
- public static class Builder {
- private final Context mContext;
- private int mUserId;
-
- @Inject
- public Builder(Context context) {
- mContext = context;
- }
-
- public Builder setUserId(int userId) {
- mUserId = userId;
- return this;
- }
-
- public AutoAddTracker build() {
- return new AutoAddTracker(mContext, mUserId);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
new file mode 100644
index 000000000000..7ffa9d931ff0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.ArraySet
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.BroadcastDispatcher
+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.util.UserAwareController
+import com.android.systemui.util.settings.SecureSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+private const val TAG = "AutoAddTracker"
+
+/**
+ * Class to track tiles that have been auto-added
+ *
+ * The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES].
+ *
+ * It also handles restore gracefully.
+ */
+class AutoAddTracker @VisibleForTesting constructor(
+ private val secureSettings: SecureSettings,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val qsHost: QSHost,
+ private val dumpManager: DumpManager,
+ private val mainHandler: Handler?,
+ private val backgroundExecutor: Executor,
+ private var userId: Int
+) : UserAwareController, Dumpable {
+
+ companion object {
+ private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
+ }
+
+ @GuardedBy("autoAdded")
+ private val autoAdded = ArraySet<String>()
+ private var restoredTiles: Set<String>? = null
+
+ override val currentUserId: Int
+ get() = userId
+
+ private val contentObserver = object : ContentObserver(mainHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: Collection<Uri>,
+ flags: Int,
+ _userId: Int
+ ) {
+ if (_userId != userId) {
+ // Ignore changes outside of our user. We'll load the correct value on user change
+ return
+ }
+ loadTiles()
+ }
+ }
+
+ private val restoreReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != Intent.ACTION_SETTING_RESTORED) return
+ processRestoreIntent(intent)
+ }
+ }
+
+ private fun processRestoreIntent(intent: Intent) {
+ when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
+ Settings.Secure.QS_TILES -> {
+ restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+ ?.split(",")
+ ?.toSet()
+ ?: run {
+ Log.w(TAG, "Null restored tiles for user $userId")
+ emptySet()
+ }
+ }
+ Settings.Secure.QS_AUTO_ADDED_TILES -> {
+ restoredTiles?.let { tiles ->
+ val restoredAutoAdded = intent
+ .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+ ?.split(",")
+ ?: emptyList()
+ val autoAddedBeforeRestore = intent
+ .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
+ ?.split(",")
+ ?: emptyList()
+
+ val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+ if (tilesToRemove.isNotEmpty()) {
+ qsHost.removeTiles(tilesToRemove)
+ }
+ val tiles = synchronized(autoAdded) {
+ autoAdded.clear()
+ autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
+ getTilesFromListLocked()
+ }
+ saveTiles(tiles)
+ } ?: run {
+ Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
+ "${Settings.Secure.QS_TILES} for user $userId")
+ }
+ }
+ else -> {} // Do nothing for other Settings
+ }
+ }
+
+ /**
+ * Init method must be called after construction to start listening
+ */
+ fun initialize() {
+ dumpManager.registerDumpable(TAG, this)
+ loadTiles()
+ secureSettings.registerContentObserverForUser(
+ secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
+ contentObserver,
+ UserHandle.USER_ALL
+ )
+ registerBroadcastReceiver()
+ }
+
+ /**
+ * Unregister listeners, receivers and observers
+ */
+ fun destroy() {
+ dumpManager.unregisterDumpable(TAG)
+ secureSettings.unregisterContentObserver(contentObserver)
+ unregisterBroadcastReceiver()
+ }
+
+ private fun registerBroadcastReceiver() {
+ broadcastDispatcher.registerReceiver(
+ restoreReceiver,
+ FILTER,
+ backgroundExecutor,
+ UserHandle.of(userId)
+ )
+ }
+
+ private fun unregisterBroadcastReceiver() {
+ broadcastDispatcher.unregisterReceiver(restoreReceiver)
+ }
+
+ override fun changeUser(newUser: UserHandle) {
+ if (newUser.identifier == userId) return
+ unregisterBroadcastReceiver()
+ userId = newUser.identifier
+ restoredTiles = null
+ loadTiles()
+ registerBroadcastReceiver()
+ }
+
+ /**
+ * Returns `true` if the tile has been auto-added before
+ */
+ fun isAdded(tile: String): Boolean {
+ return synchronized(autoAdded) {
+ tile in autoAdded
+ }
+ }
+
+ /**
+ * Sets a tile as auto-added.
+ *
+ * From here on, [isAdded] will return true for that tile.
+ */
+ fun setTileAdded(tile: String) {
+ val tiles = synchronized(autoAdded) {
+ if (autoAdded.add(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
+ }
+ tiles?.let { saveTiles(it) }
+ }
+
+ /**
+ * Removes a tile from the list of auto-added.
+ *
+ * This allows for this tile to be auto-added again in the future.
+ */
+ fun setTileRemoved(tile: String) {
+ val tiles = synchronized(autoAdded) {
+ if (autoAdded.remove(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
+ }
+ tiles?.let { saveTiles(it) }
+ }
+
+ private fun getTilesFromListLocked(): String {
+ return TextUtils.join(",", autoAdded)
+ }
+
+ private fun saveTiles(tiles: String) {
+ secureSettings.putStringForUser(
+ Settings.Secure.QS_AUTO_ADDED_TILES,
+ tiles,
+ /* tag */ null,
+ /* makeDefault */ false,
+ userId,
+ /* overrideableByRestore */ true
+ )
+ }
+
+ private fun loadTiles() {
+ synchronized(autoAdded) {
+ autoAdded.clear()
+ autoAdded.addAll(getAdded())
+ }
+ }
+
+ private fun getAdded(): Collection<String> {
+ val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
+ return current?.split(",") ?: emptySet()
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("Current user: $userId")
+ pw.println("Added tiles: $autoAdded")
+ }
+
+ @SysUISingleton
+ class Builder @Inject constructor(
+ private val secureSettings: SecureSettings,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val qsHost: QSHost,
+ private val dumpManager: DumpManager,
+ @Main private val handler: Handler,
+ @Background private val executor: Executor
+ ) {
+ private var userId: Int = 0
+
+ fun setUserId(_userId: Int): Builder {
+ userId = _userId
+ return this
+ }
+
+ fun build(): AutoAddTracker {
+ return AutoAddTracker(
+ secureSettings,
+ broadcastDispatcher,
+ qsHost,
+ dumpManager,
+ handler,
+ executor,
+ userId
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
new file mode 100644
index 000000000000..bedb33038134
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.Intent
+import android.os.UserManager
+import android.provider.Settings
+import android.view.View
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.nano.MetricsProto
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
+import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
+import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.phone.SettingsButton
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.ViewController
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Manages [FooterActionsView] behaviour, both when it's placed in QS or QQS (split shade).
+ * Main difference between QS and QQS behaviour is condition when buttons should be visible,
+ * determined by [buttonsVisibleState]
+ */
+class FooterActionsController @Inject constructor(
+ view: FooterActionsView,
+ private val qsPanelController: QSPanelController,
+ private val activityStarter: ActivityStarter,
+ private val userManager: UserManager,
+ private val userInfoController: UserInfoController,
+ private val multiUserSwitchController: MultiUserSwitchController,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val falsingManager: FalsingManager,
+ private val metricsLogger: MetricsLogger,
+ private val tunerService: TunerService,
+ private val globalActionsDialog: GlobalActionsDialogLite,
+ private val uiEventLogger: UiEventLogger,
+ @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
+ private val buttonsVisibleState: ExpansionState
+) : ViewController<FooterActionsView>(view) {
+
+ enum class ExpansionState { COLLAPSED, EXPANDED }
+
+ private var listening: Boolean = false
+
+ var expanded = false
+ set(value) {
+ if (field != value) {
+ field = value
+ updateView()
+ }
+ }
+
+ private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
+ private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
+ private val editButton: View = view.findViewById(android.R.id.edit)
+ private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
+
+ private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
+ val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
+ mView.onUserInfoChanged(picture, isGuestUser)
+ }
+
+ private val onClickListener = View.OnClickListener { v ->
+ // Don't do anything until views are unhidden. Don't do anything if the tap looks
+ // suspicious.
+ if (!buttonsVisible() || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return@OnClickListener
+ }
+ if (v === settingsButton) {
+ if (!deviceProvisionedController.isCurrentUserSetup) {
+ // If user isn't setup just unlock the device and dump them back at SUW.
+ activityStarter.postQSRunnableDismissingKeyguard {}
+ return@OnClickListener
+ }
+ metricsLogger.action(
+ if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+ else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
+ if (settingsButton.isTunerClick) {
+ activityStarter.postQSRunnableDismissingKeyguard {
+ if (isTunerEnabled()) {
+ tunerService.showResetRequest {
+ // Relaunch settings so that the tuner disappears.
+ startSettingsActivity()
+ }
+ } else {
+ Toast.makeText(context, R.string.tuner_toast, Toast.LENGTH_LONG).show()
+ tunerService.isTunerEnabled = true
+ }
+ startSettingsActivity()
+ }
+ } else {
+ startSettingsActivity()
+ }
+ } else if (v === powerMenuLite) {
+ uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+ globalActionsDialog.showOrHideDialog(false, true)
+ }
+ }
+
+ private fun buttonsVisible(): Boolean {
+ return when (buttonsVisibleState) {
+ EXPANDED -> expanded
+ COLLAPSED -> !expanded
+ }
+ }
+
+ override fun onInit() {
+ multiUserSwitchController.init()
+ }
+
+ fun hideFooter() {
+ mView.visibility = View.GONE
+ }
+
+ fun showFooter() {
+ mView.visibility = View.VISIBLE
+ updateView()
+ }
+
+ private fun startSettingsActivity() {
+ val animationController = settingsButtonContainer?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON)
+ }
+ activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS),
+ true /* dismissShade */, animationController)
+ }
+
+ @VisibleForTesting
+ public override fun onViewAttached() {
+ if (showPMLiteButton) {
+ powerMenuLite.visibility = View.VISIBLE
+ powerMenuLite.setOnClickListener(onClickListener)
+ } else {
+ powerMenuLite.visibility = View.GONE
+ }
+ settingsButton.setOnClickListener(onClickListener)
+ editButton.setOnClickListener(View.OnClickListener { view: View? ->
+ if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return@OnClickListener
+ }
+ activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
+ })
+
+ updateView()
+ }
+
+ private fun updateView() {
+ mView.updateEverything(buttonsVisible(), isTunerEnabled(),
+ multiUserSwitchController.isMultiUserEnabled)
+ }
+
+ override fun onViewDetached() {
+ setListening(false)
+ }
+
+ fun setListening(listening: Boolean) {
+ if (this.listening == listening) {
+ return
+ }
+ this.listening = listening
+ if (this.listening) {
+ userInfoController.addCallback(onUserInfoChangedListener)
+ } else {
+ userInfoController.removeCallback(onUserInfoChangedListener)
+ }
+ }
+
+ fun disable(state2: Int) {
+ mView.disable(buttonsVisible(), state2, isTunerEnabled(),
+ multiUserSwitchController.isMultiUserEnabled)
+ }
+
+ fun setExpansion(headerExpansionFraction: Float) {
+ mView.setExpansion(headerExpansionFraction)
+ }
+
+ fun updateAnimator(width: Int, numTiles: Int) {
+ mView.updateAnimator(width, numTiles)
+ }
+
+ fun setKeyguardShowing() {
+ mView.setKeyguardShowing()
+ }
+
+ private fun isTunerEnabled() = tunerService.isTunerEnabled
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
new file mode 100644
index 000000000000..fcfa72a82acb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.os.UserManager
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.qs.dagger.QSFlagsModule
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.tuner.TunerService
+import javax.inject.Inject
+import javax.inject.Named
+
+class FooterActionsControllerBuilder @Inject constructor(
+ private val qsPanelController: QSPanelController,
+ private val activityStarter: ActivityStarter,
+ private val userManager: UserManager,
+ private val userInfoController: UserInfoController,
+ private val multiUserSwitchController: MultiUserSwitchController,
+ private val deviceProvisionedController: DeviceProvisionedController,
+ private val falsingManager: FalsingManager,
+ private val metricsLogger: MetricsLogger,
+ private val tunerService: TunerService,
+ private val globalActionsDialog: GlobalActionsDialogLite,
+ private val uiEventLogger: UiEventLogger,
+ @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean
+) {
+ private lateinit var view: FooterActionsView
+ private lateinit var buttonsVisibleState: ExpansionState
+
+ fun withView(view: FooterActionsView): FooterActionsControllerBuilder {
+ this.view = view
+ return this
+ }
+
+ fun withButtonsVisibleWhen(state: ExpansionState): FooterActionsControllerBuilder {
+ buttonsVisibleState = state
+ return this
+ }
+
+ fun build(): FooterActionsController {
+ return FooterActionsController(view, qsPanelController, activityStarter, userManager,
+ userInfoController, multiUserSwitchController, deviceProvisionedController,
+ falsingManager, metricsLogger, tunerService, globalActionsDialog, uiEventLogger,
+ showPMLiteButton, buttonsVisibleState)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
new file mode 100644
index 000000000000..941e54a55393
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PorterDuff
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.RippleDrawable
+import android.os.UserManager
+import android.util.AttributeSet
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.settingslib.Utils
+import com.android.settingslib.drawable.UserIconDrawable
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.MultiUserSwitch
+import com.android.systemui.statusbar.phone.SettingsButton
+
+/**
+ * Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS,
+ * in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
+ * edit tiles, power off and conditionally: user switch and tuner
+ */
+class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+ private lateinit var settingsContainer: View
+ private lateinit var settingsButton: SettingsButton
+ private lateinit var multiUserSwitch: MultiUserSwitch
+ private lateinit var multiUserAvatar: ImageView
+ private lateinit var tunerIcon: View
+ private lateinit var editTilesButton: View
+
+ private var settingsCogAnimator: TouchAnimator? = null
+
+ private var qsDisabled = false
+ private var expansionAmount = 0f
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ editTilesButton = requireViewById(android.R.id.edit)
+ settingsButton = findViewById(R.id.settings_button)
+ settingsContainer = findViewById(R.id.settings_button_container)
+ multiUserSwitch = findViewById(R.id.multi_user_switch)
+ multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
+ tunerIcon = requireViewById(R.id.tuner_icon)
+
+ // RenderThread is doing more harm than good when touching the header (to expand quick
+ // settings), so disable it for this view
+ if (settingsButton.background is RippleDrawable) {
+ (settingsButton.background as RippleDrawable).setForceSoftware(true)
+ }
+ updateResources()
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
+ }
+
+ fun updateAnimator(width: Int, numTiles: Int) {
+ val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
+ mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
+ val remaining = (width - numTiles * size) / (numTiles - 1)
+ val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
+ val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
+ settingsCogAnimator = TouchAnimator.Builder()
+ .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
+ .addFloat(settingsButton, "rotation", -120f, 0f)
+ .build()
+ setExpansion(expansionAmount)
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ updateResources()
+ }
+
+ override fun onRtlPropertiesChanged(layoutDirection: Int) {
+ super.onRtlPropertiesChanged(layoutDirection)
+ updateResources()
+ }
+
+ private fun updateResources() {
+ val tunerIconTranslation = mContext.resources
+ .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation).toFloat()
+ tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
+ }
+
+ fun setKeyguardShowing() {
+ setExpansion(expansionAmount)
+ }
+
+ fun setExpansion(headerExpansionFraction: Float) {
+ expansionAmount = headerExpansionFraction
+ if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
+ }
+
+ fun disable(
+ buttonsVisible: Boolean,
+ state2: Int,
+ isTunerEnabled: Boolean,
+ multiUserEnabled: Boolean
+ ) {
+ val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
+ if (disabled == qsDisabled) return
+ qsDisabled = disabled
+ updateEverything(buttonsVisible, isTunerEnabled, multiUserEnabled)
+ }
+
+ fun updateEverything(
+ buttonsVisible: Boolean,
+ isTunerEnabled: Boolean,
+ multiUserEnabled: Boolean
+ ) {
+ post {
+ updateVisibilities(buttonsVisible, isTunerEnabled, multiUserEnabled)
+ updateClickabilities()
+ isClickable = false
+ }
+ }
+
+ private fun updateClickabilities() {
+ multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
+ editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
+ settingsButton.isClickable = settingsButton.visibility == VISIBLE
+ }
+
+ private fun updateVisibilities(
+ buttonsVisible: Boolean,
+ isTunerEnabled: Boolean,
+ multiUserEnabled: Boolean
+ ) {
+ settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
+ tunerIcon.visibility = if (isTunerEnabled) VISIBLE else INVISIBLE
+ multiUserSwitch.visibility = if (buttonsVisible && multiUserEnabled) VISIBLE else GONE
+ val isDemo = UserManager.isDeviceInDemoMode(context)
+ settingsButton.visibility = if (isDemo || !buttonsVisible) INVISIBLE else VISIBLE
+ }
+
+ fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) {
+ var pictureToSet = picture
+ if (picture != null && isGuestUser && picture !is UserIconDrawable) {
+ pictureToSet = picture.constantState.newDrawable(resources).mutate()
+ pictureToSet.setColorFilter(
+ Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
+ PorterDuff.Mode.SRC_IN)
+ }
+ multiUserAvatar.setImageDrawable(pictureToSet)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 4fcd46c96fe3..8659b8b868d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,9 @@
package com.android.systemui.qs;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
+
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.util.Log;
@@ -43,6 +46,7 @@ import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/** */
@QSScope
@@ -67,13 +71,15 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
* position to the normal QS panel. These views will only show once the animation is complete,
* to prevent overlapping of semi transparent views
*/
- private final ArrayList<View> mQuickQsViews = new ArrayList<>();
+ private final ArrayList<View> mAnimatedQsViews = new ArrayList<>();
private final QuickQSPanel mQuickQsPanel;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
private final QuickStatusBarHeader mQuickStatusBarHeader;
private final QSSecurityFooter mSecurityFooter;
private final QS mQs;
+ private final View mQSFooterActions;
+ private final View mQQSFooterActions;
private PagedTileLayout mPagedLayout;
@@ -88,6 +94,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
// This animates fading of SecurityFooter and media divider
private TouchAnimator mAllPagesDelayedAnimator;
private TouchAnimator mBrightnessAnimator;
+ private TouchAnimator mQQSFooterActionsAnimator;
private HeightExpansionAnimator mQQSTileHeightAnimator;
private HeightExpansionAnimator mOtherTilesExpandAnimator;
@@ -110,12 +117,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
QSPanelController qsPanelController,
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
- QSExpansionPathInterpolator qsExpansionPathInterpolator) {
+ QSExpansionPathInterpolator qsExpansionPathInterpolator,
+ @Named(QS_FOOTER) FooterActionsView qsFooterActionsView,
+ @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
mQuickStatusBarHeader = quickStatusBarHeader;
+ mQQSFooterActions = qqsFooterActionsView;
+ mQSFooterActions = qsFooterActionsView;
mSecurityFooter = securityFooter;
mHost = qsTileHost;
mExecutor = executor;
@@ -262,7 +273,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
clearAnimationState();
mAllViews.clear();
- mQuickQsViews.clear();
+ mAnimatedQsViews.clear();
mQQSTileHeightAnimator = null;
mOtherTilesExpandAnimator = null;
@@ -360,7 +371,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
- mQuickQsViews.add(tileView);
+ mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
mAllViews.add(quickTileView.getSecondaryLabel());
} else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -417,6 +428,13 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
.addFloat(tileLayout, "alpha", 0, 1);
mFirstPageDelayedAnimator = builder.build();
+ if (mQQSFooterActions.getVisibility() != View.GONE) {
+ // only when qqs footer is present (which means split shade mode) it needs to
+ // be animated
+ updateQQSFooterAnimation();
+ }
+
+
// Fade in the security footer and the divider as we reach the final position
builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
@@ -452,6 +470,20 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
.addFloat(tileLayout, "alpha", 0, 1).build();
}
+ private void updateQQSFooterAnimation() {
+ int[] qsPosition = new int[2];
+ int[] qqsPosition = new int[2];
+ View commonView = mQs.getView();
+ getRelativePositionInt(qsPosition, mQSFooterActions, commonView);
+ getRelativePositionInt(qqsPosition, mQQSFooterActions, commonView);
+ int translationY = (qsPosition[1] - qqsPosition[1])
+ - mQuickStatusBarHeader.getOffsetTranslation();
+ mQQSFooterActionsAnimator = new TouchAnimator.Builder()
+ .addFloat(mQQSFooterActions, "translationY", 0, translationY)
+ .build();
+ mAnimatedQsViews.add(mQSFooterActions);
+ }
+
private boolean isIconInAnimatedRow(int count) {
if (mPagedLayout == null) {
return false;
@@ -521,6 +553,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if (mBrightnessAnimator != null) {
mBrightnessAnimator.setPosition(position);
}
+ if (mQQSFooterActionsAnimator != null) {
+ mQQSFooterActionsAnimator.setPosition(position);
+ }
}
}
@@ -532,9 +567,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
@Override
public void onAnimationAtEnd() {
mQuickQsPanel.setVisibility(View.INVISIBLE);
- final int N = mQuickQsViews.size();
+ final int N = mAnimatedQsViews.size();
for (int i = 0; i < N; i++) {
- mQuickQsViews.get(i).setVisibility(View.VISIBLE);
+ mAnimatedQsViews.get(i).setVisibility(View.VISIBLE);
}
}
@@ -542,9 +577,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
public void onAnimationStarted() {
updateQQSVisibility();
if (mOnFirstPage) {
- final int N = mQuickQsViews.size();
+ final int N = mAnimatedQsViews.size();
for (int i = 0; i < N; i++) {
- mQuickQsViews.get(i).setVisibility(View.INVISIBLE);
+ mAnimatedQsViews.get(i).setVisibility(View.INVISIBLE);
}
}
}
@@ -569,9 +604,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha
if (mOtherTilesExpandAnimator != null) {
mOtherTilesExpandAnimator.resetViewsHeights();
}
- final int N2 = mQuickQsViews.size();
+ final int N2 = mAnimatedQsViews.size();
for (int i = 0; i < N2; i++) {
- mQuickQsViews.get(i).setVisibility(View.VISIBLE);
+ mAnimatedQsViews.get(i).setVisibility(View.VISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 59e5eb8d6ac8..a128694f3e3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -27,6 +27,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
@@ -51,6 +52,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
private NonInterceptingScrollView mQSPanelContainer;
+ private ImageView mDragHandle;
private int mSideMargins;
private boolean mQsDisabled;
@@ -69,6 +71,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
mQSDetail = findViewById(R.id.qs_detail);
mHeader = findViewById(R.id.header);
mQSCustomizer = findViewById(R.id.qs_customize);
+ mDragHandle = findViewById(R.id.qs_drag_handle);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@@ -164,8 +167,8 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
QuickStatusBarHeaderController quickStatusBarHeaderController) {
mQSPanelContainer.setPaddingRelative(
getPaddingStart(),
- mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height),
+ mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height),
getPaddingEnd(),
getPaddingBottom()
);
@@ -199,6 +202,8 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
mQSDetail.setBottom(getTop() + scrollBottom);
int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
+ // Pin the drag handle to the bottom of the panel.
+ mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
}
protected int calculateContainerHeight() {
@@ -220,6 +225,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
public void setExpansion(float expansion) {
mQsExpansion = expansion;
mQSPanelContainer.setScrollingEnabled(expansion > 0f);
+ mDragHandle.setAlpha(1.0f - expansion);
updateExpansion();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index e38bd4bd9a38..0e0681b94c62 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -35,11 +35,6 @@ public interface QSFooter {
void setExpanded(boolean expanded);
/**
- * Returns the full height of the footer.
- */
- int getHeight();
-
- /**
* Sets the percentage amount that the quick settings has been expanded.
*
* @param expansion A value from 1 to 0 that indicates how much the quick settings have been
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 57438d189b22..4d23958d56ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -21,60 +21,40 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import android.content.Context;
import android.content.res.Configuration;
import android.database.ContentObserver;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.Utils;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
-import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R;
-import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.statusbar.phone.MultiUserSwitch;
-import com.android.systemui.statusbar.phone.SettingsButton;
-/** */
+/**
+ * Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
+ * {@link FooterActionsView}
+ */
public class QSFooterView extends FrameLayout {
- private SettingsButton mSettingsButton;
- protected View mSettingsContainer;
private PageIndicator mPageIndicator;
private TextView mBuildText;
- private boolean mShouldShowBuildText;
+ private View mActionsContainer;
- private boolean mQsDisabled;
+ protected TouchAnimator mFooterAnimator;
+ private boolean mQsDisabled;
private boolean mExpanded;
-
- private boolean mListening;
-
- protected MultiUserSwitch mMultiUserSwitch;
- private ImageView mMultiUserAvatar;
-
- protected TouchAnimator mFooterAnimator;
private float mExpansionAmount;
- protected View mEdit;
- private TouchAnimator mSettingsCogAnimator;
-
- private View mActionsContainer;
- private View mTunerIcon;
- private int mTunerIconTranslation;
+ private boolean mShouldShowBuildText;
private OnClickListener mExpandClickListener;
@@ -94,27 +74,11 @@ public class QSFooterView extends FrameLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mEdit = requireViewById(android.R.id.edit);
-
mPageIndicator = findViewById(R.id.footer_page_indicator);
-
- mSettingsButton = findViewById(R.id.settings_button);
- mSettingsContainer = findViewById(R.id.settings_button_container);
-
- mMultiUserSwitch = findViewById(R.id.multi_user_switch);
- mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
-
- mActionsContainer = requireViewById(R.id.qs_footer_actions_container);
+ mActionsContainer = requireViewById(R.id.qs_footer_actions);
mBuildText = findViewById(R.id.build);
- mTunerIcon = requireViewById(R.id.tuner_icon);
- // RenderThread is doing more harm than good when touching the header (to expand quick
- // settings), so disable it for this view
- if (mSettingsButton.getBackground() instanceof RippleDrawable) {
- ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
- }
updateResources();
-
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
setBuildText();
}
@@ -137,18 +101,7 @@ public class QSFooterView extends FrameLayout {
}
}
- void updateAnimator(int width, int numTiles) {
- int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- - mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding);
- int remaining = (width - numTiles * size) / (numTiles - 1);
- int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
-
- mSettingsCogAnimator = new Builder()
- .addFloat(mSettingsButton, "translationX",
- isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
- .addFloat(mSettingsButton, "rotation", -120, 0)
- .build();
-
+ void updateExpansion() {
setExpansion(mExpansionAmount);
}
@@ -158,20 +111,11 @@ public class QSFooterView extends FrameLayout {
updateResources();
}
- @Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- super.onRtlPropertiesChanged(layoutDirection);
- updateResources();
- }
-
private void updateResources() {
updateFooterAnimator();
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
setLayoutParams(lp);
- mTunerIconTranslation = mContext.getResources()
- .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation);
- mTunerIcon.setTranslationX(isLayoutRtl() ? -mTunerIconTranslation : mTunerIconTranslation);
}
private void updateFooterAnimator() {
@@ -197,17 +141,15 @@ public class QSFooterView extends FrameLayout {
mExpandClickListener = onClickListener;
}
- void setExpanded(boolean expanded, boolean isTunerEnabled, boolean multiUserEnabled) {
+ void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
- updateEverything(isTunerEnabled, multiUserEnabled);
+ updateEverything();
}
/** */
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
- if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
-
if (mFooterAnimator != null) {
mFooterAnimator.setPosition(headerExpansionFraction);
}
@@ -228,14 +170,6 @@ public class QSFooterView extends FrameLayout {
super.onDetachedFromWindow();
}
- /** */
- public void setListening(boolean listening) {
- if (listening == mListening) {
- return;
- }
- mListening = listening;
- }
-
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
@@ -253,50 +187,26 @@ public class QSFooterView extends FrameLayout {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
}
- void disable(int state2, boolean isTunerEnabled, boolean multiUserEnabled) {
+ void disable(int state2) {
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
- updateEverything(isTunerEnabled, multiUserEnabled);
+ updateEverything();
}
- void updateEverything(boolean isTunerEnabled, boolean multiUserEnabled) {
+ void updateEverything() {
post(() -> {
- updateVisibilities(isTunerEnabled, multiUserEnabled);
+ updateVisibilities();
updateClickabilities();
setClickable(false);
});
}
private void updateClickabilities() {
- mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
- mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
- mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
}
- private void updateVisibilities(boolean isTunerEnabled, boolean multiUserEnabled) {
- mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
- mTunerIcon.setVisibility(isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
- final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(
- showUserSwitcher(multiUserEnabled) ? View.VISIBLE : View.GONE);
- mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
-
+ private void updateVisibilities() {
mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
}
-
- private boolean showUserSwitcher(boolean multiUserEnabled) {
- return mExpanded && multiUserEnabled;
- }
-
- void onUserInfoChanged(Drawable picture, boolean isGuestUser) {
- if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) {
- picture = picture.getConstantState().newDrawable(getResources()).mutate();
- picture.setColorFilter(
- Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
- Mode.SRC_IN);
- }
- mMultiUserAvatar.setImageDrawable(picture);
- }
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 929aedae6706..e7c06e3c7ede 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,35 +16,18 @@
package com.android.systemui.qs;
-import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
import android.content.ClipData;
import android.content.ClipboardManager;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.UserManager;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.globalactions.GlobalActionsDialogLite;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.MultiUserSwitchController;
-import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -56,137 +39,45 @@ import javax.inject.Named;
@QSScope
public class QSFooterViewController extends ViewController<QSFooterView> implements QSFooter {
- private final UserManager mUserManager;
- private final UserInfoController mUserInfoController;
- private final ActivityStarter mActivityStarter;
- private final DeviceProvisionedController mDeviceProvisionedController;
private final UserTracker mUserTracker;
private final QSPanelController mQsPanelController;
private final QuickQSPanelController mQuickQSPanelController;
- private final TunerService mTunerService;
- private final MetricsLogger mMetricsLogger;
- private final FalsingManager mFalsingManager;
- private final MultiUserSwitchController mMultiUserSwitchController;
- private final SettingsButton mSettingsButton;
- private final View mSettingsButtonContainer;
+ private final FooterActionsController mFooterActionsController;
private final TextView mBuildText;
- private final View mEdit;
private final PageIndicator mPageIndicator;
- private final View mPowerMenuLite;
- private final boolean mShowPMLiteButton;
- private final GlobalActionsDialogLite mGlobalActionsDialog;
- private final UiEventLogger mUiEventLogger;
-
- private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
- new UserInfoController.OnUserInfoChangedListener() {
- @Override
- public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
- boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser());
- mView.onUserInfoChanged(picture, isGuestUser);
- }
- };
-
- private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Don't do anything until views are unhidden. Don't do anything if the tap looks
- // suspicious.
- if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
-
- if (v == mSettingsButton) {
- if (!mDeviceProvisionedController.isCurrentUserSetup()) {
- // If user isn't setup just unlock the device and dump them back at SUW.
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
- });
- return;
- }
- mMetricsLogger.action(
- mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
- : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
- if (mSettingsButton.isTunerClick()) {
- mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
- if (isTunerEnabled()) {
- mTunerService.showResetRequest(
- () -> {
- // Relaunch settings so that the tuner disappears.
- startSettingsActivity();
- });
- } else {
- Toast.makeText(getContext(), R.string.tuner_toast,
- Toast.LENGTH_LONG).show();
- mTunerService.setTunerEnabled(true);
- }
- startSettingsActivity();
-
- });
- } else {
- startSettingsActivity();
- }
- } else if (v == mPowerMenuLite) {
- mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
- mGlobalActionsDialog.showOrHideDialog(false, true);
- }
- }
- };
-
- private boolean mListening;
- private boolean mExpanded;
@Inject
- QSFooterViewController(QSFooterView view, UserManager userManager,
- UserInfoController userInfoController, ActivityStarter activityStarter,
- DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
+ QSFooterViewController(QSFooterView view,
+ UserTracker userTracker,
QSPanelController qsPanelController,
- MultiUserSwitchController multiUserSwitchController,
QuickQSPanelController quickQSPanelController,
- TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
- @Named(PM_LITE_ENABLED) boolean showPMLiteButton,
- GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
+ @Named(QS_FOOTER) FooterActionsController footerActionsController) {
super(view);
- mUserManager = userManager;
- mUserInfoController = userInfoController;
- mActivityStarter = activityStarter;
- mDeviceProvisionedController = deviceProvisionedController;
mUserTracker = userTracker;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
- mTunerService = tunerService;
- mMetricsLogger = metricsLogger;
- mFalsingManager = falsingManager;
- mMultiUserSwitchController = multiUserSwitchController;
+ mFooterActionsController = footerActionsController;
- mSettingsButton = mView.findViewById(R.id.settings_button);
- mSettingsButtonContainer = mView.findViewById(R.id.settings_button_container);
mBuildText = mView.findViewById(R.id.build);
- mEdit = mView.findViewById(android.R.id.edit);
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
- mPowerMenuLite = mView.findViewById(R.id.pm_lite);
- mShowPMLiteButton = showPMLiteButton;
- mGlobalActionsDialog = globalActionsDialog;
- mUiEventLogger = uiEventLogger;
}
@Override
protected void onInit() {
super.onInit();
- mMultiUserSwitchController.init();
+ mFooterActionsController.init();
}
@Override
protected void onViewAttached() {
- if (mShowPMLiteButton) {
- mPowerMenuLite.setVisibility(View.VISIBLE);
- mPowerMenuLite.setOnClickListener(mSettingsOnClickListener);
- } else {
- mPowerMenuLite.setVisibility(View.GONE);
- }
mView.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- mView.updateAnimator(
- right - left, mQuickQSPanelController.getNumQuickTiles()));
- mSettingsButton.setOnClickListener(mSettingsOnClickListener);
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ mView.updateExpansion();
+ mFooterActionsController.updateAnimator(right - left,
+ mQuickQSPanelController.getNumQuickTiles());
+ }
+ );
+
mBuildText.setOnLongClickListener(view -> {
CharSequence buildText = mBuildText.getText();
if (!TextUtils.isEmpty(buildText)) {
@@ -200,17 +91,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
}
return false;
});
-
- mEdit.setOnClickListener(view -> {
- if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
- return;
- }
- mActivityStarter.postQSRunnableDismissingKeyguard(() ->
- mQsPanelController.showEdit(view));
- });
-
mQsPanelController.setFooterPageIndicator(mPageIndicator);
- mView.updateEverything(isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
+ mView.updateEverything();
}
@Override
@@ -225,38 +107,25 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
@Override
public void setExpanded(boolean expanded) {
- mExpanded = expanded;
- mView.setExpanded(
- expanded, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
- }
-
- @Override
- public int getHeight() {
- return mView.getHeight();
+ mFooterActionsController.setExpanded(expanded);
+ mView.setExpanded(expanded);
}
@Override
public void setExpansion(float expansion) {
mView.setExpansion(expansion);
+ mFooterActionsController.setExpansion(expansion);
}
@Override
public void setListening(boolean listening) {
- if (mListening == listening) {
- return;
- }
-
- mListening = listening;
- if (mListening) {
- mUserInfoController.addCallback(mOnUserInfoChangedListener);
- } else {
- mUserInfoController.removeCallback(mOnUserInfoChangedListener);
- }
+ mFooterActionsController.setListening(listening);
}
@Override
public void setKeyguardShowing(boolean keyguardShowing) {
mView.setKeyguardShowing();
+ mFooterActionsController.setKeyguardShowing();
}
/** */
@@ -267,19 +136,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
@Override
public void disable(int state1, int state2, boolean animate) {
- mView.disable(state2, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
- }
-
- private void startSettingsActivity() {
- ActivityLaunchAnimator.Controller animationController =
- mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
- mSettingsButtonContainer,
- InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
- mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
- true /* dismissShade */, animationController);
- }
-
- private boolean isTunerEnabled() {
- return mTunerService.isTunerEnabled();
+ mView.disable(state2);
+ mFooterActionsController.disable(state2);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 1c841eca65f3..4242e1bb666b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -32,6 +32,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout.LayoutParams;
+import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -47,11 +48,11 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.LifecycleFragment;
@@ -90,6 +91,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private QSFooter mFooter;
private float mLastQSExpansion = -1;
private boolean mQsDisabled;
+ private ImageView mQsDragHandler;
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final InjectionInflationController mInjectionInflater;
@@ -115,7 +117,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private QuickQSPanelController mQuickQSPanelController;
private QSCustomizerController mQSCustomizerController;
private ScrollListener mScrollListener;
- private FeatureFlags mFeatureFlags;
/**
* When true, QS will translate from outside the screen. It will be clipped with parallax
* otherwise.
@@ -142,7 +143,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
@Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
KeyguardBypassController keyguardBypassController,
- QSFragmentComponent.Factory qsComponentFactory, FeatureFlags featureFlags,
+ QSFragmentComponent.Factory qsComponentFactory,
FalsingManager falsingManager, DumpManager dumpManager) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mInjectionInflater = injectionInflater;
@@ -153,7 +154,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQsComponentFactory = qsComponentFactory;
commandQueue.observe(getLifecycle(), this);
mHost = qsTileHost;
- mFeatureFlags = featureFlags;
mFalsingManager = falsingManager;
mBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
@@ -196,6 +196,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mHeader = view.findViewById(R.id.header);
mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = qsFragmentComponent.getQSFooter();
+ mQsDragHandler = view.findViewById(R.id.qs_drag_handle);
mQsDetailDisplayer.setQsPanelController(mQSPanelController);
@@ -302,6 +303,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSAnimator.onRtlChanged();
}
}
+ updateQsState();
}
@Override
@@ -374,15 +376,19 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
: View.INVISIBLE);
mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
- mFooter.setVisibility(
- !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
- || mShowCollapsedOnKeyguard)
+ mFooter.setVisibility(!mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
+ || mShowCollapsedOnKeyguard)
? View.VISIBLE
: View.INVISIBLE);
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (mQsExpanded && !mStackScrollerOverscrolling));
mQSPanelController.setVisibility(
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+ mQsDragHandler.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
+ || mShowCollapsedOnKeyguard)
+ && Utils.shouldUseSplitNotificationShade(getResources())
+ ? View.VISIBLE
+ : View.GONE);
}
private boolean isKeyguardState() {
@@ -409,6 +415,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
return mQSPanelController;
}
+ public void setBrightnessMirrorController(
+ BrightnessMirrorController brightnessMirrorController) {
+ mQSPanelController.setBrightnessMirror(brightnessMirrorController);
+ mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController);
+ }
+
@Override
public boolean isShowingDetail() {
return mQSCustomizerController.isCustomizing() || mQSDetail.isShowingDetail();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 000fd1c4bd2e..9f585bdfaeb0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -37,6 +37,7 @@ public interface QSHost {
void removeCallback(Callback callback);
TileServices getTileServices();
void removeTile(String tileSpec);
+ void removeTiles(Collection<String> specs);
void unmarkTileAsAutoAdded(String tileSpec);
int indexOf(String tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index cde80e66ba26..28aa884d6f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -45,7 +45,6 @@ import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.animation.UniqueObjectHostView;
import java.util.ArrayList;
import java.util.List;
@@ -322,7 +321,6 @@ public class QSPanel extends LinearLayout implements Tunable {
super.onConfigurationChanged(newConfig);
mOnConfigurationChangedListeners.forEach(
listener -> listener.onConfigurationChange(newConfig));
- switchSecurityFooter();
}
@Override
@@ -372,30 +370,21 @@ public class QSPanel extends LinearLayout implements Tunable {
switchToParent(mFooter, parent, index);
index++;
}
-
- // The security footer is switched on orientation changes
}
- private void switchSecurityFooter() {
- if (mSecurityFooter != null) {
- if (mContext.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE && mHeaderContainer != null) {
- // Adding the security view to the header, that enables us to avoid scrolling
- switchToParent(mSecurityFooter, mHeaderContainer, 0);
- } else {
- // Where should this go? If there's media, right before it. Otherwise, at the end.
- View mediaView = findViewByPredicate(v -> v instanceof UniqueObjectHostView);
- int index = -1;
- if (mediaView != null) {
- index = indexOfChild(mediaView);
- }
- if (mSecurityFooter.getParent() == this && indexOfChild(mSecurityFooter) < index) {
- // When we remove the securityFooter to rearrange, the index of media will go
- // down by one, so we correct it
- index--;
- }
- switchToParent(mSecurityFooter, this, index);
- }
+ /** Switch the security footer between top and bottom of QS depending on orientation. */
+ public void switchSecurityFooter(boolean shouldUseSplitNotificationShade) {
+ if (mSecurityFooter == null) return;
+
+ if (!shouldUseSplitNotificationShade
+ && mContext.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE && mHeaderContainer != null) {
+ // Adding the security view to the header, that enables us to avoid scrolling
+ switchToParent(mSecurityFooter, mHeaderContainer, 0);
+ } else {
+ // Add after the footer
+ int index = indexOfChild(mFooter);
+ switchToParent(mSecurityFooter, this, index + 1);
}
}
@@ -666,9 +655,14 @@ public class QSPanel extends LinearLayout implements Tunable {
return mListening;
}
- public void setSecurityFooter(View view) {
+ /**
+ * Set the security footer view and switch it into the right place
+ * @param view the view in question
+ * @param shouldUseSplitNotificationShade if QS is in split shade mode
+ */
+ public void setSecurityFooter(View view, boolean shouldUseSplitNotificationShade) {
mSecurityFooter = view;
- switchSecurityFooter();
+ switchSecurityFooter(shouldUseSplitNotificationShade);
}
protected void setPageMargin(int pageMargin) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index ae0f5104d20f..6e09f22fba63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -40,8 +40,8 @@ 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.settings.brightness.BrightnessController;
+import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
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;
@@ -61,10 +61,9 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final FalsingManager mFalsingManager;
private final BrightnessController mBrightnessController;
- private final BrightnessSlider.Factory mBrightnessSliderFactory;
private final BrightnessSlider mBrightnessSlider;
+ private final BrightnessMirrorHandler mBrightnessMirrorHandler;
- private BrightnessMirrorController mBrightnessMirrorController;
private boolean mGridContentVisible = true;
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
@@ -76,13 +75,10 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
if (mView.isListening()) {
refreshAllTiles();
}
- updateBrightnessMirror();
+ mView.switchSecurityFooter(mShouldUseSplitNotificationShade);
}
};
- private final BrightnessMirrorController.BrightnessMirrorListener mBrightnessMirrorListener =
- mirror -> updateBrightnessMirror();
-
private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
@@ -101,22 +97,21 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
QSTileRevealController.Factory qsTileRevealControllerFactory,
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
- BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager,
- FeatureFlags featureFlags) {
+ BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager, featureFlags);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQsSecurityFooter = qsSecurityFooter;
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
mQsSecurityFooter.setHostEnvironment(qstileHost);
- mBrightnessSliderFactory = brightnessSliderFactory;
- mBrightnessSlider = mBrightnessSliderFactory.create(getContext(), mView);
+ mBrightnessSlider = brightnessSliderFactory.create(getContext(), mView);
mView.setBrightnessView(mBrightnessSlider.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
+ mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
}
@Override
@@ -141,11 +136,9 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
refreshAllTiles();
}
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mView.setSecurityFooter(mQsSecurityFooter.getView());
+ mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
switchTileLayout(true);
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
- }
+ mBrightnessMirrorHandler.onQsPanelAttached();
((PagedTileLayout) mView.getOrCreateTileLayout())
.setOnTouchListener(mTileLayoutTouchListener);
@@ -161,9 +154,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
protected void onViewDetached() {
mTunerService.removeTunable(mView);
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener);
- }
+ mBrightnessMirrorHandler.onQsPanelDettached();
super.onViewDetached();
}
@@ -197,23 +188,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
}
}
- /** */
public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
- mBrightnessMirrorController = brightnessMirrorController;
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener);
- }
- mBrightnessMirrorController = brightnessMirrorController;
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
- }
- updateBrightnessMirror();
- }
-
- private void updateBrightnessMirror() {
- if (mBrightnessMirrorController != null) {
- mBrightnessSlider.setMirrorControllerAndMirror(mBrightnessMirrorController);
- }
+ mBrightnessMirrorHandler.setController(brightnessMirrorController);
}
/** Get the QSTileHost this panel uses. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 4739a3f4c7d6..0da4814f8e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -35,7 +35,6 @@ 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;
@@ -67,9 +66,8 @@ 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 boolean mShouldUseSplitNotificationShade;
+ protected boolean mShouldUseSplitNotificationShade;
@Nullable
private Consumer<Boolean> mMediaVisibilityChangedListener;
@@ -85,14 +83,17 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
@Override
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ Utils.shouldUseSplitNotificationShade(getResources());
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
+ onScreenRotated();
switchTileLayout(false);
}
}
};
+ protected void onScreenRotated() { }
+
private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
if (mMediaVisibilityChangedListener != null) {
mMediaVisibilityChangedListener.accept(visible);
@@ -115,8 +116,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
QSLogger qsLogger,
- DumpManager dumpManager,
- FeatureFlags featureFlags
+ DumpManager dumpManager
) {
super(view);
mHost = host;
@@ -127,9 +127,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mUiEventLogger = uiEventLogger;
mQSLogger = qsLogger;
mDumpManager = dumpManager;
- mFeatureFlags = featureFlags;
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ Utils.shouldUseSplitNotificationShade(getResources());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 541ee2c4fe8f..eff34d87b2ef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -47,6 +47,7 @@ import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceKey;
+import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
@@ -106,6 +107,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
+ private final TileServiceRequestController mTileServiceRequestController;
+
@Inject
public QSTileHost(Context context,
StatusBarIconController iconController,
@@ -122,7 +125,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
UiEventLogger uiEventLogger,
UserTracker userTracker,
SecureSettings secureSettings,
- CustomTileStatePersister customTileStatePersister
+ CustomTileStatePersister customTileStatePersister,
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder
) {
mIconController = iconController;
mContext = context;
@@ -133,6 +137,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mQSLogger = qsLogger;
mUiEventLogger = uiEventLogger;
mBroadcastDispatcher = broadcastDispatcher;
+ mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher, userTracker);
@@ -152,6 +157,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
tunerService.addTunable(this, TILES_SETTING);
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
mAutoTiles = autoTiles.get();
+ mTileServiceRequestController.init();
});
}
@@ -171,6 +177,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mServices.destroy();
mPluginManager.removePluginListener(this);
mDumpManager.unregisterDumpable(TAG);
+ mTileServiceRequestController.destroy();
}
@Override
@@ -347,6 +354,17 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
}
+ /**
+ * Remove many tiles at once.
+ *
+ * It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called
+ * multiple times).
+ */
+ @Override
+ public void removeTiles(Collection<String> specs) {
+ changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs));
+ }
+
@Override
public void unmarkTileAsAutoAdded(String spec) {
if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
@@ -368,6 +386,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
* @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
*/
public void addTile(String spec, int requestPosition) {
+ if (spec.equals("work")) Log.wtfStack(TAG, "Adding work tile");
changeTileSpecs(tileSpecs -> {
if (tileSpecs.contains(spec)) return false;
@@ -382,6 +401,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
void saveTilesToSettings(List<String> tileSpecs) {
+ if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile");
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
null /* tag */, false /* default */, mCurrentUser,
true /* overrideable by restore */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
new file mode 100644
index 000000000000..14374ffe9f89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.settings.brightness.BrightnessSlider
+import com.android.systemui.settings.brightness.MirroredBrightnessController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+import javax.inject.Inject
+
+/**
+ * Controls brightness slider in QQS, which is visible only in split shade. It's responsible for
+ * showing/hiding it when appropriate and (un)registering listeners
+ */
+class QuickQSBrightnessController @VisibleForTesting constructor(
+ private val brightnessControllerFactory: () -> BrightnessController
+) : MirroredBrightnessController {
+
+ @Inject constructor(
+ brightnessControllerFactory: BrightnessController.Factory,
+ brightnessSliderFactory: BrightnessSlider.Factory,
+ quickQSPanel: QuickQSPanel
+ ) : this(brightnessControllerFactory = {
+ val slider = brightnessSliderFactory.create(quickQSPanel.context, quickQSPanel)
+ slider.init()
+ quickQSPanel.setBrightnessView(slider.rootView)
+ brightnessControllerFactory.create(slider)
+ })
+
+ private var isListening = false
+ private var brightnessController: BrightnessController? = null
+ private var mirrorController: BrightnessMirrorController? = null
+
+ fun init(shouldUseSplitNotificationShade: Boolean) {
+ refreshVisibility(shouldUseSplitNotificationShade)
+ }
+
+ /**
+ * Starts/Stops listening for brightness changing events.
+ * It's fine to call this function even if slider is not visible (which would be the case for
+ * all small screen devices), it will just do nothing in that case
+ */
+ fun setListening(listening: Boolean) {
+ if (listening) {
+ // controller can be null when slider was never shown
+ if (!isListening && brightnessController != null) {
+ brightnessController?.registerCallbacks()
+ isListening = true
+ }
+ } else {
+ brightnessController?.unregisterCallbacks()
+ isListening = false
+ }
+ }
+
+ fun checkRestrictionAndSetEnabled() {
+ brightnessController?.checkRestrictionAndSetEnabled()
+ }
+
+ fun refreshVisibility(shouldUseSplitNotificationShade: Boolean) {
+ if (shouldUseSplitNotificationShade) {
+ showBrightnessSlider()
+ } else {
+ hideBrightnessSlider()
+ }
+ }
+
+ override fun setMirror(controller: BrightnessMirrorController) {
+ mirrorController = controller
+ mirrorController?.let { brightnessController?.setMirror(it) }
+ }
+
+ private fun hideBrightnessSlider() {
+ brightnessController?.hideSlider()
+ }
+
+ private fun showBrightnessSlider() {
+ if (brightnessController == null) {
+ brightnessController = brightnessControllerFactory()
+ mirrorController?.also { brightnessController?.setMirror(it) }
+ brightnessController?.registerCallbacks()
+ isListening = true
+ }
+ brightnessController?.showSlider()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index c5bfe97403e7..613e7f87371c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -33,23 +33,16 @@ import com.android.systemui.plugins.qs.QSTile.State;
*/
public class QuickQSPanel extends QSPanel {
- public static final String NUM_QUICK_TILES = "sysui_qqs_count";
private static final String TAG = "QuickQSPanel";
- // A default value so that we never return 0.
- public static final int DEFAULT_MAX_TILES = 6;
+ // A fallback value for max tiles number when setting via Tuner (parseNumTiles)
+ public static final int TUNER_MAX_TILES_FALLBACK = 6;
private boolean mDisabledByPolicy;
private int mMaxTiles;
public QuickQSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
- mMaxTiles = Math.min(DEFAULT_MAX_TILES,
- getResources().getInteger(R.integer.quick_qs_panel_max_columns));
- }
-
- @Override
- public void setBrightnessView(View view) {
- // Don't add brightness view
+ mMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
}
@Override
@@ -106,7 +99,7 @@ public class QuickQSPanel extends QSPanel {
}
public void setMaxTiles(int maxTiles) {
- mMaxTiles = Math.min(maxTiles, DEFAULT_MAX_TILES);
+ mMaxTiles = maxTiles;
}
@Override
@@ -122,17 +115,18 @@ public class QuickQSPanel extends QSPanel {
}
/**
- * Parses the String setting into the number of tiles. Defaults to {@code mDefaultMaxTiles}
+ * Parses the String setting into the number of tiles. Defaults to
+ * {@link #TUNER_MAX_TILES_FALLBACK}
*
* @param numTilesValue value of the setting to parse
- * @return parsed value of numTilesValue OR {@code mDefaultMaxTiles} on error
+ * @return parsed value of numTilesValue OR {@link #TUNER_MAX_TILES_FALLBACK} on error
*/
public static int parseNumTiles(String numTilesValue) {
try {
return Integer.parseInt(numTilesValue);
} catch (NumberFormatException e) {
// Couldn't read an int from the new setting value. Use default.
- return DEFAULT_MAX_TILES;
+ return TUNER_MAX_TILES_FALLBACK;
}
}
@@ -193,7 +187,7 @@ public class QuickQSPanel extends QSPanel {
public boolean updateResources() {
mCellHeightResId = R.dimen.qs_quick_tile_size;
boolean b = super.updateResources();
- mMaxAllowedRows = 2;
+ mMaxAllowedRows = getResources().getInteger(R.integer.quick_qs_panel_max_rows);
return b;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index fee56b984ecc..921ee35e3890 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import com.android.internal.logging.MetricsLogger;
@@ -29,7 +30,8 @@ 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 com.android.systemui.settings.brightness.BrightnessMirrorHandler;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import java.util.ArrayList;
import java.util.List;
@@ -43,22 +45,32 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
newConfig -> {
- int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
+ int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
if (newMaxTiles != mView.getNumQuickTiles()) {
setMaxTiles(newMaxTiles);
}
};
+ // brightness is visible only in split shade
+ private final QuickQSBrightnessController mBrightnessController;
+ private final BrightnessMirrorHandler mBrightnessMirrorHandler;
+ private final FooterActionsController mFooterActionsController;
+
@Inject
QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
QSCustomizerController qsCustomizerController,
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, FeatureFlags featureFlags
+ DumpManager dumpManager,
+ QuickQSBrightnessController quickQSBrightnessController,
+ @Named(QQS_FOOTER) FooterActionsController footerActionsController
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager, featureFlags);
+ uiEventLogger, qsLogger, dumpManager);
+ mBrightnessController = quickQSBrightnessController;
+ mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
+ mFooterActionsController = footerActionsController;
}
@Override
@@ -67,30 +79,62 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
mMediaHost.setExpansion(0.0f);
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+ mBrightnessController.init(mShouldUseSplitNotificationShade);
+ mFooterActionsController.init();
+ refreshFooterVisibility();
}
@Override
protected void onViewAttached() {
super.onViewAttached();
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+ mBrightnessMirrorHandler.onQsPanelAttached();
}
@Override
protected void onViewDetached() {
super.onViewDetached();
mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
+ mBrightnessMirrorHandler.onQsPanelDettached();
+ }
+
+ @Override
+ void setListening(boolean listening) {
+ super.setListening(listening);
+ mBrightnessController.setListening(listening);
+ mFooterActionsController.setListening(listening);
}
public boolean isListening() {
return mView.isListening();
}
+ private void refreshFooterVisibility() {
+ if (mShouldUseSplitNotificationShade) {
+ mFooterActionsController.showFooter();
+ } else {
+ mFooterActionsController.hideFooter();
+ }
+ }
+
private void setMaxTiles(int parseNumTiles) {
mView.setMaxTiles(parseNumTiles);
setTiles();
}
@Override
+ public void refreshAllTiles() {
+ mBrightnessController.checkRestrictionAndSetEnabled();
+ super.refreshAllTiles();
+ }
+
+ @Override
+ protected void onScreenRotated() {
+ mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
+ refreshFooterVisibility();
+ }
+
+ @Override
public void setTiles() {
List<QSTile> tiles = new ArrayList<>();
for (QSTile tile : mHost.getTiles()) {
@@ -110,4 +154,8 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
public int getNumQuickTiles() {
return mView.getNumQuickTiles();
}
+
+ public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
+ mBrightnessMirrorHandler.setController(brightnessMirrorController);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 77906abce625..a85800bfa76e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -35,13 +35,14 @@ import android.widget.Space;
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
-import com.android.systemui.BatteryMeterView;
import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateView;
import java.util.List;
@@ -62,11 +63,14 @@ public class QuickStatusBarHeader extends FrameLayout {
protected QuickQSPanel mHeaderQsPanel;
private View mDatePrivacyView;
private View mDateView;
+ // DateView next to clock. Visible on QQS
+ private VariableDateView mClockDateView;
private View mSecurityHeaderView;
- private View mClockIconsView;
+ private View mStatusIconsView;
private View mContainer;
private View mQSCarriers;
+ private ViewGroup mClockContainer;
private Clock mClockView;
private Space mDatePrivacySeparator;
private View mClockIconsSeparator;
@@ -86,7 +90,6 @@ public class QuickStatusBarHeader extends FrameLayout {
private int mWaterfallTopInset;
private int mCutOutPaddingLeft;
private int mCutOutPaddingRight;
- private float mViewAlpha = 1.0f;
private float mKeyguardExpansionFraction;
private int mTextColorPrimary = Color.TRANSPARENT;
private int mTopViewMeasureHeight;
@@ -117,18 +120,20 @@ public class QuickStatusBarHeader extends FrameLayout {
mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
mDatePrivacyView = findViewById(R.id.quick_status_bar_date_privacy);
- mClockIconsView = findViewById(R.id.quick_qs_status_icons);
+ mStatusIconsView = findViewById(R.id.quick_qs_status_icons);
mQSCarriers = findViewById(R.id.carrier_group);
mContainer = findViewById(R.id.qs_container);
mIconContainer = findViewById(R.id.statusIcons);
mPrivacyChip = findViewById(R.id.privacy_chip);
mDateView = findViewById(R.id.date);
+ mClockDateView = findViewById(R.id.date_clock);
mSecurityHeaderView = findViewById(R.id.header_text_container);
mClockIconsSeparator = findViewById(R.id.separator);
mRightLayout = findViewById(R.id.rightLayout);
mDateContainer = findViewById(R.id.date_container);
mPrivacyContainer = findViewById(R.id.privacy_container);
+ mClockContainer = findViewById(R.id.clock_container);
mClockView = findViewById(R.id.clock);
mDatePrivacySeparator = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
@@ -136,8 +141,6 @@ public class QuickStatusBarHeader extends FrameLayout {
updateResources();
- // Don't need to worry about tuner settings for this icon
- mBatteryRemainingIcon.setIgnoreTunerUpdates(true);
// QS will always show the estimate, and BatteryMeterView handles the case where
// it's unavailable or charging
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
@@ -177,7 +180,7 @@ public class QuickStatusBarHeader extends FrameLayout {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
- updateAnimators();
+ post(this::updateAnimators);
}
}
@@ -216,6 +219,11 @@ public class QuickStatusBarHeader extends FrameLayout {
void updateResources() {
Resources resources = mContext.getResources();
+ // status bar is already displayed out of QS in split shade
+ boolean shouldUseSplitShade =
+ resources.getBoolean(R.bool.config_use_split_notification_shade);
+ mStatusIconsView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
+ mDatePrivacyView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
@@ -229,13 +237,13 @@ public class QuickStatusBarHeader extends FrameLayout {
Math.max(qsOffsetHeight, mDatePrivacyView.getMinimumHeight());
mDatePrivacyView.setLayoutParams(mDatePrivacyView.getLayoutParams());
- mClockIconsView.getLayoutParams().height =
- Math.max(qsOffsetHeight, mClockIconsView.getMinimumHeight());
- mClockIconsView.setLayoutParams(mClockIconsView.getLayoutParams());
+ mStatusIconsView.getLayoutParams().height =
+ Math.max(qsOffsetHeight, mStatusIconsView.getMinimumHeight());
+ mStatusIconsView.setLayoutParams(mStatusIconsView.getLayoutParams());
ViewGroup.LayoutParams lp = getLayoutParams();
if (mQsDisabled) {
- lp.height = mClockIconsView.getLayoutParams().height;
+ lp.height = mStatusIconsView.getLayoutParams().height;
} else {
lp.height = WRAP_CONTENT;
}
@@ -280,7 +288,8 @@ public class QuickStatusBarHeader extends FrameLayout {
TouchAnimator.Builder builder = new TouchAnimator.Builder()
.addFloat(mSecurityHeaderView, "alpha", 0, 1)
// These views appear on expanding down
- .addFloat(mClockView, "alpha", 0, 1)
+ .addFloat(mDateView, "alpha", 0, 0, 1)
+ .addFloat(mClockDateView, "alpha", 1, 0, 0)
.addFloat(mQSCarriers, "alpha", 0, 1)
.setListener(new TouchAnimator.ListenerAdapter() {
@Override
@@ -289,10 +298,14 @@ public class QuickStatusBarHeader extends FrameLayout {
if (!mIsSingleCarrier) {
mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
}
+ // Make it gone so there's enough room for carrier names
+ mClockDateView.setVisibility(View.GONE);
}
@Override
public void onAnimationStarted() {
+ mClockDateView.setVisibility(View.VISIBLE);
+ mClockDateView.setFreezeSwitching(true);
setSeparatorVisibility(false);
if (!mIsSingleCarrier) {
mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
@@ -302,6 +315,7 @@ public class QuickStatusBarHeader extends FrameLayout {
@Override
public void onAnimationAtStart() {
super.onAnimationAtStart();
+ mClockDateView.setFreezeSwitching(false);
setSeparatorVisibility(mShowClockIconsSeparator);
// In QQS we never ignore RSSI.
mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
@@ -369,7 +383,7 @@ public class QuickStatusBarHeader extends FrameLayout {
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
mHeaderQsPanel.setDisabledByPolicy(disabled);
- mClockIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+ mStatusIconsView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
updateResources();
}
@@ -383,7 +397,7 @@ public class QuickStatusBarHeader extends FrameLayout {
StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
cutout, cornerCutoutPadding, -1);
mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
- mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
+ mStatusIconsView.setPadding(padding.first, 0, padding.second, 0);
LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
(LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
@@ -434,10 +448,11 @@ public class QuickStatusBarHeader extends FrameLayout {
mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+ LinearLayout.LayoutParams lp =
+ (LinearLayout.LayoutParams) mClockContainer.getLayoutParams();
lp.width = visible ? 0 : WRAP_CONTENT;
lp.weight = visible ? 1f : 0f;
- mClockView.setLayoutParams(lp);
+ mClockContainer.setLayoutParams(lp);
lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
lp.width = visible ? 0 : WRAP_CONTENT;
@@ -447,7 +462,7 @@ public class QuickStatusBarHeader extends FrameLayout {
private void updateHeadersPadding() {
setContentMargins(mDatePrivacyView, 0, 0);
- setContentMargins(mClockIconsView, 0, 0);
+ setContentMargins(mStatusIconsView, 0, 0);
int paddingLeft = 0;
int paddingRight = 0;
@@ -473,7 +488,7 @@ public class QuickStatusBarHeader extends FrameLayout {
mWaterfallTopInset,
paddingRight,
0);
- mClockIconsView.setPadding(paddingLeft,
+ mStatusIconsView.setPadding(paddingLeft,
mWaterfallTopInset,
paddingRight,
0);
@@ -500,7 +515,7 @@ public class QuickStatusBarHeader extends FrameLayout {
* @param scrollY the scroll of the QSPanel container
*/
public void setExpandedScrollAmount(int scrollY) {
- mClockIconsView.setScrollY(scrollY);
+ mStatusIconsView.setScrollY(scrollY);
mDatePrivacyView.setScrollY(scrollY);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index da75c9e45c54..9835d1720492 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -25,9 +25,11 @@ import androidx.annotation.NonNull;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.PrivacyChipEvent;
@@ -37,10 +39,10 @@ import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.privacy.logging.PrivacyLogger;
import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateViewController;
import com.android.systemui.util.ViewController;
import java.util.List;
@@ -58,7 +60,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private final ActivityStarter mActivityStarter;
private final UiEventLogger mUiEventLogger;
private final QSCarrierGroupController mQSCarrierGroupController;
- private final QuickQSPanelController mHeaderQsPanelController;
+ private final QuickQSPanelController mQuickQSPanelController;
private final OngoingPrivacyChip mPrivacyChip;
private final Clock mClockView;
private final StatusBarIconController mStatusBarIconController;
@@ -70,6 +72,10 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
private final PrivacyDialogController mPrivacyDialogController;
private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private final FeatureFlags mFeatureFlags;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+
+ private final VariableDateViewController mVariableDateViewControllerDateView;
+ private final VariableDateViewController mVariableDateViewControllerClockDateView;
private boolean mListening;
private boolean mMicCameraIndicatorsEnabled;
@@ -134,18 +140,21 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
SysuiColorExtractor colorExtractor,
PrivacyDialogController privacyDialogController,
QSExpansionPathInterpolator qsExpansionPathInterpolator,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ VariableDateViewController.Factory variableDateViewControllerFactory,
+ BatteryMeterViewController batteryMeterViewController) {
super(view);
mPrivacyItemController = privacyItemController;
mActivityStarter = activityStarter;
mUiEventLogger = uiEventLogger;
mStatusBarIconController = statusBarIconController;
mDemoModeController = demoModeController;
- mHeaderQsPanelController = quickQSPanelController;
+ mQuickQSPanelController = quickQSPanelController;
mPrivacyLogger = privacyLogger;
mPrivacyDialogController = privacyDialogController;
mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
mFeatureFlags = featureFlags;
+ mBatteryMeterViewController = batteryMeterViewController;
mQSCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
@@ -154,6 +163,12 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mPrivacyChip = mView.findViewById(R.id.privacy_chip);
mClockView = mView.findViewById(R.id.clock);
mIconContainer = mView.findViewById(R.id.statusIcons);
+ mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
+ mView.requireViewById(R.id.date)
+ );
+ mVariableDateViewControllerClockDateView = variableDateViewControllerFactory.create(
+ mView.requireViewById(R.id.date_clock)
+ );
mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags);
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
@@ -167,6 +182,14 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
+
+ // Don't need to worry about tuner settings for this icon
+ mBatteryMeterViewController.ignoreTunerUpdates();
+ }
+
+ @Override
+ protected void onInit() {
+ mBatteryMeterViewController.init();
}
@Override
@@ -205,6 +228,9 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots);
mDemoModeController.addCallback(mDemoModeReceiver);
+
+ mVariableDateViewControllerDateView.init();
+ mVariableDateViewControllerClockDateView.init();
}
@Override
@@ -225,12 +251,12 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
mListening = listening;
- mHeaderQsPanelController.setListening(listening);
- if (mHeaderQsPanelController.isListening()) {
- mHeaderQsPanelController.refreshAllTiles();
+ mQuickQSPanelController.setListening(listening);
+ if (mQuickQSPanelController.isListening()) {
+ mQuickQSPanelController.refreshAllTiles();
}
- if (mHeaderQsPanelController.switchTileLayout(false)) {
+ if (mQuickQSPanelController.switchTileLayout(false)) {
mView.updateResources();
}
@@ -286,7 +312,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader
}
public void setContentMargins(int marginStart, int marginEnd) {
- mHeaderQsPanelController.setContentMargins(marginStart, marginEnd);
+ mQuickQSPanelController.setContentMargins(marginStart, marginEnd);
}
private static class ClockDemoModeReceiver implements DemoMode {
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 67c4d33d53d3..953f9fb10657 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -40,8 +40,8 @@ import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7518b200c7e2..d33982c6e172 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -84,8 +84,8 @@ public class QSCustomizer extends LinearLayout {
void updateResources() {
LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
- lp.height = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.quick_qs_offset_height);
+ lp.height = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_header_system_icons_area_height);
mTransparentView.setLayoutParams(lp);
}
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 6fa44eb513e9..103ac656e846 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -20,7 +20,7 @@ import android.content.Context;
import android.hardware.display.ColorDisplayManager;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.util.settings.GlobalSettings;
import javax.inject.Named;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 4d633492ed76..386769cd399e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -23,8 +23,13 @@ import android.view.LayoutInflater;
import android.view.View;
import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.FooterActionsController;
+import com.android.systemui.qs.FooterActionsController.ExpansionState;
+import com.android.systemui.qs.FooterActionsControllerBuilder;
+import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSContainerImpl;
import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFooterView;
@@ -33,7 +38,6 @@ import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
-import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
@@ -49,6 +53,8 @@ import dagger.Provides;
@Module
public interface QSFragmentModule {
String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
+ String QQS_FOOTER = "qqs_footer";
+ String QS_FOOTER = "qs_footer";
String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
/**
@@ -110,12 +116,56 @@ public interface QSFragmentModule {
/** */
@Provides
+ static BatteryMeterView providesBatteryMeterView(QuickStatusBarHeader quickStatusBarHeader) {
+ return quickStatusBarHeader.findViewById(R.id.batteryRemainingIcon);
+ }
+
+ /** */
+ @Provides
static QSFooterView providesQSFooterView(@RootView View view) {
return view.findViewById(R.id.qs_footer);
}
/** */
@Provides
+ @Named(QS_FOOTER)
+ static FooterActionsView providesQSFooterActionsView(@RootView View view) {
+ return view.findViewById(R.id.qs_footer_actions);
+ }
+
+ /** */
+ @Provides
+ @Named(QQS_FOOTER)
+ static FooterActionsView providesQQSFooterActionsView(@RootView View view) {
+ return view.findViewById(R.id.qqs_footer_actions);
+ }
+
+ /** */
+ @Provides
+ @Named(QQS_FOOTER)
+ static FooterActionsController providesQQSFooterActionsController(
+ FooterActionsControllerBuilder footerActionsControllerBuilder,
+ @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
+ return footerActionsControllerBuilder
+ .withView(qqsFooterActionsView)
+ .withButtonsVisibleWhen(ExpansionState.COLLAPSED)
+ .build();
+ }
+
+ /** */
+ @Provides
+ @Named(QS_FOOTER)
+ static FooterActionsController providesQSFooterActionsController(
+ FooterActionsControllerBuilder footerActionsControllerBuilder,
+ @Named(QS_FOOTER) FooterActionsView qsFooterActionsView) {
+ return footerActionsControllerBuilder
+ .withView(qsFooterActionsView)
+ .withButtonsVisibleWhen(ExpansionState.EXPANDED)
+ .build();
+ }
+
+ /** */
+ @Provides
@QSScope
static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
qsFooterViewController.init();
@@ -146,9 +196,4 @@ public interface QSFragmentModule {
static boolean providesQSUsingMediaPlayer(Context context) {
return useQsMediaPlayer(context);
}
-
- /** */
- @Binds
- QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
- QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
new file mode 100644
index 000000000000..baf30186ea1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -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.qs.external
+
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowInsets
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.tileimpl.QSIconViewImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/**
+ * Dialog to present to the user to ask for authorization to add a [TileService].
+ */
+class TileRequestDialog(
+ context: Context
+) : SystemUIDialog(context, R.style.TileRequestDialog) {
+
+ companion object {
+ internal val CONTENT_ID = R.id.content
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window?.apply {
+ attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
+ attributes.receiveInsetsIgnoringZOrder = true
+ setLayout(
+ context.resources
+ .getDimensionPixelSize(R.dimen.qs_tile_service_request_dialog_width),
+ WRAP_CONTENT
+ )
+ }
+ }
+
+ /**
+ * Set the data of the tile to add, to show the user.
+ */
+ fun setTileData(tileData: TileData) {
+ val ll = (LayoutInflater
+ .from(context)
+ .inflate(R.layout.tile_service_request_dialog, null)
+ as ViewGroup).apply {
+ requireViewById<TextView>(R.id.text).apply {
+ text = context
+ .getString(R.string.qs_tile_request_dialog_text, tileData.appName)
+ }
+ addView(
+ createTileView(tileData),
+ context.resources.getDimensionPixelSize(
+ R.dimen.qs_tile_service_request_tile_width),
+ context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
+ )
+ }
+ val spacing = context.resources.getDimensionPixelSize(
+ R.dimen.qs_tile_service_request_content_space
+ )
+ setView(ll, spacing, spacing, spacing, spacing / 2)
+ }
+
+ private fun createTileView(tileData: TileData): QSTileView {
+ val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
+ val state = QSTile.BooleanState().apply {
+ label = tileData.label
+ icon = tileData.icon?.loadDrawable(context)?.let {
+ QSTileImpl.DrawableIcon(it)
+ } ?: ResourceIcon.get(R.drawable.android)
+ }
+ tile.onStateChanged(state)
+ tile.isSelected = true
+ return tile
+ }
+
+ /**
+ * Data bundle of information to show the user.
+ *
+ * @property appName Name of the app requesting their [TileService] to be added.
+ * @property label Label of the tile.
+ * @property icon Icon for the tile.
+ */
+ data class TileData(
+ val appName: CharSequence,
+ val label: CharSequence,
+ val icon: Icon?
+ )
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
new file mode 100644
index 000000000000..e6da234fe9c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.external
+
+import android.app.Dialog
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.graphics.drawable.Icon
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.R
+import java.io.PrintWriter
+import java.util.function.Consumer
+import javax.inject.Inject
+
+private const val TAG = "TileServiceRequestController"
+
+/**
+ * Controller to interface between [TileRequestDialog] and [QSTileHost].
+ */
+class TileServiceRequestController constructor(
+ private val qsTileHost: QSTileHost,
+ private val commandRegistry: CommandRegistry,
+ private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsTileHost.context) }
+) {
+
+ companion object {
+ // Temporary return values while there's no API
+ internal const val ADD_TILE = 0
+ internal const val DONT_ADD_TILE = 1
+ internal const val TILE_ALREADY_ADDED = 2
+ internal const val DISMISSED = 3
+ }
+
+ fun init() {
+ commandRegistry.registerCommand("tile-service-add") { TileServiceRequestCommand() }
+ }
+
+ fun destroy() {
+ commandRegistry.unregisterCommand("tile-service-add")
+ }
+
+ private fun addTile(componentName: ComponentName) {
+ qsTileHost.addTile(componentName, true)
+ }
+
+ @VisibleForTesting
+ internal fun requestTileAdd(
+ componentName: ComponentName,
+ appName: CharSequence,
+ label: CharSequence,
+ icon: Icon?,
+ callback: Consumer<Int>
+ ) {
+ if (isTileAlreadyAdded(componentName)) {
+ callback.accept(TILE_ALREADY_ADDED)
+ return
+ }
+ val dialogResponse = object : Consumer<Int> {
+ override fun accept(response: Int) {
+ if (response == ADD_TILE) {
+ addTile(componentName)
+ }
+ callback.accept(response)
+ }
+ }
+ val tileData = TileRequestDialog.TileData(appName, label, icon)
+ createDialog(tileData, dialogResponse).show()
+ }
+
+ private fun createDialog(
+ tileData: TileRequestDialog.TileData,
+ responseHandler: Consumer<Int>
+ ): SystemUIDialog {
+ val dialogClickListener = DialogInterface.OnClickListener { _, which ->
+ if (which == Dialog.BUTTON_POSITIVE) {
+ responseHandler.accept(ADD_TILE)
+ } else {
+ responseHandler.accept(DONT_ADD_TILE)
+ }
+ }
+ return dialogCreator().apply {
+ setTileData(tileData)
+ setShowForAllUsers(true)
+ setCanceledOnTouchOutside(true)
+ setOnCancelListener { responseHandler.accept(DISMISSED) }
+ setPositiveButton(R.string.qs_tile_request_dialog_add, dialogClickListener)
+ setNegativeButton(R.string.qs_tile_request_dialog_not_add, dialogClickListener)
+ }
+ }
+
+ private fun isTileAlreadyAdded(componentName: ComponentName): Boolean {
+ val spec = CustomTile.toSpec(componentName)
+ return qsTileHost.indexOf(spec) != -1
+ }
+
+ inner class TileServiceRequestCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val componentName: ComponentName = ComponentName.unflattenFromString(args[0])
+ ?: run {
+ Log.w(TAG, "Malformed componentName ${args[0]}")
+ return
+ }
+ requestTileAdd(componentName, args[1], args[2], null) {
+ Log.d(TAG, "Response: $it")
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: adb shell cmd statusbar tile-service-add " +
+ "<componentName> <appName> <label>")
+ }
+ }
+
+ @SysUISingleton
+ class Builder @Inject constructor(
+ private val commandRegistry: CommandRegistry
+ ) {
+ fun create(qsTileHost: QSTileHost): TileServiceRequestController {
+ return TileServiceRequestController(qsTileHost, commandRegistry)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 222539d49526..ee5e4dffab14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -286,6 +286,10 @@ open class QSTileViewImpl @JvmOverloads constructor(
return labelContainer
}
+ override fun getLabel(): View {
+ return label
+ }
+
override fun getSecondaryLabel(): View {
return secondaryLabel
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b13015361cc..04f089d31664 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -150,11 +150,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
List<CastDevice> activeDevices = getActiveDevices();
- // We want to pop up the media route selection dialog if we either have no active devices
- // (neither routes nor projection), or if we have an active route. In other cases, we assume
- // that a projection is active. This is messy, but this tile never correctly handled the
- // case where multiple devices were active :-/.
- if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+ if (willPopDetail()) {
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
showDetail(true);
});
@@ -163,6 +159,15 @@ public class CastTile extends QSTileImpl<BooleanState> {
}
}
+ // We want to pop up the media route selection dialog if we either have no active devices
+ // (neither routes nor projection), or if we have an active route. In other cases, we assume
+ // that a projection is active. This is messy, but this tile never correctly handled the
+ // case where multiple devices were active :-/.
+ private boolean willPopDetail() {
+ List<CastDevice> activeDevices = getActiveDevices();
+ return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+ }
+
private List<CastDevice> getActiveDevices() {
ArrayList<CastDevice> activeDevices = new ArrayList<>();
for (CastDevice device : mController.getCastDevices()) {
@@ -234,10 +239,12 @@ public class CastTile extends QSTileImpl<BooleanState> {
state.contentDescription = state.contentDescription + ","
+ mContext.getString(R.string.accessibility_quick_settings_open_details);
state.expandedAccessibilityClassName = Button.class.getName();
+ state.forceExpandIcon = willPopDetail();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
+ state.forceExpandIcon = false;
}
state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
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 7cb1421e3f0f..cc9e7485dcff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,7 +51,9 @@ import com.android.systemui.qs.AlphaControlledSignalTileView;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
@@ -66,15 +68,16 @@ import javax.inject.Inject;
/** Quick settings tile: Internet **/
public class InternetTile extends QSTileImpl<SignalState> {
private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
- private static final Intent INTERNET_PANEL =
- new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
protected final NetworkController mController;
+ private final AccessPointController mAccessPointController;
private final DataUsageController mDataController;
// The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
private int mLastTileState = -1;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+ private final InternetDialogFactory mInternetDialogFactory;
+ final Handler mHandler;
@Inject
public InternetTile(
@@ -86,11 +89,16 @@ public class InternetTile extends QSTileImpl<SignalState> {
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- NetworkController networkController
+ NetworkController networkController,
+ AccessPointController accessPointController,
+ InternetDialogFactory internetDialogFactory
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
+ mInternetDialogFactory = internetDialogFactory;
+ mHandler = mainHandler;
mController = networkController;
+ mAccessPointController = accessPointController;
mDataController = mController.getMobileDataController();
mController.observe(getLifecycle(), mSignalCallback);
}
@@ -114,7 +122,9 @@ public class InternetTile extends QSTileImpl<SignalState> {
@Override
protected void handleClick(@Nullable View view) {
- mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
+ mHandler.post(() -> mInternetDialogFactory.create(true,
+ mAccessPointController.canConfigMobileData(),
+ mAccessPointController.canConfigWifi()));
}
@Override
@@ -429,7 +439,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
}
} else if (cb.mNoDefaultNetwork) {
- if (cb.mNoNetworksAvailable) {
+ if (cb.mNoNetworksAvailable || !cb.mEnabled) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
} else {
@@ -489,7 +499,7 @@ public class InternetTile extends QSTileImpl<SignalState> {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (cb.mNoDefaultNetwork) {
- if (cb.mNoNetworksAvailable) {
+ if (cb.mNoNetworksAvailable || !mSignalCallback.mWifiInfo.mEnabled) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 82b6c0c1805d..d9919bdac889 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -131,22 +131,16 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
Intent intent = new Intent(mContext, WalletActivity.class)
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
- if (mKeyguardStateController.isUnlocked()) {
- mActivityStarter.startActivity(intent, true /* dismissShade */,
- animationController);
- } else {
- mHost.collapsePanels();
- // Do not use ActivityStarter here because the WalletActivity is required to be
- // started without prompting keyguard when the device is locked.
- mContext.startActivity(intent);
- }
+ mActivityStarter.startActivity(intent, true /* dismissShade */,
+ animationController, true /* showOverLockscreenWhenLocked */);
} else {
- if (mController.getWalletClient().createWalletIntent() == null) {
+ Intent intent = mController.getWalletClient().createWalletIntent();
+ if (intent == null) {
Log.w(TAG, "Could not get intent of the wallet app.");
return;
}
mActivityStarter.postStartActivityDismissingKeyguard(
- mController.getWalletClient().createWalletIntent(),
+ intent,
/* delay= */ 0,
animationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
new file mode 100644
index 000000000000..52afbe2ebb5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dialog;
+
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Adapter for showing Wi-Fi networks.
+ */
+public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
+
+ private static final String TAG = "InternetAdapter";
+ private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
+ private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+ private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
+
+ private final InternetDialogController mInternetDialogController;
+ private List<WifiEntry> mWifiEntries;
+ private int mWifiEntriesCount;
+
+ protected View mHolderView;
+ protected Context mContext;
+
+ public InternetAdapter(InternetDialogController controller) {
+ mInternetDialogController = controller;
+ }
+
+ @Override
+ public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+ int viewType) {
+ mContext = viewGroup.getContext();
+ mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
+ viewGroup, false);
+ return new InternetViewHolder(mHolderView, mInternetDialogController);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
+ if (mWifiEntries == null || position >= mWifiEntriesCount) {
+ return;
+ }
+ viewHolder.onBind(mWifiEntries.get(position));
+ }
+
+ /**
+ * Updates the Wi-Fi networks.
+ *
+ * @param wifiEntries the updated Wi-Fi entries.
+ * @param wifiEntriesCount the total number of Wi-Fi entries.
+ */
+ public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
+ mWifiEntries = wifiEntries;
+ mWifiEntriesCount = wifiEntriesCount;
+ }
+
+ /**
+ * Gets the total number of Wi-Fi networks.
+ *
+ * @return The total number of Wi-Fi entries.
+ */
+ @Override
+ public int getItemCount() {
+ return mWifiEntriesCount;
+ }
+
+ /**
+ * ViewHolder for binding Wi-Fi view.
+ */
+ static class InternetViewHolder extends RecyclerView.ViewHolder {
+
+ final LinearLayout mContainerLayout;
+ final LinearLayout mWifiListLayout;
+ final LinearLayout mWifiNetworkLayout;
+ final ImageView mWifiIcon;
+ final TextView mWifiTitleText;
+ final TextView mWifiSummaryText;
+ final ImageView mWifiLockedIcon;
+ final Context mContext;
+ final InternetDialogController mInternetDialogController;
+
+ protected WifiUtils.InternetIconInjector mWifiIconInjector;
+
+ InternetViewHolder(View view, InternetDialogController internetDialogController) {
+ super(view);
+ mContext = view.getContext();
+ mInternetDialogController = internetDialogController;
+ mContainerLayout = view.requireViewById(R.id.internet_container);
+ mWifiListLayout = view.requireViewById(R.id.wifi_list);
+ mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
+ mWifiIcon = view.requireViewById(R.id.wifi_icon);
+ mWifiTitleText = view.requireViewById(R.id.wifi_title);
+ mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
+ mWifiLockedIcon = view.requireViewById(R.id.wifi_locked_icon);
+ mWifiIconInjector = mInternetDialogController.getWifiIconInjector();
+ }
+
+ void onBind(WifiEntry wifiEntry) {
+ int security = wifiEntry.getSecurity();
+ try {
+ mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+ if (isOpenNetwork(security)) {
+ mWifiLockedIcon.setVisibility(View.GONE);
+ } else {
+ mWifiLockedIcon.setVisibility(View.VISIBLE);
+ mWifiLockedIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_friction_lock_closed));
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+ setWifiNetworkLayout(wifiEntry.getTitle(),
+ Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
+
+ mWifiListLayout.setOnClickListener(v -> {
+ if (wifiEntry.shouldEditBeforeConnect()) {
+ final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
+ intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
+ mContext.startActivity(intent);
+ }
+ mInternetDialogController.connect(wifiEntry);
+ });
+ }
+
+ /** Return true if this is an open network AccessPoint. */
+ boolean isOpenNetwork(int security) {
+ return security == SECURITY_NONE
+ || security == SECURITY_OWE;
+ }
+
+ void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
+ mWifiNetworkLayout.setVisibility(View.VISIBLE);
+ mWifiTitleText.setText(title);
+ if (TextUtils.isEmpty(summary)) {
+ mWifiTitleText.setGravity(Gravity.CENTER);
+ mWifiSummaryText.setVisibility(View.GONE);
+ return;
+ } else {
+ mWifiTitleText.setGravity(Gravity.BOTTOM);
+ mWifiSummaryText.setGravity(Gravity.TOP);
+ mWifiSummaryText.setVisibility(View.VISIBLE);
+ }
+ mWifiSummaryText.setText(summary);
+ }
+
+ Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) throws Throwable {
+ if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+ return null;
+ }
+ final Drawable drawable = mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(),
+ wifiEntry.getLevel());
+ if (drawable == null) {
+ return null;
+ }
+ drawable.setTint(
+ Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorTertiary));
+ final AtomicReference<Drawable> shared = new AtomicReference<>();
+ shared.set(drawable);
+ return shared.get();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
new file mode 100644
index 000000000000..6aae04bbd648
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.dialog;
+
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+
+import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+
+/**
+ * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
+ */
+@SysUISingleton
+public class InternetDialog extends SystemUIDialog implements
+ InternetDialogController.InternetDialogCallback, Window.Callback {
+ private static final String TAG = "InternetDialog";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ static final long PROGRESS_DELAY_MS = 2000L;
+
+ private final Handler mHandler;
+ private final LinearLayoutManager mLayoutManager;
+
+ @VisibleForTesting
+ protected InternetAdapter mAdapter;
+ @VisibleForTesting
+ protected WifiManager mWifiManager;
+ @VisibleForTesting
+ protected View mDialogView;
+ @VisibleForTesting
+ protected boolean mCanConfigWifi;
+
+ private InternetDialogFactory mInternetDialogFactory;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
+ private AlertDialog mAlertDialog;
+ private UiEventLogger mUiEventLogger;
+ private Context mContext;
+ private InternetDialogController mInternetDialogController;
+ private TextView mInternetDialogTitle;
+ private TextView mInternetDialogSubTitle;
+ private View mDivider;
+ private ProgressBar mProgressBar;
+ private LinearLayout mInternetListLayout;
+ private LinearLayout mConnectedWifListLayout;
+ private LinearLayout mConnectedWifList;
+ private LinearLayout mMobileNetworkLayout;
+ private LinearLayout mMobileNetworkList;
+ private LinearLayout mTurnWifiOnLayout;
+ private TextView mWifiToggleTitleText;
+ private LinearLayout mSeeAllLayout;
+ private RecyclerView mWifiRecyclerView;
+ private ImageView mConnectedWifiIcon;
+ private ImageView mWifiSettingsIcon;
+ private TextView mConnectedWifiTitleText;
+ private TextView mConnectedWifiSummaryText;
+ private ImageView mSignalIcon;
+ private TextView mMobileTitleText;
+ private TextView mMobileSummaryText;
+ private Switch mMobileDataToggle;
+ private Switch mWiFiToggle;
+ private Button mDoneButton;
+ private Drawable mBackgroundOn;
+ private int mListMaxHeight;
+ private int mListMaxWidth;
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private boolean mCanConfigMobileData;
+
+ // Wi-Fi entries
+ protected WifiEntry mConnectedWifiEntry;
+ protected int mWifiEntriesCount;
+
+ // Wi-Fi scanning progress bar
+ protected boolean mIsProgressBarVisible;
+ protected boolean mIsSearchingHidden;
+ protected final Runnable mHideProgressBarRunnable = () -> {
+ setProgressBarVisible(false);
+ };
+ protected Runnable mHideSearchingRunnable = () -> {
+ mIsSearchingHidden = true;
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ };
+
+ private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
+ // Set max height for list
+ if (mInternetListLayout.getHeight() > mListMaxHeight) {
+ ViewGroup.LayoutParams params = mInternetListLayout.getLayoutParams();
+ params.height = mListMaxHeight;
+ mInternetListLayout.setLayoutParams(params);
+ }
+ };
+
+ public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+ InternetDialogController internetDialogController, boolean canConfigMobileData,
+ boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
+ @Main Handler handler) {
+ super(context, R.style.Theme_SystemUI_Dialog_Internet);
+ if (DEBUG) {
+ Log.d(TAG, "Init InternetDialog");
+ }
+ mContext = context;
+ mHandler = handler;
+ mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogController = internetDialogController;
+ mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
+ mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
+ mTelephonyManager = mInternetDialogController.getTelephonyManager();
+ mWifiManager = mInternetDialogController.getWifiManager();
+ mCanConfigMobileData = canConfigMobileData;
+ mCanConfigWifi = canConfigWifi;
+
+ mLayoutManager = new LinearLayoutManager(mContext) {
+ @Override
+ public boolean canScrollVertically() {
+ return false;
+ }
+ };
+ mListMaxHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.internet_dialog_list_max_height);
+ mListMaxWidth = context.getResources().getDimensionPixelSize(
+ R.dimen.internet_dialog_list_max_width);
+ mUiEventLogger = uiEventLogger;
+ mAdapter = new InternetAdapter(mInternetDialogController);
+ if (!aboveStatusBar) {
+ getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+ mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
+ mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
+ null);
+ final Window window = getWindow();
+ final WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.gravity = Gravity.BOTTOM;
+ // Move down the dialog to overlay the navigation bar.
+ layoutParams.setFitInsetsTypes(
+ layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
+ layoutParams.setFitInsetsSides(WindowInsets.Side.all());
+ layoutParams.setFitInsetsIgnoringVisibility(true);
+ window.setAttributes(layoutParams);
+ window.setContentView(mDialogView);
+ window.setLayout(mListMaxWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
+ window.setWindowAnimations(R.style.Animation_InternetDialog);
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ window.addFlags(FLAG_LAYOUT_NO_LIMITS);
+
+ mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
+ mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+ mDivider = mDialogView.requireViewById(R.id.divider);
+ mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
+ mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+ mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
+ mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+ mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
+ mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
+ mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
+ mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
+ mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
+ mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
+ mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
+ mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
+ mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
+ mDoneButton = mDialogView.requireViewById(R.id.done);
+ mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
+ mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
+ mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+ mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+ mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mInternetListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+ mInternetListLayoutListener);
+ mInternetDialogTitle.setText(getDialogTitleText());
+ mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+
+ setOnClickListener();
+ mTurnWifiOnLayout.setBackground(null);
+ mWifiRecyclerView.setLayoutManager(mLayoutManager);
+ mWifiRecyclerView.setAdapter(mAdapter);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (DEBUG) {
+ Log.d(TAG, "onStart");
+ }
+ mInternetDialogController.onStart(this, mCanConfigWifi);
+ if (!mCanConfigWifi) {
+ hideWifiViews();
+ }
+ }
+
+ @VisibleForTesting
+ void hideWifiViews() {
+ setProgressBarVisible(false);
+ mTurnWifiOnLayout.setVisibility(View.GONE);
+ mConnectedWifListLayout.setVisibility(View.GONE);
+ mWifiRecyclerView.setVisibility(View.GONE);
+ mSeeAllLayout.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (DEBUG) {
+ Log.d(TAG, "onStop");
+ }
+ mHandler.removeCallbacks(mHideProgressBarRunnable);
+ mHandler.removeCallbacks(mHideSearchingRunnable);
+ mMobileNetworkLayout.setOnClickListener(null);
+ mMobileDataToggle.setOnCheckedChangeListener(null);
+ mConnectedWifListLayout.setOnClickListener(null);
+ mSeeAllLayout.setOnClickListener(null);
+ mWiFiToggle.setOnCheckedChangeListener(null);
+ mDoneButton.setOnClickListener(null);
+ mInternetDialogController.onStop();
+ mInternetDialogFactory.destroyDialog();
+ }
+
+ @Override
+ public void dismissDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "dismissDialog");
+ }
+ mInternetDialogFactory.destroyDialog();
+ dismiss();
+ }
+
+ void updateDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "updateDialog");
+ }
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ mInternetDialogSubTitle.setVisibility(View.GONE);
+ } else {
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ }
+ setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+
+ if (!mCanConfigWifi) {
+ return;
+ }
+
+ showProgressBar();
+ final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
+ final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+ updateWifiToggle(isWifiEnabled, isDeviceLocked);
+ updateConnectedWifi(isWifiEnabled, isDeviceLocked);
+
+ final int visibility = (isDeviceLocked || !isWifiEnabled || mWifiEntriesCount <= 0)
+ ? View.GONE : View.VISIBLE;
+ mWifiRecyclerView.setVisibility(visibility);
+ mSeeAllLayout.setVisibility(visibility);
+ }
+
+ private void setOnClickListener() {
+ mMobileNetworkLayout.setOnClickListener(v -> {
+ if (mInternetDialogController.isMobileDataEnabled()
+ && !mInternetDialogController.isDeviceLocked()) {
+ if (!mInternetDialogController.activeNetworkIsCellular()) {
+ mInternetDialogController.connectCarrierNetwork();
+ }
+ }
+ });
+ mMobileDataToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> {
+ if (!isChecked && shouldShowMobileDialog()) {
+ showTurnOffMobileDialog();
+ } else if (!shouldShowMobileDialog()) {
+ mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
+ isChecked, false);
+ }
+ });
+ mConnectedWifListLayout.setOnClickListener(v -> onClickConnectedWifi());
+ mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+ mWiFiToggle.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> {
+ buttonView.setChecked(isChecked);
+ mWifiManager.setWifiEnabled(isChecked);
+ });
+ mDoneButton.setOnClickListener(v -> dismiss());
+ }
+
+ private void setMobileDataLayout(boolean isCellularNetwork) {
+ if (mInternetDialogController.isAirplaneModeEnabled()
+ || !mInternetDialogController.hasCarrier()) {
+ mMobileNetworkLayout.setVisibility(View.GONE);
+ } else {
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+ mMobileNetworkLayout.setVisibility(View.VISIBLE);
+ mMobileTitleText.setText(getMobileNetworkTitle());
+ if (!TextUtils.isEmpty(getMobileNetworkSummary())) {
+ mMobileSummaryText.setText(
+ Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+ mMobileSummaryText.setVisibility(View.VISIBLE);
+ } else {
+ mMobileSummaryText.setVisibility(View.GONE);
+ }
+ mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
+ if (mInternetDialogController.isNightMode()) {
+ int titleColor = isCellularNetwork ? mContext.getColor(
+ R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorPrimary);
+ int summaryColor = isCellularNetwork ? mContext.getColor(
+ R.color.connected_network_secondary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorSecondary);
+
+ mMobileTitleText.setTextColor(titleColor);
+ mMobileSummaryText.setTextColor(summaryColor);
+ }
+ mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+
+ mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
+ mWiFiToggle.setChecked(isWifiEnabled);
+ if (isDeviceLocked && mInternetDialogController.isNightMode()) {
+ int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
+ R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorPrimary);
+ mWifiToggleTitleText.setTextColor(titleColor);
+ }
+ mTurnWifiOnLayout.setBackground(
+ (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+ }
+
+ private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
+ if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
+ mConnectedWifListLayout.setBackground(null);
+ mConnectedWifListLayout.setVisibility(View.GONE);
+ return;
+ }
+ mConnectedWifListLayout.setVisibility(View.VISIBLE);
+ mConnectedWifiTitleText.setText(mConnectedWifiEntry.getTitle());
+ mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
+ mConnectedWifiIcon.setImageDrawable(
+ mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
+ if (mInternetDialogController.isNightMode()) {
+ mConnectedWifiTitleText.setTextColor(
+ mContext.getColor(R.color.connected_network_primary_color));
+ mConnectedWifiSummaryText.setTextColor(
+ mContext.getColor(R.color.connected_network_secondary_color));
+ }
+ mWifiSettingsIcon.setColorFilter(
+ mContext.getColor(R.color.connected_network_primary_color));
+ mConnectedWifListLayout.setBackground(mBackgroundOn);
+ }
+
+ void onClickConnectedWifi() {
+ mInternetDialogController.launchWifiNetworkDetailsSetting();
+ }
+
+ void onClickSeeMoreButton() {
+ mInternetDialogController.launchNetworkSetting();
+ }
+
+ CharSequence getDialogTitleText() {
+ return mInternetDialogController.getDialogTitleText();
+ }
+
+ CharSequence getSubtitleText() {
+ return mInternetDialogController.getSubtitleText(
+ mIsProgressBarVisible && !mIsSearchingHidden);
+ }
+
+ private Drawable getSignalStrengthDrawable() {
+ return mInternetDialogController.getSignalStrengthDrawable();
+ }
+
+ CharSequence getMobileNetworkTitle() {
+ return mInternetDialogController.getMobileNetworkTitle();
+ }
+
+ String getMobileNetworkSummary() {
+ return mInternetDialogController.getMobileNetworkSummary();
+ }
+
+ protected void showProgressBar() {
+ if (mWifiManager == null || !mWifiManager.isWifiEnabled()
+ || mInternetDialogController.isDeviceLocked()) {
+ setProgressBarVisible(false);
+ return;
+ }
+ setProgressBarVisible(true);
+ if (mConnectedWifiEntry != null || mWifiEntriesCount > 0) {
+ mHandler.postDelayed(mHideProgressBarRunnable, PROGRESS_DELAY_MS);
+ } else if (!mIsSearchingHidden) {
+ mHandler.postDelayed(mHideSearchingRunnable, PROGRESS_DELAY_MS);
+ }
+ }
+
+ private void setProgressBarVisible(boolean visible) {
+ if (mWifiManager.isWifiEnabled() && mAdapter.mHolderView != null
+ && mAdapter.mHolderView.isAttachedToWindow()) {
+ mIsProgressBarVisible = true;
+ }
+ mIsProgressBarVisible = visible;
+ mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.GONE);
+ mDivider.setVisibility(mIsProgressBarVisible ? View.GONE : View.VISIBLE);
+ mInternetDialogSubTitle.setText(getSubtitleText());
+ }
+
+ private boolean shouldShowMobileDialog() {
+ boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+ false);
+ if (mInternetDialogController.isMobileDataEnabled() && !flag) {
+ return true;
+ }
+ return false;
+ }
+
+ private void showTurnOffMobileDialog() {
+ CharSequence carrierName =
+ mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
+ boolean isInService = mInternetDialogController.isVoiceStateInService();
+ if (TextUtils.isEmpty(carrierName) || !isInService) {
+ carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ }
+ mAlertDialog = new Builder(mContext)
+ .setTitle(R.string.mobile_data_disable_title)
+ .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+ .setNegativeButton(android.R.string.cancel, (d, w) -> {
+ mMobileDataToggle.setChecked(true);
+ })
+ .setPositiveButton(
+ com.android.internal.R.string.alert_windows_notification_turn_off_action,
+ (d, w) -> {
+ mInternetDialogController.setMobileDataEnabled(mContext,
+ mDefaultDataSubId, false, false);
+ mMobileDataToggle.setChecked(false);
+ Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+ })
+ .create();
+ mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true));
+ mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+ SystemUIDialog.registerDismissListener(mAlertDialog);
+ SystemUIDialog.setWindowOnTop(mAlertDialog);
+ mAlertDialog.show();
+ }
+
+ @Override
+ public void onRefreshCarrierInfo() {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSimStateChanged() {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSubscriptionsChanged(int defaultDataSubId) {
+ mDefaultDataSubId = defaultDataSubId;
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ @WorkerThread
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+ mHandler.post(() -> updateDialog());
+ }
+
+ @Override
+ @WorkerThread
+ public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
+ @Nullable WifiEntry connectedEntry) {
+ mConnectedWifiEntry = connectedEntry;
+ mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+ mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
+ mHandler.post(() -> {
+ mAdapter.notifyDataSetChanged();
+ updateDialog();
+ });
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ if (mAlertDialog != null && !mAlertDialog.isShowing()) {
+ if (!hasFocus && isShowing()) {
+ dismiss();
+ }
+ }
+ }
+
+ public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "The Internet dialog became visible on the screen.")
+ INTERNET_DIALOG_SHOW(843);
+
+ private final int mId;
+
+ InternetDialogEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
new file mode 100644
index 000000000000..dfe78de81f7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dialog;
+
+import static com.android.settingslib.mobile.MobileMappings.getIconKey;
+import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.Utils;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.net.SignalStrengthUtil;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.wifitrackerlib.MergedCarrierEntry;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+public class InternetDialogController implements WifiEntry.DisconnectCallback,
+ NetworkController.AccessPointController.AccessPointCallback {
+
+ private static final String TAG = "InternetDialogController";
+ private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+ "android.settings.NETWORK_PROVIDER_SETTINGS";
+ private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+ public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+ public static final int NO_CELL_DATA_TYPE_ICON = 0;
+ private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
+ private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
+ R.string.tap_a_network_to_connect;
+ private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS =
+ R.string.unlock_to_view_networks;
+ private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
+ R.string.wifi_empty_list_wifi_on;
+ private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.non_carrier_network_unavailable;
+ private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.all_network_unavailable;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ static final int MAX_WIFI_ENTRY_COUNT = 4;
+
+ private WifiManager mWifiManager;
+ private Context mContext;
+ private SubscriptionManager mSubscriptionManager;
+ private TelephonyManager mTelephonyManager;
+ private ConnectivityManager mConnectivityManager;
+ private TelephonyDisplayInfo mTelephonyDisplayInfo =
+ new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+ private Handler mHandler;
+ private MobileMappings.Config mConfig = null;
+ private Executor mExecutor;
+ private AccessPointController mAccessPointController;
+ private IntentFilter mConnectionStateFilter;
+ private InternetDialogCallback mCallback;
+ private WifiEntry mConnectedEntry;
+ private int mWifiEntriesCount;
+ private UiEventLogger mUiEventLogger;
+ private BroadcastDispatcher mBroadcastDispatcher;
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private GlobalSettings mGlobalSettings;
+ private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ @VisibleForTesting
+ protected ActivityStarter mActivityStarter;
+ @VisibleForTesting
+ protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
+ @VisibleForTesting
+ protected InternetTelephonyCallback mInternetTelephonyCallback;
+ @VisibleForTesting
+ protected WifiUtils.InternetIconInjector mWifiIconInjector;
+ @VisibleForTesting
+ protected boolean mCanConfigWifi;
+ @VisibleForTesting
+ protected KeyguardStateController mKeyguardStateController;
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshCarrierInfo() {
+ mCallback.onRefreshCarrierInfo();
+ }
+
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ mCallback.onSimStateChanged();
+ }
+ };
+
+ protected List<SubscriptionInfo> getSubscriptionInfo() {
+ return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+ }
+
+ @Inject
+ public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
+ ActivityStarter starter, AccessPointController accessPointController,
+ SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+ @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+ @Main Handler handler, @Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ GlobalSettings globalSettings, KeyguardStateController keyguardStateController) {
+ if (DEBUG) {
+ Log.d(TAG, "Init InternetDialogController");
+ }
+ mHandler = handler;
+ mExecutor = mainExecutor;
+ mContext = context;
+ mGlobalSettings = globalSettings;
+ mWifiManager = wifiManager;
+ mTelephonyManager = telephonyManager;
+ mConnectivityManager = connectivityManager;
+ mSubscriptionManager = subscriptionManager;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStateController = keyguardStateController;
+ mConnectionStateFilter = new IntentFilter();
+ mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ mUiEventLogger = uiEventLogger;
+ mActivityStarter = starter;
+ mAccessPointController = accessPointController;
+ mConfig = MobileMappings.Config.readConfig(mContext);
+ mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
+ }
+
+ void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
+ if (DEBUG) {
+ Log.d(TAG, "onStart");
+ }
+ mCallback = callback;
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mAccessPointController.addAccessPointCallback(this);
+ mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
+ mExecutor);
+ // Listen the subscription changes
+ mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+ mOnSubscriptionsChangedListener);
+ mDefaultDataSubId = getDefaultDataSubscriptionId();
+ if (DEBUG) {
+ Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
+ }
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mInternetTelephonyCallback = new InternetTelephonyCallback();
+ mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
+ // Listen the connectivity changes
+ mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), new DataConnectivityListener(), mHandler);
+ mCanConfigWifi = canConfigWifi;
+ scanWifiAccessPoints();
+ }
+
+ void onStop() {
+ if (DEBUG) {
+ Log.d(TAG, "onStop");
+ }
+ mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
+ mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ mAccessPointController.removeAccessPointCallback(this);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ }
+
+ @VisibleForTesting
+ boolean isAirplaneModeEnabled() {
+ return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
+ @VisibleForTesting
+ protected int getDefaultDataSubscriptionId() {
+ return mSubscriptionManager.getDefaultDataSubscriptionId();
+ }
+
+ @VisibleForTesting
+ protected Intent getSettingsIntent() {
+ return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ protected Intent getWifiDetailsSettingsIntent() {
+ String key = mConnectedEntry == null ? null : mConnectedEntry.getKey();
+ if (TextUtils.isEmpty(key)) {
+ if (DEBUG) {
+ Log.d(TAG, "connected entry's key is empty");
+ }
+ return null;
+ }
+ return WifiUtils.getWifiDetailsSettingsIntent(key);
+ }
+
+ CharSequence getDialogTitleText() {
+ if (isAirplaneModeEnabled()) {
+ return mContext.getText(R.string.airplane_mode);
+ }
+ return mContext.getText(R.string.quick_settings_internet_label);
+ }
+
+ CharSequence getSubtitleText(boolean isProgressBarVisible) {
+ if (isAirplaneModeEnabled()) {
+ return null;
+ }
+
+ if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
+ // When the airplane mode is off and Wi-Fi is disabled.
+ // Sub-Title: Wi-Fi is off
+ if (DEBUG) {
+ Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
+ }
+
+ if (isDeviceLocked()) {
+ // When the device is locked.
+ // Sub-Title: Unlock to view networks
+ if (DEBUG) {
+ Log.d(TAG, "The device is locked.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
+ }
+
+ if (mConnectedEntry != null || mWifiEntriesCount > 0) {
+ return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
+ }
+
+ if (mCanConfigWifi && isProgressBarVisible) {
+ // When the Wi-Fi scan result callback is received
+ // Sub-Title: Searching for networks...
+ return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
+ }
+
+ // Sub-Title:
+ // show non_carrier_network_unavailable
+ // - while Wi-Fi on + no Wi-Fi item
+ // - while Wi-Fi on + no Wi-Fi item + mobile data off
+ // show all_network_unavailable:
+ // - while Wi-Fi on + no Wi-Fi item + no carrier item
+ // - while Wi-Fi on + no Wi-Fi item + service is out of service
+ // - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
+ if (DEBUG) {
+ Log.d(TAG, "No Wi-Fi item.");
+ }
+ if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+ if (DEBUG) {
+ Log.d(TAG, "No carrier or service is out of service.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ if (mCanConfigWifi && !isMobileDataEnabled()) {
+ if (DEBUG) {
+ Log.d(TAG, "Mobile data off");
+ }
+ return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ if (!activeNetworkIsCellular()) {
+ if (DEBUG) {
+ Log.d(TAG, "No carrier data.");
+ }
+ return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ if (mCanConfigWifi) {
+ return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+ return null;
+ }
+
+ Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
+ final Drawable drawable =
+ mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel());
+ if (drawable == null) {
+ return null;
+ }
+ drawable.setTint(mContext.getColor(R.color.connected_network_primary_color));
+ return drawable;
+ }
+
+ boolean isNightMode() {
+ return (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ Drawable getSignalStrengthDrawable() {
+ Drawable drawable = mContext.getDrawable(
+ R.drawable.ic_signal_strength_zero_bar_no_internet);
+ try {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null");
+ }
+ return drawable;
+ }
+
+ if (isDataStateInService() || isVoiceStateInService()) {
+ AtomicReference<Drawable> shared = new AtomicReference<>();
+ shared.set(getSignalStrengthDrawableWithLevel());
+ drawable = shared.get();
+ }
+
+ drawable.setTint(activeNetworkIsCellular() ? mContext.getColor(
+ R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+ mContext, android.R.attr.textColorTertiary));
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ return drawable;
+ }
+
+ /**
+ * To get the signal bar icon with level.
+ *
+ * @return The Drawable which is a signal bar icon with level.
+ */
+ Drawable getSignalStrengthDrawableWithLevel() {
+ final SignalStrength strength = mTelephonyManager.getSignalStrength();
+ int level = (strength == null) ? 0 : strength.getLevel();
+ int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+ if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+ level += 1;
+ numLevels += 1;
+ }
+ return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+ }
+
+ Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
+ int iconType, boolean cutOut) {
+ Log.d(TAG, "getSignalStrengthIcon");
+ final SignalDrawable signalDrawable = new SignalDrawable(context);
+ signalDrawable.setLevel(
+ SignalDrawable.getState(level, numLevels, cutOut));
+
+ // Make the network type drawable
+ final Drawable networkDrawable =
+ iconType == NO_CELL_DATA_TYPE_ICON
+ ? EMPTY_DRAWABLE
+ : context.getResources().getDrawable(iconType, context.getTheme());
+
+ // Overlay the two drawables
+ final Drawable[] layers = {networkDrawable, signalDrawable};
+ final int iconSize =
+ context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+ final LayerDrawable icons = new LayerDrawable(layers);
+ // Set the network type icon at the top left
+ icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+ // Set the signal strength icon at the bottom right
+ icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+ icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+ icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+ return icons;
+ }
+
+ private boolean shouldInflateSignalStrength(int subId) {
+ return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
+ }
+
+ private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
+ final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
+ return displayNames.getOrDefault(subscriptionId, "");
+ }
+
+ private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
+ class DisplayInfo {
+ public SubscriptionInfo subscriptionInfo;
+ public CharSequence originalName;
+ public CharSequence uniqueName;
+ }
+
+ // Map of SubscriptionId to DisplayName
+ final Supplier<Stream<DisplayInfo>> originalInfos =
+ () -> getSubscriptionInfo()
+ .stream()
+ .filter(i -> {
+ // Filter out null values.
+ return (i != null && i.getDisplayName() != null);
+ })
+ .map(i -> {
+ DisplayInfo info = new DisplayInfo();
+ info.subscriptionInfo = i;
+ info.originalName = i.getDisplayName().toString().trim();
+ return info;
+ });
+
+ // A Unique set of display names
+ Set<CharSequence> uniqueNames = new HashSet<>();
+ // Return the set of duplicate names
+ final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
+ .filter(info -> !uniqueNames.add(info.originalName))
+ .map(info -> info.originalName)
+ .collect(Collectors.toSet());
+
+ // If a display name is duplicate, append the final 4 digits of the phone number.
+ // Creates a mapping of Subscription id to original display name + phone number display name
+ final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+ if (duplicateOriginalNames.contains(info.originalName)) {
+ // This may return null, if the user cannot view the phone number itself.
+ final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
+ info.subscriptionInfo);
+ String lastFourDigits = "";
+ if (phoneNumber != null) {
+ lastFourDigits = (phoneNumber.length() > 4)
+ ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
+ }
+
+ if (TextUtils.isEmpty(lastFourDigits)) {
+ info.uniqueName = info.originalName;
+ } else {
+ info.uniqueName = info.originalName + " " + lastFourDigits;
+ }
+
+ } else {
+ info.uniqueName = info.originalName;
+ }
+ return info;
+ });
+
+ // Check uniqueness a second time.
+ // We might not have had permission to view the phone numbers.
+ // There might also be multiple phone numbers whose last 4 digits the same.
+ uniqueNames.clear();
+ final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
+ .filter(info -> !uniqueNames.add(info.uniqueName))
+ .map(info -> info.uniqueName)
+ .collect(Collectors.toSet());
+
+ return uniqueInfos.get().map(info -> {
+ if (duplicatePhoneNames.contains(info.uniqueName)) {
+ info.uniqueName = info.originalName + " "
+ + info.subscriptionInfo.getSubscriptionId();
+ }
+ return info;
+ }).collect(Collectors.toMap(
+ info -> info.subscriptionInfo.getSubscriptionId(),
+ info -> info.uniqueName));
+ }
+
+ CharSequence getMobileNetworkTitle() {
+ return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
+ }
+
+ String getMobileNetworkSummary() {
+ String description = getNetworkTypeDescription(mContext, mConfig,
+ mTelephonyDisplayInfo, mDefaultDataSubId);
+ return getMobileSummary(mContext, mTelephonyManager, description);
+ }
+
+ /**
+ * Get currently description of mobile network type.
+ */
+ private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
+ TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
+ String iconKey = getIconKey(telephonyDisplayInfo);
+
+ if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
+ if (DEBUG) {
+ Log.d(TAG, "The description of network type is empty.");
+ }
+ return "";
+ }
+
+ int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ return resId != 0
+ ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
+ }
+
+ private String getMobileSummary(Context context, TelephonyManager telephonyManager,
+ String networkTypeDescription) {
+ if (!isMobileDataEnabled()) {
+ return context.getString(R.string.mobile_data_off_summary);
+ }
+ if (!isDataStateInService()) {
+ return context.getString(R.string.mobile_data_no_connection);
+ }
+ String summary = networkTypeDescription;
+ if (activeNetworkIsCellular()) {
+ summary = context.getString(R.string.preference_summary_default_combination,
+ context.getString(R.string.mobile_data_connection_active),
+ networkTypeDescription);
+ }
+ return summary;
+ }
+
+ void launchNetworkSetting() {
+ mCallback.dismissDialog();
+ mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+ }
+
+ void launchWifiNetworkDetailsSetting() {
+ Intent intent = getWifiDetailsSettingsIntent();
+ if (intent != null) {
+ mCallback.dismissDialog();
+ mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+ }
+ }
+
+ void connectCarrierNetwork() {
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
+ mergedCarrierEntry.connect(null /* ConnectCallback */);
+ }
+ }
+
+ WifiManager getWifiManager() {
+ return mWifiManager;
+ }
+
+ TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ SubscriptionManager getSubscriptionManager() {
+ return mSubscriptionManager;
+ }
+
+ /**
+ * @return whether there is the carrier item in the slice.
+ */
+ boolean hasCarrier() {
+ if (mSubscriptionManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
+ }
+ return false;
+ }
+
+ if (isAirplaneModeEnabled() || mTelephonyManager == null
+ || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return {@code true} if mobile data is enabled
+ */
+ boolean isMobileDataEnabled() {
+ if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Set whether to enable data for {@code subId}, also whether to disable data for other
+ * subscription
+ */
+ void setMobileDataEnabled(Context context, int subId, boolean enabled,
+ boolean disableOtherSubscriptions) {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
+ }
+ return;
+ }
+
+ if (mSubscriptionManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
+ }
+ return;
+ }
+
+ mTelephonyManager.setDataEnabled(enabled);
+ if (disableOtherSubscriptions) {
+ final List<SubscriptionInfo> subInfoList =
+ mSubscriptionManager.getActiveSubscriptionInfoList();
+ if (subInfoList != null) {
+ for (SubscriptionInfo subInfo : subInfoList) {
+ // We never disable mobile data for opportunistic subscriptions.
+ if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
+ context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+ subInfo.getSubscriptionId()).setDataEnabled(false);
+ }
+ }
+ }
+ }
+ }
+
+ boolean isDataStateInService() {
+ final ServiceState serviceState = mTelephonyManager.getServiceState();
+ NetworkRegistrationInfo regInfo =
+ (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return (regInfo == null) ? false : regInfo.isRegistered();
+ }
+
+ boolean isVoiceStateInService() {
+ if (mTelephonyManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
+ }
+ return false;
+ }
+
+ final ServiceState serviceState = mTelephonyManager.getServiceState();
+ return serviceState != null
+ && serviceState.getState() == serviceState.STATE_IN_SERVICE;
+ }
+
+ public boolean isDeviceLocked() {
+ return !mKeyguardStateController.isUnlocked();
+ }
+
+ boolean activeNetworkIsCellular() {
+ if (mConnectivityManager == null) {
+ if (DEBUG) {
+ Log.d(TAG, "ConnectivityManager is null, can not check active network.");
+ }
+ return false;
+ }
+
+ final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+ if (activeNetwork == null) {
+ return false;
+ }
+ final NetworkCapabilities networkCapabilities =
+ mConnectivityManager.getNetworkCapabilities(activeNetwork);
+ if (networkCapabilities == null) {
+ return false;
+ }
+ return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+ }
+
+ boolean connect(WifiEntry ap) {
+ if (ap == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No Wi-Fi ap to connect.");
+ }
+ return false;
+ }
+
+ if (ap.getWifiConfiguration() != null) {
+ if (DEBUG) {
+ Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "connect to unsaved network " + ap.getTitle());
+ }
+ }
+ ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+ return false;
+ }
+
+ static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
+ final ActivityStarter mActivityStarter;
+ final Context mContext;
+ final WifiEntry mWifiEntry;
+
+ WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
+ WifiEntry connectWifiEntry) {
+ mActivityStarter = activityStarter;
+ mContext = context;
+ mWifiEntry = connectWifiEntry;
+ }
+
+ @Override
+ public void onConnectResult(@ConnectStatus int status) {
+ if (DEBUG) {
+ Log.d(TAG, "onConnectResult " + status);
+ }
+
+ if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
+ final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
+ .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivityStarter.startActivity(intent, true);
+ } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
+ Toast.makeText(mContext, R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "connect failure reason=" + status);
+ }
+ }
+ }
+ }
+
+ private void scanWifiAccessPoints() {
+ if (mCanConfigWifi) {
+ mAccessPointController.scanForAccessPoints();
+ }
+ }
+
+ @Override
+ @WorkerThread
+ public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
+ if (!mCanConfigWifi) {
+ return;
+ }
+
+ if (accessPoints == null || accessPoints.size() == 0) {
+ mConnectedEntry = null;
+ mWifiEntriesCount = 0;
+ if (mCallback != null) {
+ mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+ }
+ return;
+ }
+
+ boolean hasConnectedWifi = false;
+ final int accessPointSize = accessPoints.size();
+ for (int i = 0; i < accessPointSize; i++) {
+ WifiEntry wifiEntry = accessPoints.get(i);
+ if (wifiEntry.isDefaultNetwork() && wifiEntry.hasInternetAccess()) {
+ mConnectedEntry = wifiEntry;
+ hasConnectedWifi = true;
+ break;
+ }
+ }
+ if (!hasConnectedWifi) {
+ mConnectedEntry = null;
+ }
+
+ int count = MAX_WIFI_ENTRY_COUNT;
+ if (hasCarrier()) {
+ count -= 1;
+ }
+ if (hasConnectedWifi) {
+ count -= 1;
+ }
+ final List<WifiEntry> wifiEntries = accessPoints.stream()
+ .filter(wifiEntry -> (!wifiEntry.isDefaultNetwork()
+ || !wifiEntry.hasInternetAccess()))
+ .limit(count)
+ .collect(Collectors.toList());
+ mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+
+ if (mCallback != null) {
+ mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
+ }
+ }
+
+ @Override
+ public void onSettingsActivityTriggered(Intent settingsIntent) {
+ }
+
+ @Override
+ public void onDisconnectResult(int status) {
+ }
+
+ private class InternetTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataConnectionStateListener,
+ TelephonyCallback.DisplayInfoListener,
+ TelephonyCallback.ServiceStateListener,
+ TelephonyCallback.SignalStrengthsListener {
+
+ @Override
+ public void onServiceStateChanged(@NonNull ServiceState serviceState) {
+ mCallback.onServiceStateChanged(serviceState);
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mCallback.onDataConnectionStateChanged(state, networkType);
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
+ mCallback.onSignalStrengthsChanged(signalStrength);
+ }
+
+ @Override
+ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+ mTelephonyDisplayInfo = telephonyDisplayInfo;
+ mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
+ }
+ }
+
+ private class InternetOnSubscriptionChangedListener
+ extends SubscriptionManager.OnSubscriptionsChangedListener {
+ InternetOnSubscriptionChangedListener() {
+ super();
+ }
+
+ @Override
+ public void onSubscriptionsChanged() {
+ updateListener();
+ }
+ }
+
+ private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
+ @Override
+ @WorkerThread
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ if (mCanConfigWifi) {
+ for (int transport : networkCapabilities.getTransportTypes()) {
+ if (transport == NetworkCapabilities.TRANSPORT_WIFI) {
+ scanWifiAccessPoints();
+ break;
+ }
+ }
+ }
+ final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+ if (activeNetwork != null && activeNetwork.equals(network)) {
+ // update UI
+ mCallback.onCapabilitiesChanged(network, networkCapabilities);
+ }
+ }
+ }
+
+ private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+ if (DEBUG) {
+ Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
+ }
+ updateListener();
+ }
+ }
+ };
+
+ private void updateListener() {
+ int defaultDataSubId = getDefaultDataSubscriptionId();
+ if (mDefaultDataSubId == getDefaultDataSubscriptionId()) {
+ if (DEBUG) {
+ Log.d(TAG, "DDS: no change");
+ }
+ return;
+ }
+
+ mDefaultDataSubId = defaultDataSubId;
+ if (DEBUG) {
+ Log.d(TAG, "DDS: defaultDataSubId:" + mDefaultDataSubId);
+ }
+ if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
+ mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+ mTelephonyManager.registerTelephonyCallback(mHandler::post,
+ mInternetTelephonyCallback);
+ mCallback.onSubscriptionsChanged(mDefaultDataSubId);
+ }
+ }
+
+ public WifiUtils.InternetIconInjector getWifiIconInjector() {
+ return mWifiIconInjector;
+ }
+
+ interface InternetDialogCallback {
+
+ void onRefreshCarrierInfo();
+
+ void onSimStateChanged();
+
+ void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+
+ void onSubscriptionsChanged(int defaultDataSubId);
+
+ void onServiceStateChanged(ServiceState serviceState);
+
+ void onDataConnectionStateChanged(int state, int networkType);
+
+ void onSignalStrengthsChanged(SignalStrength signalStrength);
+
+ void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
+
+ void dismissDialog();
+
+ void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
+ @Nullable WifiEntry connectedEntry);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
new file mode 100644
index 000000000000..11c6980678b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.dialog
+
+import android.content.Context
+import android.os.Handler
+import android.util.Log
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+private const val TAG = "InternetDialogFactory"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+/**
+ * Factory to create [InternetDialog] objects.
+ */
+@SysUISingleton
+class InternetDialogFactory @Inject constructor(
+ @Main private val handler: Handler,
+ private val internetDialogController: InternetDialogController,
+ private val context: Context,
+ private val uiEventLogger: UiEventLogger
+) {
+ companion object {
+ var internetDialog: InternetDialog? = null
+ }
+
+ /** Creates a [InternetDialog]. */
+ fun create(aboveStatusBar: Boolean, canConfigMobileData: Boolean, canConfigWifi: Boolean) {
+ if (internetDialog != null) {
+ if (DEBUG) {
+ Log.d(TAG, "InternetDialog is showing, do not create it twice.")
+ }
+ return
+ } else {
+ internetDialog = InternetDialog(context, this, internetDialogController,
+ canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler)
+ internetDialog?.show()
+ }
+ }
+
+ fun destroyDialog() {
+ if (DEBUG) {
+ Log.d(TAG, "destroyDialog")
+ }
+ internetDialog = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
new file mode 100644
index 000000000000..6aaba997faad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+public class InternetDialogUtil {
+
+ public static boolean isProviderModelEnabled(Context context) {
+ if (context == null) {
+ return false;
+ }
+ return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index bbeff6ece902..77c61a4f1845 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -42,7 +42,7 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private Context mContext;
private Handler mHandler;
@@ -51,8 +51,8 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
- mStatusBarLazy = statusBarLazy.orElse(null);
+ public OverviewProxyRecentsImpl(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
}
@Override
@@ -109,8 +109,9 @@ public class OverviewProxyRecentsImpl implements RecentsImplementation {
}
};
// Preload only if device for current user is unlocked
- if (mStatusBarLazy != null && mStatusBarLazy.get().isKeyguardShowing()) {
- mStatusBarLazy.get().executeRunnableDismissingKeyguard(() -> {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
// Flush trustmanager before checking device locked per user
mTrustManager.reportKeyguardShowingChanged();
mHandler.post(toggleRecents);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cb0c411b2753..658be729a34c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -34,6 +34,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUP
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -77,6 +78,7 @@ import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBar;
import com.android.systemui.navigationbar.NavigationBarController;
@@ -109,6 +111,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Supplier;
import javax.inject.Inject;
@@ -134,7 +137,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Context mContext;
private final Optional<Pip> mPipOptional;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private SysUiState mSysUiState;
@@ -171,55 +174,33 @@ public class OverviewProxyService extends CurrentUserTracker implements
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@Override
public void startScreenPinning(int taskId) {
- if (!verifyCaller("startScreenPinning")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- mStatusBarOptionalLazy.ifPresent(
- statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId,
- false /* allowCancel */));
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.showScreenPinningRequest(taskId,
+ false /* allowCancel */)));
}
@Override
public void stopScreenPinning() {
- if (!verifyCaller("stopScreenPinning")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- try {
- ActivityTaskManager.getService().stopSystemLockTaskMode();
- } catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to stop screen pinning");
- }
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> {
+ try {
+ ActivityTaskManager.getService().stopSystemLockTaskMode();
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to stop screen pinning");
+ }
+ });
}
// TODO: change the method signature to use (boolean inputFocusTransferStarted)
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
- if (!verifyCaller("onStatusBarMotionEvent")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
+ verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
// TODO move this logic to message queue
- mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
- StatusBar statusBar = statusBarLazy.get();
+ mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
if (event.getActionMasked() == ACTION_DOWN) {
statusBar.getPanelController().startExpandLatencyTracking();
}
- mHandler.post(()-> {
+ mHandler.post(() -> {
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
mInputFocusTransferStarted = true;
@@ -231,50 +212,38 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInputFocusTransferStarted = false;
+ float velocity = (event.getY() - mInputFocusTransferStartY)
+ / (event.getEventTime() - mInputFocusTransferStartMillis);
statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
action == ACTION_CANCEL,
- (event.getY() - mInputFocusTransferStartY)
- / (event.getEventTime() - mInputFocusTransferStartMillis));
+ velocity);
}
event.recycle();
});
});
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ });
}
@Override
public void onBackPressed() throws RemoteException {
- if (!verifyCaller("onBackPressed")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
- sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
+ verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
+ sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
- notifyBackAction(true, -1, -1, true, false);
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ notifyBackAction(true, -1, -1, true, false);
+ });
}
@Override
public void setHomeRotationEnabled(boolean enabled) {
- if (!verifyCaller("setHomeRotationEnabled")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- mHandler.post(() -> notifyHomeRotationEnabled(enabled));
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
+ mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
+ }
+
+ @Override
+ public void notifyTaskbarStatus(boolean visible, boolean stashed) {
+ verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
+ onTaskbarStatusUpdated(visible, stashed));
}
private boolean sendEvent(int action, int code) {
@@ -291,124 +260,74 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void onOverviewShown(boolean fromHome) {
- if (!verifyCaller("onOverviewShown")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onOverviewShown(fromHome);
- }
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onOverviewShown(fromHome);
+ }
+ });
}
@Override
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
- if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
- return null;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- return mLegacySplitScreenOptional.map(splitScreen ->
- splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
- .orElse(null);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ return verifyCallerAndClearCallingIdentity(
+ "getNonMinimizedSplitScreenSecondaryBounds",
+ () -> mLegacySplitScreenOptional.map(splitScreen ->
+ splitScreen
+ .getDividerView()
+ .getNonMinimizedSplitScreenSecondaryBounds())
+ .orElse(null)
+ );
}
@Override
public void setNavBarButtonAlpha(float alpha, boolean animate) {
- if (!verifyCaller("setNavBarButtonAlpha")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mNavBarButtonAlpha = alpha;
- mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
+ notifyNavBarButtonAlphaChanged(alpha, animate));
}
@Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
- if (!verifyCaller("onAssistantProgress")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyAssistantProgress(progress));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
+ notifyAssistantProgress(progress));
}
@Override
public void onAssistantGestureCompletion(float velocity) {
- if (!verifyCaller("onAssistantGestureCompletion")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () ->
+ notifyAssistantGestureCompletion(velocity));
}
@Override
public void startAssistant(Bundle bundle) {
- if (!verifyCaller("startAssistant")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyStartAssistant(bundle));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("startAssistant", () ->
+ notifyStartAssistant(bundle));
}
@Override
public void notifyAccessibilityButtonClicked(int displayId) {
- if (!verifyCaller("notifyAccessibilityButtonClicked")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- AccessibilityManager.getInstance(mContext)
- .notifyAccessibilityButtonClicked(displayId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
+ AccessibilityManager.getInstance(mContext)
+ .notifyAccessibilityButtonClicked(displayId));
}
@Override
public void notifyAccessibilityButtonLongClicked() {
- if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- final Intent intent =
- new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
- final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
- intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
+ () -> {
+ final Intent intent =
+ new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+ final String chooserClassName = AccessibilityButtonChooserActivity
+ .class.getName();
+ intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ });
}
@Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
- Insets visibleInsets, int taskId) {
+ Insets visibleInsets, int taskId) {
// Deprecated
}
@@ -420,43 +339,22 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void notifySwipeToHomeFinished() {
- if (!verifyCaller("notifySwipeToHomeFinished")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mPipOptional.ifPresent(
- pip -> pip.setPinnedStackAnimationType(
- PipAnimationController.ANIM_TYPE_ALPHA));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () ->
+ mPipOptional.ifPresent(
+ pip -> pip.setPinnedStackAnimationType(
+ PipAnimationController.ANIM_TYPE_ALPHA)));
}
@Override
public void notifySwipeUpGestureStarted() {
- if (!verifyCaller("notifySwipeUpGestureStarted")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifySwipeUpGestureStartedInternal());
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () ->
+ notifySwipeUpGestureStartedInternal());
}
@Override
public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
- if (!verifyCaller("notifyPrioritizedRotation")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHandler.post(() -> notifyPrioritizedRotationInternal(rotation));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
+ notifyPrioritizedRotationInternal(rotation));
}
@Override
@@ -476,15 +374,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
@Override
public void expandNotificationPanel() {
- if (!verifyCaller("expandNotificationPanel")) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ verifyCallerAndClearCallingIdentity("expandNotificationPanel",
+ () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
}
private boolean verifyCaller(String reason) {
@@ -496,6 +387,29 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
return true;
}
+
+ private <T> T verifyCallerAndClearCallingIdentity(String reason, Supplier<T> supplier) {
+ if (!verifyCaller(reason)) {
+ return null;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return supplier.get();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) {
+ verifyCallerAndClearCallingIdentity(reason, () -> {
+ runnable.run();
+ return null;
+ });
+ }
+
+ private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) {
+ verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable));
+ }
};
private final Runnable mDeferredConnectionCallback = () -> {
@@ -514,6 +428,19 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
};
+ private final BroadcastReceiver mDebugAnyPackageChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String[] stringArrayExtra = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ Log.e("b/188806432", intent.toString()
+ + (stringArrayExtra != null
+ ? ", EXTRA_CHANGED_COMPONENT_NAME_LIST: " + String.join(", ",
+ stringArrayExtra)
+ : ""));
+ }
+ };
+
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -613,10 +540,11 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> legacySplitScreenOptional,
Optional<SplitScreen> splitScreenOptional,
- Optional<Lazy<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
+ ScreenLifecycle screenLifecycle,
Optional<StartingSurface> startingSurface,
SmartspaceTransitionController smartspaceTransitionController) {
super(broadcastDispatcher);
@@ -653,6 +581,13 @@ public class OverviewProxyService extends CurrentUserTracker implements
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
+ // b/188806432
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart("", PatternMatcher.PATTERN_PREFIX);
+ mContext.registerReceiver(mDebugAnyPackageChangedReceiver, filter);
+
// Listen for status bar state changes
statusBarWinController.registerCallback(mStatusBarWindowCallback);
mScreenshotHelper = new ScreenshotHelper(context);
@@ -675,6 +610,13 @@ public class OverviewProxyService extends CurrentUserTracker implements
// Listen for user setup
startTracking();
+ screenLifecycle.addObserver(new ScreenLifecycle.Observer() {
+ @Override
+ public void onScreenTurnedOn() {
+ notifyScreenTurnedOn();
+ }
+ });
+
// Connect to the service
updateEnabledState();
startConnectionToCurrentUser();
@@ -736,12 +678,13 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
- boolean bouncerShowing) {
+ boolean bouncerShowing, boolean isDozing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded)
.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded)
.setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
+ .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
.commitUpdate(mContext.getDisplayId());
}
@@ -766,10 +709,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
+ mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
mInputFocusTransferStarted = false;
- statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
- 0 /* velocity */);
+ statusBar.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
});
});
}
@@ -881,6 +823,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
+ private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
+ }
+ }
+
private void notifyConnectionChanged() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
@@ -961,25 +909,61 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
+ /**
+ * Notifies the Launcher that screen turned on and ready to use
+ */
+ public void notifyScreenTurnedOn() {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onScreenTurnedOn();
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for screen turned on event.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call notifyScreenTurnedOn()", e);
+ }
+ }
+
void notifyToggleRecentApps() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onToggleRecentApps();
}
}
- public void notifyImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
+ public void disable(int displayId, int state1, int state2, boolean animate) {
try {
if (mOverviewProxy != null) {
- mOverviewProxy.onImeWindowStatusChanged(displayId, token, vis, backDisposition,
- showImeSwitcher);
+ mOverviewProxy.disable(displayId, state1, state2, animate);
} else {
- Log.e(TAG_OPS, "Failed to get overview proxy for setting IME status.");
+ Log.e(TAG_OPS, "Failed to get overview proxy for disable flags.");
}
} catch (RemoteException e) {
- Log.e(TAG_OPS, "Failed to call notifyImeWindowStatus()", e);
+ Log.e(TAG_OPS, "Failed to call disable()", e);
}
+ }
+ public void onRotationProposal(int rotation, boolean isValid) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onRotationProposal(rotation, isValid);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for proposing rotation.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onRotationProposal()", e);
+ }
+ }
+
+ public void onSystemBarAttributesChanged(int displayId, int behavior) {
+ try {
+ if (mOverviewProxy != null) {
+ mOverviewProxy.onSystemBarAttributesChanged(displayId, behavior);
+ } else {
+ Log.e(TAG_OPS, "Failed to get overview proxy for system bar attr change.");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onSystemBarAttributesChanged()", e);
+ }
}
private void updateEnabledState() {
@@ -1026,11 +1010,10 @@ public class OverviewProxyService extends CurrentUserTracker implements
/** Notify changes in the nav bar button alpha */
default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
default void onHomeRotationEnabled(boolean enabled) {}
+ default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
- default void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
- int backDisposition, boolean showImeSwitcher) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index aa8d710e7570..85bf98c09f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -51,10 +51,10 @@ import android.widget.TextView;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.leak.RotationUtils;
@@ -69,7 +69,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
NavigationModeController.ModeChangedListener {
private final Context mContext;
- private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
@@ -82,7 +82,7 @@ public class ScreenPinningRequest implements View.OnClickListener,
private int taskId;
@Inject
- public ScreenPinningRequest(Context context, Optional<Lazy<StatusBar>> statusBarOptionalLazy) {
+ public ScreenPinningRequest(Context context, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
mContext = context;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mAccessibilityService = (AccessibilityManager)
@@ -266,8 +266,9 @@ public class ScreenPinningRequest implements View.OnClickListener,
.setVisibility(View.INVISIBLE);
}
- NavigationBarView navigationBarView = mStatusBarOptionalLazy.map(
- statusBarLazy -> statusBarLazy.get().getNavigationBarView()).orElse(null);
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ NavigationBarView navigationBarView =
+ statusBarOptional.map(StatusBar::getNavigationBarView).orElse(null);
final boolean recentsVisible = navigationBarView != null
&& navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index 0aa9d4d662a5..5a6f2a2a912d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -27,6 +27,7 @@ import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.util.Log;
+import android.util.MathUtils;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -128,30 +129,64 @@ public class ScreenInternalAudioRecorder {
mThread = new Thread(() -> {
short[] bufferInternal = null;
short[] bufferMic = null;
- byte[] buffer = null;
+ byte[] buffer = new byte[size];
if (mMic) {
bufferInternal = new short[size / 2];
bufferMic = new short[size / 2];
- } else {
- buffer = new byte[size];
}
+ int readBytes = 0;
+ int readShortsInternal = 0;
+ int offsetShortsInternal = 0;
+ int readShortsMic = 0;
+ int offsetShortsMic = 0;
while (true) {
- int readBytes = 0;
- int readShortsInternal = 0;
- int readShortsMic = 0;
if (mMic) {
- readShortsInternal = mAudioRecord.read(bufferInternal, 0,
- bufferInternal.length);
- readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length);
+ readShortsInternal = mAudioRecord.read(bufferInternal, offsetShortsInternal,
+ bufferInternal.length - offsetShortsInternal);
+ readShortsMic = mAudioRecordMic.read(
+ bufferMic, offsetShortsMic, bufferMic.length - offsetShortsMic);
+
+ // if both error, end the recording
+ if (readShortsInternal < 0 && readShortsMic < 0) {
+ break;
+ }
+
+ // if one has an errors, fill its buffer with zeros and assume it is mute
+ // with the same size as the other buffer
+ if (readShortsInternal < 0) {
+ readShortsInternal = readShortsMic;
+ offsetShortsInternal = offsetShortsMic;
+ java.util.Arrays.fill(bufferInternal, (short) 0);
+ }
+
+ if (readShortsMic < 0) {
+ readShortsMic = readShortsInternal;
+ offsetShortsMic = offsetShortsInternal;
+ java.util.Arrays.fill(bufferMic, (short) 0);
+ }
+
+ // Add offset (previous unmixed values) to the buffer
+ readShortsInternal += offsetShortsInternal;
+ readShortsMic += offsetShortsMic;
+
+ int minShorts = Math.min(readShortsInternal, readShortsMic);
+ readBytes = minShorts * 2;
// modify the volume
- bufferMic = scaleValues(bufferMic,
- readShortsMic, MIC_VOLUME_SCALE);
- readBytes = Math.min(readShortsInternal, readShortsMic) * 2;
- buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic,
- readShortsMic);
+ // scale only mixed shorts
+ scaleValues(bufferMic, minShorts, MIC_VOLUME_SCALE);
+ // Mix the two buffers
+ addAndConvertBuffers(bufferInternal, bufferMic, buffer, minShorts);
+
+ // shift unmixed shorts to the beginning of the buffer
+ shiftToStart(bufferInternal, minShorts, offsetShortsInternal);
+ shiftToStart(bufferMic, minShorts, offsetShortsMic);
+
+ // reset the offset for the next loop
+ offsetShortsInternal = readShortsInternal - minShorts;
+ offsetShortsMic = readShortsMic - minShorts;
} else {
readBytes = mAudioRecord.read(buffer, 0, buffer.length);
}
@@ -169,40 +204,31 @@ public class ScreenInternalAudioRecorder {
});
}
- private short[] scaleValues(short[] buff, int len, float scale) {
+ /**
+ * moves all bits from start to end to the beginning of the array
+ */
+ private void shiftToStart(short[] target, int start, int end) {
+ for (int i = 0; i < end - start; i++) {
+ target[i] = target[start + i];
+ }
+ }
+
+ private void scaleValues(short[] buff, int len, float scale) {
for (int i = 0; i < len; i++) {
- int oldValue = buff[i];
int newValue = (int) (buff[i] * scale);
- if (newValue > Short.MAX_VALUE) {
- newValue = Short.MAX_VALUE;
- } else if (newValue < Short.MIN_VALUE) {
- newValue = Short.MIN_VALUE;
- }
- buff[i] = (short) (newValue);
+ buff[i] = (short) MathUtils.constrain(newValue, Short.MIN_VALUE, Short.MAX_VALUE);
}
- return buff;
}
- private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) {
- int size = Math.max(a1Limit, a2Limit);
- if (size < 0) return new byte[0];
- byte[] buff = new byte[size * 2];
- for (int i = 0; i < size; i++) {
- int sum;
- if (i > a1Limit) {
- sum = a2[i];
- } else if (i > a2Limit) {
- sum = a1[i];
- } else {
- sum = (int) a1[i] + (int) a2[i];
- }
- if (sum > Short.MAX_VALUE) sum = Short.MAX_VALUE;
- if (sum < Short.MIN_VALUE) sum = Short.MIN_VALUE;
+ private void addAndConvertBuffers(short[] src1, short[] src2, byte[] dst, int sizeShorts) {
+ for (int i = 0; i < sizeShorts; i++) {
+ int sum;
+ sum = (short) MathUtils.constrain(
+ (int) src1[i] + (int) src2[i], Short.MIN_VALUE, Short.MAX_VALUE);
int byteIndex = i * 2;
- buff[byteIndex] = (byte) (sum & 0xff);
- buff[byteIndex + 1] = (byte) ((sum >> 8) & 0xff);
+ dst[byteIndex] = (byte) (sum & 0xff);
+ dst[byteIndex + 1] = (byte) ((sum >> 8) & 0xff);
}
- return buff;
}
private void encode(byte[] buffer, int readBytes) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 16872b08b9c8..cab2168d44e4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -660,7 +660,7 @@ public class ScreenshotController {
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
- mScreenshotView.showScrollChip(/* onClick */ () -> {
+ mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
Bitmap newScreenshot = captureScreenshot(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 5cf018813133..2b7bcc04e43c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -71,7 +71,13 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "User has shared a long screenshot")
SCREENSHOT_LONG_SCREENSHOT_SHARE(689),
@UiEvent(doc = "User has sent a long screenshot to the editor")
- SCREENSHOT_LONG_SCREENSHOT_EDIT(690);
+ SCREENSHOT_LONG_SCREENSHOT_EDIT(690),
+ @UiEvent(doc = "A long screenshot capture has started")
+ SCREENSHOT_LONG_SCREENSHOT_STARTED(880),
+ @UiEvent(doc = "The long screenshot capture failed")
+ SCREENSHOT_LONG_SCREENSHOT_FAILURE(881),
+ @UiEvent(doc = "The long screenshot capture completed successfully")
+ SCREENSHOT_LONG_SCREENSHOT_COMPLETED(882);
private final int mId;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index e9e62f26a10e..e5e690bcb8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -242,19 +242,21 @@ public class ScreenshotView extends FrameLayout implements
/**
* Called to display the scroll action chip when support is detected.
*
+ * @param packageName the owning package of the window to be captured
* @param onClick the action to take when the chip is clicked.
*/
- public void showScrollChip(Runnable onClick) {
+ public void showScrollChip(String packageName, Runnable onClick) {
if (DEBUG_SCROLL) {
Log.d(TAG, "Showing Scroll option");
}
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION, 0, packageName);
mScrollChip.setVisibility(VISIBLE);
mScrollChip.setOnClickListener((v) -> {
if (DEBUG_INPUT) {
Log.d(TAG, "scroll chip tapped");
}
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED);
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED, 0,
+ packageName);
onClick.run();
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 6dc68746e3ec..ef7355a09fbf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -28,6 +28,7 @@ import androidx.concurrent.futures.CallbackToFutureAdapter;
import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
@@ -61,6 +62,7 @@ public class ScrollCaptureController {
private final Context mContext;
private final Executor mBgExecutor;
private final ImageTileSet mImageTileSet;
+ private final UiEventLogger mEventLogger;
private final ScrollCaptureClient mClient;
private Completer<LongScreenshot> mCaptureCompleter;
@@ -69,6 +71,7 @@ public class ScrollCaptureController {
private Session mSession;
private ListenableFuture<CaptureResult> mTileFuture;
private ListenableFuture<Void> mEndFuture;
+ private String mWindowOwner;
static class LongScreenshot {
private final ImageTileSet mImageTileSet;
@@ -135,11 +138,12 @@ public class ScrollCaptureController {
@Inject
ScrollCaptureController(Context context, @Background Executor bgExecutor,
- ScrollCaptureClient client, ImageTileSet imageTileSet) {
+ ScrollCaptureClient client, ImageTileSet imageTileSet, UiEventLogger logger) {
mContext = context;
mBgExecutor = bgExecutor;
mClient = client;
mImageTileSet = imageTileSet;
+ mEventLogger = logger;
}
@VisibleForTesting
@@ -157,6 +161,7 @@ public class ScrollCaptureController {
ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
return CallbackToFutureAdapter.getFuture(completer -> {
mCaptureCompleter = completer;
+ mWindowOwner = response.getPackageName();
mBgExecutor.execute(() -> {
float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
@@ -173,11 +178,13 @@ public class ScrollCaptureController {
if (LogConfig.DEBUG_SCROLL) {
Log.d(TAG, "got session " + mSession);
}
+ mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_STARTED, 0, mWindowOwner);
requestNextTile(0);
} catch (InterruptedException | ExecutionException e) {
// Failure to start, propagate to caller
Log.e(TAG, "session start failed!");
mCaptureCompleter.setException(e);
+ mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner);
}
}
@@ -297,6 +304,11 @@ public class ScrollCaptureController {
if (LogConfig.DEBUG_SCROLL) {
Log.d(TAG, "finishCapture()");
}
+ if (mImageTileSet.getHeight() > 0) {
+ mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_COMPLETED, 0, mWindowOwner);
+ } else {
+ mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner);
+ }
mEndFuture = mSession.end();
mEndFuture.addListener(() -> {
if (LogConfig.DEBUG_SCROLL) {
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 1ad253e7f867..acc6ee130539 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -30,7 +30,6 @@ import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -47,15 +46,16 @@ 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;
-import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import java.util.ArrayList;
import javax.inject.Inject;
-public class BrightnessController implements ToggleSlider.Listener {
+public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
private static final String TAG = "StatusBar.BrightnessController";
private static final int SLIDER_ANIMATION_DURATION = 3000;
@@ -109,6 +109,11 @@ public class BrightnessController implements ToggleSlider.Listener {
private ValueAnimator mSliderAnimator;
+ @Override
+ public void setMirror(BrightnessMirrorController controller) {
+ mControl.setMirrorControllerAndMirror(controller);
+ }
+
public interface BrightnessStateChangeCallback {
/** Indicates that some of the brightness settings have changed */
void onBrightnessLevelChanged();
@@ -282,12 +287,15 @@ public class BrightnessController implements ToggleSlider.Listener {
}
};
- public BrightnessController(Context context, ToggleSlider control,
- BroadcastDispatcher broadcastDispatcher) {
+ public BrightnessController(
+ Context context,
+ ToggleSlider control,
+ BroadcastDispatcher broadcastDispatcher,
+ @Background Handler bgHandler) {
mContext = context;
mControl = control;
mControl.setMax(GAMMA_SPACE_MAX);
- mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
+ mBackgroundHandler = bgHandler;
mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
@Override
public void onUserSwitched(int newUserId) {
@@ -385,6 +393,14 @@ public class BrightnessController implements ToggleSlider.Listener {
});
}
+ public void hideSlider() {
+ mControl.hideView();
+ }
+
+ public void showSlider() {
+ mControl.showView();
+ }
+
private void setBrightness(float brightness) {
mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
}
@@ -450,16 +466,25 @@ public class BrightnessController implements ToggleSlider.Listener {
public static class Factory {
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Handler mBackgroundHandler;
@Inject
- public Factory(Context context, BroadcastDispatcher broadcastDispatcher) {
+ public Factory(
+ Context context,
+ BroadcastDispatcher broadcastDispatcher,
+ @Background Handler bgHandler) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
+ mBackgroundHandler = bgHandler;
}
/** Create a {@link BrightnessController} */
public BrightnessController create(ToggleSlider toggleSlider) {
- return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher);
+ return new BrightnessController(
+ mContext,
+ toggleSlider,
+ mBroadcastDispatcher,
+ mBackgroundHandler);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 0f97e43c466b..8fc831a7ce4d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.Activity;
import android.os.Bundle;
+import android.os.Handler;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
@@ -32,6 +33,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import javax.inject.Inject;
@@ -41,13 +43,16 @@ public class BrightnessDialog extends Activity {
private BrightnessController mBrightnessController;
private final BrightnessSlider.Factory mToggleSliderFactory;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Handler mBackgroundHandler;
@Inject
public BrightnessDialog(
BroadcastDispatcher broadcastDispatcher,
- BrightnessSlider.Factory factory) {
+ BrightnessSlider.Factory factory,
+ @Background Handler bgHandler) {
mBroadcastDispatcher = broadcastDispatcher;
mToggleSliderFactory = factory;
+ mBackgroundHandler = bgHandler;
}
@@ -76,7 +81,8 @@ public class BrightnessDialog extends Activity {
controller.init();
frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
- mBrightnessController = new BrightnessController(this, controller, mBroadcastDispatcher);
+ mBrightnessController = new BrightnessController(
+ this, controller, mBroadcastDispatcher, mBackgroundHandler);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
new file mode 100644
index 000000000000..51aa339149a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.settings.brightness
+
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener
+
+class BrightnessMirrorHandler(private val brightnessController: MirroredBrightnessController) {
+
+ private var mirrorController: BrightnessMirrorController? = null
+
+ private val brightnessMirrorListener = BrightnessMirrorListener { updateBrightnessMirror() }
+
+ fun onQsPanelAttached() {
+ mirrorController?.addCallback(brightnessMirrorListener)
+ }
+
+ fun onQsPanelDettached() {
+ mirrorController?.removeCallback(brightnessMirrorListener)
+ }
+
+ fun setController(controller: BrightnessMirrorController) {
+ mirrorController?.removeCallback(brightnessMirrorListener)
+ mirrorController = controller
+ mirrorController?.addCallback(brightnessMirrorListener)
+ updateBrightnessMirror()
+ }
+
+ private fun updateBrightnessMirror() {
+ mirrorController?.let { brightnessController.setMirror(it) }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 0ff6216ea87e..b0e320ad1e2f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -140,13 +140,7 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple
@Override
public void setMirrorControllerAndMirror(BrightnessMirrorController c) {
mMirrorController = c;
- if (c != null) {
- setMirror(c.getToggleSlider());
- } else {
- // If there's no mirror, we may be the ones dispatching, events but we should not mirror
- // them
- mView.setOnDispatchTouchEventListener(null);
- }
+ setMirror(c.getToggleSlider());
}
@Override
@@ -180,6 +174,16 @@ public class BrightnessSlider extends ViewController<BrightnessSliderView> imple
return mView.getValue();
}
+ @Override
+ public void hideView() {
+ mView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void showView() {
+ mView.setVisibility(View.VISIBLE);
+ }
+
private final SeekBar.OnSeekBarChangeListener mSeekListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
new file mode 100644
index 000000000000..8d857dec2108
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
@@ -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.systemui.settings.brightness
+
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+
+/**
+ * Indicates controller that has brightness slider and uses [BrightnessMirrorController]
+ */
+interface MirroredBrightnessController {
+ fun setMirror(controller: BrightnessMirrorController)
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
index a988c7aeb436..5de22d43a21b 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
@@ -35,4 +35,7 @@ public interface ToggleSlider {
int getMax();
void setValue(int value);
int getValue();
+
+ void showView();
+ void hideView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8e52b0da54ef..90158c32c099 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -51,6 +51,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -337,7 +338,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
*/
default void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) { }
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities,
+ String packageName) { }
/**
* @see IStatusBar#showTransient(int, int[]).
@@ -996,7 +998,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.argi1 = displayId;
@@ -1004,7 +1006,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
args.argi3 = navbarColorManagedByIme ? 1 : 0;
args.arg1 = appearanceRegions;
args.argi4 = behavior;
- args.argi5 = isFullscreen ? 1 : 0;
+ args.arg2 = requestedVisibilities;
+ args.arg3 = packageName;
mHandler.obtainMessage(MSG_SYSTEM_BAR_CHANGED, args).sendToTarget();
}
}
@@ -1387,7 +1390,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController<
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onSystemBarAttributesChanged(args.argi1, args.argi2,
(AppearanceRegion[]) args.arg1, args.argi3 == 1, args.argi4,
- args.argi5 == 1);
+ (InsetsVisibilities) args.arg2, (String) args.arg3);
}
args.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 503b5c0ee4b0..1431c9e6f641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -821,7 +821,7 @@ public class KeyguardIndicationController {
}
}
- private void showTryFingerprintMsg() {
+ private void showTryFingerprintMsg(String a11yString) {
if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
@@ -833,6 +833,11 @@ public class KeyguardIndicationController {
} else {
showTransientIndication(R.string.keyguard_try_fingerprint);
}
+
+ // Although we suppress face auth errors visually, we still announce them for a11y
+ if (!TextUtils.isEmpty(a11yString)) {
+ mLockScreenIndicationView.announceForAccessibility(a11yString);
+ }
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -913,7 +918,7 @@ public class KeyguardIndicationController {
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
- showTryFingerprintMsg();
+ showTryFingerprintMsg(helpString);
return;
}
showTransientIndication(helpString, false /* isError */, showActionToUnlock);
@@ -933,7 +938,7 @@ public class KeyguardIndicationController {
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()
&& !mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isScreenOn()) {
- showTryFingerprintMsg();
+ showTryFingerprintMsg(errString);
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -942,7 +947,7 @@ public class KeyguardIndicationController {
if (!mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isUdfpsEnrolled()
&& mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- showTryFingerprintMsg();
+ showTryFingerprintMsg(errString);
} else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
mStatusBarKeyguardViewManager.showBouncerMessage(
mContext.getResources().getString(R.string.keyguard_unlock_press),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 538db6168408..51dbd85775c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -83,6 +83,35 @@ object LiftReveal : LightRevealEffect {
}
}
+class LinearLightRevealEffect(private val isVertical: Boolean) : LightRevealEffect {
+
+ private val INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN_REVERSE
+
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
+ val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
+
+ // TODO(b/193801466): add alpha reveal in the beginning as well
+ scrim.revealGradientEndColorAlpha =
+ 1f - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, threshold = 0.6f)
+
+ if (isVertical) {
+ scrim.setRevealGradientBounds(
+ left = scrim.width / 2 - (scrim.width / 2) * interpolatedAmount,
+ top = 0f,
+ right = scrim.width / 2 + (scrim.width / 2) * interpolatedAmount,
+ bottom = scrim.height.toFloat()
+ )
+ } else {
+ scrim.setRevealGradientBounds(
+ left = 0f,
+ top = scrim.height / 2 - (scrim.height / 2) * interpolatedAmount,
+ right = scrim.width.toFloat(),
+ bottom = scrim.height / 2 + (scrim.height / 2) * interpolatedAmount
+ )
+ }
+ }
+}
+
class CircleReveal(
/** X-value of the circle center of the reveal. */
val centerX: Float,
@@ -149,7 +178,10 @@ class PowerButtonReveal(
*/
class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- lateinit var revealAmountListener: Consumer<Float>
+ /**
+ * Listener that is called if the scrim's opaqueness changes
+ */
+ lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
/**
* How much of the underlying views are revealed, in percent. 0 means they will be completely
@@ -161,7 +193,7 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
field = value
revealEffect.setRevealAmountOnScrim(value, this)
- revealAmountListener.accept(value)
+ updateScrimOpaque()
invalidate()
}
}
@@ -201,6 +233,31 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context,
}
/**
+ * Is the scrim currently fully opaque
+ */
+ var isScrimOpaque = false
+ private set(value) {
+ if (field != value) {
+ field = value
+ isScrimOpaqueChangedListener.accept(field)
+ }
+ }
+
+ private fun updateScrimOpaque() {
+ isScrimOpaque = revealAmount == 0.0f && alpha == 1.0f && visibility == VISIBLE
+ }
+
+ override fun setAlpha(alpha: Float) {
+ super.setAlpha(alpha)
+ updateScrimOpaque()
+ }
+
+ override fun setVisibility(visibility: Int) {
+ super.setVisibility(visibility)
+ updateScrimOpaque()
+ }
+
+ /**
* Paint used to draw a transparent-to-white radial gradient. This will be scaled and translated
* via local matrix in [onDraw] so we never need to construct a new shader.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 6fa06a76dc5e..ca18b076b1e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -60,7 +60,6 @@ class LockscreenShadeTransitionController @Inject constructor(
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimController: ScrimController,
private val depthController: NotificationShadeDepthController,
- private val featureFlags: FeatureFlags,
private val context: Context,
configurationController: ConfigurationController,
falsingManager: FalsingManager
@@ -147,7 +146,7 @@ class LockscreenShadeTransitionController @Inject constructor(
R.dimen.lockscreen_shade_scrim_transition_distance)
fullTransitionDistance = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_qs_transition_distance)
- useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources)
+ useSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
fun setStackScroller(nsslController: NotificationStackScrollLayoutController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index e7e9404c9d3e..c681a4471584 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -285,6 +285,7 @@ public class NotificationLockscreenUserManagerImpl implements
internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+ mCurrentUserId = ActivityManager.getCurrentUser(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
mSettingsObserver.onChange(false); // set up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 25cbdc5c2187..36d2d866f74d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -52,6 +52,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaData;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.SmartspaceMediaData;
@@ -84,6 +85,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import dagger.Lazy;
@@ -132,7 +134,7 @@ public class NotificationMediaManager implements Dumpable {
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -177,7 +179,7 @@ public class NotificationMediaManager implements Dumpable {
*/
public NotificationMediaManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -197,7 +199,7 @@ public class NotificationMediaManager implements Dumpable {
mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(
Context.MEDIA_SESSION_SERVICE);
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mEntryManager = notificationEntryManager;
mMainExecutor = mainExecutor;
@@ -694,7 +696,8 @@ public class NotificationMediaManager implements Dumpable {
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
+ boolean hideBecauseOccluded =
+ mStatusBarOptionalLazy.get().map(StatusBar::isOccluded).orElse(false);
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 4552138761c0..625d9cd72e04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -45,6 +45,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RemoteViews;
+import android.widget.RemoteViews.InteractionHandler;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -69,7 +70,9 @@ 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.Optional;
import java.util.Set;
import java.util.stream.Stream;
@@ -118,7 +121,7 @@ public class NotificationRemoteInputManager implements Dumpable {
private final Handler mMainHandler;
private final ActionClickLogger mLogger;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
protected final Context mContext;
private final UserManager mUserManager;
@@ -134,14 +137,16 @@ public class NotificationRemoteInputManager implements Dumpable {
protected Callback mCallback;
protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
- private final RemoteViews.InteractionHandler
- mInteractionHandler = new RemoteViews.InteractionHandler() {
+ private final List<RemoteInputController.Callback> mControllerCallbacks = new ArrayList<>();
+
+ private final InteractionHandler mInteractionHandler = new InteractionHandler() {
@Override
public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
- "NOTIFICATION_CLICK");
+ mStatusBarOptionalLazy.get().ifPresent(
+ statusBar -> statusBar.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"));
final NotificationEntry entry = getNotificationForParent(view.getParent());
mLogger.logInitialClick(entry, pendingIntent);
@@ -280,7 +285,7 @@ public class NotificationRemoteInputManager implements Dumpable {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -290,7 +295,7 @@ public class NotificationRemoteInputManager implements Dumpable {
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
mEntryManager = notificationEntryManager;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mMainHandler = mainHandler;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
@@ -330,6 +335,11 @@ public class NotificationRemoteInputManager implements Dumpable {
public void setUpWithCallback(Callback callback, RemoteInputController.Delegate delegate) {
mCallback = callback;
mRemoteInputController = new RemoteInputController(delegate, mRemoteInputUriController);
+ // Register all stored callbacks from before the Controller was initialized.
+ for (RemoteInputController.Callback cb : mControllerCallbacks) {
+ mRemoteInputController.addCallback(cb);
+ }
+ mControllerCallbacks.clear();
mRemoteInputController.addCallback(new RemoteInputController.Callback() {
@Override
public void onRemoteInputSent(NotificationEntry entry) {
@@ -375,6 +385,22 @@ public class NotificationRemoteInputManager implements Dumpable {
});
}
+ public void addControllerCallback(RemoteInputController.Callback callback) {
+ if (mRemoteInputController != null) {
+ mRemoteInputController.addCallback(callback);
+ } else {
+ mControllerCallbacks.add(callback);
+ }
+ }
+
+ public void removeControllerCallback(RemoteInputController.Callback callback) {
+ if (mRemoteInputController != null) {
+ mRemoteInputController.removeCallback(callback);
+ } else {
+ mControllerCallbacks.remove(callback);
+ }
+ }
+
/**
* Activates a given {@link RemoteInput}
*
@@ -561,17 +587,12 @@ public class NotificationRemoteInputManager implements Dumpable {
return mLifetimeExtenders;
}
- @Nullable
- public RemoteInputController getController() {
- return mRemoteInputController;
- }
-
@VisibleForTesting
void onPerformRemoveNotification(NotificationEntry entry, final String key) {
if (mKeysKeptForRemoteInputHistory.contains(key)) {
mKeysKeptForRemoteInputHistory.remove(key);
}
- if (mRemoteInputController.isRemoteInputActive(entry)) {
+ if (isRemoteInputActive(entry)) {
entry.mRemoteEditImeVisible = false;
mRemoteInputController.removeRemoteInput(entry, null);
}
@@ -580,7 +601,9 @@ public class NotificationRemoteInputManager implements Dumpable {
public void onPanelCollapsed() {
for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
- mRemoteInputController.removeRemoteInput(entry, null);
+ if (mRemoteInputController != null) {
+ mRemoteInputController.removeRemoteInput(entry, null);
+ }
if (mNotificationLifetimeFinishedCallback != null) {
mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.getKey());
}
@@ -596,8 +619,7 @@ public class NotificationRemoteInputManager implements Dumpable {
if (!FORCE_REMOTE_INPUT_HISTORY) {
return false;
}
- return (mRemoteInputController.isSpinning(entry.getKey())
- || entry.hasJustSentRemoteInput());
+ return isSpinning(entry.getKey()) || entry.hasJustSentRemoteInput();
}
/**
@@ -630,8 +652,8 @@ public class NotificationRemoteInputManager implements Dumpable {
public void checkRemoteInputOutside(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
- && mRemoteInputController.isRemoteInputActive()) {
- mRemoteInputController.closeRemoteInputs();
+ && isRemoteInputActive()) {
+ closeRemoteInputs();
}
}
@@ -713,6 +735,24 @@ public class NotificationRemoteInputManager implements Dumpable {
return mEntriesKeptForRemoteInputActive;
}
+ public boolean isRemoteInputActive() {
+ return mRemoteInputController != null && mRemoteInputController.isRemoteInputActive();
+ }
+
+ public boolean isRemoteInputActive(NotificationEntry entry) {
+ return mRemoteInputController != null && mRemoteInputController.isRemoteInputActive(entry);
+ }
+
+ public boolean isSpinning(String entryKey) {
+ return mRemoteInputController != null && mRemoteInputController.isSpinning(entryKey);
+ }
+
+ public void closeRemoteInputs() {
+ if (mRemoteInputController != null) {
+ mRemoteInputController.closeRemoteInputs();
+ }
+ }
+
/**
* NotificationRemoteInputManager has multiple reasons to keep notification lifetime extended
* so we implement multiple NotificationLifetimeExtenders
@@ -820,7 +860,7 @@ public class NotificationRemoteInputManager implements Dumpable {
protected class RemoteInputActiveExtender extends RemoteInputExtender {
@Override
public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
- return mRemoteInputController.isRemoteInputActive(entry);
+ return isRemoteInputActive(entry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index f0d779ce1e0f..6ea79af8b9ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -182,10 +182,10 @@ public interface NotificationShadeWindowController extends RemoteInputController
default void setFaceAuthDisplayBrightness(float brightness) {}
/**
- * How much {@link LightRevealScrim} obscures the UI.
- * @param amount 0 when opaque, 1 when not transparent
+ * If {@link LightRevealScrim} obscures the UI.
+ * @param opaque if the scrim is opaque
*/
- default void setLightRevealScrimAmount(float amount) {}
+ default void setLightRevealScrimOpaque(boolean opaque) {}
/**
* Custom listener to pipe data back to plugins about whether or not the status bar would be
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index cd5cce4f3ee7..3bd7dd339a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -183,7 +183,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
}
viewState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing()
|| algorithmState.firstViewInShelf == null;
final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index cc7a4f836c63..4a6d7e184ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -15,46 +15,18 @@
package com.android.systemui.statusbar;
import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.WirelessUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.demomode.DemoModeCommandReceiver;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
import java.util.List;
/** Shows the operator name */
-public class OperatorNameView extends TextView implements DemoModeCommandReceiver, DarkReceiver,
- SignalCallback, Tunable {
-
- private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
-
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+public class OperatorNameView extends TextView {
private boolean mDemoMode;
- private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onRefreshCarrierInfo() {
- updateText();
- }
- };
-
public OperatorNameView(Context context) {
this(context, null);
}
@@ -67,62 +39,14 @@ public class OperatorNameView extends TextView implements DemoModeCommandReceive
super(context, attrs, defStyle);
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- mKeyguardUpdateMonitor.registerCallback(mCallback);
- Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
- Dependency.get(NetworkController.class).addCallback(this);
- Dependency.get(TunerService.class).addTunable(this, KEY_SHOW_OPERATOR_NAME);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mKeyguardUpdateMonitor.removeCallback(mCallback);
- Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
- Dependency.get(NetworkController.class).removeCallback(this);
- Dependency.get(TunerService.class).removeTunable(this);
- }
-
- @Override
- public void onDarkChanged(Rect area, float darkIntensity, int tint) {
- setTextColor(DarkIconDispatcher.getTint(area, this, tint));
- }
-
- @Override
- public void setIsAirplaneMode(IconState icon) {
- update();
- }
-
- @Override
- public void onTuningChanged(String key, String newValue) {
- update();
- }
-
- @Override
- public void dispatchDemoCommand(String command, Bundle args) {
- setText(args.getString("name"));
- }
-
- @Override
- public void onDemoModeStarted() {
- mDemoMode = true;
- }
-
- @Override
- public void onDemoModeFinished() {
- mDemoMode = false;
- update();
+ void setDemoMode(boolean demoMode) {
+ mDemoMode = demoMode;
}
- private void update() {
- boolean showOperatorName = Dependency.get(TunerService.class)
- .getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0;
+ void update(boolean showOperatorName, boolean hasMobile,
+ List<OperatorNameViewController.SubInfo> subs) {
setVisibility(showOperatorName ? VISIBLE : GONE);
- boolean hasMobile = mContext.getSystemService(TelephonyManager.class).isDataCapable();
boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
if (!hasMobile || airplaneMode) {
setText(null);
@@ -131,22 +55,19 @@ public class OperatorNameView extends TextView implements DemoModeCommandReceive
}
if (!mDemoMode) {
- updateText();
+ updateText(subs);
}
}
- private void updateText() {
+ void updateText(List<OperatorNameViewController.SubInfo> subs) {
CharSequence displayText = null;
- List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
final int N = subs.size();
for (int i = 0; i < N; i++) {
- int subId = subs.get(i).getSubscriptionId();
- int simState = mKeyguardUpdateMonitor.getSimState(subId);
+ OperatorNameViewController.SubInfo subInfo = subs.get(i);
CharSequence carrierName = subs.get(i).getCarrierName();
- if (!TextUtils.isEmpty(carrierName) && simState == TelephonyManager.SIM_STATE_READY) {
- ServiceState ss = mKeyguardUpdateMonitor.getServiceState(subId);
- if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
- displayText = carrierName;
+ if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
+ if (subInfo.stateInService()) {
+ displayText = subInfo.getCarrierName();
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
new file mode 100644
index 000000000000..e49f48f2cf49
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.os.Bundle;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+import android.view.View;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/** Controller for {@link OperatorNameView}. */
+public class OperatorNameViewController extends ViewController<OperatorNameView> {
+ private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
+
+ private final DarkIconDispatcher mDarkIconDispatcher;
+ private final NetworkController mNetworkController;
+ private final TunerService mTunerService;
+ private final TelephonyManager mTelephonyManager;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ private OperatorNameViewController(OperatorNameView view,
+ DarkIconDispatcher darkIconDispatcher,
+ NetworkController networkController,
+ TunerService tunerService,
+ TelephonyManager telephonyManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ super(view);
+ mDarkIconDispatcher = darkIconDispatcher;
+ mNetworkController = networkController;
+ mTunerService = tunerService;
+ mTelephonyManager = telephonyManager;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mDarkIconDispatcher.addDarkReceiver(mDarkReceiver);
+ mNetworkController.addCallback(mSignalCallback);
+ mTunerService.addTunable(mTunable, KEY_SHOW_OPERATOR_NAME);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mDarkIconDispatcher.removeDarkReceiver(mDarkReceiver);
+ mNetworkController.removeCallback(mSignalCallback);
+ mTunerService.removeTunable(mTunable);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+ }
+
+ private void update() {
+ mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0,
+ mTelephonyManager.isDataCapable(), getSubInfos());
+ }
+
+ private List<SubInfo> getSubInfos() {
+ List<SubInfo> result = new ArrayList<>();
+ List<SubscriptionInfo> subscritionInfos =
+ mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+
+ for (SubscriptionInfo subscriptionInfo : subscritionInfos) {
+ int subId = subscriptionInfo.getSubscriptionId();
+ result.add(new SubInfo(
+ subscriptionInfo.getCarrierName(),
+ mKeyguardUpdateMonitor.getSimState(subId),
+ mKeyguardUpdateMonitor.getServiceState(subId)));
+ }
+
+ return result;
+ }
+
+ /** Factory for constructing an {@link OperatorNameViewController}. */
+ public static class Factory {
+ private final DarkIconDispatcher mDarkIconDispatcher;
+ private final NetworkController mNetworkController;
+ private final TunerService mTunerService;
+ private final TelephonyManager mTelephonyManager;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ @Inject
+ public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController,
+ TunerService tunerService, TelephonyManager telephonyManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mDarkIconDispatcher = darkIconDispatcher;
+ mNetworkController = networkController;
+ mTunerService = tunerService;
+ mTelephonyManager = telephonyManager;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ /** Create an {@link OperatorNameViewController}. */
+ public OperatorNameViewController create(OperatorNameView view) {
+ return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController,
+ mTunerService, mTelephonyManager, mKeyguardUpdateMonitor);
+ }
+ }
+
+ /**
+ * Needed because of how {@link CollapsedStatusBarFragment} works.
+ *
+ * Ideally this can be done internally.
+ **/
+ public View getView() {
+ return mView;
+ }
+
+ private final DarkIconDispatcher.DarkReceiver mDarkReceiver =
+ (area, darkIntensity, tint) ->
+ mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint));
+
+ private final NetworkController.SignalCallback mSignalCallback =
+ new NetworkController.SignalCallback() {
+ @Override
+ public void setIsAirplaneMode(NetworkController.IconState icon) {
+ update();
+ }
+ };
+
+ private final TunerService.Tunable mTunable = (key, newValue) -> update();
+
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshCarrierInfo() {
+ mView.updateText(getSubInfos());
+ }
+ };
+
+ // TODO: do we even register this anywhere?
+ private final DemoModeCommandReceiver mDemoModeCommandReceiver = new DemoModeCommandReceiver() {
+ @Override
+ public void onDemoModeStarted() {
+ mView.setDemoMode(true);
+ }
+
+ @Override
+ public void onDemoModeFinished() {
+ mView.setDemoMode(false);
+ update();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ mView.setText(args.getString("name"));
+ }
+ };
+
+ static class SubInfo {
+ private final CharSequence mCarrierName;
+ private final int mSimState;
+ private final ServiceState mServiceState;
+
+ private SubInfo(CharSequence carrierName,
+ int simState, ServiceState serviceState) {
+ mCarrierName = carrierName;
+ mSimState = simState;
+ mServiceState = serviceState;
+ }
+
+ boolean simReady() {
+ return mSimState == TelephonyManager.SIM_STATE_READY;
+ }
+
+ CharSequence getCarrierName() {
+ return mCarrierName;
+ }
+
+ boolean stateInService() {
+ return mServiceState != null
+ && mServiceState.getState() == ServiceState.STATE_IN_SERVICE;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 180f81c22a9d..83701a040f24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -25,7 +25,6 @@ import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Pair;
-import com.android.internal.util.Preconditions;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -245,6 +244,10 @@ public class RemoteInputController {
mCallbacks.add(callback);
}
+ public void removeCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
public void remoteInputSent(NotificationEntry entry) {
int N = mCallbacks.size();
for (int i = 0; i < N; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0725bf961e13..545dca8e6140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -16,6 +16,10 @@
package com.android.systemui.statusbar;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
@@ -23,10 +27,16 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.os.SystemProperties;
import android.text.format.DateFormat;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.InsetsFlags;
+import android.view.InsetsVisibilities;
import android.view.View;
+import android.view.ViewDebug;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -56,6 +66,9 @@ import javax.inject.Inject;
public class StatusBarStateControllerImpl implements SysuiStatusBarStateController,
CallbackController<StateListener>, Dumpable {
private static final String TAG = "SbStateController";
+ private static final boolean DEBUG_IMMERSIVE_APPS =
+ SystemProperties.getBoolean("persist.debug.immersive_apps", false);
+
// Must be a power of 2
private static final int HISTORY_SIZE = 32;
@@ -420,7 +433,10 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
@Override
- public void setFullscreenState(boolean isFullscreen) {
+ public void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsVisibilities requestedVisibilities, String packageName) {
+ boolean isFullscreen = !requestedVisibilities.getVisibility(ITYPE_STATUS_BAR)
+ || !requestedVisibilities.getVisibility(ITYPE_NAVIGATION_BAR);
if (mIsFullscreen != isFullscreen) {
mIsFullscreen = isFullscreen;
synchronized (mListeners) {
@@ -429,6 +445,19 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
}
}
+
+ // TODO (b/190543382): Finish the logging logic.
+ // This section can be removed if we don't need to print it on logcat.
+ if (DEBUG_IMMERSIVE_APPS) {
+ boolean dim = (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0;
+ String behaviorName = ViewDebug.flagsToString(InsetsFlags.class, "behavior", behavior);
+ String requestedVisibilityString = requestedVisibilities.toString();
+ if (requestedVisibilityString.isEmpty()) {
+ requestedVisibilityString = "none";
+ }
+ Log.d(TAG, packageName + " dim=" + dim + " behavior=" + behaviorName
+ + " requested visibilities: " + requestedVisibilityString);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index 25200501a916..f0b2c2d54dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -19,7 +19,10 @@ package com.android.systemui.statusbar;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.view.InsetsVisibilities;
import android.view.View;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -155,9 +158,10 @@ public interface SysuiStatusBarStateController extends StatusBarStateController
boolean isKeyguardRequested();
/**
- * Set the fullscreen state
+ * Set the system bar attributes
*/
- void setFullscreenState(boolean isFullscreen);
+ void setSystemBarAttributes(@Appearance int appearance, @Behavior int behavior,
+ InsetsVisibilities requestedVisibilities, String packageName);
/**
* Set pulsing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 4a467ce3c987..d01fc93ee84c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -104,7 +104,7 @@ class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context
// the active effect area. Values here should be kept in sync with the
// animation implementation in the ripple shader.
val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * radius * 1.5f
+ (1 - rippleShader.progress)) * radius * 2
canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 22bbb81b44e6..d74297ee8b76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -29,7 +29,7 @@ import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.Utils
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 491959320ab7..f2cf93ed9dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -24,12 +24,13 @@ import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.carrier.QSCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationListener;
@@ -92,7 +93,7 @@ public interface StatusBarDependenciesModule {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -103,7 +104,7 @@ public interface StatusBarDependenciesModule {
lockscreenUserManager,
smartReplyController,
notificationEntryManager,
- statusBarLazy,
+ statusBarOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
@@ -116,7 +117,7 @@ public interface StatusBarDependenciesModule {
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -129,7 +130,7 @@ public interface StatusBarDependenciesModule {
MediaDataManager mediaDataManager) {
return new NotificationMediaManager(
context,
- statusBarLazy,
+ statusBarOptionalLazy,
notificationShadeWindowController,
notificationEntryManager,
mediaArtworkProcessor,
@@ -253,4 +254,9 @@ public interface StatusBarDependenciesModule {
ongoingCallController.init();
return ongoingCallController;
}
+
+ /** */
+ @Binds
+ QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
+ QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 1037e576f263..1000788908dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -104,6 +104,7 @@ class PrivacyDotViewController @Inject constructor(
setNewLayoutRects()
}
})
+
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
override fun onLayoutDirectionChanged(isRtl: Boolean) {
synchronized(this) {
@@ -477,7 +478,7 @@ class PrivacyDotViewController @Inject constructor(
private fun resolveState(state: ViewState) {
dlog("resolveState $state")
if (!state.viewInitialized) {
- dlog("resolveState: view is not initialized. skipping.")
+ dlog("resolveState: view is not initialized. skipping")
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 71546ae07ffc..fdbe72879374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -31,6 +31,8 @@ import android.os.Handler
import android.os.UserHandle
import android.provider.Settings
import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
import android.view.ViewGroup
import com.android.settingslib.Utils
import com.android.systemui.R
@@ -43,15 +45,22 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
+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
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.settings.SecureSettings
-import java.lang.RuntimeException
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
+private val ANIMATION_PROPERTIES = AnimationProperties()
+ .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD.toLong())
+
/**
* Controller for managing the smartspace view on the lockscreen
*/
@@ -72,10 +81,15 @@ class LockscreenSmartspaceController @Inject constructor(
@Main private val handler: Handler,
optionalPlugin: Optional<BcSmartspaceDataPlugin>
) {
+
+ var splitShadeContainer: ViewGroup? = null
+ private var singlePaneContainer: ViewGroup? = null
+
private var session: SmartspaceSession? = null
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
private lateinit var smartspaceView: SmartspaceView
+ // smartspace casted to View
lateinit var view: View
private set
@@ -83,12 +97,60 @@ class LockscreenSmartspaceController @Inject constructor(
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
- fun isEnabled(): Boolean {
+ private var isAod = false
+ private var isSplitShade = false
+
+ fun isSmartspaceEnabled(): Boolean {
execution.assertIsMainThread()
return featureFlags.isSmartspaceEnabled && plugin != null
}
+ fun setKeyguardStatusContainer(container: ViewGroup) {
+ singlePaneContainer = container
+ // reattach smartspace if necessary as this might be a new container
+ updateSmartSpaceContainer()
+ }
+
+ fun onSplitShadeChanged(splitShade: Boolean) {
+ isSplitShade = splitShade
+ updateSmartSpaceContainer()
+ }
+
+ private fun updateSmartSpaceContainer() {
+ if (!isSmartspaceEnabled()) return
+ // in AOD we always want to show smartspace on the left i.e. in singlePaneContainer
+ if (isSplitShade && !isAod) {
+ switchContainerVisibility(
+ newParent = splitShadeContainer,
+ oldParent = singlePaneContainer)
+ } else {
+ switchContainerVisibility(
+ newParent = singlePaneContainer,
+ oldParent = splitShadeContainer)
+ }
+ requestSmartspaceUpdate()
+ }
+
+ private fun switchContainerVisibility(newParent: ViewGroup?, oldParent: ViewGroup?) {
+ // it might be the case that smartspace was already attached and we just needed to update
+ // visibility, e.g. going from lockscreen -> unlocked -> lockscreen
+ if (newParent?.childCount == 0) {
+ oldParent?.removeAllViews()
+ newParent.addView(buildAndConnectView(newParent))
+ }
+ oldParent?.visibility = GONE
+ newParent?.visibility = VISIBLE
+ }
+
+ fun setSplitShadeSmartspaceAlpha(alpha: Float) {
+ // the other container's alpha is modified as a part of keyguard status view, so we don't
+ // have to do that here
+ if (splitShadeContainer?.visibility == VISIBLE) {
+ splitShadeContainer?.alpha = alpha
+ }
+ }
+
/**
* Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
* are idempotent until [disconnect] is called.
@@ -96,7 +158,7 @@ class LockscreenSmartspaceController @Inject constructor(
fun buildAndConnectView(parent: ViewGroup): View {
execution.assertIsMainThread()
- if (!isEnabled()) {
+ if (!isSmartspaceEnabled()) {
throw RuntimeException("Cannot build view when not enabled")
}
@@ -182,7 +244,6 @@ class LockscreenSmartspaceController @Inject constructor(
userTracker.removeCallback(userTrackerCallback)
contentResolver.unregisterContentObserver(settingsObserver)
configurationController.removeCallback(configChangeListener)
- statusBarStateController.removeCallback(statusBarStateListener)
session = null
plugin?.onTargetsAvailable(emptyList())
@@ -198,6 +259,13 @@ class LockscreenSmartspaceController @Inject constructor(
plugin?.unregisterListener(listener)
}
+ fun shiftSplitShadeSmartspace(y: Int, animate: Boolean) {
+ if (splitShadeContainer?.visibility == VISIBLE) {
+ PropertyAnimator.setProperty(splitShadeContainer, AnimatableProperty.Y, y.toFloat(),
+ ANIMATION_PROPERTIES, animate)
+ }
+ }
+
private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
execution.assertIsMainThread()
val filteredTargets = targets.filter(::filterSmartspaceTarget)
@@ -233,6 +301,23 @@ class LockscreenSmartspaceController @Inject constructor(
execution.assertIsMainThread()
smartspaceView.setDozeAmount(eased)
}
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ isAod = isDozing
+ updateSmartSpaceContainer()
+ }
+
+ override fun onStateChanged(newState: Int) {
+ if (newState == StatusBarState.KEYGUARD) {
+ if (isSmartspaceEnabled()) {
+ updateSmartSpaceContainer()
+ }
+ } else {
+ splitShadeContainer?.visibility = GONE
+ singlePaneContainer?.visibility = GONE
+ disconnect()
+ }
+ }
}
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
index 2537b19513d2..129fa5a7cc17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -20,6 +20,7 @@ import android.content.Intent;
import android.service.notification.StatusBarNotification;
import android.view.View;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -37,6 +38,9 @@ public interface NotificationActivityStarter {
/** Called when the user clicks "Manage" or "History" in the Shade. */
void startHistoryIntent(View view, boolean showHistory);
+ /** Called when the user succeed to drop notification to proper target view. */
+ void onDragSuccess(NotificationEntry entry);
+
default boolean isCollapsingToShowActivityOverLockscreen() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 0fb1c54bb150..da706215863e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -43,6 +43,14 @@ public final class NotificationClicker implements View.OnClickListener {
private final Optional<Bubbles> mBubblesOptional;
private final NotificationActivityStarter mNotificationActivityStarter;
+ private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
+ new ExpandableNotificationRow.OnDragSuccessListener() {
+ @Override
+ public void onDragSuccess(NotificationEntry entry) {
+ mNotificationActivityStarter.onDragSuccess(entry);
+ }
+ };
+
private NotificationClicker(
NotificationClickerLogger logger,
Optional<StatusBar> statusBarOptional,
@@ -111,8 +119,10 @@ public final class NotificationClicker implements View.OnClickListener {
if (notification.contentIntent != null || notification.fullScreenIntent != null
|| row.getEntry().isBubble()) {
row.setOnClickListener(this);
+ row.setOnDragSuccessListener(mOnDragSuccessListener);
} else {
row.setOnClickListener(null);
+ row.setOnDragSuccessListener(null);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 1ab2a9e320b0..a65f3d5a20a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -37,7 +37,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 8ae31cba4cfb..277b4acb3237 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -63,7 +63,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.LogBufferEulogizer;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 94ee868ceebc..6369a7ecb6cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -127,11 +127,8 @@ public final class NotificationEntry extends ListEntry {
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
public CharSequence remoteInputText;
- // Mimetype and Uri used to display the image in the notification *after* it has been sent.
public String remoteInputMimeType;
public Uri remoteInputUri;
- // ContentInfo used to keep the attachment permission alive until RemoteInput is sent or
- // cancelled.
public ContentInfo remoteInputAttachment;
private Notification.BubbleMetadata mBubbleMetadata;
private ShortcutInfo mShortcutInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
new file mode 100644
index 000000000000..369e52f55278
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.collection.coordinator;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+
+/**
+ * {@link CommunalCoordinator} prevents notifications from showing on the keyguard when the communal
+ * view is present.
+ */
+public class CommunalCoordinator implements Coordinator {
+ final CommunalStateController mCommunalStateController;
+ final NotificationEntryManager mNotificationEntryManager;
+ final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+
+ @Inject
+ public CommunalCoordinator(NotificationEntryManager notificationEntryManager,
+ NotificationLockscreenUserManager notificationLockscreenUserManager,
+ CommunalStateController communalStateController) {
+ mNotificationEntryManager = notificationEntryManager;
+ mNotificationLockscreenUserManager = notificationLockscreenUserManager;
+ mCommunalStateController = communalStateController;
+ }
+
+ final NotifFilter mFilter = new NotifFilter("CommunalCoordinator") {
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return mCommunalStateController.getCommunalViewShowing();
+ }
+ };
+
+ final CommunalStateController.Callback mStateCallback = new CommunalStateController.Callback() {
+ @Override
+ public void onCommunalViewShowingChanged() {
+ mFilter.invalidateList();
+ mNotificationEntryManager.updateNotifications("Communal mode state changed");
+ }
+ };
+
+ @Override
+ public void attach(@NonNull NotifPipeline pipeline) {
+ pipeline.addPreGroupFilter(mFilter);
+ mCommunalStateController.addCallback(mStateCallback);
+ mNotificationLockscreenUserManager.addKeyguardNotificationSuppressor(
+ entry -> mCommunalStateController.getCommunalViewShowing());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
index be1383fb6b8a..6e98c27fe9a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -149,7 +149,7 @@ public class HeadsUpCoordinator implements Coordinator {
final String entryKey = entry.getKey();
if (mHeadsUpManager.isAlerting(entryKey)) {
boolean removeImmediatelyForRemoteInput =
- mRemoteInputManager.getController().isSpinning(entryKey)
+ mRemoteInputManager.isSpinning(entryKey)
&& !FORCE_REMOTE_INPUT_HISTORY;
mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index d80cc082aada..47bc444396b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
@@ -61,7 +61,8 @@ public class NotifCoordinators implements Dumpable {
PreparationCoordinator preparationCoordinator,
MediaCoordinator mediaCoordinator,
SmartspaceDedupingCoordinator smartspaceDedupingCoordinator,
- VisualStabilityCoordinator visualStabilityCoordinator) {
+ VisualStabilityCoordinator visualStabilityCoordinator,
+ CommunalCoordinator communalCoordinator) {
dumpManager.registerDumpable(TAG, this);
mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
@@ -74,6 +75,7 @@ public class NotifCoordinators implements Dumpable {
mCoordinators.add(conversationCoordinator);
mCoordinators.add(mediaCoordinator);
mCoordinators.add(visualStabilityCoordinator);
+ mCoordinators.add(communalCoordinator);
if (featureFlags.isSmartspaceDedupingEnabled()) {
mCoordinators.add(smartspaceDedupingCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
index aec26474cf7d..518c3f1d1948 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index db49e4476a99..168e08603f3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,7 +21,7 @@ import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6964838e7e41..55620b634008 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -30,11 +30,11 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -126,7 +126,7 @@ public interface NotificationsModule {
@Provides
static NotificationGutsManager provideNotificationGutsManager(
Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -145,7 +145,7 @@ public interface NotificationsModule {
ShadeController shadeController) {
return new NotificationGutsManager(
context,
- statusBarLazy,
+ statusBarOptionalLazy,
mainHandler,
bgHandler,
accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 32d90d3333d0..c5899ad0d1a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -20,7 +20,7 @@ import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.NotificationListener
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
index 4d56e6013d71..b1c69e44da12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpController.java
@@ -152,7 +152,7 @@ public class HeadsUpController {
// Also we should not defer the removal if reordering isn't allowed since otherwise
// some notifications can't disappear before the panel is closed.
boolean ignoreEarliestRemovalTime =
- mRemoteInputManager.getController().isSpinning(key)
+ mRemoteInputManager.isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 4f3406c405ad..acb0e82c24f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.MathUtils;
@@ -139,6 +140,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mIsHeadsUpAnimation;
private int mHeadsUpAddStartLocation;
private float mHeadsUpLocation;
+ /* In order to track headsup longpress coorindate. */
+ protected Point mTargetPoint;
private boolean mIsAppearing;
private boolean mDismissed;
private boolean mRefocusOnDismiss;
@@ -521,8 +524,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public void onAnimationStart(Animator animation) {
mWasCancelled = false;
- Configuration.Builder builder = new Configuration.Builder(getCujType(isAppearing))
- .setView(ActivatableNotificationView.this);
+ Configuration.Builder builder = Configuration.Builder
+ .withView(getCujType(isAppearing), ActivatableNotificationView.this);
InteractionJankMonitor.getInstance().begin(builder);
}
@@ -568,8 +571,19 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
final int actualHeight = getActualHeight();
float bottom = actualHeight * interpolatedFraction;
- setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
- bottom + mAppearAnimationTranslation);
+ if (mTargetPoint != null) {
+ int width = getWidth();
+ float fraction = 1 - mAppearAnimationFraction;
+
+ setOutlineRect(mTargetPoint.x * fraction,
+ mAnimationTranslationY
+ + (mAnimationTranslationY - mTargetPoint.y) * fraction,
+ width - (width - mTargetPoint.x) * fraction,
+ actualHeight - (actualHeight - mTargetPoint.y) * fraction);
+ } else {
+ setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
+ bottom + mAppearAnimationTranslation);
+ }
}
private float getInterpolatedAppearAnimationFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 9bba7effd824..1cc1dcf3c90b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -280,7 +280,8 @@ class ChannelEditorDialogController @Inject constructor(
@VisibleForTesting
fun launchSettings(sender: View) {
- onSettingsClickListener?.onClick(sender, null, appUid!!)
+ val channel = if (providedChannels.size == 1) providedChannels[0] else null
+ onSettingsClickListener?.onClick(sender, channel, appUid!!)
}
private fun initDialog() {
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 73bb6cd9ba1c..0d8e85094646 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
@@ -37,6 +37,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Path;
+import android.graphics.Point;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -260,6 +261,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// Use #setLongPressPosition to optionally assign positional data with the long press.
private LongPressListener mLongPressListener;
+ private ExpandableNotificationRowDragController mDragController;
+
private boolean mGroupExpansionChanging;
/**
@@ -331,6 +334,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
};
private OnClickListener mOnClickListener;
+ private OnDragSuccessListener mOnDragSuccessListener;
private boolean mHeadsupDisappearRunning;
private View mChildAfterViewWhenDismissed;
private View mGroupParentWhenDismissed;
@@ -1083,6 +1087,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mLongPressListener = longPressListener;
}
+ public void setDragController(ExpandableNotificationRowDragController dragController) {
+ mDragController = dragController;
+ }
+
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
super.setOnClickListener(l);
@@ -1329,6 +1337,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void dismiss(boolean refocusOnDismiss) {
super.dismiss(refocusOnDismiss);
setLongPressListener(null);
+ setDragController(null);
mGroupParentWhenDismissed = mNotificationParent;
mChildAfterViewWhenDismissed = null;
mEntry.getIcons().getStatusBarIcon().setDismissed();
@@ -1637,6 +1646,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
onHeightReset();
requestLayout();
+
+ setTargetPoint(null);
}
public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
@@ -1727,6 +1738,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateableViews.remove(mGutsStub);
}
+ /**
+ * Called once when starting drag motion after opening notification guts,
+ * in case of notification that has {@link android.app.Notification#contentIntent}
+ * and it is to start an activity.
+ */
+ public void doDragCallback(float x, float y) {
+ if (mDragController != null) {
+ setTargetPoint(new Point((int) x, (int) y));
+ mDragController.startDragAndDrop(this);
+ }
+ }
+
+ public void setOnDragSuccessListener(OnDragSuccessListener listener) {
+ mOnDragSuccessListener = listener;
+ }
+
+ /**
+ * Called when a notification is dropped on proper target window.
+ */
+ public void dragAndDropSuccess() {
+ mOnDragSuccessListener.onDragSuccess(getEntry());
+ }
+
private void doLongClickCallback() {
doLongClickCallback(getWidth() / 2, getHeight() / 2);
}
@@ -3255,6 +3289,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
+ * Called when notification drag and drop is finished successfully.
+ */
+ public interface OnDragSuccessListener {
+ /**
+ * @param entry NotificationEntry that succeed to drop on proper target window.
+ */
+ void onDragSuccess(NotificationEntry entry);
+ }
+
+ /**
* Equivalent to View.OnClickListener with coordinates
*/
public interface CoordinateOnClickListener {
@@ -3321,4 +3365,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
}
+
+ private void setTargetPoint(Point p) {
+ mTargetPoint = p;
+ }
+ public Point getTargetPoint() {
+ return mTargetPoint;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c9fcdac8e45f..0662a1eba8b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -85,6 +86,8 @@ public class ExpandableNotificationRowController implements NodeController {
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private final Optional<BubblesManager> mBubblesManagerOptional;
+ private final ExpandableNotificationRowDragController mDragController;
+
@Inject
public ExpandableNotificationRowController(
ExpandableNotificationRow view,
@@ -109,7 +112,8 @@ public class ExpandableNotificationRowController implements NodeController {
FalsingManager falsingManager,
FalsingCollector falsingCollector,
PeopleNotificationIdentifier peopleNotificationIdentifier,
- Optional<BubblesManager> bubblesManagerOptional) {
+ Optional<BubblesManager> bubblesManagerOptional,
+ ExpandableNotificationRowDragController dragController) {
mView = view;
mListContainer = listContainer;
mActivatableNotificationViewController = activatableNotificationViewController;
@@ -134,6 +138,7 @@ public class ExpandableNotificationRowController implements NodeController {
mFalsingCollector = falsingCollector;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
+ mDragController = dragController;
}
/**
@@ -164,6 +169,10 @@ public class ExpandableNotificationRowController implements NodeController {
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
+ if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) {
+ mView.setDragController(mDragController);
+ }
+
mView.setLongPressListener((v, x, y, item) -> {
if (mView.isSummaryWithChildren()) {
mView.expandNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
new file mode 100644
index 000000000000..06b739b33e77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for Notification to window.
+ */
+public class ExpandableNotificationRowDragController {
+ private static final String TAG = ExpandableNotificationRowDragController.class.getSimpleName();
+ private int mIconSize;
+
+ private final Context mContext;
+ private final HeadsUpManager mHeadsUpManager;
+
+ @Inject
+ public ExpandableNotificationRowDragController(Context context,
+ HeadsUpManager headsUpManager) {
+ mContext = context;
+ mHeadsUpManager = headsUpManager;
+
+ init();
+ }
+
+ private void init() {
+ mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.drag_and_drop_icon_size);
+ }
+
+ /**
+ * Called when drag event beyond the touchslop,
+ * and start drag and drop.
+ *
+ * @param view notification that was long pressed and started to drag and drop.
+ */
+ @VisibleForTesting
+ public void startDragAndDrop(View view) {
+ ExpandableNotificationRow enr = null;
+ if (view instanceof ExpandableNotificationRow) {
+ enr = (ExpandableNotificationRow) view;
+ }
+
+ StatusBarNotification sn = enr.getEntry().getSbn();
+ Notification notification = sn.getNotification();
+ final PendingIntent contentIntent = notification.contentIntent != null
+ ? notification.contentIntent
+ : notification.fullScreenIntent;
+ Bitmap iconBitmap = getBitmapFromDrawable(
+ getPkgIcon(enr.getEntry().getSbn().getPackageName()));
+
+ final ImageView snapshot = new ImageView(mContext);
+ snapshot.setImageBitmap(iconBitmap);
+ snapshot.layout(0, 0, mIconSize, mIconSize);
+
+ ClipDescription clipDescription = new ClipDescription("Drag And Drop",
+ new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
+ Intent dragIntent = new Intent();
+ dragIntent.putExtra("android.intent.extra.PENDING_INTENT", contentIntent);
+ dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
+ ClipData.Item item = new ClipData.Item(dragIntent);
+ ClipData dragData = new ClipData(clipDescription, item);
+ View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
+ view.setOnDragListener(getDraggedViewDragListener());
+ view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL);
+ }
+
+
+ private Drawable getPkgIcon(String pkgName) {
+ Drawable pkgicon = null;
+ PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo info;
+ try {
+ info = pm.getApplicationInfo(
+ pkgName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (info != null) {
+ pkgicon = pm.getApplicationIcon(info);
+ } else {
+ Log.d(TAG, " application info is null ");
+ pkgicon = pm.getDefaultActivityIcon();
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, "can not find package with : " + pkgName);
+ pkgicon = pm.getDefaultActivityIcon();
+ }
+
+ return pkgicon;
+ }
+
+ private Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) {
+ final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bmp);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bmp;
+ }
+
+ private View.OnDragListener getDraggedViewDragListener() {
+ return (view, dragEvent) -> {
+ switch (dragEvent.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
+ if (enr.isPinned()) {
+ mHeadsUpManager.releaseAllImmediately();
+ } else {
+ Dependency.get(ShadeController.class).animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+ }
+ }
+ return true;
+ case DragEvent.ACTION_DRAG_ENDED:
+ if (dragEvent.getResult()) {
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
+ enr.dragAndDropSuccess();
+ }
+ }
+ return true;
+ }
+ return false;
+ };
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 86c90c7bcb2e..9eb95c409009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -28,15 +27,12 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
public class FooterView extends StackScrollerDecorView {
- private final int mClearAllTopPadding;
private FooterViewButton mDismissButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
- mClearAllTopPadding = context.getResources().getDimensionPixelSize(
- R.dimen.clear_all_padding_top);
}
@Override
@@ -55,11 +51,6 @@ public class FooterView extends StackScrollerDecorView {
mManageButton = findViewById(R.id.manage_text);
}
- public void setTextColor(@ColorInt int color) {
- mManageButton.setTextColor(color);
- mDismissButton.setTextColor(color);
- }
-
public void setManageButtonClickListener(OnClickListener listener) {
mManageButton.setOnClickListener(listener);
}
@@ -95,21 +86,25 @@ public class FooterView extends StackScrollerDecorView {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- int textColor = getResources().getColor(R.color.notif_pill_text);
- Resources.Theme theme = getContext().getTheme();
- mDismissButton.setBackground(
- getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
- mDismissButton.setTextColor(textColor);
- mManageButton.setBackground(
- getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
- mManageButton = findViewById(R.id.manage_text);
+ updateColors();
mDismissButton.setText(R.string.clear_all_notifications_text);
- mManageButton.setTextColor(textColor);
mDismissButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
showHistory(mShowHistory);
}
+ /**
+ * Update the text and background colors for the current color palette and night mode setting.
+ */
+ public void updateColors() {
+ Resources.Theme theme = mContext.getTheme();
+ int textColor = getResources().getColor(R.color.notif_pill_text, theme);
+ mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+ mDismissButton.setTextColor(textColor);
+ mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+ mManageButton.setTextColor(textColor);
+ }
+
@Override
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
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 73c4b054fd4e..1530e5238c67 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
@@ -874,7 +874,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRow.getImageResolver().purgeCache();
}
- private class RtlEnabledContext extends ContextWrapper {
+ private static class RtlEnabledContext extends ContextWrapper {
private RtlEnabledContext(Context packageContext) {
super(packageContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 4319e29985d8..8fe0894f39c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -116,7 +116,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
- private final Lazy<StatusBar> mStatusBarLazy;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -135,7 +135,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
* Injected constructor. See {@link NotificationsModule}.
*/
public NotificationGutsManager(Context context,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -153,7 +153,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
OnUserInteractionCallback onUserInteractionCallback,
ShadeController shadeController) {
mContext = context;
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mAccessibilityManager = accessibilityManager;
@@ -561,17 +561,22 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx
.setLeaveOpenOnKeyguardHide(true);
}
- Runnable r = () -> mMainHandler.post(
- () -> openGutsInternal(view, x, y, menuItem));
-
- mStatusBarLazy.get().executeRunnableDismissingKeyguard(
- r,
- null /* cancelAction */,
- false /* dismissShade */,
- true /* afterKeyguardGone */,
- true /* deferred */);
-
- return true;
+ Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (statusBarOptional.isPresent()) {
+ Runnable r = () -> mMainHandler.post(
+ () -> openGutsInternal(view, x, y, menuItem));
+ statusBarOptional.get().executeRunnableDismissingKeyguard(
+ r,
+ null /* cancelAction */,
+ false /* dismissShade */,
+ true /* afterKeyguardGone */,
+ true /* deferred */);
+ return true;
+ }
+ /**
+ * When {@link StatusBar} doesn't exist, falling through to call
+ * {@link #openGutsInternal(View,int,int,NotificationMenuRowPlugin.MenuItem)}.
+ */
}
}
return openGutsInternal(view, x, y, menuItem);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index dba3401cc28e..9c755e970a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -85,23 +85,26 @@ public abstract class StackScrollerDecorView extends ExpandableView {
}
/**
- * Set the content of this view to be visible in an animated way.
- *
- * @param contentVisible True if the content should be visible or false if it should be hidden.
+ * @param visible True if we should animate contents visible
*/
- public void setContentVisible(boolean contentVisible) {
- setContentVisible(contentVisible, true /* animate */);
+ public void setContentVisible(boolean visible) {
+ setContentVisible(visible, true /* animate */, null /* runAfter */);
}
+
/**
- * Set the content of this view to be visible.
- * @param contentVisible True if the content should be visible or false if it should be hidden.
- * @param animate Should an animation be performed.
+ * @param visible True if the contents should be visible
+ * @param animate True if we should fade to new visibility
+ * @param runAfter Runnable to run after visibility updates
*/
- private void setContentVisible(boolean contentVisible, boolean animate) {
- if (mContentVisible != contentVisible) {
+ public void setContentVisible(boolean visible, boolean animate, Runnable runAfter) {
+ if (mContentVisible != visible) {
mContentAnimating = animate;
- mContentVisible = contentVisible;
- setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
+ mContentVisible = visible;
+ Runnable endRunnable = runAfter == null ? mContentVisibilityEndRunnable : () -> {
+ mContentVisibilityEndRunnable.run();
+ runAfter.run();
+ };
+ setViewVisible(mContent, visible, animate, endRunnable);
}
if (!mContentAnimating) {
@@ -113,6 +116,10 @@ public abstract class StackScrollerDecorView extends ExpandableView {
return mContentVisible;
}
+ public void setVisible(boolean nowVisible, boolean animate) {
+ setVisible(nowVisible, animate, null);
+ }
+
/**
* Make this view visible. If {@code false} is passed, the view will fade out it's content
* and set the view Visibility to GONE. If only the content should be changed
@@ -121,7 +128,7 @@ public abstract class StackScrollerDecorView extends ExpandableView {
* @param nowVisible should the view be visible
* @param animate should the change be animated.
*/
- public void setVisible(boolean nowVisible, boolean animate) {
+ public void setVisible(boolean nowVisible, boolean animate, Runnable runAfter) {
if (mIsVisible != nowVisible) {
mIsVisible = nowVisible;
if (animate) {
@@ -132,10 +139,10 @@ public abstract class StackScrollerDecorView extends ExpandableView {
} else {
setWillBeGone(true);
}
- setContentVisible(nowVisible, true /* animate */);
+ setContentVisible(nowVisible, true /* animate */, runAfter);
} else {
setVisibility(nowVisible ? VISIBLE : GONE);
- setContentVisible(nowVisible, false /* animate */);
+ setContentVisible(nowVisible, false /* animate */, runAfter);
setWillBeGone(false);
notifyHeightChanged(false /* needsAnimation */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 41c88cc0a74a..45fd5efd00f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,7 +72,6 @@ public class AmbientState {
private boolean mPanelFullWidth;
private boolean mPulsing;
private boolean mUnlockHintRunning;
- private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
private float mHideAmount;
private boolean mAppearing;
@@ -494,14 +493,6 @@ public class AmbientState {
return mUnlockHintRunning;
}
- public boolean isQsCustomizerShowing() {
- return mQsCustomizerShowing;
- }
-
- public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
- mQsCustomizerShowing = qsCustomizerShowing;
- }
-
public void setIntrinsicPadding(int intrinsicPadding) {
mIntrinsicPadding = intrinsicPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 23aefd9bfd8e..135b7df4db9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -24,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import java.util.HashSet;
@@ -40,27 +39,25 @@ public class NotificationRoundnessManager {
private final ExpandableView[] mLastInSectionViews;
private final ExpandableView[] mTmpFirstInSectionViews;
private final ExpandableView[] mTmpLastInSectionViews;
- private final KeyguardBypassController mBypassController;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
+ private boolean mRoundForPulsingViews;
+ private boolean mIsDismissAllInProgress;
private ExpandableView mSwipedView = null;
private ExpandableView mViewBeforeSwipedView = null;
private ExpandableView mViewAfterSwipedView = null;
@Inject
- NotificationRoundnessManager(
- KeyguardBypassController keyguardBypassController,
- NotificationSectionsFeatureManager sectionsFeatureManager) {
+ NotificationRoundnessManager(NotificationSectionsFeatureManager sectionsFeatureManager) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
mTmpFirstInSectionViews = new ExpandableView[numberOfSections];
mTmpLastInSectionViews = new ExpandableView[numberOfSections];
- mBypassController = keyguardBypassController;
}
public void updateView(ExpandableView view, boolean animate) {
@@ -85,8 +82,8 @@ public class NotificationRoundnessManager {
return false;
}
- final float topRoundness = getRoundness(view, true /* top */);
- final float bottomRoundness = getRoundness(view, false /* top */);
+ final float topRoundness = getRoundnessFraction(view, true /* top */);
+ final float bottomRoundness = getRoundnessFraction(view, false /* top */);
final boolean topChanged = view.setTopRoundness(topRoundness, animate);
final boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);
@@ -131,7 +128,7 @@ public class NotificationRoundnessManager {
ExpandableView oldViewBefore = mViewBeforeSwipedView;
mViewBeforeSwipedView = viewBefore;
if (oldViewBefore != null) {
- final float bottomRoundness = getRoundness(oldViewBefore, false /* top */);
+ final float bottomRoundness = getRoundnessFraction(oldViewBefore, false /* top */);
oldViewBefore.setBottomRoundness(bottomRoundness, animate);
}
if (viewBefore != null) {
@@ -141,8 +138,8 @@ public class NotificationRoundnessManager {
ExpandableView oldSwipedview = mSwipedView;
mSwipedView = viewSwiped;
if (oldSwipedview != null) {
- final float bottomRoundness = getRoundness(oldSwipedview, false /* top */);
- final float topRoundness = getRoundness(oldSwipedview, true /* top */);
+ final float bottomRoundness = getRoundnessFraction(oldSwipedview, false /* top */);
+ final float topRoundness = getRoundnessFraction(oldSwipedview, true /* top */);
oldSwipedview.setTopRoundness(topRoundness, animate);
oldSwipedview.setBottomRoundness(bottomRoundness, animate);
}
@@ -154,7 +151,7 @@ public class NotificationRoundnessManager {
ExpandableView oldViewAfter = mViewAfterSwipedView;
mViewAfterSwipedView = viewAfter;
if (oldViewAfter != null) {
- final float topRoundness = getRoundness(oldViewAfter, true /* top */);
+ final float topRoundness = getRoundnessFraction(oldViewAfter, true /* top */);
oldViewAfter.setTopRoundness(topRoundness, animate);
}
if (viewAfter != null) {
@@ -162,7 +159,11 @@ public class NotificationRoundnessManager {
}
}
- private float getRoundness(ExpandableView view, boolean top) {
+ void setDismissAllInProgress(boolean isClearingAll) {
+ mIsDismissAllInProgress = isClearingAll;
+ }
+
+ private float getRoundnessFraction(ExpandableView view, boolean top) {
if (view == null) {
return 0f;
}
@@ -171,6 +172,11 @@ public class NotificationRoundnessManager {
|| view == mViewAfterSwipedView) {
return 1f;
}
+ if (view instanceof ExpandableNotificationRow
+ && ((ExpandableNotificationRow) view).canViewBeDismissed()
+ && mIsDismissAllInProgress) {
+ return 1.0f;
+ }
if ((view.isPinned()
|| (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
return 1.0f;
@@ -186,7 +192,7 @@ public class NotificationRoundnessManager {
// rounded.
return MathUtils.saturate(1.0f - mAppearFraction);
}
- if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
+ if (view.showingPulsing() && mRoundForPulsingViews) {
return 1.0f;
}
final Resources resources = view.getResources();
@@ -289,4 +295,8 @@ public class NotificationRoundnessManager {
updateView(previous, true /* animate */);
}
}
+
+ public void setShouldRoundPulsingViews(boolean shouldRoundPulsingViews) {
+ mRoundForPulsingViews = shouldRoundPulsingViews;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 289c32f17b31..3d72ae73378d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -81,15 +81,11 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
@@ -112,7 +108,6 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
-import com.android.systemui.util.leak.RotationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -144,6 +139,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
"persist.debug.nssl.dismiss", false /* default */);
+ // Delay in milli-seconds before shade closes for clear all.
+ private final int DELAY_BEFORE_SHADE_CLOSE = 200;
+ private boolean mShadeNeedsToClose = false;
+
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
@@ -156,7 +155,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* gap is drawn between them). In this case we don't want to round their corners.
*/
private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
- private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
+ private boolean mKeyguardBypassEnabled;
private ExpandHelper mExpandHelper;
private NotificationSwipeHelper mSwipeHelper;
@@ -213,7 +212,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private GroupMembershipManager mGroupMembershipManager;
private GroupExpansionManager mGroupExpansionManager;
- private NotificationActivityStarter mNotificationActivityStarter;
private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
@@ -259,7 +257,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
- private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
private boolean mFlingAfterUpEvent;
@@ -413,6 +410,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private boolean mBackwardScrollable;
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
+ private float mKeyguardBottomPadding = -1;
private int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
@@ -444,7 +442,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private final Rect mTmpRect = new Rect();
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
- private NotificationRemoteInputManager mRemoteInputManager;
private ShadeController mShadeController;
private Consumer<Boolean> mOnStackYChanged;
@@ -459,6 +456,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private float mLastSentExpandedHeight;
private boolean mWillExpand;
private int mGapHeight;
+ private boolean mIsRemoteInputActive;
/**
* The extra inset during the full shade transition
@@ -530,7 +528,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
- private final FeatureFlags mFeatureFlags;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mShouldUseSplitNotificationShade;
@@ -565,6 +562,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
};
+ @Nullable
+ private OnClickListener mManageButtonClickListener;
+
@Inject
public NotificationStackScrollLayout(
@Named(VIEW_CONTEXT) Context context,
@@ -573,12 +573,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
AmbientState ambientState,
- FeatureFlags featureFlags,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
super(context, attrs, 0, 0);
Resources res = getResources();
mSectionsManager = notificationSectionsManager;
- mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
updateSplitNotificationShade();
mSectionsManager.initialize(this, LayoutInflater.from(context));
@@ -651,12 +649,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
/**
+ * Sets whether keyguard bypass is enabled. If true, this layout will be rendered in bypass
+ * mode when it is on the keyguard.
+ */
+ public void setKeyguardBypassEnabled(boolean isEnabled) {
+ mKeyguardBypassEnabled = isEnabled;
+ }
+
+ /**
* @return the height at which we will wake up when pulsing
*/
public float getWakeUpHeight() {
ExpandableView firstChild = getFirstChildWithBackground();
if (firstChild != null) {
- if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+ if (mKeyguardBypassEnabled) {
return firstChild.getHeadsUpHeightWithoutHeader();
} else {
return firstChild.getCollapsedHeight();
@@ -672,6 +678,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSectionsManager.reinflateViews(LayoutInflater.from(mContext));
}
+ public void setIsRemoteInputActive(boolean isActive) {
+ mIsRemoteInputActive = isActive;
+ }
+
@VisibleForTesting
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooter() {
@@ -681,11 +691,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// TODO: move this logic to controller, which will invoke updateFooterView directly
boolean showDismissView = mClearAllEnabled &&
mController.hasActiveClearableNotifications(ROWS_ALL);
- RemoteInputController remoteInputController = mRemoteInputManager.getController();
boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0)
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
- && (remoteInputController == null || !remoteInputController.isRemoteInputActive());
+ && !mIsRemoteInputActive;
boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
@@ -737,6 +746,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mDebugPaint.setColor(Color.YELLOW);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ y = (int) mMaxLayoutHeight;
+ mDebugPaint.setColor(Color.MAGENTA);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+ if (mKeyguardBottomPadding >= 0) {
+ y = getHeight() - (int) mKeyguardBottomPadding;
+ mDebugPaint.setColor(Color.GRAY);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ }
+
y = getHeight() - getEmptyBottomMargin();
mDebugPaint.setColor(Color.GREEN);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -784,7 +803,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
boolean shouldDrawBackground;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabled && onKeyguard()) {
shouldDrawBackground = isPulseExpanding();
} else {
shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild;
@@ -899,15 +918,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void reinitView() {
- initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper);
+ initView(getContext(), mSwipeHelper);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- void initView(Context context,
- KeyguardBypassEnabledProvider keyguardBypassEnabledProvider,
- NotificationSwipeHelper swipeHelper) {
+ void initView(Context context, NotificationSwipeHelper swipeHelper) {
mScroller = new OverScroller(getContext());
- mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider;
mSwipeHelper = swipeHelper;
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -949,7 +965,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return;
}
// Portrait is easy, just use the dimen for paddings
- if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
mSidePaddings = mMinimumPaddings;
return;
}
@@ -1192,7 +1208,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void clampScrollPosition() {
int scrollRange = getScrollRange();
- if (scrollRange < mOwnScrollY) {
+ if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
boolean animateStackY = false;
if (scrollRange < getScrollAmountToScrollBoundary()
&& mAnimateStackYForContentHeightChange) {
@@ -1347,7 +1363,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private void notifyAppearChangedListeners() {
float appear;
float expandAmount;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) {
+ if (mKeyguardBypassEnabled && onKeyguard()) {
appear = calculateAppearFractionBypass();
expandAmount = getPulseHeight();
} else {
@@ -1708,6 +1724,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
+ if (child instanceof SectionHeaderView) {
+ ((StackScrollerDecorView) child).setContentVisible(
+ false /* visible */, true /* animate */, endRunnable);
+ return;
+ }
mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
true /* isDismissAll */);
}
@@ -2385,8 +2406,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
minTopPosition = firstVisibleSection.getBounds().top;
}
boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
- && (mAmbientState.isDozing()
- || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
+ && (mAmbientState.isDozing() || (mKeyguardBypassEnabled && onKeyguard));
for (NotificationSection section : mSections) {
int minBottomPosition = minTopPosition;
if (section == lastSection) {
@@ -2529,7 +2549,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} else {
mTopPaddingOverflow = 0;
}
- setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled());
+ setTopPadding(topPadding, animate && !mKeyguardBypassEnabled);
setExpandedHeight(mExpandedHeight);
}
@@ -3093,7 +3113,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
boolean performDisappearAnimation = !mIsExpanded
// Only animate if we still have pinned heads up, otherwise we just have the
// regular collapse animation of the lock screen
- || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
+ || (mKeyguardBypassEnabled && onKeyguard()
&& mInHeadsUpPinnedMode);
if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
@@ -4050,6 +4070,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
runAnimationFinishedRunnables();
clearTransient();
clearHeadsUpDisappearRunning();
+
+ if (mAmbientState.isDismissAllInProgress()) {
+ setDismissAllInProgress(false);
+ if (mShadeNeedsToClose) {
+ mShadeNeedsToClose = false;
+ postDelayed(
+ () -> {
+ mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ },
+ DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
+ }
+ }
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4231,7 +4263,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final @ColorInt int textColor =
Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
mSectionsManager.setHeaderForegroundColor(textColor);
- mFooterView.setTextColor(textColor);
+ mFooterView.updateColors();
mEmptyShadeView.setTextColor(textColor);
}
@@ -4316,7 +4348,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
// Since we are clipping to the outline we need to make sure that the shadows aren't
// clipped when pulsing
float ownTranslationZ = 0;
- if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) {
+ if (mKeyguardBypassEnabled && mAmbientState.isHiddenAtAll()) {
ExpandableView firstChildNotGone = getFirstChildNotGone();
if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) {
ownTranslationZ = firstChildNotGone.getTranslationZ();
@@ -4358,6 +4390,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
return -1;
}
+ /**
+ * Returns whether or not a History button is shown in the footer. If there is no footer, then
+ * this will return false.
+ **/
+ public boolean isHistoryShown() {
+ return mFooterView != null && mFooterView.isHistoryShown();
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void setFooterView(@NonNull FooterView footerView) {
int index = -1;
@@ -4367,6 +4407,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
mFooterView = footerView;
addView(mFooterView, index);
+ if (mManageButtonClickListener != null) {
+ mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+ }
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -4407,6 +4450,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public void setDismissAllInProgress(boolean dismissAllInProgress) {
mDismissAllInProgress = dismissAllInProgress;
mAmbientState.setDismissAllInProgress(dismissAllInProgress);
+ mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
handleDismissAllClipping();
}
@@ -4772,6 +4816,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mKeyguardBottomPadding = keyguardBottomPadding;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly;
@@ -4854,25 +4908,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setQsCustomizerShowing(boolean isShowing) {
- mAmbientState.setQsCustomizerShowing(isShowing);
- requestChildrenUpdate();
- }
-
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ pw.println(String.format("[%s: pulsing=%s visibility=%s"
+ " alpha=%f scrollY:%d maxTopPadding=%d showShelfOnly=%s"
+ " qsExpandFraction=%f"
+ " hideAmount=%f]",
this.getClass().getSimpleName(),
mPulsing ? "T" : "f",
- mAmbientState.isQsCustomizerShowing() ? "T" : "f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
: "invisible",
@@ -4947,132 +4994,143 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mHeadsUpAppearanceController = headsUpAppearanceController;
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- @VisibleForTesting
- void clearNotifications(@SelectedRows int selection, boolean closeShade) {
- // animate-swipe all dismissable notifications, then animate the shade closed
- int numChildren = getChildCount();
+ private boolean isVisible(View child) {
+ boolean hasClipBounds = child.getClipBounds(mTmpRect);
+ return child.getVisibility() == View.VISIBLE
+ && (!hasClipBounds || mTmpRect.height() > 0);
+ }
- final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
- final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
- for (int i = 0; i < numChildren; i++) {
- final View child = getChildAt(i);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- boolean parentVisible = false;
- boolean hasClipBounds = child.getClipBounds(mTmpRect);
- if (includeChildInDismissAll(row, selection)) {
- viewsToRemove.add(row);
- if (child.getVisibility() == View.VISIBLE
- && (!hasClipBounds || mTmpRect.height() > 0)) {
- viewsToHide.add(child);
- parentVisible = true;
- }
- } else if (child.getVisibility() == View.VISIBLE
- && (!hasClipBounds || mTmpRect.height() > 0)) {
- parentVisible = true;
- }
- List<ExpandableNotificationRow> children = row.getAttachedChildren();
- if (children != null) {
- for (ExpandableNotificationRow childRow : children) {
- if (includeChildInDismissAll(row, selection)) {
- viewsToRemove.add(childRow);
- if (parentVisible && row.areChildrenExpanded()) {
- hasClipBounds = childRow.getClipBounds(mTmpRect);
- if (childRow.getVisibility() == View.VISIBLE
- && (!hasClipBounds || mTmpRect.height() > 0)) {
- viewsToHide.add(childRow);
- }
- }
- }
- }
- }
+ private boolean shouldHideParent(View view, @SelectedRows int selection) {
+ final boolean silentSectionWillBeGone =
+ !mController.hasNotifications(ROWS_GENTLE, false /* clearable */);
+
+ // The only SectionHeaderView we have is the silent section header.
+ if (view instanceof SectionHeaderView && silentSectionWillBeGone) {
+ return true;
+ }
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ if (isVisible(row) && includeChildInDismissAll(row, selection)) {
+ return true;
}
}
+ return false;
+ }
- if (mDismissListener != null) {
- mDismissListener.onDismiss(selection);
- }
+ private boolean isChildrenVisible(ExpandableNotificationRow parent) {
+ List<ExpandableNotificationRow> children = parent.getAttachedChildren();
+ return isVisible(parent)
+ && children != null
+ && parent.areChildrenExpanded();
+ }
- if (viewsToRemove.isEmpty()) {
- if (closeShade && mShadeController != null) {
- mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+ // Similar to #getRowsToDismissInBackend, but filters for visible views.
+ private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection) {
+ final int viewCount = getChildCount();
+ final ArrayList<View> viewsToHide = new ArrayList<>(viewCount);
+
+ for (int i = 0; i < viewCount; i++) {
+ final View view = getChildAt(i);
+
+ if (shouldHideParent(view, selection)) {
+ viewsToHide.add(view);
}
- return;
- }
+ if (view instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
- performDismissAllAnimations(
- viewsToHide,
- closeShade,
- () -> onDismissAllAnimationsEnd(viewsToRemove, selection));
+ if (isChildrenVisible(parent)) {
+ for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
+ if (isVisible(child) && includeChildInDismissAll(child, selection)) {
+ viewsToHide.add(child);
+ }
+ }
+ }
+ }
+ }
+ return viewsToHide;
}
- private boolean includeChildInDismissAll(
- ExpandableNotificationRow row,
+ private ArrayList<ExpandableNotificationRow> getRowsToDismissInBackend(
@SelectedRows int selection) {
- return canChildBeDismissed(row) && matchesSelection(row, selection);
+ final int childCount = getChildCount();
+ final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(childCount);
+
+ for (int i = 0; i < childCount; i++) {
+ final View view = getChildAt(i);
+ if (!(view instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+ if (includeChildInDismissAll(parent, selection)) {
+ viewsToRemove.add(parent);
+ }
+ List<ExpandableNotificationRow> children = parent.getAttachedChildren();
+ if (isVisible(parent) && children != null) {
+ for (ExpandableNotificationRow child : children) {
+ if (includeChildInDismissAll(parent, selection)) {
+ viewsToRemove.add(child);
+ }
+ }
+ }
+ }
+ return viewsToRemove;
}
/**
- * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
- * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
- * handler.
- *
- * @param hideAnimatedList List of rows to animated away. Should only be views that are
- * currently visible, or else the stagger will look funky.
- * @param closeShade Whether to close the shade after the stagger animation completes.
- * @param onAnimationComplete Called after the entire animation completes (including the shade
- * closing if appropriate). The rows must be dismissed for real here.
+ * Collects a list of visible rows, and animates them away in a staggered fashion as if they
+ * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void performDismissAllAnimations(
- final ArrayList<View> hideAnimatedList,
- final boolean closeShade,
- final Runnable onAnimationComplete) {
-
- final Runnable onSlideAwayAnimationComplete = () -> {
- if (closeShade) {
- mShadeController.addPostCollapseAction(() -> {
- setDismissAllInProgress(false);
- onAnimationComplete.run();
- });
- mShadeController.animateCollapsePanels(
- CommandQueue.FLAG_EXCLUDE_NONE);
- } else {
- setDismissAllInProgress(false);
- onAnimationComplete.run();
- }
+ @VisibleForTesting
+ void clearNotifications(@SelectedRows int selection, boolean closeShade) {
+ // Animate-swipe all dismissable notifications, then animate the shade closed
+ final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
+ final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
+ getRowsToDismissInBackend(selection);
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(selection);
+ }
+ final Runnable dismissInBackend = () -> {
+ onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
};
-
- if (hideAnimatedList.isEmpty()) {
- onSlideAwayAnimationComplete.run();
+ if (viewsToAnimateAway.isEmpty()) {
+ dismissInBackend.run();
return;
}
-
- // let's disable our normal animations
+ // Disable normal animations
setDismissAllInProgress(true);
+ mShadeNeedsToClose = closeShade;
// Decrease the delay for every row we animate to give the sense of
// accelerating the swipes
- int rowDelayDecrement = 10;
- int currentDelay = 140;
- int totalDelay = 180;
- int numItems = hideAnimatedList.size();
+ final int rowDelayDecrement = 5;
+ int currentDelay = 60;
+ int totalDelay = 0;
+ final int numItems = viewsToAnimateAway.size();
for (int i = numItems - 1; i >= 0; i--) {
- View view = hideAnimatedList.get(i);
+ View view = viewsToAnimateAway.get(i);
Runnable endRunnable = null;
if (i == 0) {
- endRunnable = onSlideAwayAnimationComplete;
+ endRunnable = dismissInBackend;
}
dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
- currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
+ currentDelay = Math.max(30, currentDelay - rowDelayDecrement);
totalDelay += currentDelay;
}
}
- public void setNotificationActivityStarter(
- NotificationActivityStarter notificationActivityStarter) {
- mNotificationActivityStarter = notificationActivityStarter;
+ private boolean includeChildInDismissAll(
+ ExpandableNotificationRow row,
+ @SelectedRows int selection) {
+ return canChildBeDismissed(row) && matchesSelection(row, selection);
+ }
+
+ /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
+ public void setManageButtonClickListener(@Nullable OnClickListener listener) {
+ mManageButtonClickListener = listener;
+ if (mFooterView != null) {
+ mFooterView.setManageButtonClickListener(mManageButtonClickListener);
+ }
}
@VisibleForTesting
@@ -5085,9 +5143,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mFooterDismissListener.onDismiss();
}
clearNotifications(ROWS_ALL, true /* closeShade */);
- });
- footerView.setManageButtonClickListener(v -> {
- mNotificationActivityStarter.startHistoryIntent(v, mFooterView.isHistoryShown());
+ footerView.setSecondaryVisible(false /* visible */, true /* animate */);
});
setFooterView(footerView);
}
@@ -5140,7 +5196,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
public float setPulseHeight(float height) {
float overflow;
mAmbientState.setPulseHeight(height);
- if (mKeyguardBypassEnabledProvider.getBypassEnabled()) {
+ if (mKeyguardBypassEnabled) {
notifyAppearChangedListeners();
overflow = Math.max(0, height - getIntrinsicPadding());
} else {
@@ -5342,10 +5398,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mFooterDismissListener = listener;
}
- public void setRemoteInputManager(NotificationRemoteInputManager remoteInputManager) {
- mRemoteInputManager = remoteInputManager;
- }
-
void setShadeController(ShadeController shadeController) {
mShadeController = shadeController;
}
@@ -5397,7 +5449,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void updateSplitNotificationShade() {
- boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ boolean split = shouldUseSplitNotificationShade(getResources());
if (split != mShouldUseSplitNotificationShade) {
mShouldUseSplitNotificationShade = split;
updateDismissBehavior();
@@ -5644,6 +5696,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mSwipeHelper.resetExposedMenuView(animate, force);
}
+ boolean isUsingSplitNotificationShade() {
+ return mShouldUseSplitNotificationShade;
+ }
+
static boolean matchesSelection(
ExpandableNotificationRow row,
@SelectedRows int selection) {
@@ -6068,10 +6124,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
/** Only rows where entry.isHighPriority() is false. */
public static final int ROWS_GENTLE = 2;
- interface KeyguardBypassEnabledProvider {
- boolean getBypassEnabled();
- }
-
interface DismissListener {
void onDismiss(@SelectedRows int selectedRows);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 9e4adce47e0c..f1ae3dad9d3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -48,6 +48,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.jank.InteractionJankMonitor;
@@ -66,13 +68,13 @@ import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -181,9 +183,14 @@ public class NotificationStackScrollLayoutController {
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private View mLongPressedView;
+
private final NotificationListContainerImpl mNotificationListContainer =
new NotificationListContainerImpl();
+ @Nullable
+ private NotificationActivityStarter mNotificationActivityStarter;
+
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
/**
@@ -487,6 +494,11 @@ public class NotificationStackScrollLayoutController {
}
@Override
+ public void onLongPressSent(View v) {
+ mLongPressedView = v;
+ }
+
+ @Override
public void onBeginDrag(View v) {
mFalsingCollector.onNotificationStartDismissing();
mView.onSwipeBegin(v);
@@ -677,9 +689,20 @@ public class NotificationStackScrollLayoutController {
NotificationPanelEvent.fromSelection(selection)));
mView.setFooterDismissListener(() ->
mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
- mView.setRemoteInputManager(mRemoteInputManager);
+ mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
+ mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
+ @Override
+ public void onRemoteInputActive(boolean active) {
+ mView.setIsRemoteInputActive(active);
+ }
+ });
mView.setShadeController(mShadeController);
+ mKeyguardBypassController.registerOnBypassStateChangedListener(
+ isEnabled -> mNotificationRoundnessManager.setShouldRoundPulsingViews(!isEnabled));
+ mNotificationRoundnessManager.setShouldRoundPulsingViews(
+ !mKeyguardBypassController.getBypassEnabled());
+
if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
mView.initializeForegroundServiceSection(
(ForegroundServiceDungeonView) mFgServicesSectionController.createView(
@@ -708,8 +731,15 @@ public class NotificationStackScrollLayoutController {
});
}
- mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
- mSwipeHelper);
+ mView.initView(mView.getContext(), mSwipeHelper);
+ mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
+ mKeyguardBypassController
+ .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
+ mView.setManageButtonClickListener(v -> {
+ if (mNotificationActivityStarter != null) {
+ mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
+ }
+ });
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
@@ -1112,16 +1142,25 @@ public class NotificationStackScrollLayoutController {
/**
* Update whether we should show the empty shade view (no notifications in the shade).
* If so, send the update to our view.
+ *
+ * When in split mode, notifications are always visible regardless of the state of the
+ * QuickSettings panel. That being the case, empty view is always shown if the other conditions
+ * are true.
*/
public void updateShowEmptyShadeView() {
mShowEmptyShadeView = mBarState != KEYGUARD
- && !mView.isQsExpanded()
+ && (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
&& mView.getVisibleNotificationCount() == 0;
+
mView.updateEmptyShadeView(
mShowEmptyShadeView,
mZenModeController.areNotificationsHiddenInShade());
}
+ public boolean areNotificationsHiddenInShade() {
+ return mZenModeController.areNotificationsHiddenInShade();
+ }
+
public boolean isShowingEmptyShadeView() {
return mShowEmptyShadeView;
}
@@ -1170,6 +1209,10 @@ public class NotificationStackScrollLayoutController {
* Return whether there are any clearable notifications
*/
public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
+ return hasNotifications(selection, true /* clearable */);
+ }
+
+ public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
if (mDynamicPrivacyController.isInLockedDownShade()) {
return false;
}
@@ -1180,8 +1223,11 @@ public class NotificationStackScrollLayoutController {
continue;
}
final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (row.canViewBeDismissed() &&
- NotificationStackScrollLayout.matchesSelection(row, selection)) {
+ final boolean matchClearable =
+ isClearable ? row.canViewBeDismissed() : !row.canViewBeDismissed();
+ final boolean inSection =
+ NotificationStackScrollLayout.matchesSelection(row, selection);
+ if (matchClearable && inSection) {
return true;
}
}
@@ -1195,6 +1241,16 @@ public class NotificationStackScrollLayoutController {
mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
}
+ /**
+ * This is used for debugging only; it will be used to draw the otherwise invisible line which
+ * NotificationPanelViewController treats as the bottom when calculating how many notifications
+ * appear on the keyguard.
+ * Setting a negative number will disable rendering this line.
+ */
+ public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+ mView.setKeyguardBottomPadding(keyguardBottomPadding);
+ }
+
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry,
@@ -1402,6 +1458,10 @@ public class NotificationStackScrollLayoutController {
return mDynamicPrivacyController.isInLockedDownShade();
}
+ public boolean isLongPressInProgress() {
+ return mLongPressedView != null;
+ }
+
/**
* Set the dimmed state for all of the notification views.
*/
@@ -1439,6 +1499,11 @@ public class NotificationStackScrollLayoutController {
mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
}
+ /** */
+ public void setWillExpand(boolean willExpand) {
+ mView.setWillExpand(willExpand);
+ }
+
/**
* Set a listener to when scrolling changes.
*/
@@ -1461,6 +1526,10 @@ public class NotificationStackScrollLayoutController {
mView.animateNextTopPaddingChange();
}
+ public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
+ mNotificationActivityStarter = activityStarter;
+ }
+
/**
* Enum for UiEvent logged from this class
*/
@@ -1532,7 +1601,8 @@ public class NotificationStackScrollLayoutController {
@Override
public void setNotificationActivityStarter(
NotificationActivityStarter notificationActivityStarter) {
- mView.setNotificationActivityStarter(notificationActivityStarter);
+ NotificationStackScrollLayoutController.this
+ .setNotificationActivityStarter(notificationActivityStarter);
}
@Override
@@ -1646,17 +1716,23 @@ public class NotificationStackScrollLayoutController {
mView.handleEmptySpaceClick(ev);
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+
+ boolean longPressWantsIt = false;
+ if (mLongPressedView != null) {
+ longPressWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+ }
boolean expandWantsIt = false;
- if (!mSwipeHelper.isSwiping()
+ if (mLongPressedView == null && !mSwipeHelper.isSwiping()
&& !mView.getOnlyScrollingInThisMotion() && guts == null) {
expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
}
boolean scrollWantsIt = false;
- if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
+ if (mLongPressedView == null && !mSwipeHelper.isSwiping()
+ && !mView.isExpandingNotification()) {
scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
}
boolean swipeWantsIt = false;
- if (!mView.isBeingDragged()
+ if (mLongPressedView == null && !mView.isBeingDragged()
&& !mView.isExpandingNotification()
&& !mView.getExpandedInThisMotion()
&& !mView.getOnlyScrollingInThisMotion()
@@ -1684,7 +1760,7 @@ public class NotificationStackScrollLayoutController {
InteractionJankMonitor.getInstance().begin(mView,
CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
- return swipeWantsIt || scrollWantsIt || expandWantsIt;
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt;
}
@Override
@@ -1693,11 +1769,15 @@ public class NotificationStackScrollLayoutController {
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
mView.handleEmptySpaceClick(ev);
+ boolean longPressWantsIt = false;
+ if (guts != null && mLongPressedView != null) {
+ longPressWantsIt = mSwipeHelper.onTouchEvent(ev);
+ }
boolean expandWantsIt = false;
boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
boolean expandingNotification = mView.isExpandingNotification();
- if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
- && guts == null) {
+ if (mLongPressedView == null && mView.getIsExpanded()
+ && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion && guts == null) {
ExpandHelper expandHelper = mView.getExpandHelper();
if (isCancelOrUp) {
expandHelper.onlyObserveMovements(false);
@@ -1711,12 +1791,12 @@ public class NotificationStackScrollLayoutController {
}
}
boolean scrollerWantsIt = false;
- if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
- && !mView.getDisallowScrollingInThisMotion()) {
+ if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
+ && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
scrollerWantsIt = mView.onScrollTouch(ev);
}
boolean horizontalSwipeWantsIt = false;
- if (!mView.isBeingDragged()
+ if (mLongPressedView == null && !mView.isBeingDragged()
&& !expandingNotification
&& !mView.getExpandedInThisMotion()
&& !onlyScrollingInThisMotion
@@ -1742,7 +1822,7 @@ public class NotificationStackScrollLayoutController {
mView.setCheckForLeaveBehind(true);
}
traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
- return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
+ return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || longPressWantsIt;
}
private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2c810c93b2ee..8be5de7ae56e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -366,6 +366,20 @@ public class StackScrollAlgorithm {
return stackHeight / stackEndHeight;
}
+ public boolean hasOngoingNotifs(StackScrollAlgorithmState algorithmState) {
+ for (int i = 0; i < algorithmState.visibleChildren.size(); i++) {
+ View child = algorithmState.visibleChildren.get(i);
+ if (!(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ if (!row.canViewBeDismissed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// TODO(b/172289889) polish shade open from HUN
/**
* Populates the {@link ExpandableViewState} for a single child.
@@ -430,7 +444,9 @@ public class StackScrollAlgorithm {
+ view.getIntrinsicHeight();
final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
((FooterView.FooterViewState) viewState).hideContent =
- isShelfShowing || noSpaceForFooter;
+ isShelfShowing || noSpaceForFooter
+ || (ambientState.isDismissAllInProgress()
+ && !hasOngoingNotifs(algorithmState));
}
} else {
if (view != ambientState.getTrackedHeadsUpRow()) {
@@ -467,7 +483,6 @@ public class StackScrollAlgorithm {
}
}
}
-
// Clip height of view right before shelf.
viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index ee12b4b2d728..e3a4bf0170fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -46,7 +46,7 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_WAKEUP = 500;
public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
- public static final int ANIMATION_DURATION_SWIPE = 260;
+ public static final int ANIMATION_DURATION_SWIPE = 200;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
@@ -433,6 +433,7 @@ public class StackStateAnimator {
if (row.isDismissed()) {
needsAnimation = false;
}
+
NotificationEntry entry = row.getEntry();
StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
@@ -442,7 +443,8 @@ public class StackStateAnimator {
if (icon.getParent() != null) {
icon.getLocationOnScreen(mTmpLocation);
float iconPosition = mTmpLocation[0] - icon.getTranslationX()
- + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+ + ViewState.getFinalTranslationX(icon)
+ + icon.getWidth() * 0.25f;
mHostLayout.getLocationOnScreen(mTmpLocation);
targetLocation = iconPosition - mTmpLocation[0];
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 6d12a1cf10f5..45348d9f5977 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -39,9 +39,11 @@ import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.OperatorNameView;
+import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -57,9 +59,12 @@ import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -85,10 +90,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private View mCenteredIconArea;
private int mDisabled1;
private int mDisabled2;
- private final StatusBar mStatusBarComponent;
+ private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
- private View mOperatorNameFrame;
private final CommandQueue mCommandQueue;
+ private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final StatusBarLocationPublisher mLocationPublisher;
@@ -111,6 +116,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
}
};
+ private OperatorNameViewController mOperatorNameViewController;
@Inject
public CollapsedStatusBarFragment(
@@ -123,8 +129,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
KeyguardStateController keyguardStateController,
NetworkController networkController,
StatusBarStateController statusBarStateController,
- StatusBar statusBarComponent,
- CommandQueue commandQueue
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ CommandQueue commandQueue,
+ OperatorNameViewController.Factory operatorNameViewControllerFactory
) {
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
@@ -135,8 +142,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mKeyguardStateController = keyguardStateController;
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
- mStatusBarComponent = statusBarComponent;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mCommandQueue = commandQueue;
+ mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
}
@Override
@@ -272,7 +280,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
protected int adjustDisableFlags(int state) {
- boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible();
+ boolean headsUpVisible = mStatusBarOptionalLazy.get()
+ .map(StatusBar::headsUpShouldBeVisible).orElse(false);
if (headsUpVisible) {
state |= DISABLE_CLOCK;
}
@@ -300,7 +309,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
// The shelf will be hidden when dozing with a custom clock, we must show notification
// icons in this occasion.
if (mStatusBarStateController.isDozing()
- && mStatusBarComponent.getPanelController().hasCustomClock()) {
+ && mStatusBarOptionalLazy.get().map(
+ sb -> sb.getPanelController().hasCustomClock()).orElse(false)) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
}
@@ -341,10 +351,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private boolean shouldHideNotificationIcons() {
- if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) {
+ final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ if (!mStatusBar.isClosed()
+ && statusBarOptional.map(
+ StatusBar::hideStatusBarIconsWhenExpanded).orElse(false)) {
return true;
}
- if (mStatusBarComponent.hideStatusBarIconsForBouncer()) {
+ if (statusBarOptional.map(StatusBar::hideStatusBarIconsForBouncer).orElse(false)) {
return true;
}
return false;
@@ -403,14 +416,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
public void hideOperatorName(boolean animate) {
- if (mOperatorNameFrame != null) {
- animateHide(mOperatorNameFrame, animate);
+ if (mOperatorNameViewController != null) {
+ animateHide(mOperatorNameViewController.getView(), animate);
}
}
public void showOperatorName(boolean animate) {
- if (mOperatorNameFrame != null) {
- animateShow(mOperatorNameFrame, animate);
+ if (mOperatorNameViewController != null) {
+ animateShow(mOperatorNameViewController.getView(), animate);
}
}
@@ -487,7 +500,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private void initOperatorName() {
if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
- mOperatorNameFrame = stub.inflate();
+ mOperatorNameViewController =
+ mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate());
+ mOperatorNameViewController.init();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index b4f8126042ce..908cd34d7cad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -29,9 +29,9 @@ import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.R;
import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
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 5a6db213d87f..84b8f52c23ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -33,7 +33,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
@@ -268,6 +268,13 @@ public class DozeParameters implements TunerService.Tunable,
}
/**
+ * Whether the brightness sensor uses the proximity sensor.
+ */
+ public boolean brightnessUsesProx() {
+ return mResources.getBoolean(R.bool.doze_brightness_uses_prox);
+ }
+
+ /**
* Callback to listen for DozeParameter changes.
*/
public void addCallback(Callback callback) {
@@ -303,6 +310,7 @@ public class DozeParameters implements TunerService.Tunable,
pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
pw.println(getSelectivelyRegisterSensorsUsingProx());
+ pw.print("brightnessUsesProx(): "); pw.println(brightnessUsesProx());
}
interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b2cf72aca864..21c3e5e0a8d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -116,11 +116,18 @@ public class DozeScrimController implements StateListener {
if (!mDozing || mPulseCallback != null) {
if (DEBUG) {
- Log.d(TAG, "Pulse supressed. Dozing: " + mDozeParameters + " had callback? "
+ Log.d(TAG, "Pulse suppressed. Dozing: " + mDozeParameters + " had callback? "
+ (mPulseCallback != null));
}
// Pulse suppressed.
callback.onPulseFinished();
+ if (!mDozing) {
+ mDozeLog.tracePulseDropped("device isn't dozing");
+ } else {
+ mDozeLog.tracePulseDropped("already has pulse callback mPulseCallback="
+ + mPulseCallback);
+ }
+
return;
}
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 0a4e59c0391e..a56b8acf0470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -80,6 +80,7 @@ import com.android.settingslib.Utils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.camera.CameraIntents;
@@ -1044,11 +1045,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
return;
}
+ ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v);
if (mHasCard) {
Intent intent = new Intent(mContext, WalletActivity.class)
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
+ mActivityStarter.startActivity(intent, true /* dismissShade */, animationController,
+ true /* showOverLockscreenWhenLocked */);
} else {
if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) {
Log.w(TAG, "Could not get intent of the wallet app.");
@@ -1056,10 +1059,14 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
mActivityStarter.postStartActivityDismissingKeyguard(
mQuickAccessWalletController.getWalletClient().createWalletIntent(),
- /* delay= */ 0);
+ /* delay= */ 0, animationController);
}
}
+ protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) {
+ return ActivityLaunchAnimator.Controller.fromView(view, null);
+ }
+
private void onControlsClick(View v) {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 3a4a819bc623..c0600b6d02f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -43,8 +43,13 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
@BypassOverride private val bypassOverride: Int
private var hasFaceFeature: Boolean
private var pendingUnlock: PendingUnlock? = null
+ private val listeners = mutableListOf<OnBypassStateChangedListener>()
var userHasDeviceEntryIntent: Boolean = false // ie: attempted udfps auth
+ private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
+ override fun onFaceAuthEnabledChanged() = notifyListeners()
+ }
+
@IntDef(
FACE_UNLOCK_BYPASS_NO_OVERRIDE,
FACE_UNLOCK_BYPASS_ALWAYS,
@@ -84,7 +89,10 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
}
return enabled && mKeyguardStateController.isFaceAuthEnabled
}
- private set
+ private set(value) {
+ field = value
+ notifyListeners()
+ }
var bouncerShowing: Boolean = false
var altBouncerShowing: Boolean = false
@@ -141,6 +149,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
})
}
+ private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
+
/**
* Notify that the biometric unlock has happened.
*
@@ -225,6 +235,32 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr
pw.println(" userHasDeviceEntryIntent: $userHasDeviceEntryIntent")
}
+ /** Registers a listener for bypass state changes. */
+ fun registerOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+ val start = listeners.isEmpty()
+ listeners.add(listener)
+ if (start) {
+ mKeyguardStateController.addCallback(faceAuthEnabledChangedCallback)
+ }
+ }
+
+ /**
+ * Unregisters a listener for bypass state changes, previous registered with
+ * [registerOnBypassStateChangedListener]
+ */
+ fun unregisterOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
+ listeners.remove(listener)
+ if (listeners.isEmpty()) {
+ mKeyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
+ }
+ }
+
+ /** Listener for bypass state change events. */
+ interface OnBypassStateChangedListener {
+ /** Invoked when bypass becomes enabled or disabled. */
+ fun onBypassStateChanged(isEnabled: Boolean)
+ }
+
companion object {
const val BYPASS_FADE_DURATION = 67
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 f77c0520cdb1..19c258558a7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -33,23 +33,11 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
*/
public class KeyguardClockPositionAlgorithm {
/**
- * How much the clock height influences the shade position.
- * 0 means nothing, 1 means move the shade up by the height of the clock
- * 0.5f means move the shade up by half of the size of the clock.
- */
- private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
-
- /**
* Margin between the bottom of the status view and the notification shade.
*/
private int mStatusViewBottomMargin;
/**
- * Height of the parent view - display size in px.
- */
- private int mHeight;
-
- /**
* Height of {@link KeyguardStatusView}.
*/
private int mKeyguardStatusHeight;
@@ -68,21 +56,6 @@ public class KeyguardClockPositionAlgorithm {
private int mUserSwitchPreferredY;
/**
- * Whether or not there is a custom clock face on keyguard.
- */
- private boolean mHasCustomClock;
-
- /**
- * Whether or not the NSSL contains any visible notifications.
- */
- private boolean mHasVisibleNotifs;
-
- /**
- * Height of notification stack: Sum of height of each notification.
- */
- private int mNotificationStackHeight;
-
- /**
* Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
* avatar.
*/
@@ -148,6 +121,7 @@ public class KeyguardClockPositionAlgorithm {
private int mUnlockedStackScrollerPadding;
private boolean mIsSplitShade;
+ private int mSplitShadeSmartspaceHeight;
/**
* Refreshes the dimension values.
@@ -170,28 +144,25 @@ public class KeyguardClockPositionAlgorithm {
* Sets up algorithm values.
*/
public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
- int notificationStackHeight, float panelExpansion, int parentHeight,
- int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
- boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
+ float panelExpansion,
+ int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY, float dark,
float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
- float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
+ float qsExpansion, int cutoutTopInset, int splitShadeSmartspaceHeight,
+ boolean isSplitShade) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight);
mMaxShadeBottom = maxShadeBottom;
- mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
- mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
mUserSwitchHeight = userSwitchHeight;
mUserSwitchPreferredY = userSwitchPreferredY;
- mHasCustomClock = hasCustomClock;
- mHasVisibleNotifs = hasVisibleNotifs;
mDarkAmount = dark;
mOverStretchAmount = overStrechAmount;
mBypassEnabled = bypassEnabled;
mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
mQsExpansion = qsExpansion;
mCutoutTopInset = cutoutTopInset;
+ mSplitShadeSmartspaceHeight = splitShadeSmartspaceHeight;
mIsSplitShade = isSplitShade;
}
@@ -213,7 +184,7 @@ public class KeyguardClockPositionAlgorithm {
if (mBypassEnabled) {
return (int) (mUnlockedStackScrollerPadding + mOverStretchAmount);
} else if (mIsSplitShade) {
- return clockYPosition;
+ return clockYPosition + mSplitShadeSmartspaceHeight;
} else {
return clockYPosition + mKeyguardStatusHeight;
}
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 e272d2713e2a..df4bbcfc46bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,10 +18,7 @@ package com.android.systemui.statusbar.phone;
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
-import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
-import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Configuration;
@@ -45,36 +42,18 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.settingslib.Utils;
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
-import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-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.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
/**
* The header group on Keyguard.
*/
-public class KeyguardStatusBarView extends RelativeLayout implements
- BatteryStateChangeCallback,
- OnUserInfoChangedListener,
- ConfigurationListener,
- SystemStatusAnimationCallback {
+public class KeyguardStatusBarView extends RelativeLayout {
private static final int LAYOUT_NONE = 0;
private static final int LAYOUT_CUTOUT = 1;
@@ -84,30 +63,23 @@ public class KeyguardStatusBarView extends RelativeLayout implements
private boolean mShowPercentAvailable;
private boolean mBatteryCharging;
- private boolean mBatteryListening;
private TextView mCarrierLabel;
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
- private BatteryController mBatteryController;
private boolean mKeyguardUserSwitcherEnabled;
private final UserManager mUserManager;
private int mSystemIconsSwitcherHiddenExpandedMargin;
private int mSystemIconsBaseMargin;
private View mSystemIconsContainer;
- private TintedIconManager mIconManager;
- private List<String> mBlockedIcons = new ArrayList<>();
private View mCutoutSpace;
private ViewGroup mStatusIconArea;
private int mLayoutState = LAYOUT_NONE;
- private SystemStatusAnimationScheduler mAnimationScheduler;
- private FeatureFlags mFeatureFlags;
-
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
@@ -141,10 +113,6 @@ public class KeyguardStatusBarView extends RelativeLayout implements
mStatusIconContainer = findViewById(R.id.statusIcons);
loadDimens();
- loadBlockList();
- mBatteryController = Dependency.get(BatteryController.class);
- mAnimationScheduler = Dependency.get(SystemStatusAnimationScheduler.class);
- mFeatureFlags = Dependency.get(FeatureFlags.class);
}
@Override
@@ -190,7 +158,7 @@ public class KeyguardStatusBarView extends RelativeLayout implements
setLayoutParams(lp);
}
- private void loadDimens() {
+ void loadDimens() {
Resources res = getResources();
mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize(
R.dimen.system_icons_switcher_hidden_expanded_margin);
@@ -204,14 +172,6 @@ public class KeyguardStatusBarView extends RelativeLayout implements
R.dimen.rounded_corner_content_padding);
}
- // Set hidden status bar items
- private void loadBlockList() {
- Resources r = getResources();
- mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_volume));
- mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_alarm_clock));
- mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_call_strength));
- }
-
private void updateVisibilities() {
if (mMultiUserAvatar.getParent() != mStatusIconArea
&& !mKeyguardUserSwitcherEnabled) {
@@ -348,60 +308,20 @@ public class KeyguardStatusBarView extends RelativeLayout implements
return true;
}
- public void setListening(boolean listening) {
- if (listening == mBatteryListening) {
- return;
- }
- mBatteryListening = listening;
- if (mBatteryListening) {
- mBatteryController.addCallback(this);
- } else {
- mBatteryController.removeCallback(this);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- UserInfoController userInfoController = Dependency.get(UserInfoController.class);
- userInfoController.addCallback(this);
- userInfoController.reloadUserInfo();
- Dependency.get(ConfigurationController.class).addCallback(this);
- mIconManager = new TintedIconManager(findViewById(R.id.statusIcons), mFeatureFlags);
- mIconManager.setBlockList(mBlockedIcons);
- Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
- mAnimationScheduler.addCallback(this);
- onThemeChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(UserInfoController.class).removeCallback(this);
- Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
- Dependency.get(ConfigurationController.class).removeCallback(this);
- mAnimationScheduler.removeCallback(this);
- }
-
- @Override
- public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ void onUserInfoChanged(Drawable picture) {
mMultiUserAvatar.setImageDrawable(picture);
}
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ void onBatteryLevelChanged(boolean charging) {
if (mBatteryCharging != charging) {
mBatteryCharging = charging;
updateVisibilities();
}
}
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- // could not care less
- }
-
- public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+ void setKeyguardUserSwitcherEnabled(boolean enabled) {
mKeyguardUserSwitcherEnabled = enabled;
}
@@ -467,28 +387,20 @@ public class KeyguardStatusBarView extends RelativeLayout implements
return false;
}
- public void onThemeChanged() {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ void onThemeChanged(StatusBarIconController.TintedIconManager iconManager) {
mBatteryView.setColorsFromContext(mContext);
- updateIconsAndTextColors();
- // Reload user avatar
- ((UserInfoControllerImpl) Dependency.get(UserInfoController.class))
- .onDensityOrFontScaleChanged();
+ updateIconsAndTextColors(iconManager);
}
- @Override
- public void onDensityOrFontScaleChanged() {
- loadDimens();
- }
-
- @Override
- public void onOverlayChanged() {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ void onOverlayChanged() {
mCarrierLabel.setTextAppearance(
Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
- onThemeChanged();
mBatteryView.updatePercentView();
}
- private void updateIconsAndTextColors() {
+ private void updateIconsAndTextColors(StatusBarIconController.TintedIconManager iconManager) {
@ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
@ColorInt int iconColor = Utils.getColorStateListDefaultColor(mContext,
@@ -496,8 +408,8 @@ public class KeyguardStatusBarView extends RelativeLayout implements
R.color.light_mode_icon_color_single_tone);
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
- if (mIconManager != null) {
- mIconManager.setTint(iconColor);
+ if (iconManager != null) {
+ iconManager.setTint(iconColor);
}
applyDarkness(R.id.battery, mEmptyRect, intensity, iconColor);
@@ -522,10 +434,10 @@ public class KeyguardStatusBarView extends RelativeLayout implements
}
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ /** Should only be called from {@link KeyguardStatusBarViewController}. */
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("KeyguardStatusBarView:");
pw.println(" mBatteryCharging: " + mBatteryCharging);
- pw.println(" mBatteryListening: " + mBatteryListening);
pw.println(" mLayoutState: " + mLayoutState);
pw.println(" mKeyguardUserSwitcherEnabled: " + mKeyguardUserSwitcherEnabled);
if (mBatteryView != null) {
@@ -533,19 +445,16 @@ public class KeyguardStatusBarView extends RelativeLayout implements
}
}
- /** SystemStatusAnimationCallback */
- @Override
- public void onSystemChromeAnimationStart() {
- if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT) {
+ void onSystemChromeAnimationStart(boolean isAnimatingOut) {
+ if (isAnimatingOut) {
mSystemIconsContainer.setVisibility(View.VISIBLE);
mSystemIconsContainer.setAlpha(0f);
}
}
- @Override
- public void onSystemChromeAnimationEnd() {
+ void onSystemChromeAnimationEnd(boolean isAnimatingIn) {
// Make sure the system icons are out of the way
- if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) {
+ if (isAnimatingIn) {
mSystemIconsContainer.setVisibility(View.INVISIBLE);
mSystemIconsContainer.setAlpha(0f);
} else {
@@ -554,9 +463,8 @@ public class KeyguardStatusBarView extends RelativeLayout implements
}
}
- @Override
- public void onSystemChromeAnimationUpdate(ValueAnimator anim) {
- mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue());
+ void onSystemChromeAnimationUpdate(float animatedValue) {
+ mSystemIconsContainer.setAlpha(animatedValue);
}
@Override
@@ -567,8 +475,10 @@ public class KeyguardStatusBarView extends RelativeLayout implements
/**
* Set the clipping on the top of the view.
+ *
+ * Should only be called from {@link KeyguardStatusBarViewController}.
*/
- public void setTopClipping(int topClipping) {
+ void setTopClipping(int topClipping) {
if (topClipping != mTopClipping) {
mTopClipping = topClipping;
updateClipping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 377fb92ac6ba..ef3dcedf8315 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -16,33 +16,199 @@
package com.android.systemui.statusbar.phone;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+
+import androidx.annotation.NonNull;
+
import com.android.keyguard.CarrierTextController;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.util.ViewController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
import javax.inject.Inject;
/** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
private final CarrierTextController mCarrierTextController;
+ private final ConfigurationController mConfigurationController;
+ private final SystemStatusAnimationScheduler mAnimationScheduler;
+ private final BatteryController mBatteryController;
+ private final UserInfoController mUserInfoController;
+ private final StatusBarIconController mStatusBarIconController;
+ private final StatusBarIconController.TintedIconManager.Factory mTintedIconManagerFactory;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+
+ private final ConfigurationController.ConfigurationListener mConfigurationListener =
+ new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mView.loadDimens();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ KeyguardStatusBarViewController.this.onThemeChanged();
+ mView.onOverlayChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ KeyguardStatusBarViewController.this.onThemeChanged();
+ }
+ };
+
+ private final SystemStatusAnimationCallback mAnimationCallback =
+ new SystemStatusAnimationCallback() {
+ @Override
+ public void onSystemChromeAnimationStart() {
+ mView.onSystemChromeAnimationStart(
+ mAnimationScheduler.getAnimationState() == ANIMATING_OUT);
+ }
+
+ @Override
+ public void onSystemChromeAnimationEnd() {
+ mView.onSystemChromeAnimationEnd(
+ mAnimationScheduler.getAnimationState() == ANIMATING_IN);
+ }
+
+ @Override
+ public void onSystemChromeAnimationUpdate(@NonNull ValueAnimator anim) {
+ mView.onSystemChromeAnimationUpdate((float) anim.getAnimatedValue());
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ mView.onBatteryLevelChanged(charging);
+ }
+ };
+
+ private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
+ (name, picture, userAccount) -> mView.onUserInfoChanged(picture);
+
+ private final List<String> mBlockedIcons;
+
+ private boolean mBatteryListening;
+ private StatusBarIconController.TintedIconManager mTintedIconManager;
@Inject
public KeyguardStatusBarViewController(
- KeyguardStatusBarView view, CarrierTextController carrierTextController) {
+ KeyguardStatusBarView view,
+ CarrierTextController carrierTextController,
+ ConfigurationController configurationController,
+ SystemStatusAnimationScheduler animationScheduler,
+ BatteryController batteryController,
+ UserInfoController userInfoController,
+ StatusBarIconController statusBarIconController,
+ StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory,
+ BatteryMeterViewController batteryMeterViewController) {
super(view);
mCarrierTextController = carrierTextController;
+ mConfigurationController = configurationController;
+ mAnimationScheduler = animationScheduler;
+ mBatteryController = batteryController;
+ mUserInfoController = userInfoController;
+ mStatusBarIconController = statusBarIconController;
+ mTintedIconManagerFactory = tintedIconManagerFactory;
+ mBatteryMeterViewController = batteryMeterViewController;
+
+ Resources r = getResources();
+ mBlockedIcons = Collections.unmodifiableList(Arrays.asList(
+ r.getString(com.android.internal.R.string.status_bar_volume),
+ r.getString(com.android.internal.R.string.status_bar_alarm_clock),
+ r.getString(com.android.internal.R.string.status_bar_call_strength)));
}
@Override
protected void onInit() {
super.onInit();
mCarrierTextController.init();
+ mBatteryMeterViewController.init();
}
@Override
protected void onViewAttached() {
+ mConfigurationController.addCallback(mConfigurationListener);
+ mAnimationScheduler.addCallback(mAnimationCallback);
+ mUserInfoController.addCallback(mOnUserInfoChangedListener);
+ if (mTintedIconManager == null) {
+ mTintedIconManager =
+ mTintedIconManagerFactory.create(mView.findViewById(R.id.statusIcons));
+ mTintedIconManager.setBlockList(mBlockedIcons);
+ mStatusBarIconController.addIconGroup(mTintedIconManager);
+ }
+ onThemeChanged();
}
@Override
protected void onViewDetached() {
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mAnimationScheduler.removeCallback(mAnimationCallback);
+ mUserInfoController.removeCallback(mOnUserInfoChangedListener);
+ if (mTintedIconManager != null) {
+ mStatusBarIconController.removeIconGroup(mTintedIconManager);
+ }
+ }
+
+ /** Should be called when the theme changes. */
+ public void onThemeChanged() {
+ mView.onThemeChanged(mTintedIconManager);
+ }
+
+ /** Sets whether user switcher is enabled. */
+ public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+ mView.setKeyguardUserSwitcherEnabled(enabled);
+ }
+
+ /** Sets whether this controller should listen to battery updates. */
+ public void setBatteryListening(boolean listening) {
+ if (listening == mBatteryListening) {
+ return;
+ }
+ mBatteryListening = listening;
+ if (mBatteryListening) {
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+ } else {
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+ }
+ }
+
+ /** Set the view to have no top clipping. */
+ public void setNoTopClipping() {
+ mView.setTopClipping(0);
+ }
+
+ /**
+ * Update the view's top clipping based on the value of notificationPanelTop and the view's
+ * current top.
+ *
+ * @param notificationPanelTop the current top of the notification panel view.
+ */
+ public void updateTopClipping(int notificationPanelTop) {
+ mView.setTopClipping(notificationPanelTop - mView.getTop());
+ }
+
+ /** */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("KeyguardStatusBarView:");
+ pw.println(" mBatteryListening: " + mBatteryListening);
+ mView.dump(fd, pw, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
index 7d134057ee76..3f3328172e12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightsOutNotifController.java
@@ -21,6 +21,7 @@ import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
+import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -149,7 +150,8 @@ public class LightsOutNotifController {
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities,
+ String packageName) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 094ebb9ef0a7..5f222afd77e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -24,7 +24,6 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.dagger.SysUISingleton;
@@ -88,10 +87,11 @@ public class LockscreenGestureLogger {
}
private ArrayMap<Integer, Integer> mLegacyMap;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final MetricsLogger mMetricsLogger;
@Inject
- public LockscreenGestureLogger() {
+ public LockscreenGestureLogger(MetricsLogger metricsLogger) {
+ mMetricsLogger = metricsLogger;
mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length);
for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) {
mLegacyMap.put(EventLogConstants.METRICS_GESTURE_TYPE_MAP[i], i);
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 15e0716f8c49..e1dac45f97de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -21,11 +21,16 @@ 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 androidx.constraintlayout.widget.ConstraintSet.TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static java.lang.Float.isNaN;
@@ -58,6 +63,8 @@ import android.os.SystemClock;
import android.os.UserManager;
import android.os.VibrationEffect;
import android.provider.Settings;
+import android.transition.ChangeBounds;
+import android.transition.TransitionManager;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -73,6 +80,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
@@ -97,12 +105,21 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.communal.CommunalHostView;
+import com.android.systemui.communal.CommunalHostViewController;
+import com.android.systemui.communal.CommunalSource;
+import com.android.systemui.communal.CommunalSourceMonitor;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.idle.IdleHostView;
+import com.android.systemui.idle.IdleHostViewController;
+import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -117,7 +134,6 @@ import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -132,6 +148,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -167,6 +184,7 @@ import com.android.wm.shell.animation.FlingAnimationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -223,6 +241,7 @@ public class NotificationPanelViewController extends PanelViewController {
private final HeightListener mHeightListener = new HeightListener();
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
private final SettingsChangeObserver mSettingsChangeObserver;
+ private final LockscreenSmartspaceController mLockscreenSmartspaceController;
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
@@ -308,17 +327,20 @@ public class NotificationPanelViewController extends PanelViewController {
private final PulseExpansionHandler mPulseExpansionHandler;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final CommunalSourceMonitor mCommunalSourceMonitor;
+ private final CommunalStateController mCommunalStateController;
private final ConversationNotificationManager mConversationNotificationManager;
private final AuthController mAuthController;
private final MediaHierarchyManager mMediaHierarchyManager;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final CommunalViewComponent.Factory mCommunalViewComponentFactory;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
+ private final IdleViewComponent.Factory mIdleViewComponentFactory;
private final QSDetailDisplayer mQSDetailDisplayer;
private final FragmentService mFragmentService;
- private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
private final PrivacyDotViewController mPrivacyDotViewController;
private final QuickAccessWalletController mQuickAccessWalletController;
@@ -330,8 +352,11 @@ public class NotificationPanelViewController extends PanelViewController {
private final int mMaxKeyguardNotifications;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController;
+ private final SplitShadeHeaderController mSplitShadeHeaderController;
private final RecordingController mRecordingController;
private boolean mShouldUseSplitNotificationShade;
+ // The bottom padding reserved for elements of the keyguard measuring notifications
+ private float mKeyguardNotificationBottomPadding;
// Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications;
@@ -340,13 +365,19 @@ public class NotificationPanelViewController extends PanelViewController {
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
- private KeyguardStatusBarViewController mKeyguarStatusBarViewController;
+ private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
private ViewGroup mBigClockContainer;
@VisibleForTesting QS mQs;
private FrameLayout mQsFrame;
+ @Nullable
+ private CommunalHostViewController mCommunalViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
+ @Nullable
+ private IdleHostViewController mIdleHostViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ private FrameLayout mSplitShadeSmartspaceContainer;
+
private boolean mAnimateNextPositionUpdate;
private float mQuickQsOffsetHeight;
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -355,6 +386,8 @@ public class NotificationPanelViewController extends PanelViewController {
private VelocityTracker mQsVelocityTracker;
private boolean mQsTracking;
+ private CommunalHostView mCommunalView;
+
/**
* If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
* the expansion for quick settings.
@@ -393,7 +426,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float mDownY;
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
- private int mSplitShadeNotificationsTopPadding;
+ private int mSplitShadeStatusBarHeight;
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
@@ -470,7 +503,6 @@ public class NotificationPanelViewController extends PanelViewController {
private float mLinearDarkAmount;
private boolean mPulsing;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mUserSetupComplete;
private int mQsNotificationTopPadding;
private boolean mHideIconsDuringLaunchAnimation = true;
@@ -502,6 +534,24 @@ public class NotificationPanelViewController extends PanelViewController {
mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
private final NotificationEntryManager mEntryManager;
+ private final CommunalSourceMonitor.Callback mCommunalSourceMonitorCallback =
+ new CommunalSourceMonitor.Callback() {
+ @Override
+ public void onSourceAvailable(WeakReference<CommunalSource> source) {
+ setCommunalSource(source);
+ }
+ };
+
+ private WeakReference<CommunalSource> mCommunalSource;
+
+ private final CommunalSource.Callback mCommunalSourceCallback =
+ new CommunalSource.Callback() {
+ @Override
+ public void onDisconnected() {
+ setCommunalSource(null /*source*/);
+ }
+ };
+
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager;
@@ -641,6 +691,8 @@ public class NotificationPanelViewController extends PanelViewController {
private KeyguardMediaController mKeyguardMediaController;
+ private boolean mStatusViewCentered = false;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -661,6 +713,32 @@ public class NotificationPanelViewController extends PanelViewController {
}
};
+ private final CommunalStateController.Callback mCommunalStateCallback =
+ new CommunalStateController.Callback() {
+ @Override
+ public void onCommunalViewShowingChanged() {
+ mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
+ mBarState,
+ mKeyguardStateController.isKeyguardFadingAway(),
+ mStatusBarStateController.goingToFullShade(),
+ mBarState);
+ if (mKeyguardUserSwitcherController != null) {
+ mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
+ mBarState,
+ mKeyguardStateController.isKeyguardFadingAway(),
+ mStatusBarStateController.goingToFullShade(),
+ mBarState);
+ }
+ if (mKeyguardQsUserSwitchController != null) {
+ mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
+ mBarState,
+ mKeyguardStateController.isKeyguardFadingAway(),
+ mStatusBarStateController.goingToFullShade(),
+ mBarState);
+ }
+ }
+ };
+
private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() {
@Override
public void onDoubleTapRequired() {
@@ -685,12 +763,14 @@ public class NotificationPanelViewController extends PanelViewController {
FalsingCollector falsingCollector,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager,
+ CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController, DozeLog dozeLog,
DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
LatencyTracker latencyTracker, PowerManager powerManager,
AccessibilityManager accessibilityManager, @DisplayId int displayId,
- KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ CommunalSourceMonitor communalSourceMonitor, MetricsLogger metricsLogger,
ActivityManager activityManager,
ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
@@ -704,6 +784,8 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
+ CommunalViewComponent.Factory communalViewComponentFactory,
+ IdleViewComponent.Factory idleViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
QSDetailDisplayer qsDetailDisplayer,
NotificationGroupManagerLegacy groupManager,
@@ -715,7 +797,6 @@ public class NotificationPanelViewController extends PanelViewController {
NotificationShadeDepthController notificationShadeDepthController,
AmbientState ambientState,
LockIconViewController lockIconViewController,
- FeatureFlags featureFlags,
KeyguardMediaController keyguardMediaController,
PrivacyDotViewController privacyDotViewController,
TapAgainViewController tapAgainViewController,
@@ -726,13 +807,24 @@ public class NotificationPanelViewController extends PanelViewController {
RecordingController recordingController,
@Main Executor uiExecutor,
SecureSettings secureSettings,
+ SplitShadeHeaderController splitShadeHeaderController,
+ LockscreenSmartspaceController lockscreenSmartspaceController,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ LockscreenGestureLogger lockscreenGestureLogger,
NotificationRemoteInputManager remoteInputManager,
ControlsComponent controlsComponent) {
- super(view, falsingManager, dozeLog, keyguardStateController,
- (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
- statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
- statusBarTouchableRegionManager, ambientState);
+ super(view,
+ falsingManager,
+ dozeLog,
+ keyguardStateController,
+ (SysuiStatusBarStateController) statusBarStateController,
+ vibratorHelper,
+ statusBarKeyguardViewManager,
+ latencyTracker,
+ flingAnimationUtilsBuilder.get(),
+ statusBarTouchableRegionManager,
+ lockscreenGestureLogger,
+ ambientState);
mView = view;
mVibratorHelper = vibratorHelper;
mKeyguardMediaController = keyguardMediaController;
@@ -748,19 +840,23 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
+ mCommunalStateController = communalStateController;
+ mCommunalViewComponentFactory = communalViewComponentFactory;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
+ mIdleViewComponentFactory = idleViewComponentFactory;
mDepthController = notificationShadeDepthController;
- mFeatureFlags = featureFlags;
mContentResolver = contentResolver;
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
mFragmentService = fragmentService;
mSettingsChangeObserver = new SettingsChangeObserver(handler);
+ mLockscreenSmartspaceController = lockscreenSmartspaceController;
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
+ Utils.shouldUseSplitNotificationShade(mResources);
mView.setWillNotDraw(!DEBUG);
+ mSplitShadeHeaderController = splitShadeHeaderController;
mLayoutInflater = layoutInflater;
mFalsingManager = falsingManager;
mFalsingCollector = falsingCollector;
@@ -790,6 +886,7 @@ public class NotificationPanelViewController extends PanelViewController {
mThemeResId = mView.getContext().getThemeResId();
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
+ mCommunalSourceMonitor = communalSourceMonitor;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
KeyguardStateController.Callback
keyguardMonitorCallback =
@@ -851,6 +948,10 @@ public class NotificationPanelViewController extends PanelViewController {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
+ mCommunalView = mView.findViewById(R.id.communal_host);
+ mSplitShadeSmartspaceContainer = mView.findViewById(R.id.split_shade_smartspace_container);
+ mLockscreenSmartspaceController.setSplitShadeContainer(mSplitShadeSmartspaceContainer);
+ mLockscreenSmartspaceController.onSplitShadeChanged(mShouldUseSplitNotificationShade);
UserAvatarView userAvatarView = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
@@ -865,11 +966,17 @@ public class NotificationPanelViewController extends PanelViewController {
}
}
+ mKeyguardStatusBarViewController =
+ mKeyguardStatusBarViewComponentFactory.build(mKeyguardStatusBar)
+ .getKeyguardStatusBarViewController();
+ mKeyguardStatusBarViewController.init();
+
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
userAvatarView,
- mKeyguardStatusBar,
- keyguardUserSwitcherView);
+ keyguardUserSwitcherView,
+ mCommunalView,
+ mView.findViewById(R.id.idle_host_view));
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
R.id.notification_stack_scroller);
@@ -960,19 +1067,26 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
UserAvatarView userAvatarView,
- KeyguardStatusBarView keyguardStatusBarView,
- KeyguardUserSwitcherView keyguardUserSwitcherView) {
+ KeyguardUserSwitcherView keyguardUserSwitcherView,
+ CommunalHostView communalView,
+ IdleHostView idleHostView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
- KeyguardStatusBarViewComponent statusBarViewComponent =
- mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView);
- mKeyguarStatusBarViewController =
- statusBarViewComponent.getKeyguardStatusBarViewController();
- mKeyguarStatusBarViewController.init();
+ IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
+ mIdleHostViewController = idleViewComponent.getIdleHostViewController();
+ mIdleHostViewController.init();
+
+ if (communalView != null) {
+ CommunalViewComponent communalViewComponent =
+ mCommunalViewComponentFactory.build(communalView);
+ mCommunalViewController =
+ communalViewComponent.getCommunalHostViewController();
+ mCommunalViewController.init();
+ }
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
@@ -991,16 +1105,16 @@ public class NotificationPanelViewController extends PanelViewController {
userSwitcherComponent.getKeyguardQsUserSwitchController();
mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
mKeyguardQsUserSwitchController.init();
- mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
} else if (keyguardUserSwitcherView != null) {
KeyguardUserSwitcherComponent userSwitcherComponent =
mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
mKeyguardUserSwitcherController =
userSwitcherComponent.getKeyguardUserSwitcherController();
mKeyguardUserSwitcherController.init();
- mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
} else {
- mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false);
+ mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
}
}
@@ -1020,20 +1134,26 @@ public class NotificationPanelViewController extends PanelViewController {
public void updateResources() {
mQuickQsOffsetHeight = mResources.getDimensionPixelSize(
com.android.internal.R.dimen.quick_qs_offset_height);
- mSplitShadeNotificationsTopPadding =
- mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
+ mSplitShadeStatusBarHeight =
+ mResources.getDimensionPixelSize(R.dimen.split_shade_header_height);
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
mShouldUseSplitNotificationShade =
- Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
+ Utils.shouldUseSplitNotificationShade(mResources);
mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
if (mQs != null) {
mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
}
+
+ int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
+ mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
+ mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
+
// To change the constraints at runtime, all children of the ConstraintLayout must have ids
ensureAllViewsHaveIds(mNotificationContainerParent);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mNotificationContainerParent);
+
if (mShouldUseSplitNotificationShade) {
// width = 0 to take up all available space within constraints
qsWidth = 0;
@@ -1042,16 +1162,19 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.connect(
R.id.notification_stack_scroller, START,
R.id.qs_edge_guideline, START);
- constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END);
} else {
constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
- constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END);
}
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
+ constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
+ constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
+ mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
+ updateKeyguardStatusViewAlignment(false /* animate */);
+ mLockscreenSmartspaceController.onSplitShadeChanged(mShouldUseSplitNotificationShade);
mKeyguardMediaController.refreshMediaPosition();
}
@@ -1125,7 +1248,8 @@ public class NotificationPanelViewController extends PanelViewController {
mBigClockContainer.removeAllViews();
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
- mKeyguardStatusBar, keyguardUserSwitcherView);
+ keyguardUserSwitcherView, mCommunalView,
+ mView.findViewById(R.id.idle_host_view));
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1141,10 +1265,6 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
- if (mKeyguardStatusBar != null) {
- mKeyguardStatusBar.onThemeChanged();
- }
-
mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
mBarState,
false,
@@ -1190,9 +1310,12 @@ public class NotificationPanelViewController extends PanelViewController {
if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(
+ mKeyguardNotificationBottomPadding);
} else {
// no max when not on the keyguard
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+ mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f);
}
}
@@ -1270,7 +1393,14 @@ public class NotificationPanelViewController extends PanelViewController {
updateClockAppearance();
}
if (!onKeyguard) {
- stackScrollerPadding = getUnlockedStackScrollerPadding();
+ if (mShouldUseSplitNotificationShade) {
+ // Quick settings are not on the top of the notifications
+ // when in split shade mode (they are on the left side),
+ // so we should not add a padding for them
+ stackScrollerPadding = 0;
+ } else {
+ stackScrollerPadding = getUnlockedStackScrollerPadding();
+ }
} else {
stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
}
@@ -1291,7 +1421,13 @@ public class NotificationPanelViewController extends PanelViewController {
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
- mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
+ if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
+ || (mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia())) {
+ mKeyguardStatusViewController.displayClock(SMALL);
+ } else {
+ mKeyguardStatusViewController.displayClock(LARGE);
+ }
+ updateKeyguardStatusViewAlignment(true /* animate */);
int userIconHeight = mKeyguardQsUserSwitchController != null
? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
float expandedFraction =
@@ -1302,16 +1438,15 @@ public class NotificationPanelViewController extends PanelViewController {
? 1.0f : mInterpolatedDarkAmount;
mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
totalHeight - bottomPadding,
- mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
expandedFraction,
- totalHeight,
mKeyguardStatusViewController.getLockscreenHeight(),
userIconHeight,
- userSwitcherPreferredY, hasCustomClock(),
- hasVisibleNotifications, darkamount, mOverStretchAmount,
+ userSwitcherPreferredY,
+ darkamount, mOverStretchAmount,
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayTopInset,
+ mSplitShadeSmartspaceContainer.getHeight(),
mShouldUseSplitNotificationShade);
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
@@ -1331,10 +1466,36 @@ public class NotificationPanelViewController extends PanelViewController {
mClockPositionResult.userSwitchY,
animateClock);
}
+ // no need to translate in X axis - horizontal position is determined by constraints
+ mLockscreenSmartspaceController
+ .shiftSplitShadeSmartspace(mClockPositionResult.clockY, animateClock);
updateNotificationTranslucency();
updateClock();
}
+ private void updateKeyguardStatusViewAlignment(boolean animate) {
+ boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ boolean hasCommunalSurface = mCommunalSource != null && mCommunalSource.get() != null;
+ boolean shouldBeCentered = !mShouldUseSplitNotificationShade
+ || (!hasVisibleNotifications && !hasCommunalSurface);
+ if (mStatusViewCentered != shouldBeCentered) {
+ mStatusViewCentered = shouldBeCentered;
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mNotificationContainerParent);
+ int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline;
+ constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
+ if (animate) {
+ ChangeBounds transition = new ChangeBounds();
+ transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition);
+ }
+
+ constraintSet.applyTo(mNotificationContainerParent);
+ }
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -1346,6 +1507,11 @@ public class NotificationPanelViewController extends PanelViewController {
* @return the maximum keyguard notifications that can fit on the screen
*/
private int computeMaxKeyguardNotifications() {
+ // Do not show any notifications on the keyguard if a communal source is set.
+ if (mCommunalSource != null && mCommunalSource.get() != null) {
+ return 0;
+ }
+
float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
int notificationPadding = Math.max(
1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
@@ -1362,6 +1528,7 @@ public class NotificationPanelViewController extends PanelViewController {
float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
bottomPadding = Math.max(lockIconPadding, bottomPadding);
+ mKeyguardNotificationBottomPadding = bottomPadding;
float availableSpace =
mNotificationStackScrollLayoutController.getHeight()
@@ -1477,6 +1644,7 @@ public class NotificationPanelViewController extends PanelViewController {
if (mKeyguardUserSwitcherController != null) {
mKeyguardUserSwitcherController.setAlpha(alpha);
}
+ mLockscreenSmartspaceController.setSplitShadeSmartspaceAlpha(alpha);
}
public void animateToFullShade(long delay) {
@@ -1558,7 +1726,7 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean isQsExpansionEnabled() {
return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient
- && !mRemoteInputManager.getController().isRemoteInputActive();
+ && !mRemoteInputManager.isRemoteInputActive();
}
public void expandWithQs() {
@@ -2246,6 +2414,10 @@ public class NotificationPanelViewController extends PanelViewController {
setQSClippingBounds();
mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
mDepthController.setQsPanelExpansion(qsExpansionFraction);
+
+ if (mCommunalViewController != null) {
+ mCommunalViewController.updateQsExpansion(qsExpansionFraction);
+ }
}
private void onStackYChanged(boolean shouldAnimate) {
@@ -2271,7 +2443,8 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateQSExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
- mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop;
+ mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
+ || (mAmbientState.getScrollY() <= scrollRangeToTop);
setQsExpansionEnabled();
}
@@ -2314,8 +2487,8 @@ public class NotificationPanelViewController extends PanelViewController {
left = 0;
right = getView().getRight() + mDisplayRightInset;
} else {
- top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
- bottom = mNotificationStackScrollLayoutController.getHeight();
+ top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
+ bottom = top + mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
@@ -2377,7 +2550,6 @@ public class NotificationPanelViewController extends PanelViewController {
boolean qsVisible) {
// Fancy clipping for quick settings
int radius = mScrimCornerRadius;
- int statusBarClipTop = 0;
boolean clipStatusView = false;
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
@@ -2386,7 +2558,6 @@ public class NotificationPanelViewController extends PanelViewController {
float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
- statusBarClipTop = top - mKeyguardStatusBar.getTop();
}
if (mQs != null) {
float qsTranslation = 0;
@@ -2424,12 +2595,17 @@ public class NotificationPanelViewController extends PanelViewController {
mScrimController.setNotificationsBounds(left, top, right, bottom);
}
+ if (mShouldUseSplitNotificationShade) {
+ mKeyguardStatusBarViewController.setNoTopClipping();
+ } else {
+ mKeyguardStatusBarViewController.updateTopClipping(top);
+ }
+
mScrimController.setScrimCornerRadius(radius);
- mKeyguardStatusBar.setTopClipping(statusBarClipTop);
int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
- int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+ int nsslBottom = bottom;
int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
mNotificationStackScrollLayoutController.setRoundedClippingBounds(
nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
@@ -2470,7 +2646,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float calculateNotificationsTopPadding() {
if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
- return mSplitShadeNotificationsTopPadding;
+ return 0;
}
if (mKeyguardShowing && (mQsExpandImmediate
|| mIsExpanding && mQsExpandedWhenExpandingStarted)) {
@@ -2580,6 +2756,10 @@ public class NotificationPanelViewController extends PanelViewController {
}
mTransitionToFullShadeQSPosition = position;
updateQsExpansion();
+
+ if (mCommunalViewController != null) {
+ mCommunalViewController.updateShadeExpansion(mTransitioningToFullShadeProgress);
+ }
}
/**
@@ -3096,7 +3276,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
private void setListening(boolean listening) {
- mKeyguardStatusBar.setListening(listening);
+ mKeyguardStatusBarViewController.setBatteryListening(listening);
if (mQs == null) return;
mQs.setListening(listening);
}
@@ -3657,6 +3837,7 @@ public class NotificationPanelViewController extends PanelViewController {
public void dozeTimeTick() {
mKeyguardBottomArea.dozeTimeTick();
mKeyguardStatusViewController.dozeTimeTick();
+ mLockscreenSmartspaceController.requestSmartspaceUpdate();
if (mInterpolatedDarkAmount > 0) {
positionClockAndNotifications();
}
@@ -3746,8 +3927,8 @@ public class NotificationPanelViewController extends PanelViewController {
+ " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
+ ") qsVisible(" + mQsVisible
);
- if (mKeyguardStatusBar != null) {
- mKeyguardStatusBar.dump(fd, pw, args);
+ if (mKeyguardStatusBarViewController != null) {
+ mKeyguardStatusBarViewController.dump(fd, pw, args);
}
}
@@ -3879,7 +4060,9 @@ public class NotificationPanelViewController extends PanelViewController {
if (mStatusBar.isBouncerShowing()) {
return true;
}
- if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+ if (mBar.panelEnabled()
+ && !mNotificationStackScrollLayoutController.isLongPressInProgress()
+ && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
return true;
@@ -3931,6 +4114,7 @@ public class NotificationPanelViewController extends PanelViewController {
return true;
}
if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
+ && !mNotificationStackScrollLayoutController.isLongPressInProgress()
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
}
@@ -4438,6 +4622,8 @@ public class NotificationPanelViewController extends PanelViewController {
maybeAnimateBottomAreaAlpha();
resetHorizontalPanelPosition();
updateQsState();
+ mSplitShadeHeaderController.setShadeExpanded(
+ mBarState == SHADE || mBarState == SHADE_LOCKED);
}
@Override
@@ -4465,6 +4651,27 @@ public class NotificationPanelViewController extends PanelViewController {
setExpandedFraction(1f);
}
+ private void setCommunalSource(WeakReference<CommunalSource> source) {
+ CommunalSource existingSource = mCommunalSource != null ? mCommunalSource.get() : null;
+
+ if (existingSource != null) {
+ existingSource.removeCallback(mCommunalSourceCallback);
+ mCommunalViewController.show(null /*source*/);
+ }
+
+ mCommunalSource = source;
+
+ CommunalSource currentSource = mCommunalSource != null ? mCommunalSource.get() : null;
+ // Set source and register callback
+ if (currentSource != null && mCommunalViewController != null) {
+ currentSource.addCallback(mCommunalSourceCallback);
+ mCommunalViewController.show(source);
+ }
+
+ updateKeyguardStatusViewAlignment(true /*animate*/);
+ updateMaxDisplayedNotifications(true /*recompute*/);
+ }
+
/**
* Sets the overstretch amount in raw pixels when dragging down.
*/
@@ -4483,6 +4690,7 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarStateController.addCallback(mStatusBarStateListener);
mConfigurationController.addCallback(mConfigurationListener);
mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+ mCommunalSourceMonitor.addCallback(mCommunalSourceMonitorCallback);
// Theme might have changed between inflating this view and attaching it to the
// window, so
// force a call to onThemeChanged
@@ -4490,6 +4698,7 @@ public class NotificationPanelViewController extends PanelViewController {
mFalsingManager.addTapListener(mFalsingTapListener);
mKeyguardIndicationController.init();
registerSettingsChangeListener();
+ mCommunalStateController.addCallback(mCommunalStateCallback);
}
@Override
@@ -4500,7 +4709,11 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+ mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback);
+ // Clear source when detached.
+ setCommunalSource(null /*source*/);
mFalsingManager.removeTapListener(mFalsingTapListener);
+ mCommunalStateController.removeCallback(mCommunalStateCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 246810a2d70b..030a8951943d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -84,8 +84,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private final WindowManager mWindowManager;
private final IActivityManager mActivityManager;
private final DozeParameters mDozeParameters;
+ private final KeyguardStateController mKeyguardStateController;
private final LayoutParams mLpChanged;
- private final boolean mKeyguardScreenRotation;
private final long mLockScreenDisplayTimeout;
private final float mKeyguardPreferredRefreshRate; // takes precedence over max
private final float mKeyguardMaxRefreshRate;
@@ -123,8 +123,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
mContext = context;
mWindowManager = windowManager;
mActivityManager = activityManager;
- mKeyguardScreenRotation = keyguardStateController.isKeyguardScreenRotationAllowed();
mDozeParameters = dozeParameters;
+ mKeyguardStateController = keyguardStateController;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
mKeyguardViewMediator = keyguardViewMediator;
@@ -323,7 +323,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private void adjustScreenOrientation(State state) {
if (state.isKeyguardShowingAndNotOccluded() || state.mDozing) {
- if (mKeyguardScreenRotation) {
+ if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
} else {
mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
@@ -473,7 +473,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
for (StatusBarWindowCallback cb : activeCallbacks) {
cb.onStateChanged(mCurrentState.mKeyguardShowing,
mCurrentState.mKeyguardOccluded,
- mCurrentState.mBouncerShowing);
+ mCurrentState.mBouncerShowing,
+ mCurrentState.mDozing);
}
}
@@ -605,12 +606,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
}
@Override
- public void setLightRevealScrimAmount(float amount) {
- boolean lightRevealScrimOpaque = amount == 0;
- if (mCurrentState.mLightRevealScrimOpaque == lightRevealScrimOpaque) {
+ public void setLightRevealScrimOpaque(boolean opaque) {
+ if (mCurrentState.mLightRevealScrimOpaque == opaque) {
return;
}
- mCurrentState.mLightRevealScrimOpaque = lightRevealScrimOpaque;
+ mCurrentState.mLightRevealScrimOpaque = opaque;
apply(mCurrentState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index b5d9bd67bd2d..e57e2005e3b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -311,6 +311,12 @@ public class NotificationShadeWindowViewController {
// Capture all touch events in always-on.
return true;
}
+
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+ // capture all touches if the alt auth bouncer is showing
+ return true;
+ }
+
boolean intercept = false;
if (mNotificationPanelViewController.isFullyExpanded()
&& mDragDownHelper.isDragDownEnabled()
@@ -338,6 +344,12 @@ public class NotificationShadeWindowViewController {
if (mStatusBarStateController.isDozing()) {
handled = !mService.isPulsing();
}
+
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+ // eat the touch
+ handled = true;
+ }
+
if ((mDragDownHelper.isDragDownEnabled() && !handled)
|| mDragDownHelper.isDraggingDown()) {
// we still want to finish our drag down gesture when locking the screen
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 ed8fb31aea32..68e28cdba975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -22,7 +22,6 @@ import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
@@ -31,7 +30,6 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
import java.util.Comparator;
@@ -42,8 +40,8 @@ import java.util.Comparator;
public class NotificationsQuickSettingsContainer extends ConstraintLayout
implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
- private FrameLayout mQsFrame;
- private NotificationStackScrollLayout mStackScroller;
+ private View mQsFrame;
+ private View mStackScroller;
private View mKeyguardStatusBar;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
@@ -52,10 +50,10 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private int mBottomPadding;
private int mStackScrollerMargin;
- private boolean mHasViewsAboveShelf;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
+ private boolean mSplitShadeEnabled;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -144,7 +142,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
public void setCustomizerShowing(boolean isShowing) {
mCustomizing = isShowing;
updateBottomMargin();
- mStackScroller.setQsCustomizerShowing(isShowing);
}
public void setDetailShowing(boolean isShowing) {
@@ -152,8 +149,19 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
updateBottomMargin();
}
+ /**
+ * Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
+ * customizer state
+ */
+ public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+ mSplitShadeEnabled = splitShadeEnabled;
+ // in case device was rotated while showing QS details/customizer
+ updateBottomMargin();
+ }
+
private void updateBottomMargin() {
- if (mCustomizing || mDetailShowing) {
+ // in split shade, QS state changes should not influence notifications panel
+ if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
// Clear out bottom paddings/margins so the qs customization can be full height.
setPadding(0, 0, 0, 0);
setBottomMargin(mStackScroller, 0);
@@ -171,7 +179,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
- mHasViewsAboveShelf = hasViewsAboveShelf;
invalidate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 323a1128d3bb..a3877b0eb5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -79,7 +79,6 @@ public abstract class PanelViewController {
protected long mDownTime;
protected boolean mTouchSlopExceededBeforeDown;
private float mMinExpandHeight;
- private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mPanelUpdateWhenAnimatorEnds;
private boolean mVibrateOnOpening;
protected boolean mIsLaunchAnimationRunning;
@@ -182,6 +181,7 @@ public abstract class PanelViewController {
protected final KeyguardStateController mKeyguardStateController;
protected final SysuiStatusBarStateController mStatusBarStateController;
protected final AmbientState mAmbientState;
+ protected final LockscreenGestureLogger mLockscreenGestureLogger;
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -217,10 +217,12 @@ public abstract class PanelViewController {
LatencyTracker latencyTracker,
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+ LockscreenGestureLogger lockscreenGestureLogger,
AmbientState ambientState) {
mAmbientState = ambientState;
mView = view;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mLockscreenGestureLogger = lockscreenGestureLogger;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1211,10 +1213,14 @@ public abstract class PanelViewController {
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
addMovement(event);
- if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+ final boolean openShadeWithoutHun =
+ mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown;
+ if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown
+ || openShadeWithoutHun) {
float hAbs = Math.abs(h);
float touchSlop = getTouchSlop(event);
- if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
+ if ((h < -touchSlop
+ || ((openShadeWithoutHun || mAnimatingOnDown) && hAbs > touchSlop))
&& hAbs > Math.abs(x - mInitialTouchX)) {
cancelHeightAnimator();
startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
@@ -1227,10 +1233,7 @@ public abstract class PanelViewController {
mVelocityTracker.clear();
break;
}
-
- // Finally, if none of the above cases applies, ensure that touches do not get handled
- // by the contents of a panel that is not showing (a bit of a hack to avoid b/178277858)
- return (mView.getVisibility() != View.VISIBLE);
+ return false;
}
@Override
@@ -1397,8 +1400,7 @@ public abstract class PanelViewController {
private void beginJankMonitoring(int cuj) {
InteractionJankMonitor.Configuration.Builder builder =
- new InteractionJankMonitor.Configuration.Builder(cuj)
- .setView(mView)
+ InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
.setTag(isFullyCollapsed() ? "Expand" : "Collapse");
InteractionJankMonitor.getInstance().begin(builder);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c300b11b9a34..70a46b25c1eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -27,6 +27,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.util.Log;
import android.util.Pair;
import android.view.DisplayCutout;
import android.view.Gravity;
@@ -42,7 +43,6 @@ import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.leak.RotationUtils;
import java.util.List;
@@ -52,7 +52,6 @@ public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
private static final boolean DEBUG = StatusBar.DEBUG;
private static final boolean DEBUG_GESTURES = false;
- private final CommandQueue mCommandQueue;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
StatusBar mBar;
@@ -81,6 +80,8 @@ public class PhoneStatusBarView extends PanelBar {
@Nullable
private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
+ private PanelEnabledProvider mPanelEnabledProvider;
+
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
*/
@@ -89,7 +90,6 @@ public class PhoneStatusBarView extends PanelBar {
public PhoneStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mCommandQueue = Dependency.get(CommandQueue.class);
mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
}
@@ -177,7 +177,11 @@ public class PhoneStatusBarView extends PanelBar {
@Override
public boolean panelEnabled() {
- return mCommandQueue.panelsEnabled();
+ if (mPanelEnabledProvider == null) {
+ Log.e(TAG, "panelEnabledProvider is null; defaulting to super class.");
+ return super.panelEnabled();
+ }
+ return mPanelEnabledProvider.panelEnabled();
}
@Override
@@ -294,6 +298,11 @@ public class PhoneStatusBarView extends PanelBar {
}
}
+ /** Set the {@link PanelEnabledProvider} to use. */
+ public void setPanelEnabledProvider(PanelEnabledProvider panelEnabledProvider) {
+ mPanelEnabledProvider = panelEnabledProvider;
+ }
+
private void updateScrimFraction() {
float scrimFraction = mPanelFraction;
if (mMinFraction < 1.0f) {
@@ -391,4 +400,10 @@ public class PhoneStatusBarView extends PanelBar {
protected boolean shouldPanelBeVisible() {
return mHeadsUpVisible || super.shouldPanelBeVisible();
}
+
+ /** An interface that will provide whether panel is enabled. */
+ interface PanelEnabledProvider {
+ /** Returns true if the panel is enabled and false otherwise. */
+ boolean panelEnabled();
+ }
}
diff --git a/services/uwb/java/com/android/server/uwb/UwbService.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
index 4bb280f75ed1..b36b67dc02c0 100644
--- a/services/uwb/java/com/android/server/uwb/UwbService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
@@ -14,29 +14,26 @@
* limitations under the License.
*/
-package com.android.server.uwb;
+package com.android.systemui.statusbar.phone;
-import android.content.Context;
-import android.util.Log;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.ViewController;
-import com.android.server.SystemService;
+/** Controller for {@link PhoneStatusBarView}. */
+public class PhoneStatusBarViewController extends ViewController<PhoneStatusBarView> {
-/**
- * Uwb System service.
- */
-public class UwbService extends SystemService {
- private static final String TAG = "UwbService";
-
- private final UwbServiceImpl mImpl;
+ protected PhoneStatusBarViewController(
+ PhoneStatusBarView view,
+ CommandQueue commandQueue) {
+ super(view);
+ mView.setPanelEnabledProvider(commandQueue::panelsEnabled);
+ }
- public UwbService(Context context) {
- super(context);
- mImpl = new UwbServiceImpl(context, new UwbInjector(context));
+ @Override
+ protected void onViewAttached() {
}
@Override
- public void onStart() {
- Log.i(TAG, "Registering " + Context.UWB_SERVICE);
- publishBinderService(Context.UWB_SERVICE, mImpl);
+ protected void onViewDetached() {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cfcea9684c3b..e3319e5ef200 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -111,6 +111,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
*/
private boolean mTransitioningToFullShade;
+ /**
+ * Is there currently an unocclusion animation running. Used to avoid bright flickers
+ * of the notification scrim.
+ */
+ private boolean mUnOcclusionAnimationRunning;
+
+ /**
+ * Set whether an unocclusion animation is currently running on the notification panel. Used
+ * to avoid bright flickers of the notification scrim.
+ */
+ public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) {
+ mUnOcclusionAnimationRunning = unocclusionAnimationRunning;
+ }
+
@IntDef(prefix = {"VISIBILITY_"}, value = {
TRANSPARENT,
SEMI_TRANSPARENT,
@@ -136,12 +150,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public static final float BUSY_SCRIM_ALPHA = 1f;
/**
- * The default scrim under the expanded bubble stack.
- * This should not be lower than 0.54, otherwise we won't pass GAR.
- */
- public static final float BUBBLE_SCRIM_ALPHA = 0.6f;
-
- /**
* Scrim opacity that can have text on top.
*/
public static final float GAR_SCRIM_ALPHA = 0.6f;
@@ -156,8 +164,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private ScrimView mScrimInFront;
private ScrimView mNotificationsScrim;
private ScrimView mScrimBehind;
- @Nullable
- private ScrimView mScrimForBubble;
private Runnable mScrimBehindChangeRunnable;
@@ -195,12 +201,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private float mInFrontAlpha = NOT_INITIALIZED;
private float mBehindAlpha = NOT_INITIALIZED;
private float mNotificationsAlpha = NOT_INITIALIZED;
- private float mBubbleAlpha = NOT_INITIALIZED;
private int mInFrontTint;
private int mBehindTint;
private int mNotificationsTint;
- private int mBubbleTint;
private boolean mWallpaperVisibilityTimedOut;
private int mScrimsVisibility;
@@ -229,7 +233,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
- ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(BUBBLE_SCRIM_ALPHA);
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -276,11 +279,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
* Attach the controller to the supplied views.
*/
public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim,
- ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
+ ScrimView scrimInFront) {
mNotificationsScrim = notificationsScrim;
mScrimBehind = behindScrim;
mScrimInFront = scrimInFront;
- mScrimForBubble = scrimForBubble;
updateThemeColors();
behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
@@ -293,8 +295,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
final ScrimState[] states = ScrimState.values();
for (int i = 0; i < states.length; i++) {
- states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
- mDockManager);
+ states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
}
@@ -302,9 +303,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mScrimBehind.setDefaultFocusHighlightEnabled(false);
mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
mScrimInFront.setDefaultFocusHighlightEnabled(false);
- if (mScrimForBubble != null) {
- mScrimForBubble.setDefaultFocusHighlightEnabled(false);
- }
updateScrims();
mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
}
@@ -359,21 +357,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mAnimateChange = state.getAnimateChange();
mAnimationDuration = state.getAnimationDuration();
- mInFrontTint = state.getFrontTint();
- mBehindTint = state.getBehindTint();
- mNotificationsTint = state.getNotifTint();
- mBubbleTint = state.getBubbleTint();
-
- mInFrontAlpha = state.getFrontAlpha();
- mBehindAlpha = state.getBehindAlpha();
- mBubbleAlpha = state.getBubbleAlpha();
- mNotificationsAlpha = state.getNotifAlpha();
- if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
- throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: "
- + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
- + mNotificationsAlpha);
- }
- applyStateToAlpha();
+ applyState();
// Scrim might acquire focus when user is navigating with a D-pad or a keyboard.
// We need to disable focus otherwise AOD would end up with a gray overlay.
@@ -466,6 +450,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
public void onExpandingFinished() {
mTracking = false;
+ setUnocclusionAnimationRunning(false);
}
@VisibleForTesting
@@ -510,8 +495,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
boolean relevantState = (mState == ScrimState.UNLOCKED
|| mState == ScrimState.KEYGUARD
|| mState == ScrimState.SHADE_LOCKED
- || mState == ScrimState.PULSING
- || mState == ScrimState.BUBBLE_EXPANDED);
+ || mState == ScrimState.PULSING);
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
@@ -578,8 +562,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mQsBottomVisible = qsBottomVisible;
boolean relevantState = (mState == ScrimState.SHADE_LOCKED
|| mState == ScrimState.KEYGUARD
- || mState == ScrimState.PULSING
- || mState == ScrimState.BUBBLE_EXPANDED);
+ || mState == ScrimState.PULSING);
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
@@ -637,12 +620,22 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
- private void applyStateToAlpha() {
+ private void applyState() {
+ mInFrontTint = mState.getFrontTint();
+ mBehindTint = mState.getBehindTint();
+ mNotificationsTint = mState.getNotifTint();
+
+ mInFrontAlpha = mState.getFrontAlpha();
+ mBehindAlpha = mState.getBehindAlpha();
+ mNotificationsAlpha = mState.getNotifAlpha();
+
+ assertAlphasValid();
+
if (!mExpansionAffectsAlpha) {
return;
}
- if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) {
+ if (mState == ScrimState.UNLOCKED) {
// Darken scrim as you pull down the shade when unlocked, unless the shade is expanding
// because we're doing the screen off animation.
if (!mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
@@ -694,7 +687,23 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mNotificationsTint = mState.getNotifTint();
mBehindTint = behindTint;
}
+
+ // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or
+ // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0
+ // otherwise there is a flicker to its previous value.
+ if (mKeyguardOccluded) {
+ mNotificationsAlpha = 0;
+ }
+ if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
+ // We're unoccluding the keyguard and don't want to have a bright flash.
+ mNotificationsAlpha = 0f;
+ }
}
+
+ assertAlphasValid();
+ }
+
+ private void assertAlphasValid() {
if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) {
throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
+ ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: "
@@ -734,14 +743,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void applyAndDispatchState() {
- applyStateToAlpha();
+ applyState();
if (mUpdatePending) {
return;
}
setOrAdaptCurrentAnimation(mScrimBehind);
setOrAdaptCurrentAnimation(mNotificationsScrim);
setOrAdaptCurrentAnimation(mScrimInFront);
- setOrAdaptCurrentAnimation(mScrimForBubble);
dispatchBackScrimState(mScrimBehind.getViewAlpha());
// Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
@@ -849,11 +857,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
setScrimAlpha(mScrimBehind, mBehindAlpha);
setScrimAlpha(mNotificationsScrim, mNotificationsAlpha);
- if (mScrimForBubble != null) {
- boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
- mScrimForBubble.setColors(mColors, animateScrimForBubble);
- setScrimAlpha(mScrimForBubble, mBubbleAlpha);
- }
// The animation could have all already finished, let's call onFinished just in case
onFinished(mState);
dispatchScrimsVisible();
@@ -906,8 +909,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
return "behind_scrim";
} else if (scrim == mNotificationsScrim) {
return "notifications_scrim";
- } else if (scrim == mScrimForBubble) {
- return "bubble_scrim";
}
return "unknown_scrim";
}
@@ -980,8 +981,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
return mBehindAlpha;
} else if (scrim == mNotificationsScrim) {
return mNotificationsAlpha;
- } else if (scrim == mScrimForBubble) {
- return mBubbleAlpha;
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -994,8 +993,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
return mBehindTint;
} else if (scrim == mNotificationsScrim) {
return mNotificationsTint;
- } else if (scrim == mScrimForBubble) {
- return mBubbleTint;
} else {
throw new IllegalArgumentException("Unknown scrim view");
}
@@ -1027,8 +1024,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
if (isAnimating(mScrimBehind)
|| isAnimating(mNotificationsScrim)
- || isAnimating(mScrimInFront)
- || isAnimating(mScrimForBubble)) {
+ || isAnimating(mScrimInFront)) {
if (callback != null && callback != mCallback) {
// Since we only notify the callback that we're finished once everything has
// finished, we need to make sure that any changing callbacks are also invoked
@@ -1055,13 +1051,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mInFrontTint = Color.TRANSPARENT;
mBehindTint = mState.getBehindTint();
mNotificationsTint = mState.getNotifTint();
- mBubbleTint = Color.TRANSPARENT;
updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint);
- if (mScrimForBubble != null) {
- updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
- }
}
}
@@ -1203,6 +1195,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
pw.println(" ScrimController: ");
pw.print(" state: ");
pw.println(mState);
+ pw.println(" mClipQsScrim = " + mState.mClipQsScrim);
pw.print(" frontScrim:");
pw.print(" viewAlpha=");
@@ -1228,14 +1221,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
pw.print(" tint=0x");
pw.println(Integer.toHexString(mNotificationsScrim.getTint()));
- pw.print(" bubbleScrim:");
- pw.print(" viewAlpha=");
- pw.print(mScrimForBubble.getViewAlpha());
- pw.print(" alpha=");
- pw.print(mBubbleAlpha);
- pw.print(" tint=0x");
- pw.println(Integer.toHexString(mScrimForBubble.getTint()));
-
pw.print(" mTracking=");
pw.println(mTracking);
pw.print(" mDefaultScrimAlpha=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 06811932ac0c..e33c9f84aa73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.phone;
import android.graphics.Color;
import android.os.Trace;
-import androidx.annotation.Nullable;
-
import com.android.systemui.dock.DockManager;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -43,11 +41,9 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mFrontTint = Color.BLACK;
mBehindTint = Color.BLACK;
- mBubbleTint = previousState.mBubbleTint;
mFrontAlpha = 1f;
mBehindAlpha = 1f;
- mBubbleAlpha = previousState.mBubbleAlpha;
mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
}
@@ -81,12 +77,10 @@ public enum ScrimState {
mFrontTint = Color.BLACK;
mBehindTint = Color.BLACK;
mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT;
- mBubbleTint = Color.TRANSPARENT;
mFrontAlpha = 0;
mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
- mBubbleAlpha = 0;
if (mClipQsScrim) {
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
}
@@ -118,7 +112,6 @@ public enum ScrimState {
mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
mNotifTint = Color.TRANSPARENT;
mFrontAlpha = 0f;
- mBubbleAlpha = 0f;
}
},
@@ -129,7 +122,6 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
- mBubbleAlpha = 0f;
mFrontAlpha = mDefaultScrimAlpha;
}
},
@@ -139,7 +131,6 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
mNotifAlpha = 1f;
- mBubbleAlpha = 0f;
mFrontAlpha = 0f;
mBehindTint = Color.BLACK;
@@ -163,7 +154,6 @@ public enum ScrimState {
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
mFrontAlpha = 0;
- mBubbleAlpha = 0;
}
},
@@ -185,9 +175,6 @@ public enum ScrimState {
mBehindTint = Color.BLACK;
mBehindAlpha = ScrimController.TRANSPARENT;
- mBubbleTint = Color.TRANSPARENT;
- mBubbleAlpha = ScrimController.TRANSPARENT;
-
mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
// DisplayPowerManager may blank the screen for us, or we might blank it for ourselves
// by animating the screen off via the LightRevelScrim. In either case we just need to
@@ -214,7 +201,6 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
mFrontAlpha = mAodFrontScrimAlpha;
- mBubbleAlpha = 0f;
mBehindTint = Color.BLACK;
mFrontTint = Color.BLACK;
mBlankScreen = mDisplayRequiresBlanking;
@@ -238,7 +224,6 @@ public enum ScrimState {
mBehindAlpha = mClipQsScrim ? 1 : 0;
mNotifAlpha = 0;
mFrontAlpha = 0;
- mBubbleAlpha = 0;
mAnimationDuration = mKeyguardFadingAway
? mKeyguardFadingAwayDuration
@@ -248,21 +233,16 @@ public enum ScrimState {
mFrontTint = Color.TRANSPARENT;
mBehindTint = Color.BLACK;
- mBubbleTint = Color.TRANSPARENT;
mBlankScreen = false;
if (previousState == ScrimState.AOD) {
// Set all scrims black, before they fade transparent.
updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
- if (mScrimForBubble != null) {
- updateScrimColor(mScrimForBubble, 1f /* alpha */, Color.BLACK /* tint */);
- }
// Scrims should still be black at the end of the transition.
mFrontTint = Color.BLACK;
mBehindTint = Color.BLACK;
- mBubbleTint = Color.BLACK;
mBlankScreen = true;
}
@@ -270,45 +250,24 @@ public enum ScrimState {
updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
}
}
- },
-
- /**
- * Unlocked with a bubble expanded.
- */
- BUBBLE_EXPANDED {
- @Override
- public void prepare(ScrimState previousState) {
- mFrontTint = Color.TRANSPARENT;
- mBehindTint = Color.TRANSPARENT;
- mBubbleTint = Color.BLACK;
-
- mFrontAlpha = 0f;
- mBehindAlpha = mDefaultScrimAlpha;
-
- mAnimationDuration = ScrimController.ANIMATION_DURATION;
- mBlankScreen = false;
- }
};
boolean mBlankScreen = false;
long mAnimationDuration = ScrimController.ANIMATION_DURATION;
int mFrontTint = Color.TRANSPARENT;
int mBehindTint = Color.TRANSPARENT;
- int mBubbleTint = Color.TRANSPARENT;
int mNotifTint = Color.TRANSPARENT;
boolean mAnimateChange = true;
float mAodFrontScrimAlpha;
float mFrontAlpha;
float mBehindAlpha;
- float mBubbleAlpha;
float mNotifAlpha;
float mScrimBehindAlphaKeyguard;
float mDefaultScrimAlpha;
ScrimView mScrimInFront;
ScrimView mScrimBehind;
- @Nullable ScrimView mScrimForBubble;
DozeParameters mDozeParameters;
DockManager mDockManager;
@@ -321,11 +280,10 @@ public enum ScrimState {
long mKeyguardFadingAwayDuration;
boolean mClipQsScrim;
- public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
- DozeParameters dozeParameters, DockManager dockManager) {
+ public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters,
+ DockManager dockManager) {
mScrimInFront = scrimInFront;
mScrimBehind = scrimBehind;
- mScrimForBubble = scrimForBubble;
mDozeParameters = dozeParameters;
mDockManager = dockManager;
@@ -352,10 +310,6 @@ public enum ScrimState {
return mNotifAlpha;
}
- public float getBubbleAlpha() {
- return mBubbleAlpha;
- }
-
public int getFrontTint() {
return mFrontTint;
}
@@ -368,10 +322,6 @@ public enum ScrimState {
return mNotifTint;
}
- public int getBubbleTint() {
- return mBubbleTint;
- }
-
public long getAnimationDuration() {
return mAnimationDuration;
}
@@ -409,10 +359,6 @@ public enum ScrimState {
mDefaultScrimAlpha = defaultScrimAlpha;
}
- public void setBubbleAlpha(float alpha) {
- mBubbleAlpha = alpha;
- }
-
public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index d4458e29a306..768222d34862 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -48,7 +48,7 @@ public class ShadeControllerImpl implements ShadeController {
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final int mDisplayId;
- protected final Lazy<StatusBar> mStatusBarLazy;
+ protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Optional<Bubbles> mBubblesOptional;
@@ -61,7 +61,7 @@ public class ShadeControllerImpl implements ShadeController {
NotificationShadeWindowController notificationShadeWindowController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
WindowManager windowManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
Lazy<AssistManager> assistManagerLazy,
Optional<Bubbles> bubblesOptional
) {
@@ -71,7 +71,7 @@ public class ShadeControllerImpl implements ShadeController {
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
// TODO: Remove circular reference to StatusBar when possible.
- mStatusBarLazy = statusBarLazy;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
mAssistManagerLazy = assistManagerLazy;
mBubblesOptional = bubblesOptional;
}
@@ -118,10 +118,6 @@ public class ShadeControllerImpl implements ShadeController {
+ " flags=" + flags);
}
- if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
- getStatusBar().postHideRecentApps();
- }
-
// TODO(b/62444020): remove when this bug is fixed
Log.v(TAG, "NotificationShadeWindow: " + getNotificationShadeWindowView()
+ " canPanelBeCollapsed(): "
@@ -210,7 +206,7 @@ public class ShadeControllerImpl implements ShadeController {
}
private StatusBar getStatusBar() {
- return mStatusBarLazy.get();
+ return mStatusBarOptionalLazy.get().get();
}
private NotificationPresenter getPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
new file mode 100644
index 000000000000..4b7fe4e7ea29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.phone
+
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.qs.carrier.QSCarrierGroupController
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
+import javax.inject.Inject
+import javax.inject.Named
+
+@StatusBarScope
+class SplitShadeHeaderController @Inject constructor(
+ @Named(SPLIT_SHADE_HEADER) private val statusBar: View,
+ private val statusBarIconController: StatusBarIconController,
+ qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
+ featureFlags: FeatureFlags,
+ batteryMeterViewController: BatteryMeterViewController
+) {
+
+ // TODO(b/194178072) Handle RSSI hiding when multi carrier
+ private val iconManager: StatusBarIconController.IconManager
+ private val qsCarrierGroupController: QSCarrierGroupController
+ private var visible = false
+
+ var shadeExpanded = false
+ set(value) {
+ field = value
+ updateVisibility()
+ }
+
+ var splitShadeMode = false
+ set(value) {
+ field = value
+ updateVisibility()
+ }
+
+ init {
+ batteryMeterViewController.init()
+ val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
+
+ // battery settings same as in QS icons
+ batteryMeterViewController.ignoreTunerUpdates()
+ batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+ val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+ qsCarrierGroupController = qsCarrierGroupControllerBuilder
+ .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
+ .build()
+ }
+
+ private fun updateVisibility() {
+ val shouldBeVisible = shadeExpanded && splitShadeMode
+ if (visible != shouldBeVisible) {
+ visible = shouldBeVisible
+ statusBar.visibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+ updateListeners(shouldBeVisible)
+ }
+ }
+
+ private fun updateListeners(visible: Boolean) {
+ qsCarrierGroupController.setListening(visible)
+ if (visible) {
+ statusBarIconController.addIconGroup(iconManager)
+ } else {
+ statusBarIconController.removeIconGroup(iconManager)
+ }
+ }
+} \ No newline at end of file
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 394e4ad76df1..be0130899e61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -32,16 +31,12 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
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.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;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;
@@ -56,6 +51,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.TaskInfo;
+import android.app.TaskStackBuilder;
import android.app.UiModeManager;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
@@ -71,17 +67,13 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
-import android.media.AudioAttributes;
import android.metrics.LogMaker;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -89,8 +81,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
@@ -105,7 +95,6 @@ import android.util.Slog;
import android.view.Display;
import android.view.IRemoteAnimationRunner;
import android.view.IWindowManager;
-import android.view.InsetsState.InternalInsetsType;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
@@ -113,7 +102,6 @@ import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
@@ -133,7 +121,6 @@ import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.internal.view.AppearanceRegion;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
@@ -146,24 +133,23 @@ import com.android.systemui.InitController;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.emergency.EmergencyGesture;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -190,7 +176,6 @@ import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CircleReveal;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -205,19 +190,18 @@ import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PowerButtonReveal;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
@@ -240,12 +224,15 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -265,23 +252,15 @@ import javax.inject.Provider;
import dagger.Lazy;
-public class StatusBar extends SystemUI implements DemoMode,
- ActivityStarter, KeyguardStateController.Callback,
- OnHeadsUpChangedListener, CommandQueue.Callbacks,
- ColorExtractor.OnColorsChangedListener, ConfigurationListener,
- StatusBarStateController.StateListener,
- LifecycleOwner, BatteryController.BatteryStateChangeCallback,
- ActivityLaunchAnimator.Callback {
+/** */
+public class StatusBar extends SystemUI implements
+ ActivityStarter,
+ LifecycleOwner {
public static final boolean MULTIUSER_DEBUG = false;
- protected static final int MSG_HIDE_RECENT_APPS = 1020;
- protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
- protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
- protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
// Should match the values in PhoneWindowManager
- public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
@@ -306,14 +285,12 @@ public class StatusBar extends SystemUI implements DemoMode,
public static final String ACTION_FAKE_ARTWORK = "fake_artwork";
- private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
- private static final int MSG_CLOSE_PANELS = 1001;
private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
// 1020-1040 reserved for BaseStatusBar
// Time after we abort the launch transition.
- private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
+ static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
@@ -322,11 +299,6 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
private static final int HINT_RESET_DELAY_MS = 1200;
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
public static final int FADE_KEYGUARD_START_DELAY = 100;
public static final int FADE_KEYGUARD_DURATION = 300;
public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
@@ -352,14 +324,114 @@ public class StatusBar extends SystemUI implements DemoMode,
try {
IPackageManager packageManager =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
- onlyCoreApps = packageManager.isOnlyCoreApps();
+ onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps();
} catch (RemoteException e) {
onlyCoreApps = false;
}
ONLY_CORE_APPS = onlyCoreApps;
}
- private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+
+ void setWindowState(int state) {
+ mStatusBarWindowState = state;
+ mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
+ }
+
+ void acquireGestureWakeLock(long time) {
+ mGestureWakeLock.acquire(time);
+ }
+
+ boolean setAppearance(int appearance) {
+ if (mAppearance != appearance) {
+ mAppearance = appearance;
+ return updateBarMode(barMode(isTransientShown(), appearance));
+ }
+
+ return false;
+ }
+
+ int getBarMode() {
+ return mStatusBarMode;
+ }
+
+ boolean getWereIconsJustHidden() {
+ return mWereIconsJustHidden;
+ }
+
+ void setWereIconsJustHidden(boolean justHidden) {
+ mWereIconsJustHidden = justHidden;
+ }
+
+ void resendMessage(int msg) {
+ mMessageRouter.cancelMessages(msg);
+ mMessageRouter.sendMessage(msg);
+ }
+
+ void resendMessage(Object msg) {
+ mMessageRouter.cancelMessages(msg.getClass());
+ mMessageRouter.sendMessage(msg);
+ }
+
+ int getDisabled1() {
+ return mDisabled1;
+ }
+
+ void setDisabled1(int disabled) {
+ mDisabled1 = disabled;
+ }
+
+ int getDisabled2() {
+ return mDisabled2;
+ }
+
+ void setDisabled2(int disabled) {
+ mDisabled2 = disabled;
+ }
+
+ void setLastCameraLaunchSource(int source) {
+ mLastCameraLaunchSource = source;
+ }
+
+ void setLaunchCameraOnFinishedGoingToSleep(boolean launch) {
+ mLaunchCameraOnFinishedGoingToSleep = launch;
+ }
+
+ void setLaunchCameraOnFinishedWaking(boolean launch) {
+ mLaunchCameraWhenFinishedWaking = launch;
+ }
+
+ void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) {
+ mLaunchEmergencyActionOnFinishedGoingToSleep = launch;
+ }
+
+ void setLaunchEmergencyActionOnFinishedWaking(boolean launch) {
+ mLaunchEmergencyActionWhenFinishedWaking = launch;
+ }
+
+ void setTopHidesStatusBar(boolean hides) {
+ mTopHidesStatusBar = hides;
+ }
+
+ QSPanelController getQSPanelController() {
+ return mQSPanelController;
+ }
+
+ /** */
+ public void animateExpandNotificationsPanel() {
+ mCommandQueueCallbacks.animateExpandNotificationsPanel();
+ }
+
+ /** */
+ public void animateExpandSettingsPanel(@Nullable String subpanel) {
+ mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel);
+ }
+
+ /** */
+ public void animateCollapsePanels(int flags, boolean force) {
+ mCommandQueueCallbacks.animateCollapsePanels(flags, force);
+ }
public interface ExpansionChangedListener {
void onExpansionChanged(float expansion, boolean expanded);
@@ -371,8 +443,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
- private PhoneStatusBarPolicy mIconPolicy;
- private StatusBarSignalPolicy mSignalPolicy;
+ private final PhoneStatusBarPolicy mIconPolicy;
private final VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
@@ -380,17 +451,17 @@ public class StatusBar extends SystemUI implements DemoMode,
private BiometricUnlockController mBiometricUnlockController;
private final LightBarController mLightBarController;
private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+ private final LockscreenGestureLogger mLockscreenGestureLogger;
@Nullable
protected LockscreenWallpaper mLockscreenWallpaper;
private final AutoHideController mAutoHideController;
- @Nullable
- private final KeyguardLiftController mKeyguardLiftController;
private final Point mCurrentDisplaySize = new Point();
protected NotificationShadeWindowView mNotificationShadeWindowView;
protected StatusBarWindowView mPhoneStatusBarWindow;
protected PhoneStatusBarView mStatusBarView;
+ private PhoneStatusBarViewController mPhoneStatusBarViewController;
private AuthRippleController mAuthRippleController;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected NotificationShadeWindowController mNotificationShadeWindowController;
@@ -401,7 +472,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
private LightRevealScrim mLightRevealScrim;
- private WiredChargingRippleController mChargingRippleAnimationController;
private PowerButtonReveal mPowerButtonReveal;
private final Object mQueueLock = new Object();
@@ -435,9 +505,8 @@ public class StatusBar extends SystemUI implements DemoMode,
private final KeyguardDismissUtil mKeyguardDismissUtil;
private final ExtensionController mExtensionController;
private final UserInfoControllerImpl mUserInfoControllerImpl;
- private final DismissCallbackRegistry mDismissCallbackRegistry;
private final DemoModeController mDemoModeController;
- private NotificationsController mNotificationsController;
+ private final NotificationsController mNotificationsController;
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
private final StatusBarLocationPublisher mStatusBarLocationPublisher;
@@ -450,16 +519,16 @@ public class StatusBar extends SystemUI implements DemoMode,
// settings
private QSPanelController mQSPanelController;
+ private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
KeyguardIndicationController mKeyguardIndicationController;
- private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-
private View mReportRejectedTouch;
private boolean mExpandedVisible;
private final int[] mAbsPos = new int[2];
+ protected final NotificationEntryManager mEntryManager;
private final NotificationGutsManager mGutsManager;
private final NotificationLogger mNotificationLogger;
private final NotificationViewHierarchyManager mViewHierarchyManager;
@@ -467,16 +536,22 @@ public class StatusBar extends SystemUI implements DemoMode,
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
+ private final UnfoldTransitionConfig mUnfoldTransitionConfig;
+ private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+ private final MessageRouter mMessageRouter;
+ private final WallpaperManager mWallpaperManager;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ private final TunerService mTunerService;
private final List<ExpansionChangedListener> mExpansionChangedListeners;
- // for disabling the status bar
+ // Flags for disabling the status bar
+ // Two variables becaseu the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
private int mDisabled2 = 0;
- /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+ /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
private boolean mTransientShown;
@@ -495,29 +570,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// ensure quick settings is disabled until the current user makes it through the setup wizard
@VisibleForTesting
protected boolean mUserSetup = false;
- private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
- @Override
- public void onUserSetupChanged() {
- final boolean userSetup = mDeviceProvisionedController.isUserSetup(
- mDeviceProvisionedController.getCurrentUser());
- Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
- + mDeviceProvisionedController.getCurrentUser());
- if (MULTIUSER_DEBUG) {
- Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
- userSetup, mUserSetup));
- }
-
- if (userSetup != mUserSetup) {
- mUserSetup = userSetup;
- if (!mUserSetup && mStatusBarView != null)
- animateCollapseQuickSettings();
- if (mNotificationPanelViewController != null) {
- mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
- }
- updateQsExpansionEnabled();
- }
- }
- };
@VisibleForTesting
public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum {
@@ -557,12 +609,13 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- protected final H mHandler = createHandler();
+ private Handler mMainHandler;
+ private final DelayableExecutor mMainExecutor;
private int mInteractingWindows;
private @TransitionMode int mStatusBarMode;
- private ViewMediatorCallback mKeyguardViewMediatorCallback;
+ private final ViewMediatorCallback mKeyguardViewMediatorCallback;
private final ScrimController mScrimController;
protected DozeScrimController mDozeScrimController;
private final Executor mUiBgExecutor;
@@ -574,46 +627,13 @@ public class StatusBar extends SystemUI implements DemoMode,
private final NotificationRemoteInputManager mRemoteInputManager;
private boolean mWallpaperSupported;
- private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mWallpaperSupported) {
- // Receiver should not have been registered at all...
- Log.wtf(TAG, "WallpaperManager not supported");
- return;
- }
- WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
- WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
- final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
- // If WallpaperInfo is null, it must be ImageWallpaper.
- final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info != null && info.supportsAmbientMode());
-
- mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
- }
- };
-
- BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().onTaskbarChanged(intent.getExtras());
- }
- }
- };
-
private Runnable mLaunchTransitionEndRunnable;
- private NotificationEntry mDraggedDownEntry;
private boolean mLaunchCameraWhenFinishedWaking;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private boolean mLaunchEmergencyActionWhenFinishedWaking;
private boolean mLaunchEmergencyActionOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
protected PowerManager.WakeLock mGestureWakeLock;
- private Vibrator mVibrator;
- private VibrationEffect mCameraLaunchGestureVibrationEffect;
private final int[] mTmpInt2 = new int[2];
@@ -625,29 +645,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mIsOccluded;
private boolean mWereIconsJustHidden;
private boolean mBouncerWasShowingWhenHidden;
-
- // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
- // this animation is tied to the scrim for historic reasons.
- // TODO: notify when keyguard has faded away instead of the scrim.
- private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
- .Callback() {
- @Override
- public void onFinished() {
- if (mStatusBarKeyguardViewManager == null) {
- Log.w(TAG, "Tried to notify keyguard visibility when "
- + "mStatusBarKeyguardViewManager was null");
- return;
- }
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
- }
- }
-
- @Override
- public void onCancelled() {
- onFinished();
- }
- };
+ private boolean mIsLaunchingActivityOverLockscreen;
private final UserSwitcherController mUserSwitcherController;
private final NetworkController mNetworkController;
@@ -666,51 +664,24 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
- private final KeyguardUpdateMonitorCallback mUpdateCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onDreamingStateChanged(boolean dreaming) {
- if (dreaming) {
- maybeEscalateHeadsUp();
- }
- }
-
- // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
- // KeyguardCoordinator
- @Override
- public void onStrongAuthStateChanged(int userId) {
- super.onStrongAuthStateChanged(userId);
- mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
- }
- };
-
-
- private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
- new FalsingManager.FalsingBeliefListener() {
- @Override
- public void onFalse() {
- // Hides quick settings, bouncer, and quick-quick settings.
- mStatusBarKeyguardViewManager.reset(true);
- }
- };
-
- private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
-
private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private boolean mVibrateOnOpening;
- private final VibratorHelper mVibratorHelper;
private ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
- private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
+ private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
private final Optional<BubblesManager> mBubblesManagerOptional;
private final Optional<Bubbles> mBubblesOptional;
private final Bubbles.BubbleExpandListener mBubbleExpandListener;
private final Optional<StartingSurface> mStartingSurfaceOptional;
- private ActivityIntentHelper mActivityIntentHelper;
+ private final ActivityIntentHelper mActivityIntentHelper;
private NotificationStackScrollLayoutController mStackScrollerController;
+ private BatteryMeterViewController mBatteryMeterViewController;
+
+ private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
+ (extractor, which) -> updateTheme();
+
/**
* Public constructor for StatusBar.
@@ -725,7 +696,6 @@ public class StatusBar extends SystemUI implements DemoMode,
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarSignalPolicy signalPolicy,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
@@ -736,7 +706,7 @@ public class StatusBar extends SystemUI implements DemoMode,
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
@@ -755,20 +725,18 @@ public class StatusBar extends SystemUI implements DemoMode,
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
- AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
DozeParameters dozeParameters,
ScrimController scrimController,
- @Nullable KeyguardLiftController keyguardLiftController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ LockscreenGestureLogger lockscreenGestureLogger,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
DozeServiceHost dozeServiceHost,
PowerManager powerManager,
@@ -792,15 +760,16 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
- DismissCallbackRegistry dismissCallbackRegistry,
DemoModeController demoModeController,
Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
BrightnessSlider.Factory brightnessSliderFactory,
- WiredChargingRippleController chargingRippleAnimationController,
+ UnfoldTransitionConfig unfoldTransitionConfig,
+ Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -808,19 +777,24 @@ public class StatusBar extends SystemUI implements DemoMode,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ @Main Handler mainHandler,
+ @Main DelayableExecutor delayableExecutor,
+ @Main MessageRouter messageRouter,
+ WallpaperManager wallpaperManager,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Optional<StartingSurface> startingSurfaceOptional) {
+ Optional<StartingSurface> startingSurfaceOptional,
+ TunerService tunerService) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
mAutoHideController = autoHideController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mSignalPolicy = signalPolicy;
mPulseExpansionHandler = pulseExpansionHandler;
mWakeUpCoordinator = notificationWakeUpCoordinator;
mKeyguardBypassController = keyguardBypassController;
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
+ mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mDynamicPrivacyController = dynamicPrivacyController;
@@ -828,7 +802,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
- mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
+ mEntryManager = notificationEntryManager;
mGutsManager = notificationGutsManager;
mNotificationLogger = notificationLogger;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
@@ -847,7 +821,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
- mVibratorHelper = vibratorHelper;
mBubblesManagerOptional = bubblesManagerOptional;
mBubblesOptional = bubblesOptional;
mVisualStabilityManager = visualStabilityManager;
@@ -860,8 +833,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
mScrimController = scrimController;
- mKeyguardLiftController = keyguardLiftController;
mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
+ mLockscreenGestureLogger = lockscreenGestureLogger;
mScreenPinningRequest = screenPinningRequest;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
@@ -883,18 +856,23 @@ public class StatusBar extends SystemUI implements DemoMode,
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
mIconPolicy = phoneStatusBarPolicy;
- mDismissCallbackRegistry = dismissCallbackRegistry;
mDemoModeController = demoModeController;
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
- mChargingRippleAnimationController = chargingRippleAnimationController;
+ mUnfoldTransitionConfig = unfoldTransitionConfig;
+ mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
mStatusBarIconController = statusBarIconController;
mFeatureFlags = featureFlags;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+ mMainHandler = mainHandler;
+ mMainExecutor = delayableExecutor;
+ mMessageRouter = messageRouter;
+ mWallpaperManager = wallpaperManager;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+ mTunerService = tunerService;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -903,15 +881,24 @@ public class StatusBar extends SystemUI implements DemoMode,
mExpansionChangedListeners = new ArrayList<>();
mBubbleExpandListener =
- (isExpanding, key) -> {
- mContext.getMainExecutor().execute(() -> {
- mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
- updateScrimController();
- });
- };
+ (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
+ mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
+ updateScrimController();
+ });
mActivityIntentHelper = new ActivityIntentHelper(mContext);
+
+ // TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
+
+ mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class,
+ data -> toggleKeyboardShortcuts(data.mDeviceId));
+ mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU,
+ id -> dismissKeyboardShortcuts());
+ mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class,
+ data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
+ mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
+ id -> onLaunchTransitionTimeout());
}
@Override
@@ -928,21 +915,18 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardIndicationController.init();
- mColorExtractor.addOnColorsChangedListener(this);
- mStatusBarStateController.addCallback(this,
+ mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
+ mStatusBarStateController.addCallback(mStateListener,
SysuiStatusBarStateController.RANK_STATUS_BAR);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
- mDisplay = mWindowManager.getDefaultDisplay();
+ mDisplay = mContext.getDisplay();
mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
- mVibrateOnOpening = mContext.getResources().getBoolean(
- R.bool.config_vibrateOnIconAnimation);
-
// start old BaseStatusBar.start().
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
@@ -956,14 +940,7 @@ public class StatusBar extends SystemUI implements DemoMode,
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mWallpaperSupported =
- mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();
-
- // Connect in to the status bar manager service
- mCommandQueue.addCallback(this);
-
- // Listen for demo mode changes
- mDemoModeController.addCallback(this);
+ mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
RegisterStatusBarResult result = null;
try {
@@ -990,12 +967,13 @@ public class StatusBar extends SystemUI implements DemoMode,
if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) {
showTransientUnchecked();
}
- onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
- result.mNavbarColorManagedByIme, result.mBehavior, result.mAppFullscreen);
+ mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
+ result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
+ result.mRequestedVisibilities, result.mPackageName);
// StatusBarManagerService has a back up of IME token and it's restored here.
- setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
- result.mImeBackDisposition, result.mShowImeSwitcher);
+ mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher);
// Set up the initial icon state
int numIcons = result.mIcons.size();
@@ -1034,7 +1012,13 @@ public class StatusBar extends SystemUI implements DemoMode,
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy.init();
- mKeyguardStateController.addCallback(this);
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ updateKeyguardState();
+ logStateToEventlog();
+ }
+ });
startKeyguard();
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
@@ -1046,9 +1030,9 @@ public class StatusBar extends SystemUI implements DemoMode,
mAmbientIndicationContainer);
mDozeParameters.addCallback(this::updateLightRevealScrimVisibility);
- mConfigurationController.addCallback(this);
+ mConfigurationController.addCallback(mConfigurationListener);
- mBatteryController.observe(mLifecycle, this);
+ mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
mLifecycle.setCurrentState(RESUMED);
// set the initial view visibility
@@ -1059,13 +1043,17 @@ public class StatusBar extends SystemUI implements DemoMode,
mFalsingManager.addFalsingBeliefListener(mFalsingBeliefListener);
+ if (mUnfoldTransitionConfig.isEnabled()) {
+ mUnfoldLightRevealOverlayAnimation.get().init();
+ }
+
mPluginManager.addPluginListener(
new PluginListener<OverlayPlugin>() {
- private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
+ private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
@Override
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
- mMainThreadHandler.post(
+ mMainExecutor.execute(
() -> plugin.setup(getNotificationShadeWindowView(),
getNavigationBarView(),
new Callback(plugin), mDozeParameters));
@@ -1073,7 +1061,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onPluginDisconnected(OverlayPlugin plugin) {
- mMainThreadHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mOverlays.remove(plugin);
mNotificationShadeWindowController
.setForcePluginOpen(mOverlays.size() != 0, this);
@@ -1094,7 +1082,7 @@ public class StatusBar extends SystemUI implements DemoMode,
} else {
mOverlays.remove(mPlugin);
}
- mMainThreadHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mNotificationShadeWindowController
.setStateListener(b -> mOverlays.forEach(
o -> o.setCollapseDesired(b)));
@@ -1110,7 +1098,6 @@ public class StatusBar extends SystemUI implements DemoMode,
// Constructing the view
// ================================================================================
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
- final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
@@ -1148,6 +1135,20 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
+ mPhoneStatusBarViewController =
+ new PhoneStatusBarViewController(mStatusBarView, mCommandQueue);
+ mPhoneStatusBarViewController.init();
+
+ mBatteryMeterViewController = new BatteryMeterViewController(
+ mStatusBarView.findViewById(R.id.battery),
+ mConfigurationController,
+ mTunerService,
+ mBroadcastDispatcher,
+ mMainHandler,
+ mContext.getContentResolver(),
+ mBatteryController
+ );
+ mBatteryMeterViewController.init();
// CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
// mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
@@ -1198,15 +1199,15 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardStateController,
mNetworkController,
mStatusBarStateController,
- this,
- mCommandQueue
+ () -> Optional.of(this),
+ mCommandQueue,
+ mOperatorNameViewControllerFactory
),
CollapsedStatusBarFragment.TAG)
.commit();
mHeadsUpManager.setup(mVisualStabilityManager);
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
- mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
@@ -1231,7 +1232,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public boolean shouldHideOnTouch() {
- return !mRemoteInputManager.getController().isRemoteInputActive();
+ return !mRemoteInputManager.isRemoteInputActive();
}
@Override
@@ -1249,17 +1250,26 @@ public class StatusBar extends SystemUI implements DemoMode,
ScrimView notificationsScrim = mNotificationShadeWindowView
.findViewById(R.id.scrim_notifications);
ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
- ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
- ? mBubblesManagerOptional.get().getScrimForBubble() : null;
mScrimController.setScrimVisibleListener(scrimsVisible -> {
mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
});
- mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront, scrimForBubble);
+ mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
- mLightRevealScrim.setRevealAmountListener(
- mNotificationShadeWindowController::setLightRevealScrimAmount);
+ mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
+ Runnable updateOpaqueness = () -> {
+ mNotificationShadeWindowController.setLightRevealScrimOpaque(
+ mLightRevealScrim.isScrimOpaque());
+ };
+ if (opaque) {
+ // Delay making the view opaque for a frame, because it needs some time to render
+ // otherwise this can lead to a flicker where the scrim doesn't cover the screen
+ mLightRevealScrim.post(updateOpaqueness);
+ } else {
+ updateOpaqueness.run();
+ }
+ });
mUnlockedScreenOffAnimationController.initialize(this, mLightRevealScrim);
updateLightRevealScrimVisibility();
@@ -1305,7 +1315,7 @@ public class StatusBar extends SystemUI implements DemoMode,
QS qs = (QS) f;
if (qs instanceof QSFragment) {
mQSPanelController = ((QSFragment) qs).getQSPanelController();
- mQSPanelController.setBrightnessMirror(mBrightnessMirrorController);
+ ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
}
});
}
@@ -1336,14 +1346,11 @@ public class StatusBar extends SystemUI implements DemoMode,
});
}
- if (!mPowerManager.isScreenOn()) {
+ if (!mPowerManager.isInteractive()) {
mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
}
mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
- "GestureWakeLock");
- mVibrator = mContext.getSystemService(Vibrator.class);
- mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
- mVibrator, context.getResources());
+ "sysui:GestureWakeLock");
// receive broadcasts
registerBroadcastReceiver();
@@ -1352,7 +1359,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (DEBUG_MEDIA_FAKE_ARTWORK) {
demoFilter.addAction(ACTION_FAKE_ARTWORK);
}
- context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
+ mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
android.Manifest.permission.DUMP, null);
// listen for USER_SETUP_COMPLETE setting (per-user)
@@ -1405,19 +1412,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return mLifecycle;
}
- @Override
- public void onPowerSaveChanged(boolean isPowerSave) {
- mHandler.post(mCheckBarModes);
- if (mDozeServiceHost != null) {
- mDozeServiceHost.firePowerSaveChanged(isPowerSave);
- }
- }
-
- @Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- // noop
- }
-
@VisibleForTesting
protected void registerBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
@@ -1433,7 +1427,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private void setUpPresenter() {
// Set up the initial notification state.
- mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext);
+ mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
mStackScrollerController.getNotificationListContainer(),
@@ -1441,17 +1435,37 @@ public class StatusBar extends SystemUI implements DemoMode,
);
// TODO: inject this.
- mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
- mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
- mDozeScrimController, mScrimController, mNotificationShadeWindowController,
- mDynamicPrivacyController, mKeyguardStateController,
+ mPresenter = new StatusBarNotificationPresenter(
+ mContext,
+ mNotificationPanelViewController,
+ mHeadsUpManager,
+ mNotificationShadeWindowView,
+ mStackScrollerController,
+ mDozeScrimController,
+ mScrimController,
+ mNotificationShadeWindowController,
+ mDynamicPrivacyController,
+ mKeyguardStateController,
mKeyguardIndicationController,
- this /* statusBar */, mShadeController,
- mLockscreenShadeTransitionController, mCommandQueue, mInitController,
- mNotificationInterruptStateProvider);
+ this /* statusBar */,
+ mShadeController,
+ mLockscreenShadeTransitionController,
+ mCommandQueue,
+ mViewHierarchyManager,
+ mLockscreenUserManager,
+ mStatusBarStateController,
+ mEntryManager,
+ mMediaManager,
+ mGutsManager,
+ mKeyguardUpdateMonitor,
+ mLockscreenGestureLogger,
+ mInitController,
+ mNotificationInterruptStateProvider,
+ mRemoteInputManager,
+ mConfigurationController);
mNotificationShelfController.setOnActivatedListener(mPresenter);
- mRemoteInputManager.getController().addCallback(mNotificationShadeWindowController);
+ mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
mNotificationActivityStarter =
mStatusBarNotificationActivityStarterBuilder
@@ -1461,7 +1475,7 @@ public class StatusBar extends SystemUI implements DemoMode,
.setNotificationPresenter(mPresenter)
.setNotificationPanelViewController(mNotificationPanelViewController)
.build();
- mStackScroller.setNotificationActivityStarter(mNotificationActivityStarter);
+ mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
mNotificationsController.initialize(
@@ -1529,48 +1543,6 @@ public class StatusBar extends SystemUI implements DemoMode,
.getNotificationShelfController(mStackScroller);
}
- @Override
- public void onDensityOrFontScaleChanged() {
- // TODO: Remove this.
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onDensityOrFontScaleChanged();
- }
- // TODO: Bring these out of StatusBar.
- mUserInfoControllerImpl.onDensityOrFontScaleChanged();
- mUserSwitcherController.onDensityOrFontScaleChanged();
- mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
- mHeadsUpManager.onDensityOrFontScaleChanged();
- }
-
- @Override
- public void onThemeChanged() {
- if (mStatusBarKeyguardViewManager != null) {
- mStatusBarKeyguardViewManager.onThemeChanged();
- }
- if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
- ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
- }
- mNotificationIconAreaController.onThemeChanged();
- }
-
- @Override
- public void onOverlayChanged() {
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onOverlayChanged();
- }
- // We need the new R.id.keyguard_indication_area before recreating
- // mKeyguardIndicationController
- mNotificationPanelViewController.onThemeChanged();
- onThemeChanged();
- }
-
- @Override
- public void onUiModeChanged() {
- if (mBrightnessMirrorController != null) {
- mBrightnessMirrorController.onUiModeChanged();
- }
- }
-
private void inflateStatusBarWindow() {
mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
@@ -1586,6 +1558,20 @@ public class StatusBar extends SystemUI implements DemoMode,
mAuthRippleController = statusBarComponent.getAuthRippleController();
mAuthRippleController.init();
+
+ mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+
+ mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+
+ // Listen for demo mode changes
+ mDemoModeController.addCallback(statusBarComponent.getStatusBarDemoMode());
+
+ if (mCommandQueueCallbacks != null) {
+ mCommandQueue.removeCallback(mCommandQueueCallbacks);
+ }
+ mCommandQueueCallbacks = statusBarComponent.getStatusBarCommandQueueCallbacks();
+ // Connect in to the status bar manager service
+ mCommandQueue.addCallback(mCommandQueueCallbacks);
}
protected void startKeyguard() {
@@ -1626,7 +1612,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mKeyguardIndicationController
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
- mRemoteInputManager.getController().addCallback(mStatusBarKeyguardViewManager);
+ mRemoteInputManager.addControllerCallback(mStatusBarKeyguardViewManager);
mDynamicPrivacyController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mLightBarController.setBiometricUnlockController(mBiometricUnlockController);
@@ -1635,7 +1621,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Trace.endSection();
}
- protected View getStatusBarView() {
+ protected PhoneStatusBarView getStatusBarView() {
return mStatusBarView;
}
@@ -1656,7 +1642,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
protected ViewGroup getBouncerContainer() {
- return mNotificationShadeWindowView;
+ return mNotificationShadeWindowView.findViewById(R.id.keyboard_bouncer_container);
}
public int getStatusBarHeight() {
@@ -1697,7 +1683,7 @@ public class StatusBar extends SystemUI implements DemoMode,
* If the user switcher is simple then disable QS during setup because
* the user intends to use the lock screen user switcher, QS in not needed.
*/
- private void updateQsExpansionEnabled() {
+ void updateQsExpansionEnabled() {
final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
@@ -1713,22 +1699,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
}
- public void addQsTile(ComponentName tile) {
- if (mQSPanelController != null && mQSPanelController.getHost() != null) {
- mQSPanelController.getHost().addTile(tile);
- }
- }
-
- public void remQsTile(ComponentName tile) {
- if (mQSPanelController != null && mQSPanelController.getHost() != null) {
- mQSPanelController.getHost().removeTile(tile);
- }
- }
-
- public void clickTile(ComponentName tile) {
- mQSPanelController.clickTile(tile);
- }
-
/**
* Request a notification update
* @param reason why we're requesting a notification update
@@ -1754,102 +1724,10 @@ public class StatusBar extends SystemUI implements DemoMode,
&& mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
}
- /**
- * State is one or more of the DISABLE constants from StatusBarManager.
- */
- @Override
- public void disable(int displayId, int state1, int state2, boolean animate) {
- if (displayId != mDisplayId) {
- return;
- }
- state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
-
- animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
- final int old1 = mDisabled1;
- final int diff1 = state1 ^ old1;
- mDisabled1 = state1;
-
- final int old2 = mDisabled2;
- final int diff2 = state2 ^ old2;
- mDisabled2 = state2;
-
- if (DEBUG) {
- Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
- old1, state1, diff1));
- Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
- old2, state2, diff2));
- }
-
- StringBuilder flagdbg = new StringBuilder();
- flagdbg.append("disable<");
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
- flagdbg.append("> disable2<");
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
- flagdbg.append('>');
- Log.d(TAG, flagdbg.toString());
-
- if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
- if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
- mShadeController.animateCollapsePanels();
- }
- }
-
- if ((diff1 & StatusBarManager.DISABLE_RECENT) != 0) {
- if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) {
- // close recents if it's visible
- mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
- mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
- }
- }
-
- if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- if (areNotificationAlertsDisabled()) {
- mHeadsUpManager.releaseAllImmediately();
- }
- }
-
- if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
- updateQsExpansionEnabled();
- }
-
- if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- updateQsExpansionEnabled();
- if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- mShadeController.animateCollapsePanels();
- }
- }
- }
-
boolean areNotificationAlertsDisabled() {
return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
}
- protected H createHandler() {
- return new StatusBar.H();
- }
-
@Override
public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
int flags) {
@@ -1863,10 +1741,77 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void startActivity(Intent intent, boolean dismissShade,
- ActivityLaunchAnimator.Controller animationController) {
- startActivityDismissingKeyguard(intent, false, dismissShade,
+ @Nullable ActivityLaunchAnimator.Controller animationController,
+ boolean showOverLockscreenWhenLocked) {
+ // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't
+ // want to show the activity above it.
+ if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) {
+ startActivityDismissingKeyguard(intent, false, dismissShade,
false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
0 /* flags */, animationController);
+ return;
+ }
+
+ boolean animate =
+ animationController != null && shouldAnimateLaunch(true /* isActivityIntent */,
+ showOverLockscreenWhenLocked);
+
+ ActivityLaunchAnimator.Controller controller = null;
+ if (animate) {
+ // Wrap the animation controller to dismiss the shade and set
+ // mIsLaunchingActivityOverLockscreen during the animation.
+ ActivityLaunchAnimator.Controller delegate = wrapAnimationController(
+ animationController, dismissShade);
+ controller = new DelegateLaunchAnimatorController(delegate) {
+ @Override
+ public void onIntentStarted(boolean willAnimate) {
+ getDelegate().onIntentStarted(willAnimate);
+
+ if (willAnimate) {
+ StatusBar.this.mIsLaunchingActivityOverLockscreen = true;
+ }
+ }
+
+ @Override
+ public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+ // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+ // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+ // being true means that we will collapse the shade (or at least run the
+ // post collapse runnables) later on.
+ StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
+ }
+
+ @Override
+ public void onLaunchAnimationCancelled() {
+ // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+ // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+ // being true means that we will collapse the shade (or at least run the
+ // post collapse runnables) later on.
+ StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ getDelegate().onLaunchAnimationCancelled();
+ }
+ };
+ } else if (dismissShade) {
+ // The animation will take care of dismissing the shade at the end of the animation. If
+ // we don't animate, collapse it directly.
+ collapseShade();
+ }
+
+ mActivityLaunchAnimator.startIntentWithAnimation(controller, animate,
+ intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder
+ .create(mContext)
+ .addNextIntent(intent)
+ .startActivities(getActivityOptions(getDisplayId(), adapter),
+ UserHandle.CURRENT));
+ }
+
+ /**
+ * Whether we are currently animating an activity launch above the lockscreen (occluding
+ * activity).
+ */
+ public boolean isLaunchingActivityOverLockscreen() {
+ return mIsLaunchingActivityOverLockscreen;
}
@Override
@@ -1906,70 +1851,6 @@ public class StatusBar extends SystemUI implements DemoMode,
logStateToEventlog();
}
- @Override
- public void onUnlockedChanged() {
- updateKeyguardState();
- logStateToEventlog();
- }
-
- @Override
- public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
- if (inPinnedMode) {
- mNotificationShadeWindowController.setHeadsUpShowing(true);
- mStatusBarWindowController.setForceStatusBarVisible(true);
- if (mNotificationPanelViewController.isFullyCollapsed()) {
- // We need to ensure that the touchable region is updated before the window will be
- // resized, in order to not catch any touches. A layout will ensure that
- // onComputeInternalInsets will be called and after that we can resize the layout. Let's
- // make sure that the window stays small for one frame until the touchableRegion is set.
- mNotificationPanelViewController.getView().requestLayout();
- mNotificationShadeWindowController.setForceWindowCollapsed(true);
- mNotificationPanelViewController.getView().post(() -> {
- mNotificationShadeWindowController.setForceWindowCollapsed(false);
- });
- }
- } else {
- boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
- && mState == StatusBarState.KEYGUARD;
- if (!mNotificationPanelViewController.isFullyCollapsed()
- || mNotificationPanelViewController.isTracking() || bypassKeyguard) {
- // We are currently tracking or is open and the shade doesn't need to be kept
- // open artificially.
- mNotificationShadeWindowController.setHeadsUpShowing(false);
- if (bypassKeyguard) {
- mStatusBarWindowController.setForceStatusBarVisible(false);
- }
- } else {
- // we need to keep the panel open artificially, let's wait until the animation
- // is finished.
- mHeadsUpManager.setHeadsUpGoingAway(true);
- mNotificationPanelViewController.runAfterAnimationFinished(() -> {
- if (!mHeadsUpManager.hasPinnedHeadsUp()) {
- mNotificationShadeWindowController.setHeadsUpShowing(false);
- mHeadsUpManager.setHeadsUpGoingAway(false);
- }
- mRemoteInputManager.onPanelCollapsed();
- });
- }
- }
- }
-
- @Override
- public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
- mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
- if (mStatusBarStateController.isDozing() && isHeadsUp) {
- entry.setPulseSuppressed(false);
- mDozeServiceHost.fireNotificationPulse(entry);
- if (mDozeServiceHost.isPulsing()) {
- mDozeScrimController.cancelPendingPulseTimeout();
- }
- }
- if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
- // There are no longer any notifications to show. We should end the pulse now.
- mDozeScrimController.pulseOutNow();
- }
- }
-
public void setPanelExpanded(boolean isExpanded) {
if (mPanelExpanded != isExpanded) {
mNotificationLogger.onPanelExpandedChanged(isExpanded);
@@ -2002,11 +1883,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
}
- @Override
- public void onColorsChanged(ColorExtractor extractor, int which) {
- updateTheme();
- }
-
@Nullable
public View getAmbientIndicationContainer() {
return mAmbientIndicationContainer;
@@ -2042,7 +1918,7 @@ public class StatusBar extends SystemUI implements DemoMode,
*
* @param animate should the change of the icons be animated.
*/
- private void updateHideIconsForBouncer(boolean animate) {
+ void updateHideIconsForBouncer(boolean animate) {
boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded
&& (mStatusBarWindowHidden || mBouncerShowing);
boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing;
@@ -2053,7 +1929,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// We're delaying the showing, since most of the time the fullscreen app will
// hide the icons again and we don't want them to fade in and out immediately again.
mWereIconsJustHidden = true;
- mHandler.postDelayed(() -> {
+ mMainExecutor.executeDelayed(() -> {
mWereIconsJustHidden = false;
mCommandQueue.recomputeDisableFlags(mDisplayId, true);
}, 500);
@@ -2096,51 +1972,28 @@ public class StatusBar extends SystemUI implements DemoMode,
*
* Note: This method must be called *before* dismissing the keyguard.
*/
- public boolean shouldAnimateLaunch(boolean isActivityIntent) {
+ public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) {
// TODO(b/184121838): Support launch animations when occluded.
if (isOccluded()) {
return false;
}
- // Always animate if we are unlocked.
- if (!mKeyguardStateController.isShowing()) {
+ // Always animate if we are not showing the keyguard or if we animate over the lockscreen
+ // (without unlocking it).
+ if (showOverLockscreen || !mKeyguardStateController.isShowing()) {
return true;
}
- // If we are locked, only animate if remote unlock animations are enabled. We also don't
- // animate non-activity launches as they can break the animation.
+ // If we are locked and have to dismiss the keyguard, only animate if remote unlock
+ // animations are enabled. We also don't animate non-activity launches as they can break the
+ // animation.
// TODO(b/184121838): Support non activity launches on the lockscreen.
return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation;
}
- @Override
- public boolean isOnKeyguard() {
- return mKeyguardStateController.isShowing();
- }
-
- @Override
- public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
- // We post to the main thread for 2 reasons:
- // 1. KeyguardViewMediator is not thread-safe.
- // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
- // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur when doing
- // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
- mMainThreadHandler.post(() -> mKeyguardViewMediator.hideWithAnimation(runner));
- }
-
- @Override
- public void setBlursDisabledForAppLaunch(boolean disabled) {
- mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
- }
-
- @Override
- public int getBackgroundColor(TaskInfo task) {
- if (!mStartingSurfaceOptional.isPresent()) {
- Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
- return SplashscreenContentDrawer.getSystemBGColor();
- }
-
- return mStartingSurfaceOptional.get().getBackgroundColor(task);
+ /** Whether we should animate an activity launch. */
+ public boolean shouldAnimateLaunch(boolean isActivityIntent) {
+ return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */);
}
public boolean isDeviceInVrMode() {
@@ -2156,38 +2009,19 @@ public class StatusBar extends SystemUI implements DemoMode,
mState = state;
}
- @VisibleForTesting
- void setUserSetupForTest(boolean userSetup) {
- mUserSetup = userSetup;
+ static class KeyboardShortcutsMessage {
+ final int mDeviceId;
+
+ KeyboardShortcutsMessage(int deviceId) {
+ mDeviceId = deviceId;
+ }
}
- /**
- * All changes to the status bar and notifications funnel through here and are batched.
- */
- protected class H extends Handler {
- @Override
- public void handleMessage(Message m) {
- switch (m.what) {
- case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
- toggleKeyboardShortcuts(m.arg1);
- break;
- case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
- dismissKeyboardShortcuts();
- break;
- // End old BaseStatusBar.H handling.
- case MSG_OPEN_NOTIFICATION_PANEL:
- animateExpandNotificationsPanel();
- break;
- case MSG_OPEN_SETTINGS_PANEL:
- animateExpandSettingsPanel((String) m.obj);
- break;
- case MSG_CLOSE_PANELS:
- mShadeController.animateCollapsePanels();
- break;
- case MSG_LAUNCH_TRANSITION_TIMEOUT:
- onLaunchTransitionTimeout();
- break;
- }
+ static class AnimateExpandSettingsPanelMessage {
+ final String mSubpanel;
+
+ AnimateExpandSettingsPanelMessage(String subpanel) {
+ mSubpanel = subpanel;
}
}
@@ -2211,59 +2045,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager.releaseAllImmediately();
}
- /**
- * Called for system navigation gestures. First action opens the panel, second opens
- * settings. Down action closes the entire panel.
- */
- @Override
- public void handleSystemKey(int key) {
- if (SPEW) Log.d(TAG, "handleNavigationKey: " + key);
- if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
- || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
- return;
- }
-
- // Panels are not available in setup
- if (!mUserSetup) return;
-
- if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
- mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
- mNotificationPanelViewController.collapse(
- false /* delayed */, 1.0f /* speedUpFactor */);
- } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
- mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
- if (mNotificationPanelViewController.isFullyCollapsed()) {
- if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
- }
- mNotificationPanelViewController.expand(true /* animate */);
- mStackScroller.setWillExpand(true);
- mHeadsUpManager.unpinAll(true /* userUnpinned */);
- mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
- } else if (!mNotificationPanelViewController.isInSettings()
- && !mNotificationPanelViewController.isExpanding()) {
- mNotificationPanelViewController.flingSettings(0 /* velocity */,
- NotificationPanelView.FLING_EXPAND);
- mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
- }
- }
-
- }
-
- @Override
- public void showPinningEnterExitToast(boolean entering) {
- if (getNavigationBarView() != null) {
- getNavigationBarView().showPinningEnterExitToast(entering);
- }
- }
-
- @Override
- public void showPinningEscapeToast() {
- if (getNavigationBarView() != null) {
- getNavigationBarView().showPinningEscapeToast();
- }
- }
-
void makeExpandedVisible(boolean force) {
if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
@@ -2282,42 +2063,17 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public void postAnimateCollapsePanels() {
- mHandler.post(mShadeController::animateCollapsePanels);
+ mMainExecutor.execute(mShadeController::animateCollapsePanels);
}
public void postAnimateForceCollapsePanels() {
- mHandler.post(() -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
+ mMainExecutor.execute(
+ () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
true /* force */));
}
public void postAnimateOpenPanels() {
- mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
- }
-
- @Override
- public void togglePanel() {
- if (mPanelExpanded) {
- mShadeController.animateCollapsePanels();
- } else {
- animateExpandNotificationsPanel();
- }
- }
-
- @Override
- public void animateCollapsePanels(int flags, boolean force) {
- mShadeController.animateCollapsePanels(flags, force, false /* delayed */,
- 1.0f /* speedUpFactor */);
- }
-
- /**
- * Called by {@link ShadeController} when it calls
- * {@link ShadeController#animateCollapsePanels(int, boolean, boolean, float)}.
- */
- void postHideRecentApps() {
- if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
- mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
- mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
- }
+ mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL);
}
public boolean isExpandedVisible() {
@@ -2343,36 +2099,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- @Override
- public void animateExpandNotificationsPanel() {
- if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
- if (!mCommandQueue.panelsEnabled()) {
- return ;
- }
-
- mNotificationPanelViewController.expandWithoutQs();
-
- if (false) postStartTracing();
- }
-
- @Override
- public void animateExpandSettingsPanel(@Nullable String subPanel) {
- if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
- if (!mCommandQueue.panelsEnabled()) {
- return;
- }
-
- // Settings are not available in setup
- if (!mUserSetup) return;
-
- if (subPanel != null) {
- mQSPanelController.openDetails(subPanel);
- }
- mNotificationPanelViewController.expandWithQs();
-
- if (false) postStartTracing();
- }
-
public void animateCollapseQuickSettings() {
if (mState == StatusBarState.SHADE) {
mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
@@ -2452,11 +2178,7 @@ public class StatusBar extends SystemUI implements DemoMode,
final boolean upOrCancel =
event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL;
- if (upOrCancel && !mExpandedVisible) {
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
- } else {
- setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
- }
+ setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
}
return false;
}
@@ -2473,62 +2195,7 @@ public class StatusBar extends SystemUI implements DemoMode,
return mBiometricUnlockController;
}
- @Override // CommandQueue
- public void setWindowState(
- int displayId, @WindowType int window, @WindowVisibleState int state) {
- if (displayId != mDisplayId) {
- return;
- }
- boolean showing = state == WINDOW_STATE_SHOWING;
- if (mNotificationShadeWindowView != null
- && window == StatusBarManager.WINDOW_STATUS_BAR
- && mStatusBarWindowState != state) {
- mStatusBarWindowState = state;
- if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
- if (mStatusBarView != null) {
- if (!showing && mState == StatusBarState.SHADE) {
- mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
- 1.0f /* speedUpFactor */);
- }
- mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
- updateHideIconsForBouncer(false /* animate */);
- }
- }
-
- updateBubblesVisibility();
- }
-
- @Override
- public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
- AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
- if (displayId != mDisplayId) {
- return;
- }
- boolean barModeChanged = false;
- if (mAppearance != appearance) {
- mAppearance = appearance;
- barModeChanged = updateBarMode(barMode(mTransientShown, appearance));
- }
- mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
- mStatusBarMode, navbarColorManagedByIme);
-
- updateBubblesVisibility();
- mStatusBarStateController.setFullscreenState(isFullscreen);
- }
-
- @Override
- public void showTransient(int displayId, @InternalInsetsType int[] types) {
- if (displayId != mDisplayId) {
- return;
- }
- if (!containsType(types, ITYPE_STATUS_BAR)) {
- return;
- }
- showTransientUnchecked();
- }
-
- private void showTransientUnchecked() {
+ void showTransientUnchecked() {
if (!mTransientShown) {
mTransientShown = true;
mNoAnimationOnNextBarModeChange = true;
@@ -2536,18 +2203,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- @Override
- public void abortTransient(int displayId, @InternalInsetsType int[] types) {
- if (displayId != mDisplayId) {
- return;
- }
- if (!containsType(types, ITYPE_STATUS_BAR)) {
- return;
- }
- clearTransient();
- }
- private void clearTransient() {
+ void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
handleTransientChanged();
@@ -2589,8 +2246,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
- @Override
- public void showWirelessChargingAnimation(int batteryLevel) {
+ protected void showWirelessChargingAnimation(int batteryLevel) {
showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
}
@@ -2611,11 +2267,6 @@ public class StatusBar extends SystemUI implements DemoMode,
}, false, sUiEventLogger).show(animationDelay);
}
- @Override
- public void onRecentsAnimationStateChanged(boolean running) {
- setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
- }
-
protected BarTransitions getStatusBarTransitions() {
return mNotificationShadeWindowViewController.getBarTransitions();
}
@@ -2635,13 +2286,11 @@ public class StatusBar extends SystemUI implements DemoMode,
}
/** Temporarily hides Bubbles if the status bar is hidden. */
- private void updateBubblesVisibility() {
- if (mBubblesOptional.isPresent()) {
- mBubblesOptional.get().onStatusBarVisibilityChanged(
- mStatusBarMode != MODE_LIGHTS_OUT
- && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
- && !mStatusBarWindowHidden);
- }
+ void updateBubblesVisibility() {
+ mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
+ mStatusBarMode != MODE_LIGHTS_OUT
+ && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
+ && !mStatusBarWindowHidden));
}
void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2725,7 +2374,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanelViewController.dump(fd, pw, args);
}
pw.println(" mStackScroller: ");
- if (mStackScroller instanceof Dumpable) {
+ if (mStackScroller != null) {
pw.print (" ");
((Dumpable) mStackScroller).dump(fd, pw, args);
}
@@ -2758,19 +2407,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationsController.dump(fd, pw, args, DUMPTRUCK);
- if (DUMPTRUCK) {
- if (false) {
- pw.println("see the logcat for a dump of the views we have created.");
- // must happen on ui thread
- mHandler.post(() -> {
- mStatusBarView.getLocationOnScreen(mAbsPos);
- Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] +
- ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight());
- mStatusBarView.debug();
- });
- }
- }
-
if (DEBUG_GESTURES) {
pw.print(" status bar gestures: ");
mGestureRec.dump(fd, pw, args);
@@ -2801,7 +2437,7 @@ public class StatusBar extends SystemUI implements DemoMode,
pw.println(" Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
pw.println(" Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
pw.println(" Override package: "
- + String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
+ + CameraIntents.getOverrideCameraPackage(mContext));
}
public static void dumpBarTransitions(
@@ -2862,7 +2498,7 @@ public class StatusBar extends SystemUI implements DemoMode,
startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
}
- private void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+ void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
final Callback callback, int flags,
@Nullable ActivityLaunchAnimator.Controller animationController) {
@@ -2907,7 +2543,7 @@ public class StatusBar extends SystemUI implements DemoMode,
options.setRotationAnimationHint(
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
}
- if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
+ if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) {
// Settings Panel is implemented as activity(not a dialog), so
// underlying app is paused and may enter picture-in-picture mode
// as a result.
@@ -3006,7 +2642,7 @@ public class StatusBar extends SystemUI implements DemoMode,
&& mStatusBarKeyguardViewManager.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
} else {
- AsyncTask.execute(runnable);
+ mMainExecutor.execute(runnable);
}
}
if (dismissShade) {
@@ -3018,7 +2654,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// Do it after DismissAction has been processed to conserve the needed
// ordering.
- mHandler.post(mShadeController::runPostCollapseRunnables);
+ mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
}
} else if (StatusBar.this.isInLaunchTransition()
&& mNotificationPanelViewController.isLaunchTransitionFinished()) {
@@ -3027,7 +2663,7 @@ public class StatusBar extends SystemUI implements DemoMode,
// finished,
// so nobody will call readyForKeyguardDone anymore. Post it such that
// keyguardDonePending gets called first.
- mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+ mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone);
}
return deferred;
}
@@ -3048,9 +2684,7 @@ public class StatusBar extends SystemUI implements DemoMode,
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
KeyboardShortcuts.dismiss();
- if (mRemoteInputManager.getController() != null) {
- mRemoteInputManager.getController().closeRemoteInputs();
- }
+ mRemoteInputManager.closeRemoteInputs();
if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
mBubblesOptional.get().collapseStack();
}
@@ -3068,8 +2702,8 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationShadeWindowController.setNotTouchable(false);
}
if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
- // Post to main thread handler, since updating the UI.
- mMainThreadHandler.post(() -> mBubblesOptional.get().collapseStack());
+ // Post to main thread, since updating the UI.
+ mMainExecutor.execute(() -> mBubblesOptional.get().collapseStack());
}
finishBarAnimations();
resetUserExpandedStates();
@@ -3130,20 +2764,6 @@ public class StatusBar extends SystemUI implements DemoMode,
action.onDismiss();
}
}
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateResources();
- updateDisplaySize(); // populates mDisplayMetrics
-
- if (DEBUG) {
- Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
- }
-
- mViewHierarchyManager.updateRowStates();
- mScreenPinningRequest.onConfigurationChanged();
- }
-
/**
* Notify the shade controller that the current user changed
*
@@ -3291,43 +2911,12 @@ public class StatusBar extends SystemUI implements DemoMode,
| ((currentlyInsecure ? 1 : 0) << 12);
}
- //
- // tracing
- //
-
- void postStartTracing() {
- mHandler.postDelayed(mStartTracing, 3000);
- }
-
- void vibrate() {
- android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
- Context.VIBRATOR_SERVICE);
- vib.vibrate(250, VIBRATION_ATTRIBUTES);
- }
-
- final Runnable mStartTracing = new Runnable() {
- @Override
- public void run() {
- vibrate();
- SystemClock.sleep(250);
- Log.d(TAG, "startTracing");
- android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
- mHandler.postDelayed(mStopTracing, 10000);
- }
- };
-
- final Runnable mStopTracing = () -> {
- android.os.Debug.stopMethodTracing();
- Log.d(TAG, "stopTracing");
- vibrate();
- };
-
@Override
public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
- mHandler.post(() -> {
+ mMainExecutor.execute(() -> {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
- executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false,
- false);
+ executeRunnableDismissingKeyguard(
+ () -> mMainExecutor.execute(runnable), null, false, false, false);
});
}
@@ -3339,7 +2928,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void postStartActivityDismissingKeyguard(final PendingIntent intent,
@Nullable ActivityLaunchAnimator.Controller animationController) {
- mHandler.post(() -> startPendingIntentDismissingKeyguard(intent,
+ mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent,
null /* intentSentUiThreadCallback */, animationController));
}
@@ -3351,7 +2940,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityLaunchAnimator.Controller animationController) {
- mHandler.postDelayed(
+ mMainExecutor.executeDelayed(
() ->
startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
true /* dismissShade */,
@@ -3362,82 +2951,6 @@ public class StatusBar extends SystemUI implements DemoMode,
delay);
}
- @Override
- public List<String> demoCommands() {
- List<String> s = new ArrayList<>();
- s.add(DemoMode.COMMAND_BARS);
- s.add(DemoMode.COMMAND_CLOCK);
- s.add(DemoMode.COMMAND_OPERATOR);
- return s;
- }
-
- @Override
- public void onDemoModeStarted() {
- // Must send this message to any view that we delegate to via dispatchDemoCommandToView
- dispatchDemoModeStartedToView(R.id.clock);
- dispatchDemoModeStartedToView(R.id.operator_name);
- }
-
- @Override
- public void onDemoModeFinished() {
- dispatchDemoModeFinishedToView(R.id.clock);
- dispatchDemoModeFinishedToView(R.id.operator_name);
- checkBarModes();
- }
-
- @Override
- public void dispatchDemoCommand(String command, @NonNull Bundle args) {
- if (command.equals(COMMAND_CLOCK)) {
- dispatchDemoCommandToView(command, args, R.id.clock);
- }
- if (command.equals(COMMAND_BARS)) {
- String mode = args.getString("mode");
- int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
- "translucent".equals(mode) ? MODE_TRANSLUCENT :
- "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
- "transparent".equals(mode) ? MODE_TRANSPARENT :
- "warning".equals(mode) ? MODE_WARNING :
- -1;
- if (barMode != -1) {
- boolean animate = true;
- if (mNotificationShadeWindowController != null
- && mNotificationShadeWindowViewController.getBarTransitions() != null) {
- mNotificationShadeWindowViewController.getBarTransitions().transitionTo(
- barMode, animate);
- }
- mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
- }
- }
- if (command.equals(COMMAND_OPERATOR)) {
- dispatchDemoCommandToView(command, args, R.id.operator_name);
- }
- }
-
- //TODO: these should have controllers, and this method should be removed
- private void dispatchDemoCommandToView(String command, Bundle args, int id) {
- if (mStatusBarView == null) return;
- View v = mStatusBarView.findViewById(id);
- if (v instanceof DemoModeCommandReceiver) {
- ((DemoModeCommandReceiver) v).dispatchDemoCommand(command, args);
- }
- }
-
- private void dispatchDemoModeStartedToView(int id) {
- if (mStatusBarView == null) return;
- View v = mStatusBarView.findViewById(id);
- if (v instanceof DemoModeCommandReceiver) {
- ((DemoModeCommandReceiver) v).onDemoModeStarted();
- }
- }
-
- private void dispatchDemoModeFinishedToView(int id) {
- if (mStatusBarView == null) return;
- View v = mStatusBarView.findViewById(id);
- if (v instanceof DemoModeCommandReceiver) {
- ((DemoModeCommandReceiver) v).onDemoModeFinished();
- }
- }
-
public void showKeyguard() {
mStatusBarStateController.setKeyguardRequested(true);
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
@@ -3497,7 +3010,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanelViewController.cancelAnimation();
onLaunchTransitionFadingEnded();
}
- mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
} else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
@@ -3538,7 +3051,7 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
Runnable endRunnable) {
- mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
mLaunchTransitionEndRunnable = endRunnable;
Runnable hideRunnable = () -> {
mKeyguardStateController.setLaunchTransitionFadingAway(true);
@@ -3578,7 +3091,8 @@ public class StatusBar extends SystemUI implements DemoMode,
*/
public void animateKeyguardUnoccluding() {
mNotificationPanelViewController.setExpandedFraction(0f);
- animateExpandNotificationsPanel();
+ mCommandQueueCallbacks.animateExpandNotificationsPanel();
+ mScrimController.setUnocclusionAnimationRunning(true);
}
/**
@@ -3587,8 +3101,8 @@ public class StatusBar extends SystemUI implements DemoMode,
* because the launched app crashed or something else went wrong.
*/
public void startLaunchTransitionTimeout() {
- mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT,
- LAUNCH_TRANSITION_TIMEOUT_MS);
+ mMessageRouter.sendMessageDelayed(
+ MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS);
}
private void onLaunchTransitionTimeout() {
@@ -3643,7 +3157,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mQSPanelController != null) {
mQSPanelController.refreshAllTiles();
}
- mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
releaseGestureWakeLock();
mNotificationPanelViewController.onAffordanceLaunchEnded();
mNotificationPanelViewController.cancelAnimation();
@@ -3749,7 +3263,7 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* While IME is active and a BACK event is detected, check with
- * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme(KeyEvent)} to see if the event
+ * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
* should be handled before routing to IME, in order to prevent the user having to hit back
* twice to exit bouncer.
*/
@@ -3871,69 +3385,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanelViewController.collapseWithDuration(duration);
}
- @Override
- public void onStatePreChange(int oldState, int newState) {
- // If we're visible and switched to SHADE_LOCKED (the user dragged
- // down on the lockscreen), clear notification LED, vibration,
- // ringing.
- // Other transitions are covered in handleVisibleToUserChanged().
- if (mVisible && (newState == StatusBarState.SHADE_LOCKED
- || mStatusBarStateController.goingToFullShade())) {
- clearNotificationEffects();
- }
- if (newState == StatusBarState.KEYGUARD) {
- mRemoteInputManager.onPanelCollapsed();
- maybeEscalateHeadsUp();
- }
- }
-
- @Override
- public void onStateChanged(int newState) {
- mState = newState;
- updateReportRejectedTouchVisibility();
- mDozeServiceHost.updateDozing();
- updateTheme();
- mNavigationBarController.touchAutoDim(mDisplayId);
- Trace.beginSection("StatusBar#updateKeyguardState");
- if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
- mStatusBarView.removePendingHideExpandedRunnables();
- }
- updateDozingState();
- checkBarModes();
- updateScrimController();
- mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
- updateKeyguardState();
- Trace.endSection();
- }
-
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mFeatureFlags.useNewLockscreenAnimations()
- && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
- mLightRevealScrim.setRevealAmount(1f - linear);
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- Trace.beginSection("StatusBar#updateDozing");
- mDozing = isDozing;
-
- // Collapse the notification panel if open
- boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
- && mDozeParameters.shouldControlScreenOff();
- mNotificationPanelViewController.resetViews(dozingAnimated);
-
- updateQsExpansionEnabled();
- mKeyguardViewMediator.setDozing(mDozing);
- mNotificationsController.requestNotificationUpdate("onDozingChanged");
- updateDozingState();
- mDozeServiceHost.updateDozing();
- updateScrimController();
- updateReportRejectedTouchVisibility();
- Trace.endSection();
- }
/**
* Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
@@ -4075,7 +3527,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// This gets executed before we will show Keyguard, so post it in order that the state
// is correct.
- mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource));
+ mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected(
+ mLastCameraLaunchSource));
}
if (mLaunchEmergencyActionOnFinishedGoingToSleep) {
@@ -4083,7 +3536,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// This gets executed before we will show Keyguard, so post it in order that the
// state is correct.
- mHandler.post(() -> onEmergencyActionLaunchGestureDetected());
+ mMainExecutor.execute(
+ () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected());
}
updateIsKeyguard();
}
@@ -4196,36 +3650,6 @@ public class StatusBar extends SystemUI implements DemoMode,
return mWakefulnessLifecycle.getWakefulness();
}
- private void vibrateForCameraGesture() {
- // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
- mVibrator.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES);
- }
-
- private static VibrationEffect getCameraGestureVibrationEffect(Vibrator vibrator,
- Resources resources) {
- if (vibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_CLICK)) {
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 50)
- .compose();
- }
- if (vibrator.hasAmplitudeControl()) {
- return VibrationEffect.createWaveform(
- CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
- CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
- /* repeat= */ -1);
- }
-
- int[] pattern = resources.getIntArray(R.array.config_cameraLaunchGestureVibePattern);
- long[] timings = new long[pattern.length];
- for (int i = 0; i < pattern.length; i++) {
- timings[i] = pattern[i];
- }
- return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
- }
-
/**
* @return true if the screen is currently fully off, i.e. has finished turning off and has
* since not started turning on.
@@ -4234,139 +3658,11 @@ public class StatusBar extends SystemUI implements DemoMode,
return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
}
- @Override
- public void showScreenPinningRequest(int taskId) {
- if (mKeyguardStateController.isShowing()) {
- // Don't allow apps to trigger this from keyguard.
- return;
- }
- // Show screen pinning request, since this comes from an app, show 'no thanks', button.
- showScreenPinningRequest(taskId, true);
- }
-
public void showScreenPinningRequest(int taskId, boolean allowCancel) {
mScreenPinningRequest.showPrompt(taskId, allowCancel);
}
- @Override
- public void appTransitionCancelled(int displayId) {
- if (displayId == mDisplayId) {
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
- }
- }
-
- @Override
- public void appTransitionFinished(int displayId) {
- if (displayId == mDisplayId) {
- mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
- }
- }
-
- @Override
- public void onCameraLaunchGestureDetected(int source) {
- mLastCameraLaunchSource = source;
- if (isGoingToSleep()) {
- if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera");
- mLaunchCameraOnFinishedGoingToSleep = true;
- return;
- }
- if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
- if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
- return;
- }
- if (!mDeviceInteractive) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
- "com.android.systemui:CAMERA_GESTURE");
- }
- vibrateForCameraGesture();
-
- if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
- Log.v(TAG, "Camera launch");
- mKeyguardUpdateMonitor.onCameraLaunched();
- }
-
- if (!mStatusBarKeyguardViewManager.isShowing()) {
- final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
- startActivityDismissingKeyguard(cameraIntent,
- false /* onlyProvisioned */, true /* dismissShade */,
- true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */);
- } else {
- if (!mDeviceInteractive) {
- // Avoid flickering of the scrim when we instant launch the camera and the bouncer
- // comes on.
- mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
- }
- if (isWakingUpOrAwake()) {
- if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
- if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.reset(true /* hide */);
- }
- mNotificationPanelViewController.launchCamera(
- mDeviceInteractive /* animate */, source);
- updateScrimController();
- } else {
- // We need to defer the camera launch until the screen comes on, since otherwise
- // we will dismiss us too early since we are waiting on an activity to be drawn and
- // incorrectly get notified because of the screen on event (which resumes and pauses
- // some activities)
- if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
- mLaunchCameraWhenFinishedWaking = true;
- }
- }
- }
-
- @Override
- public void onEmergencyActionLaunchGestureDetected() {
- Intent emergencyIntent = getEmergencyActionIntent();
-
- if (emergencyIntent == null) {
- Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
- return;
- }
-
- if (isGoingToSleep()) {
- mLaunchEmergencyActionOnFinishedGoingToSleep = true;
- return;
- }
-
- if (!mDeviceInteractive) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(),
- PowerManager.WAKE_REASON_GESTURE,
- "com.android.systemui:EMERGENCY_GESTURE");
- }
- // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
- // app-side haptic experimentation.
-
- if (!mStatusBarKeyguardViewManager.isShowing()) {
- startActivityDismissingKeyguard(emergencyIntent,
- false /* onlyProvisioned */, true /* dismissShade */,
- true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
- null /* animationController */);
- return;
- }
-
- if (!mDeviceInteractive) {
- // Avoid flickering of the scrim when we instant launch the camera and the bouncer
- // comes on.
- mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
- }
-
- if (isWakingUpOrAwake()) {
- if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.reset(true /* hide */);
- }
- mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT);
- return;
- }
- // We need to defer the emergency action launch until the screen comes on, since otherwise
- // we will dismiss us too early since we are waiting on an activity to be drawn and
- // incorrectly get notified because of the screen on event (which resumes and pauses
- // some activities)
- mLaunchEmergencyActionWhenFinishedWaking = true;
- }
-
- private @Nullable Intent getEmergencyActionIntent() {
+ @Nullable Intent getEmergencyActionIntent() {
Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
@@ -4424,16 +3720,11 @@ public class StatusBar extends SystemUI implements DemoMode,
return true;
}
- private boolean isGoingToSleep() {
+ boolean isGoingToSleep() {
return mWakefulnessLifecycle.getWakefulness()
== WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
}
- private boolean isWakingUpOrAwake() {
- return mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
- || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
- }
-
public void notifyBiometricAuthModeChanged() {
mDozeServiceHost.updateDozing();
updateScrimController();
@@ -4465,10 +3756,8 @@ public class StatusBar extends SystemUI implements DemoMode,
ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
mScrimController.transitionTo(state);
- } else if (isInLaunchTransition()
- || mLaunchCameraWhenFinishedWaking
- || launchingAffordanceWithPreview) {
- // TODO(b/170133395) Investigate whether Emergency Gesture flag should be included here.
+ } else if (launchingAffordanceWithPreview) {
+ // We want to avoid animating when launching with a preview.
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
@@ -4488,8 +3777,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
- } else if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
- mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED, mUnlockScrimCallback);
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
@@ -4586,10 +3873,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
- @Override
- public void toggleSplitScreen() {
- toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
- }
public void awakenDreams() {
mUiBgExecutor.execute(() -> {
@@ -4601,46 +3884,6 @@ public class StatusBar extends SystemUI implements DemoMode,
});
}
- @Override
- public void preloadRecentApps() {
- int msg = MSG_PRELOAD_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void cancelPreloadRecentApps() {
- int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void dismissKeyboardShortcutsMenu() {
- int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
- mHandler.removeMessages(msg);
- mHandler.sendEmptyMessage(msg);
- }
-
- @Override
- public void toggleKeyboardShortcutsMenu(int deviceId) {
- int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
- mHandler.removeMessages(msg);
- mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
- }
-
- @Override
- public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
- mTopHidesStatusBar = topAppHidesStatusBar;
- if (!topAppHidesStatusBar && mWereIconsJustHidden) {
- // Immediately update the icon hidden state, since that should only apply if we're
- // staying fullscreen.
- mWereIconsJustHidden = false;
- mCommandQueue.recomputeDisableFlags(mDisplayId, true);
- }
- updateHideIconsForBouncer(true /* animate */);
- }
-
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
}
@@ -4760,7 +4003,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
private void postOnUiThread(Runnable runnable) {
- mMainThreadHandler.post(runnable);
+ mMainExecutor.execute(runnable);
}
/**
@@ -4902,34 +4145,19 @@ public class StatusBar extends SystemUI implements DemoMode,
}
return mStatusBarKeyguardViewManager.isSecure();
}
-
- @Override
- public void showAssistDisclosure() {
- mAssistManagerLazy.get().showDisclosure();
- }
-
public NotificationPanelViewController getPanelController() {
return mNotificationPanelViewController;
}
-
- @Override
- public void startAssist(Bundle args) {
- mAssistManagerLazy.get().startAssist(args);
- }
// End Extra BaseStatusBarMethods.
public NotificationGutsManager getGutsManager() {
return mGutsManager;
}
- private boolean isTransientShown() {
+ boolean isTransientShown() {
return mTransientShown;
}
- @Override
- public void suppressAmbientDisplay(boolean suppressed) {
- mDozeServiceHost.setDozeSuppressed(suppressed);
- }
public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
mExpansionChangedListeners.add(listener);
@@ -4953,4 +4181,277 @@ public class StatusBar extends SystemUI implements DemoMode,
mLightRevealScrim.setVisibility(View.GONE);
}
}
+
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ if (dreaming) {
+ maybeEscalateHeadsUp();
+ }
+ }
+
+ // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+ // KeyguardCoordinator
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ super.onStrongAuthStateChanged(userId);
+ mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
+ }
+ };
+
+
+ private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
+ new FalsingManager.FalsingBeliefListener() {
+ @Override
+ public void onFalse() {
+ // Hides quick settings, bouncer, and quick-quick settings.
+ mStatusBarKeyguardViewManager.reset(true);
+ }
+ };
+
+ // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
+ // this animation is tied to the scrim for historic reasons.
+ // TODO: notify when keyguard has faded away instead of the scrim.
+ private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
+ .Callback() {
+ @Override
+ public void onFinished() {
+ if (mStatusBarKeyguardViewManager == null) {
+ Log.w(TAG, "Tried to notify keyguard visibility when "
+ + "mStatusBarKeyguardViewManager was null");
+ return;
+ }
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ onFinished();
+ }
+ };
+
+ private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ final boolean userSetup = mDeviceProvisionedController.isUserSetup(
+ mDeviceProvisionedController.getCurrentUser());
+ Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
+ + mDeviceProvisionedController.getCurrentUser());
+ if (MULTIUSER_DEBUG) {
+ Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
+ userSetup, mUserSetup));
+ }
+
+ if (userSetup != mUserSetup) {
+ mUserSetup = userSetup;
+ if (!mUserSetup && mStatusBarView != null) {
+ animateCollapseQuickSettings();
+ }
+ if (mNotificationPanelViewController != null) {
+ mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+ }
+ updateQsExpansionEnabled();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mWallpaperSupported) {
+ // Receiver should not have been registered at all...
+ Log.wtf(TAG, "WallpaperManager not supported");
+ return;
+ }
+ WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
+ final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+ // If WallpaperInfo is null, it must be ImageWallpaper.
+ final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+ && (info != null && info.supportsAmbientMode());
+
+ mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ }
+ };
+
+ BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mBubblesOptional.ifPresent(bubbles -> bubbles.onTaskbarChanged(intent.getExtras()));
+ }
+ };
+
+ private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateResources();
+ updateDisplaySize(); // populates mDisplayMetrics
+
+ if (DEBUG) {
+ Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+ }
+
+ mViewHierarchyManager.updateRowStates();
+ mScreenPinningRequest.onConfigurationChanged();
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ // TODO: Remove this.
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onDensityOrFontScaleChanged();
+ }
+ // TODO: Bring these out of StatusBar.
+ mUserInfoControllerImpl.onDensityOrFontScaleChanged();
+ mUserSwitcherController.onDensityOrFontScaleChanged();
+ mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+ mHeadsUpManager.onDensityOrFontScaleChanged();
+ }
+
+ @Override
+ public void onThemeChanged() {
+ if (mStatusBarKeyguardViewManager != null) {
+ mStatusBarKeyguardViewManager.onThemeChanged();
+ }
+ if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
+ ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
+ }
+ mNotificationIconAreaController.onThemeChanged();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onOverlayChanged();
+ }
+ // We need the new R.id.keyguard_indication_area before recreating
+ // mKeyguardIndicationController
+ mNotificationPanelViewController.onThemeChanged();
+ onThemeChanged();
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onUiModeChanged();
+ }
+ }
+ };
+
+ private StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStatePreChange(int oldState, int newState) {
+ // If we're visible and switched to SHADE_LOCKED (the user dragged
+ // down on the lockscreen), clear notification LED, vibration,
+ // ringing.
+ // Other transitions are covered in handleVisibleToUserChanged().
+ if (mVisible && (newState == StatusBarState.SHADE_LOCKED
+ || mStatusBarStateController.goingToFullShade())) {
+ clearNotificationEffects();
+ }
+ if (newState == StatusBarState.KEYGUARD) {
+ mRemoteInputManager.onPanelCollapsed();
+ maybeEscalateHeadsUp();
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ mState = newState;
+ updateReportRejectedTouchVisibility();
+ mDozeServiceHost.updateDozing();
+ updateTheme();
+ mNavigationBarController.touchAutoDim(mDisplayId);
+ Trace.beginSection("StatusBar#updateKeyguardState");
+ if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+ mStatusBarView.removePendingHideExpandedRunnables();
+ }
+ updateDozingState();
+ checkBarModes();
+ updateScrimController();
+ mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
+ updateKeyguardState();
+ Trace.endSection();
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ if (mFeatureFlags.useNewLockscreenAnimations()
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
+ mLightRevealScrim.setRevealAmount(1f - linear);
+ }
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ Trace.beginSection("StatusBar#updateDozing");
+ mDozing = isDozing;
+
+ // Collapse the notification panel if open
+ boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
+ && mDozeParameters.shouldControlScreenOff();
+ mNotificationPanelViewController.resetViews(dozingAnimated);
+
+ updateQsExpansionEnabled();
+ mKeyguardViewMediator.setDozing(mDozing);
+
+ mNotificationsController.requestNotificationUpdate("onDozingChanged");
+ updateDozingState();
+ mDozeServiceHost.updateDozing();
+ updateScrimController();
+ updateReportRejectedTouchVisibility();
+ Trace.endSection();
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onPowerSaveChanged(boolean isPowerSave) {
+ mMainExecutor.execute(mCheckBarModes);
+ if (mDozeServiceHost != null) {
+ mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+ }
+ }
+ };
+
+ private final ActivityLaunchAnimator.Callback mKeyguardHandler =
+ new ActivityLaunchAnimator.Callback() {
+ @Override
+ public boolean isOnKeyguard() {
+ return mKeyguardStateController.isShowing();
+ }
+
+ @Override
+ public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
+ // We post to the main thread for 2 reasons:
+ // 1. KeyguardViewMediator is not thread-safe.
+ // 2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
+ // ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur
+ // when doing
+ // dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
+ mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner));
+ }
+
+ @Override
+ public void setBlursDisabledForAppLaunch(boolean disabled) {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
+ }
+
+ @Override
+ public int getBackgroundColor(TaskInfo task) {
+ if (!mStartingSurfaceOptional.isPresent()) {
+ Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
+ return SplashscreenContentDrawer.getSystemBGColor();
+ }
+
+ return mStartingSurfaceOptional.get().getBackgroundColor(task);
+ }
+ };
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
new file mode 100644
index 000000000000..6a510c92a3a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.phone;
+
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.windowStateToString;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.containsType;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+
+import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.AudioAttributes;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Log;
+import android.util.Slog;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
+import android.view.KeyEvent;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.view.AppearanceRegion;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.camera.CameraIntents;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+/** */
+@StatusBarComponent.StatusBarScope
+public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
+ private final StatusBar mStatusBar;
+ private final Context mContext;
+ private final ShadeController mShadeController;
+ private final CommandQueue mCommandQueue;
+ private final NotificationPanelViewController mNotificationPanelViewController;
+ private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private final MetricsLogger mMetricsLogger;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardStateController mKeyguardStateController;
+ private final HeadsUpManager mHeadsUpManager;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private final AssistManager mAssistManager;
+ private final DozeServiceHost mDozeServiceHost;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotificationShadeWindowView mNotificationShadeWindowView;
+ private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ private final PowerManager mPowerManager;
+ private final VibratorHelper mVibratorHelper;
+ private final Optional<Vibrator> mVibratorOptional;
+ private final LightBarController mLightBarController;
+ private final int mDisplayId;
+ private final boolean mVibrateOnOpening;
+ private final VibrationEffect mCameraLaunchGestureVibrationEffect;
+
+
+ private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build();
+
+ @Inject
+ StatusBarCommandQueueCallbacks(
+ StatusBar statusBar,
+ Context context,
+ @Main Resources resources,
+ ShadeController shadeController,
+ CommandQueue commandQueue,
+ NotificationPanelViewController notificationPanelViewController,
+ Optional<LegacySplitScreen> splitScreenOptional,
+ RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ MetricsLogger metricsLogger,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController,
+ HeadsUpManager headsUpManager,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ DeviceProvisionedController deviceProvisionedController,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ AssistManager assistManager,
+ DozeServiceHost dozeServiceHost,
+ SysuiStatusBarStateController statusBarStateController,
+ NotificationShadeWindowView notificationShadeWindowView,
+ NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+ PowerManager powerManager,
+ VibratorHelper vibratorHelper,
+ Optional<Vibrator> vibratorOptional,
+ LightBarController lightBarController,
+ @DisplayId int displayId) {
+
+ mStatusBar = statusBar;
+ mContext = context;
+ mShadeController = shadeController;
+ mCommandQueue = commandQueue;
+ mNotificationPanelViewController = notificationPanelViewController;
+ mSplitScreenOptional = splitScreenOptional;
+ mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
+ mMetricsLogger = metricsLogger;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStateController = keyguardStateController;
+ mHeadsUpManager = headsUpManager;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mAssistManager = assistManager;
+ mDozeServiceHost = dozeServiceHost;
+ mStatusBarStateController = statusBarStateController;
+ mNotificationShadeWindowView = notificationShadeWindowView;
+ mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+ mPowerManager = powerManager;
+ mVibratorHelper = vibratorHelper;
+ mVibratorOptional = vibratorOptional;
+ mLightBarController = lightBarController;
+ mDisplayId = displayId;
+
+ mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
+ mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
+ mVibratorOptional, resources);
+ }
+
+ @Override
+ public void abortTransient(int displayId, @InternalInsetsType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, ITYPE_STATUS_BAR)) {
+ return;
+ }
+ mStatusBar.clearTransient();
+ }
+
+ @Override
+ public void addQsTile(ComponentName tile) {
+ QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ if (qsPanelController != null && qsPanelController.getHost() != null) {
+ qsPanelController.getHost().addTile(tile);
+ }
+ }
+
+ @Override
+ public void remQsTile(ComponentName tile) {
+ QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ if (qsPanelController != null && qsPanelController.getHost() != null) {
+ qsPanelController.getHost().removeTile(tile);
+ }
+ }
+
+ @Override
+ public void clickTile(ComponentName tile) {
+ QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ if (qsPanelController != null) {
+ qsPanelController.clickTile(tile);
+ }
+ }
+
+ @Override
+ public void animateCollapsePanels(int flags, boolean force) {
+ mShadeController.animateCollapsePanels(flags, force, false /* delayed */,
+ 1.0f /* speedUpFactor */);
+ }
+
+ @Override
+ public void animateExpandNotificationsPanel() {
+ if (StatusBar.SPEW) {
+ Log.d(StatusBar.TAG,
+ "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ }
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
+
+ mNotificationPanelViewController.expandWithoutQs();
+ }
+
+ @Override
+ public void animateExpandSettingsPanel(@Nullable String subPanel) {
+ if (StatusBar.SPEW) {
+ Log.d(StatusBar.TAG,
+ "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ }
+ if (!mCommandQueue.panelsEnabled()) {
+ return;
+ }
+
+ // Settings are not available in setup
+ if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
+
+
+ QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ if (subPanel != null && qsPanelController != null) {
+ qsPanelController.openDetails(subPanel);
+ }
+ mNotificationPanelViewController.expandWithQs();
+ }
+
+ @Override
+ public void appTransitionCancelled(int displayId) {
+ if (displayId == mDisplayId) {
+ mSplitScreenOptional.ifPresent(LegacySplitScreen::onAppTransitionFinished);
+ }
+ }
+
+ @Override
+ public void appTransitionFinished(int displayId) {
+ if (displayId == mDisplayId) {
+ mSplitScreenOptional.ifPresent(LegacySplitScreen::onAppTransitionFinished);
+ }
+ }
+
+ @Override
+ public void dismissKeyboardShortcutsMenu() {
+ mStatusBar.resendMessage(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
+ }
+ /**
+ * State is one or more of the DISABLE constants from StatusBarManager.
+ */
+ @Override
+ public void disable(int displayId, int state1, int state2, boolean animate) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+
+ final int old1 = mStatusBar.getDisabled1();
+ final int diff1 = state1 ^ old1;
+ mStatusBar.setDisabled1(state1);
+
+ final int old2 = mStatusBar.getDisabled2();
+ final int diff2 = state2 ^ old2;
+ mStatusBar.setDisabled2(state2);
+
+ if (StatusBar.DEBUG) {
+ Log.d(StatusBar.TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
+ old1, state1, diff1));
+ Log.d(StatusBar.TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
+ old2, state2, diff2));
+ }
+
+ StringBuilder flagdbg = new StringBuilder();
+ flagdbg.append("disable<");
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
+ flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
+ flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
+ flagdbg.append("> disable2<");
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
+ flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
+ flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
+ flagdbg.append('>');
+ Log.d(StatusBar.TAG, flagdbg.toString());
+
+ if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
+ mShadeController.animateCollapsePanels();
+ }
+ }
+
+ if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
+ if (mStatusBar.areNotificationAlertsDisabled()) {
+ mHeadsUpManager.releaseAllImmediately();
+ }
+ }
+
+ if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
+ mStatusBar.updateQsExpansionEnabled();
+ }
+
+ if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ mStatusBar.updateQsExpansionEnabled();
+ if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+ mShadeController.animateCollapsePanels();
+ }
+ }
+ }
+
+ /**
+ * Called for system navigation gestures. First action opens the panel, second opens
+ * settings. Down action closes the entire panel.
+ */
+ @Override
+ public void handleSystemKey(int key) {
+ if (StatusBar.SPEW) {
+ Log.d(StatusBar.TAG, "handleNavigationKey: " + key);
+ }
+ if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
+ || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
+ return;
+ }
+
+ // Panels are not available in setup
+ if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
+
+ if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+ mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
+ mNotificationPanelViewController.collapse(
+ false /* delayed */, 1.0f /* speedUpFactor */);
+ } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+ mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
+ if (mNotificationPanelViewController.isFullyCollapsed()) {
+ if (mVibrateOnOpening) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ }
+ mNotificationPanelViewController.expand(true /* animate */);
+ mNotificationStackScrollLayoutController.setWillExpand(true);
+ mHeadsUpManager.unpinAll(true /* userUnpinned */);
+ mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
+ } else if (!mNotificationPanelViewController.isInSettings()
+ && !mNotificationPanelViewController.isExpanding()) {
+ mNotificationPanelViewController.flingSettings(0 /* velocity */,
+ NotificationPanelView.FLING_EXPAND);
+ mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
+ }
+ }
+
+ }
+
+ @Override
+ public void onCameraLaunchGestureDetected(int source) {
+ mStatusBar.setLastCameraLaunchSource(source);
+ if (mStatusBar.isGoingToSleep()) {
+ if (StatusBar.DEBUG_CAMERA_LIFT) {
+ Slog.d(StatusBar.TAG, "Finish going to sleep before launching camera");
+ }
+ mStatusBar.setLaunchCameraOnFinishedGoingToSleep(true);
+ return;
+ }
+ if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
+ if (StatusBar.DEBUG_CAMERA_LIFT) {
+ Slog.d(StatusBar.TAG, "Can't launch camera right now");
+ }
+ return;
+ }
+ if (!mStatusBar.isDeviceInteractive()) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+ "com.android.systemui:CAMERA_GESTURE");
+ }
+ vibrateForCameraGesture();
+
+ if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+ Log.v(StatusBar.TAG, "Camera launch");
+ mKeyguardUpdateMonitor.onCameraLaunched();
+ }
+
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
+ mStatusBar.startActivityDismissingKeyguard(cameraIntent,
+ false /* onlyProvisioned */, true /* dismissShade */,
+ true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+ null /* animationController */);
+ } else {
+ if (!mStatusBar.isDeviceInteractive()) {
+ // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+ // comes on.
+ mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ }
+ if (isWakingUpOrAwake()) {
+ if (StatusBar.DEBUG_CAMERA_LIFT) {
+ Slog.d(StatusBar.TAG, "Launching camera");
+ }
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.reset(true /* hide */);
+ }
+ mNotificationPanelViewController.launchCamera(
+ mStatusBar.isDeviceInteractive() /* animate */, source);
+ mStatusBar.updateScrimController();
+ } else {
+ // We need to defer the camera launch until the screen comes on, since otherwise
+ // we will dismiss us too early since we are waiting on an activity to be drawn and
+ // incorrectly get notified because of the screen on event (which resumes and pauses
+ // some activities)
+ if (StatusBar.DEBUG_CAMERA_LIFT) {
+ Slog.d(StatusBar.TAG, "Deferring until screen turns on");
+ }
+ mStatusBar.setLaunchCameraOnFinishedWaking(true);
+ }
+ }
+ }
+
+ @Override
+ public void onEmergencyActionLaunchGestureDetected() {
+ Intent emergencyIntent = mStatusBar.getEmergencyActionIntent();
+
+ if (emergencyIntent == null) {
+ Log.wtf(StatusBar.TAG, "Couldn't find an app to process the emergency intent.");
+ return;
+ }
+
+ if (isGoingToSleep()) {
+ mStatusBar.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
+ return;
+ }
+
+ if (!mStatusBar.isDeviceInteractive()) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:EMERGENCY_GESTURE");
+ }
+ // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
+ // app-side haptic experimentation.
+
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ mStatusBar.startActivityDismissingKeyguard(emergencyIntent,
+ false /* onlyProvisioned */, true /* dismissShade */,
+ true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+ null /* animationController */);
+ return;
+ }
+
+ if (!mStatusBar.isDeviceInteractive()) {
+ // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+ // comes on.
+ mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ }
+
+ if (isWakingUpOrAwake()) {
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.reset(true /* hide */);
+ }
+ mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT);
+ return;
+ }
+ // We need to defer the emergency action launch until the screen comes on, since otherwise
+ // we will dismiss us too early since we are waiting on an activity to be drawn and
+ // incorrectly get notified because of the screen on event (which resumes and pauses
+ // some activities)
+ mStatusBar.setLaunchEmergencyActionOnFinishedWaking(true);
+ }
+
+ @Override
+ public void onRecentsAnimationStateChanged(boolean running) {
+ mStatusBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
+ }
+
+
+ @Override
+ public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
+ AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ boolean barModeChanged = mStatusBar.setAppearance(appearance);
+
+ mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
+ mStatusBar.getBarMode(), navbarColorManagedByIme);
+
+ mStatusBar.updateBubblesVisibility();
+ mStatusBarStateController.setSystemBarAttributes(
+ appearance, behavior, requestedVisibilities, packageName);
+ }
+
+ @Override
+ public void showTransient(int displayId, @InternalInsetsType int[] types) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ if (!containsType(types, ITYPE_STATUS_BAR)) {
+ return;
+ }
+ mStatusBar.showTransientUnchecked();
+ }
+
+ @Override
+ public void toggleKeyboardShortcutsMenu(int deviceId) {
+ mStatusBar.resendMessage(new StatusBar.KeyboardShortcutsMessage(deviceId));
+ }
+
+ @Override
+ public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
+ mStatusBar.setTopHidesStatusBar(topAppHidesStatusBar);
+ if (!topAppHidesStatusBar && mStatusBar.getWereIconsJustHidden()) {
+ // Immediately update the icon hidden state, since that should only apply if we're
+ // staying fullscreen.
+ mStatusBar.setWereIconsJustHidden(false);
+ mCommandQueue.recomputeDisableFlags(mDisplayId, true);
+ }
+ mStatusBar.updateHideIconsForBouncer(true /* animate */);
+ }
+
+ @Override
+ public void setWindowState(
+ int displayId, @StatusBarManager.WindowType int window,
+ @StatusBarManager.WindowVisibleState int state) {
+ if (displayId != mDisplayId) {
+ return;
+ }
+ boolean showing = state == WINDOW_STATE_SHOWING;
+ if (mNotificationShadeWindowView != null
+ && window == StatusBarManager.WINDOW_STATUS_BAR
+ && !mStatusBar.isSameStatusBarState(state)) {
+ mStatusBar.setWindowState(state);
+ if (StatusBar.DEBUG_WINDOW_STATE) {
+ Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
+ }
+ if (mStatusBar.getStatusBarView() != null) {
+ if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+ mStatusBar.getStatusBarView().collapsePanel(
+ false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
+ }
+
+ mStatusBar.updateHideIconsForBouncer(false /* animate */);
+ }
+ }
+
+ mStatusBar.updateBubblesVisibility();
+ }
+
+ @Override
+ public void showAssistDisclosure() {
+ mAssistManager.showDisclosure();
+ }
+
+ @Override
+ public void showPinningEnterExitToast(boolean entering) {
+ if (mStatusBar.getNavigationBarView() != null) {
+ mStatusBar.getNavigationBarView().showPinningEnterExitToast(entering);
+ }
+ }
+
+ @Override
+ public void showPinningEscapeToast() {
+ if (mStatusBar.getNavigationBarView() != null) {
+ mStatusBar.getNavigationBarView().showPinningEscapeToast();
+ }
+ }
+
+ @Override
+ public void showScreenPinningRequest(int taskId) {
+ if (mKeyguardStateController.isShowing()) {
+ // Don't allow apps to trigger this from keyguard.
+ return;
+ }
+ // Show screen pinning request, since this comes from an app, show 'no thanks', button.
+ mStatusBar.showScreenPinningRequest(taskId, true);
+ }
+
+ @Override
+ public void showWirelessChargingAnimation(int batteryLevel) {
+ mStatusBar.showWirelessChargingAnimation(batteryLevel);
+ }
+
+ @Override
+ public void startAssist(Bundle args) {
+ mAssistManager.startAssist(args);
+ }
+
+ @Override
+ public void suppressAmbientDisplay(boolean suppressed) {
+ mDozeServiceHost.setDozeSuppressed(suppressed);
+ }
+
+ @Override
+ public void togglePanel() {
+ if (mStatusBar.isPanelExpanded()) {
+ mShadeController.animateCollapsePanels();
+ } else {
+ animateExpandNotificationsPanel();
+ }
+ }
+
+ @Override
+ public void toggleSplitScreen() {
+ mStatusBar.toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
+ }
+
+ private boolean isGoingToSleep() {
+ return mWakefulnessLifecycle.getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+ }
+
+ private boolean isWakingUpOrAwake() {
+ return mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
+ || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
+ }
+
+ private void vibrateForCameraGesture() {
+ // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
+ mVibratorOptional.ifPresent(
+ v -> v.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES));
+ }
+
+ private static VibrationEffect getCameraGestureVibrationEffect(
+ Optional<Vibrator> vibratorOptional, Resources resources) {
+ if (vibratorOptional.isPresent() && vibratorOptional.get().areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_CLICK)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 50)
+ .compose();
+ }
+ if (vibratorOptional.isPresent() && vibratorOptional.get().hasAmplitudeControl()) {
+ return VibrationEffect.createWaveform(
+ StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
+ StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
+ /* repeat= */ -1);
+ }
+
+ int[] pattern = resources.getIntArray(R.array.config_cameraLaunchGestureVibePattern);
+ long[] timings = new long[pattern.length];
+ for (int i = 0; i < pattern.length; i++) {
+ timings[i] = pattern[i];
+ }
+ return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index fe1f63a34acd..1dd22b468071 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
-import android.util.Log
import android.util.Pair
import android.view.DisplayCutout
import android.view.View.LAYOUT_DIRECTION_RTL
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
new file mode 100644
index 000000000000..e642b2e244e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.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.systemui.statusbar.phone;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.navigationbar.NavigationBarController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/** */
+@StatusBarComponent.StatusBarScope
+public class StatusBarDemoMode implements DemoMode {
+ private final StatusBar mStatusBar;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+ private final NavigationBarController mNavigationBarController;
+ private final int mDisplayId;
+
+ @Inject
+ StatusBarDemoMode(
+ StatusBar statusBar,
+ NotificationShadeWindowController notificationShadeWindowController,
+ NotificationShadeWindowViewController notificationShadeWindowViewController,
+ NavigationBarController navigationBarController,
+ @DisplayId int displayId) {
+ mStatusBar = statusBar;
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mNotificationShadeWindowViewController = notificationShadeWindowViewController;
+ mNavigationBarController = navigationBarController;
+ mDisplayId = displayId;
+ }
+
+ @Override
+ public List<String> demoCommands() {
+ List<String> s = new ArrayList<>();
+ s.add(DemoMode.COMMAND_BARS);
+ s.add(DemoMode.COMMAND_CLOCK);
+ s.add(DemoMode.COMMAND_OPERATOR);
+ return s;
+ }
+
+ @Override
+ public void onDemoModeStarted() {
+ // Must send this message to any view that we delegate to via dispatchDemoCommandToView
+ dispatchDemoModeStartedToView(R.id.clock);
+ dispatchDemoModeStartedToView(R.id.operator_name);
+ }
+
+ @Override
+ public void onDemoModeFinished() {
+ dispatchDemoModeFinishedToView(R.id.clock);
+ dispatchDemoModeFinishedToView(R.id.operator_name);
+ mStatusBar.checkBarModes();
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, @NonNull Bundle args) {
+ if (command.equals(COMMAND_CLOCK)) {
+ dispatchDemoCommandToView(command, args, R.id.clock);
+ }
+ if (command.equals(COMMAND_BARS)) {
+ String mode = args.getString("mode");
+ int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
+ "translucent".equals(mode) ? MODE_TRANSLUCENT :
+ "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
+ "transparent".equals(mode) ? MODE_TRANSPARENT :
+ "warning".equals(mode) ? MODE_WARNING :
+ -1;
+ if (barMode != -1) {
+ boolean animate = true;
+ if (mNotificationShadeWindowController != null
+ && mNotificationShadeWindowViewController.getBarTransitions() != null) {
+ mNotificationShadeWindowViewController.getBarTransitions().transitionTo(
+ barMode, animate);
+ }
+ mNavigationBarController.transitionTo(mDisplayId, barMode, animate);
+ }
+ }
+ if (command.equals(COMMAND_OPERATOR)) {
+ dispatchDemoCommandToView(command, args, R.id.operator_name);
+ }
+ }
+
+ private void dispatchDemoModeStartedToView(int id) {
+ View statusBarView = mStatusBar.getStatusBarView();
+ if (statusBarView == null) return;
+ View v = statusBarView.findViewById(id);
+ if (v instanceof DemoModeCommandReceiver) {
+ ((DemoModeCommandReceiver) v).onDemoModeStarted();
+ }
+ }
+
+ //TODO: these should have controllers, and this method should be removed
+ private void dispatchDemoCommandToView(String command, Bundle args, int id) {
+ View statusBarView = mStatusBar.getStatusBarView();
+ if (statusBarView == null) return;
+ View v = statusBarView.findViewById(id);
+ if (v instanceof DemoModeCommandReceiver) {
+ ((DemoModeCommandReceiver) v).dispatchDemoCommand(command, args);
+ }
+ }
+
+ private void dispatchDemoModeFinishedToView(int id) {
+ View statusBarView = mStatusBar.getStatusBarView();
+ if (statusBarView == null) return;
+ View v = statusBarView.findViewById(id);
+ if (v instanceof DemoModeCommandReceiver) {
+ ((DemoModeCommandReceiver) v).onDemoModeFinished();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
new file mode 100644
index 000000000000..ca877af150da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.phone;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import javax.inject.Inject;
+
+/** Ties the {@link StatusBar} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. */
+@StatusBarComponent.StatusBarScope
+public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener {
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final StatusBarWindowController mStatusBarWindowController;
+ private final NotificationPanelViewController mNotificationPanelViewController;
+ private final KeyguardBypassController mKeyguardBypassController;
+ private final HeadsUpManagerPhone mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
+ private final NotificationRemoteInputManager mNotificationRemoteInputManager;
+ private final NotificationsController mNotificationsController;
+ private final DozeServiceHost mDozeServiceHost;
+ private final DozeScrimController mDozeScrimController;
+
+ @Inject
+ StatusBarHeadsUpChangeListener(
+ NotificationShadeWindowController notificationShadeWindowController,
+ StatusBarWindowController statusBarWindowController,
+ NotificationPanelViewController notificationPanelViewController,
+ KeyguardBypassController keyguardBypassController,
+ HeadsUpManagerPhone headsUpManager,
+ StatusBarStateController statusBarStateController,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ NotificationsController notificationsController,
+ DozeServiceHost dozeServiceHost,
+ DozeScrimController dozeScrimController) {
+
+ mNotificationShadeWindowController = notificationShadeWindowController;
+ mStatusBarWindowController = statusBarWindowController;
+ mNotificationPanelViewController = notificationPanelViewController;
+ mKeyguardBypassController = keyguardBypassController;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+ mNotificationRemoteInputManager = notificationRemoteInputManager;
+ mNotificationsController = notificationsController;
+ mDozeServiceHost = dozeServiceHost;
+ mDozeScrimController = dozeScrimController;
+ }
+
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+ if (inPinnedMode) {
+ mNotificationShadeWindowController.setHeadsUpShowing(true);
+ mStatusBarWindowController.setForceStatusBarVisible(true);
+ if (mNotificationPanelViewController.isFullyCollapsed()) {
+ // We need to ensure that the touchable region is updated before the
+ //window will be
+ // resized, in order to not catch any touches. A layout will ensure that
+ // onComputeInternalInsets will be called and after that we can
+ //resize the layout. Let's
+ // make sure that the window stays small for one frame until the
+ //touchableRegion is set.
+ mNotificationPanelViewController.getView().requestLayout();
+ mNotificationShadeWindowController.setForceWindowCollapsed(true);
+ mNotificationPanelViewController.getView().post(() -> {
+ mNotificationShadeWindowController.setForceWindowCollapsed(false);
+ });
+ }
+ } else {
+ boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
+ && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ if (!mNotificationPanelViewController.isFullyCollapsed()
+ || mNotificationPanelViewController.isTracking()
+ || bypassKeyguard) {
+ // We are currently tracking or is open and the shade doesn't need to
+ //be kept
+ // open artificially.
+ mNotificationShadeWindowController.setHeadsUpShowing(false);
+ if (bypassKeyguard) {
+ mStatusBarWindowController.setForceStatusBarVisible(false);
+ }
+ } else {
+ // we need to keep the panel open artificially, let's wait until the
+ //animation
+ // is finished.
+ mHeadsUpManager.setHeadsUpGoingAway(true);
+ mNotificationPanelViewController.runAfterAnimationFinished(() -> {
+ if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+ mNotificationShadeWindowController.setHeadsUpShowing(false);
+ mHeadsUpManager.setHeadsUpGoingAway(false);
+ }
+ mNotificationRemoteInputManager.onPanelCollapsed();
+ });
+ }
+ }
+ }
+
+ @Override
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+ mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
+ if (mStatusBarStateController.isDozing() && isHeadsUp) {
+ entry.setPulseSuppressed(false);
+ mDozeServiceHost.fireNotificationPulse(entry);
+ if (mDozeServiceHost.isPulsing()) {
+ mDozeScrimController.cancelPendingPulseTimeout();
+ }
+ }
+ if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
+ // There are no longer any notifications to show. We should end the
+ //pulse now.
+ mDozeScrimController.pulseOutNow();
+ }
+ }
+}
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 2c7553487067..48fe77482340 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -35,10 +35,11 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
@@ -50,6 +51,8 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
public interface StatusBarIconController {
/**
@@ -213,6 +216,20 @@ public interface StatusBarIconController {
icons.setColor(mColor);
return icons;
}
+
+ @SysUISingleton
+ public static class Factory {
+ private final FeatureFlags mFeatureFlags;
+
+ @Inject
+ public Factory(FeatureFlags featureFlags) {
+ mFeatureFlags = featureFlags;
+ }
+
+ public TintedIconManager create(ViewGroup group) {
+ return new TintedIconManager(group, mFeatureFlags);
+ }
+ }
}
/**
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 75900a2bffa1..9d1c1e63ac2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.view.ViewGroup;
import com.android.internal.statusbar.StatusBarIcon;
@@ -88,6 +89,13 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
/** */
@Override
public void addIconGroup(IconManager group) {
+ for (IconManager existingIconManager : mIconGroups) {
+ if (existingIconManager.mGroup == group.mGroup) {
+ Log.e(TAG, "Adding new IconManager for the same ViewGroup. This could cause "
+ + "unexpected results.");
+ }
+ }
+
mIconGroups.add(group);
List<Slot> allSlots = getSlots();
for (int i = 0; i < allSlots.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 8a7708aaa8c2..119eff6f96a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,6 +73,8 @@ import java.util.Optional;
import javax.inject.Inject;
+import dagger.Lazy;
+
/**
* Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
* via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -111,6 +113,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
+ private final Lazy<ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@Override
public void onFullyShown() {
@@ -243,7 +246,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
KeyguardBouncer.Factory keyguardBouncerFactory,
WakefulnessLifecycle wakefulnessLifecycle,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- KeyguardMessageAreaController.Factory keyguardMessageAreaFactory) {
+ KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
+ Lazy<ShadeController> shadeController) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -260,6 +264,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mWakefulnessLifecycle = wakefulnessLifecycle;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
+ mShadeController = shadeController;
}
@Override
@@ -634,6 +639,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
});
return;
}
+
+ if (mStatusBar.isLaunchingActivityOverLockscreen()) {
+ mOccluded = true;
+
+ // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
+ // collapse runnables will be run.
+ mShadeController.get().addPostCollapseAction(() -> {
+ mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+ reset(true /* hideBouncerWhenShowing */);
+ });
+ return;
+ }
} else if (!occluded && mOccluded && mShowing) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
@@ -970,6 +987,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mStatusBar.setBouncerShowing(bouncerShowing);
}
+ if (occluded != mLastOccluded || mFirstUpdate) {
+ mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded);
+ }
if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) {
mKeyguardUpdateManager.onKeyguardVisibilityChanged(showing && !occluded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 9a6dd38ffca5..dba3b2418790 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,15 +52,14 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -72,6 +71,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -230,12 +230,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mLogger.logStartingActivityFromClick(sbn.getKey());
final NotificationEntry entry = row.getEntry();
- RemoteInputController controller = mRemoteInputManager.getController();
- if (controller.isRemoteInputActive(entry)
+ if (mRemoteInputManager.isRemoteInputActive(entry)
&& !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
// We have an active remote input typed and the user clicked on the notification.
// this was probably unintentional, so we're closing the edit text instead.
- controller.closeRemoteInputs();
+ mRemoteInputManager.closeRemoteInputs();
return;
}
Notification notification = sbn.getNotification();
@@ -265,8 +264,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
@Override
public boolean onDismiss() {
return handleNotificationClickAfterKeyguardDismissed(
- entry, row, controller, intent,
- isActivityIntent, animate, showOverLockscreen);
+ entry, row, intent, isActivityIntent, animate, showOverLockscreen);
}
@Override
@@ -286,7 +284,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private boolean handleNotificationClickAfterKeyguardDismissed(
NotificationEntry entry,
ExpandableNotificationRow row,
- RemoteInputController controller,
PendingIntent intent,
boolean isActivityIntent,
boolean animate,
@@ -294,8 +291,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
- entry, row, controller, intent,
- isActivityIntent, animate);
+ entry, row, intent, isActivityIntent, animate);
if (showOverLockscreen) {
mShadeController.addPostCollapseAction(runnable);
@@ -315,7 +311,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
private void handleNotificationClickAfterPanelCollapsed(
NotificationEntry entry,
ExpandableNotificationRow row,
- RemoteInputController controller,
PendingIntent intent,
boolean isActivityIntent,
boolean animate) {
@@ -354,7 +349,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
if (!TextUtils.isEmpty(entry.remoteInputText)) {
remoteInputText = entry.remoteInputText;
}
- if (!TextUtils.isEmpty(remoteInputText) && !controller.isSpinning(notificationKey)) {
+ if (!TextUtils.isEmpty(remoteInputText)
+ && !mRemoteInputManager.isSpinning(notificationKey)) {
fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
remoteInputText.toString());
}
@@ -407,6 +403,53 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
mIsCollapsingToShowActivityOverLockscreen = false;
}
+ /**
+ * Called when a notification is dropped on proper target window.
+ * Intent that is included in this entry notification,
+ * will be sent by {@link ExpandableNotificationRowDragController}
+ *
+ * @param entry notification entry that is dropped.
+ */
+ @Override
+ public void onDragSuccess(NotificationEntry entry) {
+ // this method is not responsible for intent sending.
+ // will focus follow operation only after drag-and-drop that notification.
+ NotificationVisibility.NotificationLocation location =
+ NotificationLogger.getNotificationLocation(entry);
+ final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
+ entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
+
+ // retrieve the group summary to remove with this entry before we tell NMS the
+ // notification was clicked to avoid a race condition
+ final boolean shouldAutoCancel = shouldAutoCancel(entry.getSbn());
+ final NotificationEntry summaryToRemove = shouldAutoCancel
+ ? mOnUserInteractionCallback.getGroupSummaryToDismiss(entry) : null;
+
+ String notificationKey = entry.getKey();
+ // inform NMS that the notification was clicked
+ mClickNotifier.onNotificationClick(notificationKey, nv);
+
+ if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+ notificationKey)) {
+ // Immediately remove notification from visually showing.
+ // We have to post the removal to the UI thread for synchronization.
+ mMainThreadHandler.post(() -> {
+ final Runnable removeNotification = () ->
+ mOnUserInteractionCallback.onDismiss(
+ entry, REASON_CLICK, summaryToRemove);
+ if (mPresenter.isCollapsing()) {
+ // To avoid lags we're only performing the remove
+ // after the shade is collapsed
+ mShadeController.addPostCollapseAction(removeNotification);
+ } else {
+ removeNotification.run();
+ }
+ });
+ }
+
+ mIsCollapsingToShowActivityOverLockscreen = false;
+ }
+
private void expandBubbleStackOnMainThread(NotificationEntry entry) {
if (!mBubblesManagerOptional.isPresent()) {
return;
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 47deb1f0084b..832f317d5783 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -47,7 +47,6 @@ import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -65,7 +64,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -84,28 +82,18 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
ConfigurationController.ConfigurationListener,
NotificationRowBinderImpl.BindRowCallback,
CommandQueue.Callbacks {
-
- private final LockscreenGestureLogger mLockscreenGestureLogger =
- Dependency.get(LockscreenGestureLogger.class);
-
private static final String TAG = "StatusBarNotificationPresenter";
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
private final KeyguardStateController mKeyguardStateController;
- private final NotificationViewHierarchyManager mViewHierarchyManager =
- Dependency.get(NotificationViewHierarchyManager.class);
- private final NotificationLockscreenUserManager mLockscreenUserManager =
- Dependency.get(NotificationLockscreenUserManager.class);
- private final SysuiStatusBarStateController mStatusBarStateController =
- (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
- private final NotificationEntryManager mEntryManager =
- Dependency.get(NotificationEntryManager.class);
- private final NotificationMediaManager mMediaManager =
- Dependency.get(NotificationMediaManager.class);
- private final VisualStabilityManager mVisualStabilityManager =
- Dependency.get(VisualStabilityManager.class);
- private final NotificationGutsManager mGutsManager =
- Dependency.get(NotificationGutsManager.class);
+ private final NotificationViewHierarchyManager mViewHierarchyManager;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final NotificationEntryManager mEntryManager;
+ private final NotificationMediaManager mMediaManager;
+ private final NotificationGutsManager mGutsManager;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final LockscreenGestureLogger mLockscreenGestureLogger;
private final NotificationPanelViewController mNotificationPanel;
private final HeadsUpManagerPhone mHeadsUpManager;
@@ -144,8 +132,18 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
+ NotificationViewHierarchyManager notificationViewHierarchyManager,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ SysuiStatusBarStateController sysuiStatusBarStateController,
+ NotificationEntryManager notificationEntryManager,
+ NotificationMediaManager notificationMediaManager,
+ NotificationGutsManager notificationGutsManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ LockscreenGestureLogger lockscreenGestureLogger,
InitController initController,
- NotificationInterruptStateProvider notificationInterruptStateProvider) {
+ NotificationInterruptStateProvider notificationInterruptStateProvider,
+ NotificationRemoteInputManager remoteInputManager,
+ ConfigurationController configurationController) {
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
@@ -156,6 +154,14 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
mShadeController = shadeController;
mShadeTransitionController = shadeTransitionController;
mCommandQueue = commandQueue;
+ mViewHierarchyManager = notificationViewHierarchyManager;
+ mLockscreenUserManager = lockscreenUserManager;
+ mStatusBarStateController = sysuiStatusBarStateController;
+ mEntryManager = notificationEntryManager;
+ mMediaManager = notificationMediaManager;
+ mGutsManager = notificationGutsManager;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLockscreenGestureLogger = lockscreenGestureLogger;
mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
mNotificationShadeWindowController = notificationShadeWindowController;
mAboveShelfObserver.setListener(statusBarWindow.findViewById(
@@ -176,13 +182,9 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
}
- NotificationRemoteInputManager remoteInputManager =
- Dependency.get(NotificationRemoteInputManager.class);
remoteInputManager.setUpWithCallback(
Dependency.get(NotificationRemoteInputManager.Callback.class),
mNotificationPanel.createRemoteInputDelegate());
- remoteInputManager.getController().addCallback(
- Dependency.get(NotificationShadeWindowController.class));
initController.addPostInitTask(() -> {
NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
@@ -222,14 +224,14 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
onUserSwitched(mLockscreenUserManager.getCurrentUserId());
});
- Dependency.get(ConfigurationController.class).addCallback(this);
+ configurationController.addCallback(this);
}
@Override
public void onDensityOrFontScaleChanged() {
MessagingMessage.dropCache();
MessagingGroup.dropCache();
- if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) {
+ if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
updateNotificationsOnDensityOrFontScaleChanged();
} else {
mReinflateNotificationsOnUserSwitched = true;
@@ -238,7 +240,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
@Override
public void onUiModeChanged() {
- if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) {
+ if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
updateNotificationOnUiModeChanged();
} else {
mDispatchUiModeChangeOnUserSwitched = true;
@@ -310,17 +312,11 @@ public class StatusBarNotificationPresenter implements NotificationPresenter,
private void onNotificationRemoved(String key, StatusBarNotification old, int reason) {
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
- if (old != null) {
- if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
- && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
- if (mStatusBarStateController.getState() == StatusBarState.SHADE
- && reason != NotificationListenerService.REASON_CLICK) {
- mCommandQueue.animateCollapsePanels();
- } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+ if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
+ && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()
+ && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
&& !isCollapsing()) {
- mStatusBarStateController.setState(StatusBarState.KEYGUARD);
- }
- }
+ mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
}
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 fe52281652a9..71364320839c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -25,7 +25,7 @@ import android.util.Log;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
index f33ff2732cda..ac43b679da0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java
@@ -16,5 +16,6 @@
package com.android.systemui.statusbar.phone;
public interface StatusBarWindowCallback {
- void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing);
+ void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing,
+ boolean isDozing);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 9a25a7078859..3d3b58ae0c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -34,6 +36,7 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
import android.view.IWindowManager;
+import android.view.Surface;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -118,21 +121,7 @@ public class StatusBarWindowController {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
- mLp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- mBarHeight,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
- PixelFormat.TRANSLUCENT);
- mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
- mLp.token = new Binder();
- mLp.gravity = Gravity.TOP;
- mLp.setFitInsetsTypes(0 /* types */);
- mLp.setTitle("StatusBar");
- mLp.packageName = mContext.getPackageName();
- mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
@@ -141,6 +130,63 @@ public class StatusBarWindowController {
calculateStatusBarLocationsForAllRotations();
}
+ private WindowManager.LayoutParams getBarLayoutParams(int rotation) {
+ WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation);
+ lp.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot);
+ }
+ return lp;
+ }
+
+ private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) {
+ int height = mBarHeight;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ int defaultAndUpsideDownHeight;
+ int theOtherHeight;
+ if (displayBounds.width() > displayBounds.height()) {
+ defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
+ theOtherHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ } else {
+ defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_portrait);
+ theOtherHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height_landscape);
+ }
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = defaultAndUpsideDownHeight;
+ break;
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ height = theOtherHeight;
+ break;
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ height,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+ lp.token = new Binder();
+ lp.gravity = Gravity.TOP;
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.setTitle("StatusBar");
+ lp.packageName = mContext.getPackageName();
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ return lp;
+
+ }
+
private void calculateStatusBarLocationsForAllRotations() {
Rect[] bounds = new Rect[4];
bounds[0] = mContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index fb25ae37ea74..d408c0cc3267 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -23,6 +23,10 @@ import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
+import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
+import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
+import com.android.systemui.statusbar.phone.StatusBarDemoMode;
+import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.lang.annotation.Documented;
@@ -86,4 +90,28 @@ public interface StatusBarComponent {
*/
@StatusBarScope
AuthRippleController getAuthRippleController();
+
+ /**
+ * Creates a StatusBarDemoMode.
+ */
+ @StatusBarScope
+ StatusBarDemoMode getStatusBarDemoMode();
+
+ /**
+ * Creates a StatusBarHeadsUpChangeListener.
+ */
+ @StatusBarScope
+ StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
+
+ /**
+ * Creates a StatusBarCommandQueueCallbacks.
+ */
+ @StatusBarScope
+ StatusBarCommandQueueCallbacks getStatusBarCommandQueueCallbacks();
+
+ /**
+ * Creates a SplitShadeHeaderController.
+ */
+ @StatusBarScope
+ SplitShadeHeaderController getSplitShadeHeaderController();
}
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 b6e8bd8bf7c1..b36c45e7f8ef 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
@@ -18,26 +18,25 @@ package com.android.systemui.statusbar.phone.dagger;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
+import android.app.WallpaperManager;
import android.content.Context;
import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
-import androidx.annotation.Nullable;
-
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -49,7 +48,6 @@ 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.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -58,13 +56,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -80,9 +78,9 @@ import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
@@ -93,7 +91,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -103,11 +100,15 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -138,7 +139,6 @@ public interface StatusBarPhoneModule {
LightBarController lightBarController,
AutoHideController autoHideController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- StatusBarSignalPolicy signalPolicy,
PulseExpansionHandler pulseExpansionHandler,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
KeyguardBypassController keyguardBypassController,
@@ -149,7 +149,7 @@ public interface StatusBarPhoneModule {
FalsingManager falsingManager,
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
- RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+ NotificationEntryManager notificationEntryManager,
NotificationGutsManager notificationGutsManager,
NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
@@ -168,20 +168,18 @@ public interface StatusBarPhoneModule {
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
- VibratorHelper vibratorHelper,
Optional<BubblesManager> bubblesManagerOptional,
Optional<Bubbles> bubblesOptional,
VisualStabilityManager visualStabilityManager,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
- AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
DozeParameters dozeParameters,
ScrimController scrimController,
- @Nullable KeyguardLiftController keyguardLiftController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ LockscreenGestureLogger lockscreenGestureLogger,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
DozeServiceHost dozeServiceHost,
PowerManager powerManager,
@@ -205,15 +203,16 @@ public interface StatusBarPhoneModule {
KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
PhoneStatusBarPolicy phoneStatusBarPolicy,
KeyguardIndicationController keyguardIndicationController,
DemoModeController demoModeController,
Lazy<NotificationShadeDepthController> notificationShadeDepthController,
- DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
BrightnessSlider.Factory brightnessSliderFactory,
- WiredChargingRippleController chargingRippleAnimationController,
+ UnfoldTransitionConfig unfoldTransitionConfig,
+ Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -221,15 +220,19 @@ public interface StatusBarPhoneModule {
LockscreenShadeTransitionController transitionController,
FeatureFlags featureFlags,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ @Main Handler mainHandler,
+ @Main DelayableExecutor delayableExecutor,
+ @Main MessageRouter messageRouter,
+ WallpaperManager wallpaperManager,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- Optional<StartingSurface> startingSurfaceOptional) {
+ Optional<StartingSurface> startingSurfaceOptional,
+ TunerService tunerService) {
return new StatusBar(
context,
notificationsController,
lightBarController,
autoHideController,
keyguardUpdateMonitor,
- signalPolicy,
pulseExpansionHandler,
notificationWakeUpCoordinator,
keyguardBypassController,
@@ -240,7 +243,7 @@ public interface StatusBarPhoneModule {
falsingManager,
falsingCollector,
broadcastDispatcher,
- remoteInputQuickSettingsDisabler,
+ notificationEntryManager,
notificationGutsManager,
notificationLogger,
notificationInterruptStateProvider,
@@ -259,20 +262,18 @@ public interface StatusBarPhoneModule {
screenLifecycle,
wakefulnessLifecycle,
statusBarStateController,
- vibratorHelper,
bubblesManagerOptional,
bubblesOptional,
visualStabilityManager,
deviceProvisionedController,
navigationBarController,
- accessibilityFloatingMenuController,
assistManagerLazy,
configurationController,
notificationShadeWindowController,
dozeParameters,
scrimController,
- keyguardLiftController,
lockscreenWallpaperLazy,
+ lockscreenGestureLogger,
biometricUnlockControllerLazy,
dozeServiceHost,
powerManager,
@@ -295,15 +296,16 @@ public interface StatusBarPhoneModule {
keyguardDismissUtil,
extensionController,
userInfoControllerImpl,
+ operatorNameViewControllerFactory,
phoneStatusBarPolicy,
keyguardIndicationController,
- dismissCallbackRegistry,
demoModeController,
notificationShadeDepthController,
statusBarTouchableRegionManager,
notificationIconAreaController,
brightnessSliderFactory,
- chargingRippleAnimationController,
+ unfoldTransitionConfig,
+ unfoldLightRevealOverlayAnimation,
ongoingCallController,
animationScheduler,
locationPublisher,
@@ -311,7 +313,12 @@ public interface StatusBarPhoneModule {
transitionController,
featureFlags,
keyguardUnlockAnimationController,
+ mainHandler,
+ delayableExecutor,
+ messageRouter,
+ wallpaperManager,
unlockedScreenOffAnimationController,
- startingSurfaceOptional);
+ startingSurfaceOptional,
+ tunerService);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 27d71edd5e8a..0e83eda2734a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,19 +17,26 @@
package com.android.systemui.statusbar.phone.dagger;
import android.annotation.Nullable;
+import android.view.View;
import com.android.keyguard.LockIconView;
import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.TapAgainView;
+import javax.inject.Named;
+
import dagger.Module;
import dagger.Provides;
@Module
public abstract class StatusBarViewModule {
+
+ public static final String SPLIT_SHADE_HEADER = "split_shade_header";
+
/** */
@Provides
@StatusBarComponent.StatusBarScope
@@ -57,6 +64,22 @@ public abstract class StatusBarViewModule {
/** */
@Provides
+ @Named(SPLIT_SHADE_HEADER)
+ @StatusBarComponent.StatusBarScope
+ public static View getSlitShadeStatusBarView(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.split_shade_status_bar);
+ }
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
+ return view.findViewById(R.id.batteryRemainingIcon);
+ }
+
+ /** */
+ @Provides
@StatusBarComponent.StatusBarScope
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6982631766f7..62ba56ab2077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -24,7 +24,6 @@ import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.content.Intent
import android.util.Log
import android.view.View
-import android.widget.Chronometer
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.R
@@ -32,7 +31,7 @@ import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -130,7 +129,6 @@ class OngoingCallController @Inject constructor(
}
}
-
/**
* Called when the chip's visibility may have changed.
*
@@ -224,7 +222,11 @@ class OngoingCallController @Inject constructor(
uidObserver = object : IUidObserver.Stub() {
override fun onUidStateChanged(
- uid: Int, procState: Int, procStateSeq: Long, capability: Int) {
+ uid: Int,
+ procState: Int,
+ procStateSeq: Long,
+ capability: Int
+ ) {
if (uid == currentCallNotificationInfo.uid) {
val oldIsCallAppVisible = isCallAppVisible
isCallAppVisible = isProcessVisibleToUser(procState)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index ab58286859cc..f54bb6882ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.policy;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
-import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.SimpleClock;
@@ -40,6 +39,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.settings.UserTracker;
+import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiPickerTracker;
@@ -68,6 +68,7 @@ public class AccessPointControllerImpl
private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
private final UserManager mUserManager;
+ private final UserTracker mUserTracker;
private final Executor mMainExecutor;
private @Nullable WifiPickerTracker mWifiPickerTracker;
@@ -84,6 +85,7 @@ public class AccessPointControllerImpl
WifiPickerTrackerFactory wifiPickerTrackerFactory
) {
mUserManager = userManager;
+ mUserTracker = userTracker;
mCurrentUser = userTracker.getUserId();
mMainExecutor = mainExecutor;
mWifiPickerTrackerFactory = wifiPickerTrackerFactory;
@@ -118,6 +120,11 @@ public class AccessPointControllerImpl
new UserHandle(mCurrentUser));
}
+ public boolean canConfigMobileData() {
+ return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+ UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin();
+ }
+
public void onUserSwitched(int newUserId) {
mCurrentUser = newUserId;
}
@@ -157,6 +164,15 @@ public class AccessPointControllerImpl
}
@Override
+ public MergedCarrierEntry getMergedCarrierEntry() {
+ if (mWifiPickerTracker == null) {
+ fireAcccessPointsCallback(Collections.emptyList());
+ return null;
+ }
+ return mWifiPickerTracker.getMergedCarrierEntry();
+ }
+
+ @Override
public int getIcon(WifiEntry ap) {
int level = ap.getLevel();
return ICONS[Math.max(0, level)];
@@ -271,7 +287,6 @@ public class AccessPointControllerImpl
private final Context mContext;
private final @Nullable WifiManager mWifiManager;
private final ConnectivityManager mConnectivityManager;
- private final NetworkScoreManager mNetworkScoreManager;
private final Handler mMainHandler;
private final Handler mWorkerHandler;
private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
@@ -286,14 +301,12 @@ public class AccessPointControllerImpl
Context context,
@Nullable WifiManager wifiManager,
ConnectivityManager connectivityManager,
- NetworkScoreManager networkScoreManager,
@Main Handler mainHandler,
@Background Handler workerHandler
) {
mContext = context;
mWifiManager = wifiManager;
mConnectivityManager = connectivityManager;
- mNetworkScoreManager = networkScoreManager;
mMainHandler = mainHandler;
mWorkerHandler = workerHandler;
}
@@ -317,7 +330,6 @@ public class AccessPointControllerImpl
mContext,
mWifiManager,
mConnectivityManager,
- mNetworkScoreManager,
mMainHandler,
mWorkerHandler,
mClock,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
new file mode 100644
index 000000000000..fbfa5e5ea109
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.DevicePostureController.Callback;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Listener for device posture changes. This can be used to query the current posture, or register
+ * for events when it changes.
+ */
+public interface DevicePostureController extends CallbackController<Callback> {
+ @IntDef(prefix = {"DEVICE_POSTURE_"}, value = {
+ DEVICE_POSTURE_UNKNOWN,
+ DEVICE_POSTURE_CLOSED,
+ DEVICE_POSTURE_HALF_OPENED,
+ DEVICE_POSTURE_OPENED,
+ DEVICE_POSTURE_FLIPPED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DevicePostureInt {}
+
+ // NOTE: These constants **must** match those defined for Jetpack Sidecar. This is because we
+ // use the Device State -> Jetpack Posture map in DevicePostureControllerImpl to translate
+ // between the two.
+ int DEVICE_POSTURE_UNKNOWN = 0;
+ int DEVICE_POSTURE_CLOSED = 1;
+ int DEVICE_POSTURE_HALF_OPENED = 2;
+ int DEVICE_POSTURE_OPENED = 3;
+ int DEVICE_POSTURE_FLIPPED = 4;
+
+ /** Return the current device posture. */
+ @DevicePostureInt int getDevicePosture();
+
+ /** Callback to be notified about device posture changes. */
+ interface Callback {
+ /** Called when the posture changes. */
+ void onPostureChanged(@DevicePostureInt int posture);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
new file mode 100644
index 000000000000..8471e0aa1640
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.hardware.devicestate.DeviceStateManager;
+import android.util.SparseIntArray;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/** Implementation of {@link DevicePostureController} using the DeviceStateManager. */
+@SysUISingleton
+public class DevicePostureControllerImpl implements DevicePostureController {
+ private final List<Callback> mListeners = new ArrayList<>();
+ private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
+
+ private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+
+ @Inject
+ public DevicePostureControllerImpl(
+ Context context, DeviceStateManager deviceStateManager, @Main Executor executor) {
+ // Most of this is borrowed from WindowManager/Jetpack/DeviceStateManagerPostureProducer.
+ // Using the sidecar/extension libraries directly brings in a new dependency that it'd be
+ // good to avoid (along with the fact that sidecar is deprecated, and extensions isn't fully
+ // ready yet), and we'd have to make our own layer over the sidecar library anyway to easily
+ // allow the implementation to change, so it was easier to just interface with
+ // DeviceStateManager directly.
+ String[] deviceStatePosturePairs = context.getResources()
+ .getStringArray(R.array.config_device_state_postures);
+ for (String deviceStatePosturePair : deviceStatePosturePairs) {
+ String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
+ if (deviceStatePostureMapping.length != 2) {
+ continue;
+ }
+
+ int deviceState;
+ int posture;
+ try {
+ deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
+ posture = Integer.parseInt(deviceStatePostureMapping[1]);
+ } catch (NumberFormatException e) {
+ continue;
+ }
+
+ mDeviceStateToPostureMap.put(deviceState, posture);
+ }
+
+ deviceStateManager.registerCallback(executor, state -> {
+ mCurrentDevicePosture =
+ mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
+
+ mListeners.forEach(l -> l.onPostureChanged(mCurrentDevicePosture));
+ });
+ }
+
+ @Override
+ public void addCallback(@NonNull Callback listener) {
+ mListeners.add(listener);
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public int getDevicePosture() {
+ return mCurrentDevicePosture;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
new file mode 100644
index 000000000000..41cacf5142fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
+import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+
+import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Handles reading and writing of rotation lock settings per device state, as well as setting
+ * the rotation lock when device state changes.
+ **/
+@SysUISingleton
+public final class DeviceStateRotationLockSettingController implements Listenable,
+ RotationLockController.RotationLockControllerCallback {
+
+ private static final String TAG = "DSRotateLockSettingCon";
+
+ private static final String SEPARATOR_REGEX = ":";
+
+ private final SecureSettings mSecureSettings;
+ private final RotationPolicyWrapper mRotationPolicyWrapper;
+ private final DeviceStateManager mDeviceStateManager;
+ private final Executor mMainExecutor;
+ private final String[] mDeviceStateRotationLockDefaults;
+
+ private SparseIntArray mDeviceStateRotationLockSettings;
+ // TODO(b/183001527): Add API to query current device state and initialize this.
+ private int mDeviceState = -1;
+ @Nullable
+ private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+
+
+ @Inject
+ public DeviceStateRotationLockSettingController(
+ SecureSettings secureSettings,
+ RotationPolicyWrapper rotationPolicyWrapper,
+ DeviceStateManager deviceStateManager,
+ @Main Executor executor,
+ @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
+ ) {
+ mSecureSettings = secureSettings;
+ mRotationPolicyWrapper = rotationPolicyWrapper;
+ mDeviceStateManager = deviceStateManager;
+ mMainExecutor = executor;
+ mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults;
+ }
+
+ /**
+ * Loads the settings from storage.
+ */
+ public void initialize() {
+ String serializedSetting =
+ mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT);
+ if (TextUtils.isEmpty(serializedSetting)) {
+ // No settings saved, we should load the defaults and persist them.
+ fallbackOnDefaults();
+ return;
+ }
+ String[] values = serializedSetting.split(SEPARATOR_REGEX);
+ if (values.length % 2 != 0) {
+ // Each entry should be a key/value pair, so this is corrupt.
+ Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
+ fallbackOnDefaults();
+ return;
+ }
+ mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
+ int key;
+ int value;
+
+ for (int i = 0; i < values.length - 1; ) {
+ try {
+ key = Integer.parseInt(values[i++]);
+ value = Integer.parseInt(values[i++]);
+ mDeviceStateRotationLockSettings.put(key, value);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Error deserializing one of the saved settings", e);
+ fallbackOnDefaults();
+ return;
+ }
+ }
+ }
+
+ private void fallbackOnDefaults() {
+ loadDefaults();
+ persistSettings();
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (listening) {
+ // Note that this is called once with the initial state of the device, even if there
+ // is no user action.
+ mDeviceStateCallback = this::updateDeviceState;
+ mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
+ } else {
+ if (mDeviceStateCallback != null) {
+ mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
+ }
+ }
+ }
+
+ @Override
+ public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
+ if (mDeviceState == -1) {
+ Log.wtf(TAG, "Device state was not initialized.");
+ return;
+ }
+
+ if (rotationLocked == isRotationLockedForCurrentState()) {
+ Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
+ return;
+ }
+
+ saveNewRotationLockSetting(rotationLocked);
+ }
+
+ private void saveNewRotationLockSetting(boolean isRotationLocked) {
+ Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked="
+ + isRotationLocked + "]");
+
+ mDeviceStateRotationLockSettings.put(mDeviceState,
+ isRotationLocked
+ ? DEVICE_STATE_ROTATION_LOCK_LOCKED
+ : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+ persistSettings();
+ }
+
+ private boolean isRotationLockedForCurrentState() {
+ return mDeviceStateRotationLockSettings.get(mDeviceState,
+ DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+ }
+
+ private void updateDeviceState(int state) {
+ Log.v(TAG, "updateDeviceState [state=" + state + "]");
+ if (mDeviceState == state) {
+ return;
+ }
+
+ int rotationLockSetting =
+ mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+ if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+ // We won't handle this device state. The same rotation lock setting as before should
+ // apply and any changes to the rotation lock setting will be written for the previous
+ // valid device state.
+ Log.v(TAG, "Ignoring new device state: " + state);
+ return;
+ }
+
+ // Accept the new state
+ mDeviceState = state;
+
+ // Update the rotation lock setting if needed for this new device state
+ boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+ if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
+ mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
+ }
+ }
+
+ private void persistSettings() {
+ if (mDeviceStateRotationLockSettings.size() == 0) {
+ mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"", UserHandle.USER_CURRENT);
+ return;
+ }
+
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0))
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.valueAt(0));
+
+ for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
+ stringBuilder
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.keyAt(i))
+ .append(SEPARATOR_REGEX)
+ .append(mDeviceStateRotationLockSettings.valueAt(i));
+ }
+ mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ stringBuilder.toString(), UserHandle.USER_CURRENT);
+ }
+
+ private void loadDefaults() {
+ if (mDeviceStateRotationLockDefaults.length == 0) {
+ Log.w(TAG, "Empty default settings");
+ mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */0);
+ return;
+ }
+ mDeviceStateRotationLockSettings =
+ new SparseIntArray(mDeviceStateRotationLockDefaults.length);
+ for (String serializedDefault : mDeviceStateRotationLockDefaults) {
+ String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+ try {
+ int key = Integer.parseInt(entry[0]);
+ int value = Integer.parseInt(entry[1]);
+ mDeviceStateRotationLockSettings.put(key, value);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Error deserializing default settings", e);
+ }
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 5e70d0dbc418..dd38f9826731 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -33,6 +33,7 @@ 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.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
@@ -116,6 +117,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
@Main Resources resources,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
+ CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
FalsingManager falsingManager,
ConfigurationController configurationController,
@@ -133,7 +135,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
mFalsingManager = falsingManager;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
keyguardStateController, dozeParameters,
unlockedScreenOffAnimationController, /* animateYPos= */ false);
mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index fcfc9670b8b0..742b1ab69727 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -245,5 +245,11 @@ public interface KeyguardStateController extends CallbackController<Callback> {
* animation.
*/
default void onKeyguardDismissAmountChanged() {}
+
+ /**
+ * Triggered when face auth becomes available or unavailable. Value should be queried with
+ * {@link KeyguardStateController#isFaceAuthEnabled()}.
+ */
+ default void onFaceAuthEnabledChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 43b2061ecd32..3240ad27954d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -42,6 +42,7 @@ import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -157,6 +158,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
LayoutInflater layoutInflater,
ScreenLifecycle screenLifecycle,
UserSwitcherController userSwitcherController,
+ CommunalStateController communalStateController,
KeyguardStateController keyguardStateController,
SysuiStatusBarStateController statusBarStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -172,7 +174,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mAdapter = new KeyguardUserAdapter(mContext, resources, layoutInflater,
mUserSwitcherController, this);
- mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView,
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, communalStateController,
keyguardStateController, dozeParameters,
unlockedScreenOffAnimationController, /* animateYPos= */ false);
mBackground = new KeyguardUserSwitcherScrim(context);
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 43781f3941ba..3490e1567ea8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -57,7 +57,7 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
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 ef2ca985858d..eeea699a0b74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -23,6 +23,7 @@ import android.telephony.SubscriptionInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
@@ -223,9 +224,11 @@ public interface NetworkController extends CallbackController<SignalCallback>, D
void addAccessPointCallback(AccessPointCallback callback);
void removeAccessPointCallback(AccessPointCallback callback);
void scanForAccessPoints();
+ MergedCarrierEntry getMergedCarrierEntry();
int getIcon(WifiEntry ap);
boolean connect(WifiEntry ap);
boolean canConfigWifi();
+ boolean canConfigMobileData();
public interface AccessPointCallback {
void onAccessPointsChanged(List<WifiEntry> accessPoints);
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 c49de7ab0e5d..65e691fde7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -68,11 +68,13 @@ import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
@@ -127,7 +129,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
private final FeatureFlags mFeatureFlags;
- private final DumpManager mDumpManager;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -191,6 +192,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
private boolean mUserSetup;
private boolean mSimDetected;
private boolean mForceCellularValidated;
+ private InternetDialogFactory mInternetDialogFactory;
+ private Handler mMainHandler;
private ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -220,8 +223,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
AccessPointControllerImpl accessPointController,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags,
- DumpManager dumpManager) {
+ @Main Handler handler,
+ InternetDialogFactory internetDialogFactory,
+ FeatureFlags featureFlags) {
this(context, connectivityManager,
telephonyManager,
telephonyListenerManager,
@@ -239,9 +243,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
broadcastDispatcher,
demoModeController,
carrierConfigTracker,
- featureFlags,
- dumpManager);
+ featureFlags);
mReceiverHandler.post(mRegisterListeners);
+ mMainHandler = handler;
+ mInternetDialogFactory = internetDialogFactory;
}
@VisibleForTesting
@@ -260,8 +265,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags,
- DumpManager dumpManager
+ FeatureFlags featureFlags
) {
mContext = context;
mTelephonyListenerManager = telephonyListenerManager;
@@ -280,7 +284,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
mFeatureFlags = featureFlags;
- mDumpManager = dumpManager;
// telephony
mPhone = telephonyManager;
@@ -431,8 +434,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
mDemoModeController.addCallback(this);
mProviderModelBehavior = mFeatureFlags.isCombinedStatusBarSignalIconsEnabled();
mProviderModelSetting = mFeatureFlags.isProviderModelSettingEnabled();
-
- mDumpManager.registerDumpable(TAG, this);
}
private final Runnable mClearForceValidated = () -> {
@@ -480,6 +481,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ if (InternetDialogUtil.isProviderModelEnabled(mContext)) {
+ filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+ }
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -788,6 +792,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
mConfig = Config.readConfig(mContext);
mReceiverHandler.post(this::handleConfigurationChanged);
break;
+ case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
+ mMainHandler.post(() -> mInternetDialogFactory.create(true,
+ mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi()));
+ break;
default:
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
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 84d7c05ddc14..1398c525df09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -72,12 +72,10 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -151,7 +149,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
@UiEvent(doc = "User sent data through the notification remote input view")
NOTIFICATION_REMOTE_INPUT_SEND(797),
@UiEvent(doc = "Failed attempt to send data through the notification remote input view")
- NOTIFICATION_REMOTE_INPUT_FAILURE(798);
+ NOTIFICATION_REMOTE_INPUT_FAILURE(798),
+ @UiEvent(doc = "User attached an image to the remote input view")
+ NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE(825);
private final int mId;
NotificationRemoteInputEvent(int id) {
@@ -204,7 +204,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
final int stroke = colorized ? mContext.getResources().getDimensionPixelSize(
R.dimen.remote_input_view_text_stroke) : 0;
if (colorized) {
- final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor);
+ final boolean dark = Notification.Builder.isColorDark(backgroundColor);
final int foregroundColor = dark ? Color.WHITE : Color.BLACK;
final int inverseColor = dark ? Color.BLACK : Color.WHITE;
editBgColor = backgroundColor;
@@ -283,7 +283,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
});
}
- private void setAttachment(ContentInfo item) {
+ @VisibleForTesting
+ protected void setAttachment(ContentInfo item) {
if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) {
// We need to release permissions when sending the attachment to the target
// app or if it is deleted by the user. When sending to the target app, we
@@ -309,6 +310,10 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
attachment.setVisibility(GONE);
} else {
attachment.setVisibility(VISIBLE);
+ mUiEventLogger.logWithInstanceId(
+ NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE,
+ mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
+ mEntry.getSbn().getInstanceId());
}
updateSendButton();
}
@@ -413,8 +418,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mEntry.getSbn().getPackageName(),
mEntry.getSbn().getUser().getIdentifier());
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -423,8 +426,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mPendingIntent.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -501,8 +502,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
mRemoteInputQuickSettingsDisabler.setRemoteInputActive(false);
if (logClose) {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_CLOSE,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_CLOSE,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
@@ -584,8 +583,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
public void focus() {
- MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_OPEN,
- mEntry.getSbn().getPackageName());
mUiEventLogger.logWithInstanceId(
NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN,
mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 53d68d0ff0ac..67f5364e3d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -16,36 +16,54 @@
package com.android.systemui.statusbar.policy;
-import android.content.Context;
+import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+
import android.os.UserHandle;
import androidx.annotation.NonNull;
-import com.android.internal.view.RotationPolicy;
+import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
+import javax.inject.Named;
/** Platform implementation of the rotation lock controller. **/
@SysUISingleton
public final class RotationLockControllerImpl implements RotationLockController {
- private final Context mContext;
private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
- new CopyOnWriteArrayList<RotationLockControllerCallback>();
+ new CopyOnWriteArrayList<>();
- private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
- new RotationPolicy.RotationPolicyListener() {
+ private final RotationPolicyListener mRotationPolicyListener =
+ new RotationPolicyListener() {
@Override
public void onChange() {
notifyChanged();
}
};
+ private final RotationPolicyWrapper mRotationPolicy;
+ private final DeviceStateRotationLockSettingController
+ mDeviceStateRotationLockSettingController;
+ private final boolean mIsPerDeviceStateRotationLockEnabled;
+
@Inject
- public RotationLockControllerImpl(Context context) {
- mContext = context;
+ public RotationLockControllerImpl(
+ RotationPolicyWrapper rotationPolicyWrapper,
+ DeviceStateRotationLockSettingController deviceStateRotationLockSettingController,
+ @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
+ ) {
+ mRotationPolicy = rotationPolicyWrapper;
+ mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController;
+ mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0;
+ if (mIsPerDeviceStateRotationLockEnabled) {
+ deviceStateRotationLockSettingController.initialize();
+ mCallbacks.add(mDeviceStateRotationLockSettingController);
+ }
+
setListening(true);
}
@@ -61,32 +79,35 @@ public final class RotationLockControllerImpl implements RotationLockController
}
public int getRotationLockOrientation() {
- return RotationPolicy.getRotationLockOrientation(mContext);
+ return mRotationPolicy.getRotationLockOrientation();
}
public boolean isRotationLocked() {
- return RotationPolicy.isRotationLocked(mContext);
+ return mRotationPolicy.isRotationLocked();
}
public void setRotationLocked(boolean locked) {
- RotationPolicy.setRotationLock(mContext, locked);
+ mRotationPolicy.setRotationLock(locked);
}
public void setRotationLockedAtAngle(boolean locked, int rotation){
- RotationPolicy.setRotationLockAtAngle(mContext, locked, rotation);
+ mRotationPolicy.setRotationLockAtAngle(locked, rotation);
}
public boolean isRotationLockAffordanceVisible() {
- return RotationPolicy.isRotationLockToggleVisible(mContext);
+ return mRotationPolicy.isRotationLockToggleVisible();
}
@Override
public void setListening(boolean listening) {
if (listening) {
- RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener,
+ mRotationPolicy.registerRotationPolicyListener(mRotationPolicyListener,
UserHandle.USER_ALL);
} else {
- RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
+ mRotationPolicy.unregisterRotationPolicyListener(mRotationPolicyListener);
+ }
+ if (mIsPerDeviceStateRotationLockEnabled) {
+ mDeviceStateRotationLockSettingController.setListening(listening);
}
}
@@ -97,7 +118,7 @@ public final class RotationLockControllerImpl implements RotationLockController
}
private void notifyChanged(RotationLockControllerCallback callback) {
- callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
- RotationPolicy.isRotationLockToggleVisible(mContext));
+ callback.onRotationLockStateChanged(mRotationPolicy.isRotationLocked(),
+ mRotationPolicy.isRotationLockToggleVisible());
}
}
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 41b1dd12639a..4e33529f3c36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -628,7 +628,7 @@ public class SmartReplyView extends ViewGroup {
mCurrentBackgroundColor = backgroundColor;
mCurrentColorized = colorized;
- final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor);
+ final boolean dark = Notification.Builder.isColorDark(backgroundColor);
mCurrentTextColor = ContrastColorUtil.ensureTextContrast(
dark ? mDefaultTextColorDarkBg : mDefaultTextColor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
new file mode 100644
index 000000000000..ae9d9ee445f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.text.StaticLayout
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+
+/**
+ * View for showing a date that can toggle between two different formats depending on size.
+ *
+ * If no pattern can fit, it will display empty.
+ *
+ * @see R.styleable.VariableDateView_longDatePattern
+ * @see R.styleable.VariableDateView_shortDatePattern
+ */
+class VariableDateView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {
+
+ val longerPattern: String
+ val shorterPattern: String
+
+ init {
+ val a = context.theme.obtainStyledAttributes(
+ attrs,
+ R.styleable.VariableDateView,
+ 0, 0)
+ longerPattern = a.getString(R.styleable.VariableDateView_longDatePattern)
+ ?: context.getString(R.string.system_ui_date_pattern)
+ shorterPattern = a.getString(R.styleable.VariableDateView_shortDatePattern)
+ ?: context.getString(R.string.abbrev_month_day_no_year)
+
+ a.recycle()
+ }
+
+ /**
+ * Freeze the pattern switching
+ *
+ * Use during animations if the container will change its size but this view should not change
+ */
+ var freezeSwitching = false
+
+ private var onMeasureListener: OnMeasureListener? = null
+
+ fun onAttach(listener: OnMeasureListener?) {
+ onMeasureListener = listener
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val availableWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd
+ if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED && !freezeSwitching) {
+ onMeasureListener?.onMeasureAction(availableWidth)
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ }
+
+ fun getDesiredWidthForText(text: CharSequence): Float {
+ return StaticLayout.getDesiredWidth(text, paint)
+ }
+
+ interface OnMeasureListener {
+ fun onMeasureAction(availableWidth: Int)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
new file mode 100644
index 000000000000..99d84c4d0ced
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.icu.text.DateFormat
+import android.icu.text.DisplayContext
+import android.icu.util.Calendar
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import android.text.TextUtils
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dependency
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
+import java.text.FieldPosition
+import java.text.ParsePosition
+import java.util.Date
+import java.util.Locale
+import javax.inject.Inject
+import javax.inject.Named
+
+@VisibleForTesting
+internal fun getTextForFormat(date: Date?, format: DateFormat): String {
+ return if (format === EMPTY_FORMAT) { // Check if same object
+ ""
+ } else format.format(date)
+}
+
+@VisibleForTesting
+internal fun getFormatFromPattern(pattern: String?): DateFormat {
+ if (TextUtils.equals(pattern, "")) {
+ return EMPTY_FORMAT
+ }
+ val l = Locale.getDefault()
+ val format = DateFormat.getInstanceForSkeleton(pattern, l)
+ format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
+ return format
+}
+
+private val EMPTY_FORMAT: DateFormat = object : DateFormat() {
+ override fun format(
+ cal: Calendar,
+ toAppendTo: StringBuffer,
+ fieldPosition: FieldPosition
+ ): StringBuffer? {
+ return null
+ }
+
+ override fun parse(text: String, cal: Calendar, pos: ParsePosition) {}
+}
+
+private const val DEBUG = false
+private const val TAG = "VariableDateViewController"
+
+class VariableDateViewController(
+ private val systemClock: SystemClock,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val timeTickHandler: Handler,
+ view: VariableDateView
+) : ViewController<VariableDateView>(view) {
+
+ private var dateFormat: DateFormat? = null
+ private var datePattern = view.longerPattern
+ set(value) {
+ if (field == value) return
+ field = value
+ dateFormat = null
+ if (isAttachedToWindow) {
+ post(::updateClock)
+ }
+ }
+ private var lastWidth = Integer.MAX_VALUE
+ private var lastText = ""
+ private var currentTime = Date()
+
+ // View class easy accessors
+ private val longerPattern: String
+ get() = mView.longerPattern
+ private val shorterPattern: String
+ get() = mView.shorterPattern
+ private fun post(block: () -> Unit) = mView.handler?.post(block)
+
+ private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ // If the handler is null, it means we received a broadcast while the view has not
+ // finished being attached or in the process of being detached.
+ // In that case, do not post anything.
+ val handler = mView.handler ?: return
+ val action = intent.action
+ if (
+ Intent.ACTION_TIME_TICK == action ||
+ Intent.ACTION_TIME_CHANGED == action ||
+ Intent.ACTION_TIMEZONE_CHANGED == action ||
+ Intent.ACTION_LOCALE_CHANGED == action
+ ) {
+ if (
+ Intent.ACTION_LOCALE_CHANGED == action ||
+ Intent.ACTION_TIMEZONE_CHANGED == action
+ ) {
+ // need to get a fresh date format
+ handler.post { dateFormat = null }
+ }
+ handler.post(::updateClock)
+ }
+ }
+ }
+
+ private val onMeasureListener = object : VariableDateView.OnMeasureListener {
+ override fun onMeasureAction(availableWidth: Int) {
+ if (availableWidth != lastWidth) {
+ // maybeChangeFormat will post if the pattern needs to change.
+ maybeChangeFormat(availableWidth)
+ lastWidth = availableWidth
+ }
+ }
+ }
+
+ override fun onViewAttached() {
+ val filter = IntentFilter().apply {
+ addAction(Intent.ACTION_TIME_TICK)
+ addAction(Intent.ACTION_TIME_CHANGED)
+ addAction(Intent.ACTION_TIMEZONE_CHANGED)
+ addAction(Intent.ACTION_LOCALE_CHANGED)
+ }
+
+ broadcastDispatcher.registerReceiver(intentReceiver, filter,
+ HandlerExecutor(timeTickHandler), UserHandle.SYSTEM)
+
+ post(::updateClock)
+ mView.onAttach(onMeasureListener)
+ }
+
+ override fun onViewDetached() {
+ dateFormat = null
+ mView.onAttach(null)
+ broadcastDispatcher.unregisterReceiver(intentReceiver)
+ }
+
+ private fun updateClock() {
+ if (dateFormat == null) {
+ dateFormat = getFormatFromPattern(datePattern)
+ }
+
+ currentTime.time = systemClock.currentTimeMillis()
+
+ val text = getTextForFormat(currentTime, dateFormat!!)
+ if (text != lastText) {
+ mView.setText(text)
+ lastText = text
+ }
+ }
+
+ private fun maybeChangeFormat(availableWidth: Int) {
+ if (mView.freezeSwitching ||
+ availableWidth > lastWidth && datePattern == longerPattern ||
+ availableWidth < lastWidth && datePattern == ""
+ ) {
+ // Nothing to do
+ return
+ }
+ if (DEBUG) Log.d(TAG, "Width changed. Maybe changing pattern")
+ // Start with longer pattern and see what fits
+ var text = getTextForFormat(currentTime, getFormatFromPattern(longerPattern))
+ var length = mView.getDesiredWidthForText(text)
+ if (length <= availableWidth) {
+ changePattern(longerPattern)
+ return
+ }
+
+ text = getTextForFormat(currentTime, getFormatFromPattern(shorterPattern))
+ length = mView.getDesiredWidthForText(text)
+ if (length <= availableWidth) {
+ changePattern(shorterPattern)
+ return
+ }
+
+ changePattern("")
+ }
+
+ private fun changePattern(newPattern: String) {
+ if (newPattern.equals(datePattern)) return
+ if (DEBUG) Log.d(TAG, "Changing pattern to $newPattern")
+ datePattern = newPattern
+ }
+
+ class Factory @Inject constructor(
+ private val systemClock: SystemClock,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
+ ) {
+ fun create(view: VariableDateView): VariableDateViewController {
+ return VariableDateViewController(
+ systemClock,
+ broadcastDispatcher,
+ handler,
+ view
+ )
+ }
+ }
+} \ No newline at end of file
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 f8e36476c4a6..fc19564bf554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -36,7 +36,7 @@ import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 9fb0453a2888..1eec639670ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.policy.dagger;
+import android.content.res.Resources;
import android.os.UserManager;
+import com.android.internal.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
@@ -28,6 +30,8 @@ import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastControllerImpl;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.statusbar.policy.DevicePostureControllerImpl;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -55,6 +59,8 @@ import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import java.util.concurrent.Executor;
+import javax.inject.Named;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -63,6 +69,9 @@ import dagger.Provides;
/** Dagger Module for code in the statusbar.policy package. */
@Module
public interface StatusBarPolicyModule {
+
+ String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
+
/** */
@Binds
BluetoothController provideBluetoothController(BluetoothControllerImpl controllerImpl);
@@ -130,6 +139,11 @@ public interface StatusBarPolicyModule {
AccessPointControllerImpl accessPointControllerImpl);
/** */
+ @Binds
+ DevicePostureController provideDevicePostureController(
+ DevicePostureControllerImpl devicePostureControllerImpl);
+
+ /** */
@SysUISingleton
@Provides
static AccessPointControllerImpl provideAccessPointControllerImpl(
@@ -147,4 +161,14 @@ public interface StatusBarPolicyModule {
controller.init();
return controller;
}
+
+ /**
+ * Default values for per-device state rotation lock settings.
+ */
+ @Provides
+ @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS)
+ static String[] providesDeviceStateRotationLockDefaults(@Main Resources resources) {
+ return resources.getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 81999b534046..377a7e65d0a4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -57,9 +57,9 @@ 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.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.util.settings.SecureSettings;
@@ -325,6 +325,11 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+ // All wallpaper color and keyguard logic only applies when Monet is enabled.
+ if (!mIsMonetEnabled) {
+ return;
+ }
+
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
@@ -530,6 +535,10 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
managedProfiles);
}
+ onOverlaysApplied();
+ }
+
+ protected void onOverlaysApplied() {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index 0a29e04ce20f..20857eaba7d4 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -106,8 +106,8 @@ public class PluginFragment extends PreferenceFragment {
PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
apps.forEach(app -> {
if (!plugins.containsKey(app.packageName)) return;
- if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) {
- // Don't manage whitelisted plugins, they are part of the OS.
+ if (ArrayUtils.contains(manager.getPrivilegedPlugins(), app.packageName)) {
+ // Don't manage privileged plugins, they are part of the OS.
return;
}
SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
new file mode 100644
index 000000000000..9283403edd1a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.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.unfold
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.hardware.devicestate.DeviceStateManager
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.view.Surface
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.LinearLightRevealEffect
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import javax.inject.Inject
+
+@SysUISingleton
+class UnfoldLightRevealOverlayAnimation @Inject constructor(
+ private val context: Context,
+ private val deviceStateManager: DeviceStateManager,
+ private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ @Main private val executor: Executor,
+ private val windowManager: WindowManager
+) {
+
+ private val transitionListener = TransitionListener()
+ private var scrimView: LightRevealScrim? = null
+
+ fun init() {
+ deviceStateManager.registerCallback(executor, FoldListener())
+ unfoldTransitionProgressProvider.addCallback(transitionListener)
+ }
+
+ private inner class TransitionListener : TransitionProgressListener {
+
+ override fun onTransitionProgress(progress: Float) {
+ scrimView?.revealAmount = progress
+ }
+
+ override fun onTransitionFinished() {
+ removeOverlayView()
+ }
+
+ override fun onTransitionStarted() {
+ }
+ }
+
+ private inner class FoldListener : FoldStateListener(context, Consumer { isFolded ->
+ if (isFolded) {
+ removeOverlayView()
+ } else {
+ // Add overlay view before starting the transition as soon as we unfolded the device
+ addOverlayView()
+ }
+ })
+
+ private fun addOverlayView() {
+ val params: WindowManager.LayoutParams = WindowManager.LayoutParams()
+ params.height = WindowManager.LayoutParams.MATCH_PARENT
+ params.width = WindowManager.LayoutParams.MATCH_PARENT
+ params.format = PixelFormat.TRANSLUCENT
+
+ // TODO(b/193801466): create a separate type for this overlay
+ params.type = WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+ params.title = "Unfold Light Reveal Animation"
+ params.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ params.fitInsetsTypes = 0
+ params.flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+ params.setTrustedOverlay()
+
+ val rotation = windowManager.defaultDisplay.rotation
+ val isVerticalFold = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
+
+ val newScrimView = LightRevealScrim(context, null)
+ .apply {
+ revealEffect = LinearLightRevealEffect(isVerticalFold)
+ isScrimOpaqueChangedListener = Consumer {}
+ revealAmount = 0f
+ }
+
+ val packageName: String = newScrimView.context.opPackageName
+ params.packageName = packageName
+ params.hideTimeoutMilliseconds = OVERLAY_HIDE_TIMEOUT_MILLIS
+
+ if (scrimView?.parent != null) {
+ windowManager.removeView(scrimView)
+ }
+
+ this.scrimView = newScrimView
+
+ try {
+ windowManager.addView(scrimView, params)
+ } catch (e: WindowManager.BadTokenException) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun removeOverlayView() {
+ scrimView?.let {
+ if (it.parent != null) {
+ windowManager.removeViewImmediate(it)
+ }
+ scrimView = null
+ }
+ }
+}
+
+private const val OVERLAY_HIDE_TIMEOUT_MILLIS = 10_000L
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 98b4209ede00..bfa50bcee270 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -36,6 +36,7 @@ import android.os.UserHandle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
@@ -63,6 +64,8 @@ public class UsbPermissionActivity extends AlertActivity
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ getWindow().addPrivateFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
Intent intent = getIntent();
mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index bf006672f4d9..a1cdfd8201c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -29,7 +29,6 @@ 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.List;
import java.util.function.Consumer;
@@ -161,13 +160,11 @@ public class Utils {
}
/**
- * Returns true if the device should use the split notification shade, based on feature flags,
- * orientation and screen width.
+ * Returns true if the device should use the split notification shade, based on orientation and
+ * screen width.
*/
- public static boolean shouldUseSplitNotificationShade(FeatureFlags featureFlags,
- Resources resources) {
- return featureFlags.isTwoColumnNotificationShadeEnabled()
- && resources.getBoolean(R.bool.config_use_split_notification_shade);
+ public static boolean shouldUseSplitNotificationShade(Resources resources) {
+ return resources.getBoolean(R.bool.config_use_split_notification_shade);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index 1c504961e715..107fe870a07a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -22,8 +22,10 @@ import android.os.Looper;
import com.android.systemui.dagger.qualifiers.Main;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Binds;
@@ -35,6 +37,7 @@ import dagger.Provides;
*/
@Module
public abstract class GlobalConcurrencyModule {
+ public static final String PRE_HANDLER = "pre_handler";
/**
* Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
@@ -64,13 +67,32 @@ public abstract class GlobalConcurrencyModule {
* Provide a Main-Thread Executor.
*/
@Provides
+ @Singleton
@Main
public static Executor provideMainExecutor(Context context) {
return context.getMainExecutor();
}
+ /**
+ * Provide a Main-Thread DelayableExecutor.
+ */
+ @Provides
+ @Singleton
+ @Main
+ public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
+ return new ExecutorImpl(looper);
+ }
+
+
/** */
@Binds
@Singleton
public abstract Execution provideExecution(ExecutionImpl execution);
+
+ /** */
+ @Provides
+ @Named(PRE_HANDLER)
+ public static Optional<Thread.UncaughtExceptionHandler> providesUncaughtExceptionHandler() {
+ return Optional.ofNullable(Thread.getUncaughtExceptionPreHandler());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
new file mode 100644
index 000000000000..542cf6559c64
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouter.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.concurrency;
+
+/**
+ * Allows triggering methods based on a passed in id or message, generally on another thread.
+ *
+ * Messages sent on to this router must be processed in order. That is to say, if three
+ * messages are sent with no delay, they must be processed in the order they were sent. Moreover,
+ * if messages are sent with various delays, they must be processed in order of their delay.
+ *
+ * Messages can be passed by either a simple integer or an instance of a class. Unique integers are
+ * considered unique messages. Unique message classes (not instances) are considered unique
+ * messages. You can use message classes to pass extra data for processing to subscribers.
+ *
+ * <pre>
+ * // Three messages with three unique integer messages.
+ * // They can be subscribed to independently.
+ * router.sendMessage(0);
+ * router.sendMessage(1);
+ * router.sendMessage(2);
+ *
+ * // Three messages with two unique message classes.
+ * // The first and third messages will be delivered to the same subscribers.
+ * router.sendMessage(new Foo(0));
+ * router.sendMessage(new Bar(1));
+ * router.sendMessage(new Foo(2));
+ * </pre>
+ *
+ * The number of unique ids and message types used should be relatively constrained. Construct
+ * a custom message-class and put unique, per-message data inside of it.
+ */
+public interface MessageRouter {
+ /**
+ * Alerts any listeners subscribed to the passed in id.
+ *
+ * The number of unique ids used should be relatively constrained - used to identify the type
+ * of message being sent. If unique information needs to be passed with each call, use
+ * {@link #sendMessage(Object)}.
+ *
+ * @param id An identifier for the message
+ */
+ default void sendMessage(int id) {
+ sendMessageDelayed(id, 0);
+ }
+
+ /**
+ * Alerts any listeners subscribed to the passed in message.
+ *
+ * The number of message types used should be relatively constrained. If no unique information
+ * needs to be passed in, you can simply use {@link #sendMessage(int)}} which takes an integer
+ * instead of a unique class type.
+ *
+ * The class of the passed in object will be used to router the message.
+ *
+ * @param data A message containing extra data for processing.
+ */
+ default void sendMessage(Object data) {
+ sendMessageDelayed(data, 0);
+ }
+
+ /**
+ * Alerts any listeners subscribed to the passed in id in the future.
+ *
+ * The number of unique ids used should be relatively constrained - used to identify the type
+ * of message being sent. If unique information needs to be passed with each call, use
+ * {@link #sendMessageDelayed(Object, long)}.
+ *
+ * @param id An identifier for the message
+ * @param delayMs Number of milliseconds to wait before alerting.
+ */
+ void sendMessageDelayed(int id, long delayMs);
+
+
+ /**
+ * Alerts any listeners subscribed to the passed in message in the future.
+ *
+ * The number of message types used should be relatively constrained. If no unique information
+ * needs to be passed in, you can simply use {@link #sendMessageDelayed(int, long)} which takes
+ * an integer instead of a unique class type.
+ *
+ * @param data A message containing extra data for processing.
+ * @param delayMs Number of milliseconds to wait before alerting.
+ */
+ void sendMessageDelayed(Object data, long delayMs);
+
+ /**
+ * Cancel all unprocessed messages for a given id.
+ *
+ * If a message has multiple listeners and one of those listeners has been alerted, the other
+ * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+ * that are still queued.
+ *
+ * @param id The message id to cancel.
+ */
+ void cancelMessages(int id);
+
+ /**
+ * Cancel all unprocessed messages for a given message type.
+ *
+ * If a message has multiple listeners and one of those listeners has been alerted, the other
+ * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
+ * that are still queued.
+ *
+ * @param messageType The class of the message to cancel
+ */
+ <T> void cancelMessages(Class<T> messageType);
+
+ /**
+ * Add a listener for a message that does not handle any extra data.
+ *
+ * See also {@link #subscribeTo(Class, DataMessageListener)}.
+ *
+ * @param id The message id to listener for.
+ * @param listener
+ */
+ void subscribeTo(int id, SimpleMessageListener listener);
+
+ /**
+ * Add a listener for a message of a specific type.
+ *
+ * See also {@link #subscribeTo(Class, DataMessageListener)}.
+ *
+ * @param messageType The class of message to listen for.
+ * @param listener
+ */
+ <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener);
+
+ /**
+ * Remove a listener for a specific message.
+ *
+ * See also {@link #unsubscribeFrom(Class, DataMessageListener)}
+ *
+ * @param id The message id to stop listening for.
+ * @param listener The listener to remove.
+ */
+ void unsubscribeFrom(int id, SimpleMessageListener listener);
+
+ /**
+ * Remove a listener for a specific message.
+ *
+ * See also {@link #unsubscribeFrom(int, SimpleMessageListener)}.
+ *
+ * @param messageType The class of message to stop listening for.
+ * @param listener The listener to remove.
+ */
+ <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener);
+
+ /**
+ * Remove a listener for all messages that it is subscribed to.
+ *
+ * See also {@link #unsubscribeFrom(DataMessageListener)}.
+ *
+ * @param listener The listener to remove.
+ */
+ void unsubscribeFrom(SimpleMessageListener listener);
+
+ /**
+ * Remove a listener for all messages that it is subscribed to.
+ *
+ * See also {@link #unsubscribeFrom(SimpleMessageListener)}.
+ *
+ * @param listener The listener to remove.
+ */
+ <T> void unsubscribeFrom(DataMessageListener<T> listener);
+
+ /**
+ * A Listener interface for when no extra data is expected or desired.
+ */
+ interface SimpleMessageListener {
+ /** */
+ void onMessage(int id);
+ }
+
+ /**
+ * A Listener interface for when extra data is expected or desired.
+ *
+ * @param <T>
+ */
+ interface DataMessageListener<T> {
+ /** */
+ void onMessage(T data);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
new file mode 100644
index 000000000000..7145c775fe39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/MessageRouterImpl.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of {@link MessageRouter}.
+ */
+public class MessageRouterImpl implements MessageRouter {
+
+ private final DelayableExecutor mDelayableExecutor;
+
+ private final Map<Integer, List<Runnable>> mIdMessageCancelers = new HashMap<>();
+ private final Map<Class<Object>, List<Runnable>> mDataMessageCancelers = new HashMap<>();
+ private final Map<Integer, List<SimpleMessageListener>> mSimpleMessageListenerMap =
+ new HashMap<>();
+ private final Map<Class<?>, List<DataMessageListener<Object>>> mDataMessageListenerMap =
+ new HashMap<>();
+
+ public MessageRouterImpl(DelayableExecutor delayableExecutor) {
+ mDelayableExecutor = delayableExecutor;
+ }
+
+ @Override
+ public void sendMessageDelayed(int id, long delayMs) {
+ addCanceler(id, mDelayableExecutor.executeDelayed(() -> onMessage(id), delayMs));
+ }
+
+ @Override
+ public void sendMessageDelayed(Object data, long delayMs) {
+ addCanceler((Class<Object>) data.getClass(), mDelayableExecutor.executeDelayed(
+ () -> onMessage(data), delayMs));
+ }
+
+ @Override
+ public void cancelMessages(int id) {
+ synchronized (mIdMessageCancelers) {
+ if (mIdMessageCancelers.containsKey(id)) {
+ for (Runnable canceler : mIdMessageCancelers.get(id)) {
+ canceler.run();
+ }
+ // Remove, don't clear, otherwise this could look like a memory leak as
+ // more and more unique message ids are passed in.
+ mIdMessageCancelers.remove(id);
+ }
+ }
+ }
+
+ @Override
+ public <T> void cancelMessages(Class<T> messageType) {
+ synchronized (mDataMessageCancelers) {
+ if (mDataMessageCancelers.containsKey(messageType)) {
+ for (Runnable canceler : mDataMessageCancelers.get(messageType)) {
+ canceler.run();
+ }
+ // Remove, don't clear, otherwise this could look like a memory leak as
+ // more and more unique message types are passed in.
+ mDataMessageCancelers.remove(messageType);
+ }
+ }
+ }
+
+ @Override
+ public void subscribeTo(int id, SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ mSimpleMessageListenerMap.putIfAbsent(id, new ArrayList<>());
+ mSimpleMessageListenerMap.get(id).add(listener);
+ }
+ }
+
+ @Override
+ public <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ mDataMessageListenerMap.putIfAbsent(messageType, new ArrayList<>());
+ mDataMessageListenerMap.get(messageType).add((DataMessageListener<Object>) listener);
+ }
+ }
+
+ @Override
+ public void unsubscribeFrom(int id, SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ if (mSimpleMessageListenerMap.containsKey(id)) {
+ mSimpleMessageListenerMap.get(id).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ if (mDataMessageListenerMap.containsKey(messageType)) {
+ mDataMessageListenerMap.get(messageType).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public void unsubscribeFrom(SimpleMessageListener listener) {
+ synchronized (mSimpleMessageListenerMap) {
+ for (Integer id : mSimpleMessageListenerMap.keySet()) {
+ mSimpleMessageListenerMap.get(id).remove(listener);
+ }
+ }
+ }
+
+ @Override
+ public <T> void unsubscribeFrom(DataMessageListener<T> listener) {
+ synchronized (mDataMessageListenerMap) {
+ for (Class<?> messageType : mDataMessageListenerMap.keySet()) {
+ mDataMessageListenerMap.get(messageType).remove(listener);
+ }
+ }
+ }
+
+ private void addCanceler(int id, Runnable canceler) {
+ synchronized (mIdMessageCancelers) {
+ mIdMessageCancelers.putIfAbsent(id, new ArrayList<>());
+ mIdMessageCancelers.get(id).add(canceler);
+ }
+ }
+
+ private void addCanceler(Class<Object> data, Runnable canceler) {
+ synchronized (mDataMessageCancelers) {
+ mDataMessageCancelers.putIfAbsent(data, new ArrayList<>());
+ mDataMessageCancelers.get(data).add(canceler);
+ }
+ }
+
+ private void onMessage(int id) {
+ synchronized (mSimpleMessageListenerMap) {
+ if (mSimpleMessageListenerMap.containsKey(id)) {
+ for (SimpleMessageListener listener : mSimpleMessageListenerMap.get(id)) {
+ listener.onMessage(id);
+ }
+ }
+ }
+
+ synchronized (mIdMessageCancelers) {
+ if (mIdMessageCancelers.containsKey(id) && !mIdMessageCancelers.get(id).isEmpty()) {
+ mIdMessageCancelers.get(id).remove(0);
+ if (mIdMessageCancelers.get(id).isEmpty()) {
+ mIdMessageCancelers.remove(id);
+ }
+ }
+ }
+ }
+
+ private void onMessage(Object data) {
+ synchronized (mDataMessageListenerMap) {
+ if (mDataMessageListenerMap.containsKey(data.getClass())) {
+ for (DataMessageListener<Object> listener : mDataMessageListenerMap.get(
+ data.getClass())) {
+ listener.onMessage(data);
+ }
+ }
+ }
+
+ synchronized (mDataMessageCancelers) {
+ if (mDataMessageCancelers.containsKey(data.getClass())
+ && !mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+ mDataMessageCancelers.get(data.getClass()).remove(0);
+ if (mDataMessageCancelers.get(data.getClass()).isEmpty()) {
+ mDataMessageCancelers.remove(data.getClass());
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index b9b20c73c5d5..e8a9bc702352 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -120,16 +120,6 @@ public abstract class SysUIConcurrencyModule {
}
/**
- * Provide a Main-Thread Executor.
- */
- @Provides
- @SysUISingleton
- @Main
- public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
- return new ExecutorImpl(looper);
- }
-
- /**
* Provide a Background-Thread Executor by default.
*/
@Provides
@@ -170,4 +160,20 @@ public abstract class SysUIConcurrencyModule {
public static Executor provideUiBackgroundExecutor() {
return Executors.newSingleThreadExecutor();
}
+
+ /** */
+ @Provides
+ @Main
+ public static MessageRouter providesMainMessageRouter(
+ @Main DelayableExecutor executor) {
+ return new MessageRouterImpl(executor);
+ }
+
+ /** */
+ @Provides
+ @Background
+ public static MessageRouter providesBackgroundMessageRouter(
+ @Background DelayableExecutor executor) {
+ return new MessageRouterImpl(executor);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index cdfa1457f4a5..981bf01164e3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,12 +18,15 @@ package com.android.systemui.util.dagger;
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
import dagger.Module;
/** Dagger Module for code in the util package. */
-@Module
+@Module(includes = {
+ UtilWrapperModule.class
+ })
public interface UtilModule {
/** */
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index edea3055a783..c7998882876a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -36,7 +36,6 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.Process;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -60,6 +59,8 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -110,18 +111,18 @@ public class GarbageMonitor implements Dumpable {
private static final int DO_GARBAGE_INSPECTION = 1000;
private static final int DO_HEAP_TRACK = 3000;
- private static final int GARBAGE_ALLOWANCE = 5;
+ static final int GARBAGE_ALLOWANCE = 5;
private static final String TAG = "GarbageMonitor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private final Handler mHandler;
+ private final MessageRouter mMessageRouter;
private final TrackedGarbage mTrackedGarbage;
private final LeakReporter mLeakReporter;
private final Context mContext;
- private final ActivityManager mAm;
+ private final DelayableExecutor mDelayableExecutor;
private MemoryTile mQSTile;
- private DumpTruck mDumpTruck;
+ private final DumpTruck mDumpTruck;
private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
private final ArrayList<Long> mPids = new ArrayList<>();
@@ -133,13 +134,16 @@ public class GarbageMonitor implements Dumpable {
@Inject
public GarbageMonitor(
Context context,
- @Background Looper bgLooper,
+ @Background DelayableExecutor delayableExecutor,
+ @Background MessageRouter messageRouter,
LeakDetector leakDetector,
LeakReporter leakReporter) {
mContext = context.getApplicationContext();
- mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- mHandler = new BackgroundHeapCheckHandler(bgLooper);
+ mDelayableExecutor = delayableExecutor;
+ mMessageRouter = messageRouter;
+ mMessageRouter.subscribeTo(DO_GARBAGE_INSPECTION, this::doGarbageInspection);
+ mMessageRouter.subscribeTo(DO_HEAP_TRACK, this::doHeapTrack);
mTrackedGarbage = leakDetector.getTrackedGarbage();
mLeakReporter = leakReporter;
@@ -158,13 +162,13 @@ public class GarbageMonitor implements Dumpable {
return;
}
- mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
+ mMessageRouter.sendMessage(DO_GARBAGE_INSPECTION);
}
public void startHeapTracking() {
startTrackingProcess(
android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
- mHandler.sendEmptyMessage(DO_HEAP_TRACK);
+ mMessageRouter.sendMessage(DO_HEAP_TRACK);
}
private boolean gcAndCheckGarbage() {
@@ -586,33 +590,18 @@ public class GarbageMonitor implements Dumpable {
}
}
- private class BackgroundHeapCheckHandler extends Handler {
- BackgroundHeapCheckHandler(Looper onLooper) {
- super(onLooper);
- if (Looper.getMainLooper().equals(onLooper)) {
- throw new RuntimeException(
- "BackgroundHeapCheckHandler may not run on the ui thread");
- }
+ private void doGarbageInspection(int id) {
+ if (gcAndCheckGarbage()) {
+ mDelayableExecutor.executeDelayed(this::reinspectGarbageAfterGc, 100);
}
- @Override
- public void handleMessage(Message m) {
- switch (m.what) {
- case DO_GARBAGE_INSPECTION:
- if (gcAndCheckGarbage()) {
- postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
- }
-
- removeMessages(DO_GARBAGE_INSPECTION);
- sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
- break;
+ mMessageRouter.cancelMessages(DO_GARBAGE_INSPECTION);
+ mMessageRouter.sendMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
+ }
- case DO_HEAP_TRACK:
- update();
- removeMessages(DO_HEAP_TRACK);
- sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
- break;
- }
- }
+ private void doHeapTrack(int id) {
+ update();
+ mMessageRouter.cancelMessages(DO_HEAP_TRACK);
+ mMessageRouter.sendMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 90e022a52d7a..bd1103982017 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -87,15 +87,23 @@ public class ProximitySensor implements ThresholdSensor {
&& (mLastPrimaryEvent == null
|| !mLastPrimaryEvent.getBelow()
|| !event.getBelow())) {
- mSecondaryThresholdSensor.pause();
+ chooseSensor();
if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
// Only check the secondary as long as the primary thinks we're near.
- mCancelSecondaryRunnable = null;
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
return;
} else {
// Check this sensor again in a moment.
- mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(
- mSecondaryThresholdSensor::resume, SECONDARY_PING_INTERVAL_MS);
+ mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
+ // This is safe because we know that mSecondaryThresholdSensor
+ // is loaded, otherwise we wouldn't be here.
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.resume();
+ },
+ SECONDARY_PING_INTERVAL_MS);
}
}
logDebug("Secondary sensor event: " + event.getBelow() + ".");
@@ -159,12 +167,8 @@ public class ProximitySensor implements ThresholdSensor {
* of what is reported by the primary sensor.
*/
public void setSecondarySafe(boolean safe) {
- mSecondarySafe = safe;
- if (!mSecondarySafe) {
- mSecondaryThresholdSensor.pause();
- } else {
- mSecondaryThresholdSensor.resume();
- }
+ mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
+ chooseSensor();
}
/**
@@ -209,16 +213,30 @@ public class ProximitySensor implements ThresholdSensor {
return;
}
if (!mInitializedListeners) {
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
mPrimaryThresholdSensor.register(mPrimaryEventListener);
- if (!mSecondarySafe) {
- mSecondaryThresholdSensor.pause();
- }
mSecondaryThresholdSensor.register(mSecondaryEventListener);
mInitializedListeners = true;
}
logDebug("Registering sensor listener");
- mPrimaryThresholdSensor.resume();
+
mRegistered = true;
+ chooseSensor();
+ }
+
+ private void chooseSensor() {
+ mExecution.assertIsMainThread();
+ if (!mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ if (mSecondarySafe) {
+ mSecondaryThresholdSensor.resume();
+ mPrimaryThresholdSensor.pause();
+ } else {
+ mPrimaryThresholdSensor.resume();
+ mSecondaryThresholdSensor.pause();
+ }
}
/**
@@ -312,7 +330,7 @@ public class ProximitySensor implements ThresholdSensor {
}
if (!mSecondarySafe && !event.getBelow()) {
- mSecondaryThresholdSensor.pause();
+ chooseSensor();
}
mLastEvent = event;
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
new file mode 100644
index 000000000000..2a0cc7ddacf5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.wrapper
+
+import android.content.Context
+import com.android.internal.view.RotationPolicy
+import com.android.internal.view.RotationPolicy.RotationPolicyListener
+import javax.inject.Inject
+
+/**
+ * Testable wrapper interface around RotationPolicy {link com.android.internal.view.RotationPolicy}
+ */
+interface RotationPolicyWrapper {
+ fun setRotationLock(enabled: Boolean)
+ fun setRotationLockAtAngle(enabled: Boolean, rotation: Int)
+ fun getRotationLockOrientation(): Int
+ fun isRotationLockToggleVisible(): Boolean
+ fun isRotationLocked(): Boolean
+ fun registerRotationPolicyListener(listener: RotationPolicyListener, userHandle: Int)
+ fun unregisterRotationPolicyListener(listener: RotationPolicyListener)
+}
+
+class RotationPolicyWrapperImpl @Inject constructor(private val context: Context) :
+ RotationPolicyWrapper {
+
+ override fun setRotationLock(enabled: Boolean) {
+ RotationPolicy.setRotationLock(context, enabled)
+ }
+
+ override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
+ RotationPolicy.setRotationLockAtAngle(context, enabled, rotation)
+ }
+
+ override fun getRotationLockOrientation(): Int =
+ RotationPolicy.getRotationLockOrientation(context)
+
+ override fun isRotationLockToggleVisible(): Boolean =
+ RotationPolicy.isRotationLockToggleVisible(context)
+
+ override fun isRotationLocked(): Boolean =
+ RotationPolicy.isRotationLocked(context)
+
+ override fun registerRotationPolicyListener(
+ listener: RotationPolicyListener,
+ userHandle: Int
+ ) {
+ RotationPolicy.registerRotationPolicyListener(context, listener, userHandle)
+ }
+
+ override fun unregisterRotationPolicyListener(listener: RotationPolicyListener) {
+ RotationPolicy.unregisterRotationPolicyListener(context, listener)
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl b/packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt
index 269198404873..7e3aa277db33 100644
--- a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt
@@ -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,12 +14,16 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.systemui.util.wrapper
-import com.android.internal.view.InputBindResult;
-import com.android.internal.inputmethod.ThrowableHolder;
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
-oneway interface IInputBindResultResultCallback {
- void onResult(in InputBindResult result);
- void onError(in ThrowableHolder exception);
-} \ No newline at end of file
+@Module
+abstract class UtilWrapperModule {
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindRotationPolicyWrapper(impl: RotationPolicyWrapperImpl): RotationPolicyWrapper
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 56f1c092efd9..f2e031cfa1df 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -26,7 +26,6 @@ import android.os.Bundle;
import android.view.WindowManager.LayoutParams;
import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
@@ -67,6 +66,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
private final KeyguardViewMediator mKeyguardViewMediator;
+ private final ActivityStarter mActivityStarter;
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
@@ -79,16 +79,20 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
public VolumeDialogComponent(
Context context,
KeyguardViewMediator keyguardViewMediator,
+ ActivityStarter activityStarter,
VolumeDialogControllerImpl volumeDialogController,
- DemoModeController demoModeController) {
+ DemoModeController demoModeController,
+ PluginDependencyProvider pluginDependencyProvider,
+ ExtensionController extensionController,
+ TunerService tunerService) {
mContext = context;
mKeyguardViewMediator = keyguardViewMediator;
+ mActivityStarter = activityStarter;
mController = volumeDialogController;
mController.setUserActivityListener(this);
// Allow plugins to reference the VolumeDialogController.
- Dependency.get(PluginDependencyProvider.class)
- .allowPluginDependency(VolumeDialogController.class);
- Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+ pluginDependencyProvider.allowPluginDependency(VolumeDialogController.class);
+ extensionController.newExtension(VolumeDialog.class)
.withPlugin(VolumeDialog.class)
.withDefault(this::createDefault)
.withCallback(dialog -> {
@@ -99,7 +103,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
}).build();
applyConfiguration();
- Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
+ tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
VOLUME_SILENT_DO_NOT_DISTURB);
demoModeController.addCallback(this);
}
@@ -189,8 +193,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna
}
private void startSettings(Intent intent) {
- Dependency.get(ActivityStarter.class).startActivity(intent,
- true /* onlyProvisioned */, true /* dismissShade */);
+ mActivityStarter.startActivity(intent, true /* onlyProvisioned */, true /* dismissShade */);
}
private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 60b92ef45291..dbf115b62a58 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -104,6 +104,7 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.view.RotationPolicy;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
@@ -400,7 +401,9 @@ public class VolumeDialogImpl implements VolumeDialog,
mDialog.setCanceledOnTouchOutside(true);
mDialog.setOnShowListener(dialog -> {
mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+ if (!shouldSlideInVolumeTray()) {
+ mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f);
+ }
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
@@ -587,6 +590,10 @@ public class VolumeDialogImpl implements VolumeDialog,
return (int) (alpha * 255);
}
+ private boolean shouldSlideInVolumeTray() {
+ return mContext.getDisplay().getRotation() != RotationPolicy.NATURAL_ROTATION;
+ }
+
private boolean isLandscape() {
return mContext.getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE;
@@ -1320,7 +1327,7 @@ public class VolumeDialogImpl implements VolumeDialog,
hideRingerDrawer();
}, 50));
- if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f);
+ if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f);
animator.start();
checkODICaptionsTooltip(true);
mController.notifyVisible(false);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
index 1e1b459382d7..77fd2e830873 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
@@ -190,6 +190,15 @@ public class WalletCardCarousel extends RecyclerView {
}
/**
+ * Sets the adapter again in the RecyclerView, updating the ViewHolders children's layout.
+ * This is needed when changing the state of the device (eg fold/unfold) so the ViewHolders are
+ * recreated.
+ */
+ void resetAdapter() {
+ setAdapter(mWalletCardCarouselAdapter);
+ }
+
+ /**
* Returns true if the data set is changed.
*/
boolean setData(List<WalletCardViewInfo> data, int selectedIndex, boolean hasLockStateChanged) {
@@ -376,8 +385,8 @@ public class WalletCardCarousel extends RecyclerView {
CardView cardView = viewHolder.mCardView;
cardView.setRadius(mCornerRadiusPx);
ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
- layoutParams.width = mCardWidthPx;
- layoutParams.height = mCardHeightPx;
+ layoutParams.width = getCardWidthPx();
+ layoutParams.height = getCardHeightPx();
view.setTag(viewHolder);
return viewHolder;
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 420f84abe0dd..9b2702ff7bf2 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -99,17 +99,13 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
mCardCarousel.setExpectedViewWidth(getWidth());
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- updateViewForOrientation(newConfig.orientation);
- }
-
private void updateViewForOrientation(@Configuration.Orientation int orientation) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
renderViewPortrait();
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
renderViewLandscape();
}
+ mCardCarousel.resetAdapter(); // necessary to update cards width
ViewGroup.LayoutParams params = mCardCarouselContainer.getLayoutParams();
if (params instanceof MarginLayoutParams) {
((MarginLayoutParams) params).topMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a29a638b91a8..d3581a9fd177 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -50,7 +50,6 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
-import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -61,11 +60,10 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
@@ -80,7 +78,6 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -118,7 +115,6 @@ public class BubblesManager implements Dumpable {
private final NotifPipeline mNotifPipeline;
private final Executor mSysuiMainExecutor;
- private ScrimView mBubbleScrim;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
@@ -193,12 +189,6 @@ public class BubblesManager implements Dumpable {
ServiceManager.getService(Context.STATUS_BAR_SERVICE))
: statusBarService;
- mBubbleScrim = new ScrimView(mContext);
- mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mBubbles.setBubbleScrim(mBubbleScrim, (executor, looper) -> {
- mBubbleScrim.setExecutor(executor, looper);
- });
-
if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
setupNotifPipeline();
} else {
@@ -603,15 +593,6 @@ public class BubblesManager implements Dumpable {
}
/**
- * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController}
- * since we want the scrim's appearance and behavior to be identical to that of the notification
- * shade scrim.
- */
- public ScrimView getScrimForBubble() {
- return mBubbleScrim;
- }
-
- /**
* We intercept notification entries (including group summaries) dismissed by the user when
* there is an active bubble associated with it. We do this so that developers can still
* cancel it (and hence the bubbles associated with it).
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 2216a915b0d9..3be1d3cab869 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -38,6 +38,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -144,10 +145,17 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
TvPipMenuController tvPipMenuController,
SyncTransactionQueue syncTransactionQueue,
PipBoundsState pipBoundsState,
+ PipTransitionState pipTransitionState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipAnimationController pipAnimationController,
PipTransitionController pipTransitionController,
@@ -157,7 +165,7 @@ public abstract class TvPipModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 39a8bd94bf37..dbdc460b7d83 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -25,6 +25,7 @@ import com.android.systemui.dagger.WMSingleton;
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.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -58,10 +59,10 @@ public class TvWMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
- TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainExecutor,
- transactionPool);
+ DisplayController displayController, DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
+ return new DisplayImeController(wmService, displayController, displayInsetsController,
+ mainExecutor, transactionPool);
}
//
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bc956dc85702..db965db13833 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -64,6 +64,7 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.wm.shell.splitscreen.SplitScreen;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -104,7 +105,8 @@ public final class WMShell extends SystemUI
// Shell interfaces
private final Optional<Pip> mPipOptional;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
+ private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
@@ -120,6 +122,7 @@ public final class WMShell extends SystemUI
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
+ private KeyguardUpdateMonitorCallback mLegacySplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@@ -128,7 +131,8 @@ public final class WMShell extends SystemUI
@Inject
public WMShell(Context context,
Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<LegacySplitScreen> legacySplitScreenOptional,
+ Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
@@ -149,6 +153,7 @@ public final class WMShell extends SystemUI
mScreenLifecycle = screenLifecycle;
mSysUiState = sysUiState;
mPipOptional = pipOptional;
+ mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
@@ -165,6 +170,7 @@ public final class WMShell extends SystemUI
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
+ mLegacySplitScreenOptional.ifPresent(this::initLegacySplitScreen);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
mOneHandedOptional.ifPresent(this::initOneHanded);
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
@@ -218,8 +224,8 @@ public final class WMShell extends SystemUI
}
@VisibleForTesting
- void initSplitScreen(LegacySplitScreen legacySplitScreen) {
- mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ void initLegacySplitScreen(LegacySplitScreen legacySplitScreen) {
+ mLegacySplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onKeyguardVisibilityChanged(boolean showing) {
// Hide the divider when keyguard is showing. Even though keyguard/statusbar is
@@ -229,6 +235,22 @@ public final class WMShell extends SystemUI
legacySplitScreen.onKeyguardVisibilityChanged(showing);
}
};
+ mKeyguardUpdateMonitor.registerCallback(mLegacySplitScreenKeyguardCallback);
+ }
+
+ @VisibleForTesting
+ void initSplitScreen(SplitScreen splitScreen) {
+ mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ splitScreen.onKeyguardVisibilityChanged(showing);
+ }
+
+ @Override
+ public void onKeyguardOccludedChanged(boolean occluded) {
+ splitScreen.onKeyguardOccludedChanged(occluded);
+ }
+ };
mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 6ef74505a681..c178b29326f7 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -55,6 +56,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -109,6 +111,14 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
+ static DisplayInsetsController provideDisplayInsetsController( IWindowManager wmService,
+ DisplayController displayController,
+ @ShellMainThread ShellExecutor mainExecutor) {
+ return new DisplayInsetsController(wmService, displayController, mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
static DisplayLayout provideDisplayLayout() {
return new DisplayLayout();
}
@@ -116,8 +126,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static DragAndDropController provideDragAndDropController(Context context,
- DisplayController displayController) {
- return new DragAndDropController(context, displayController);
+ DisplayController displayController, UiEventLogger uiEventLogger) {
+ return new DragAndDropController(context, displayController, uiEventLogger);
}
@WMSingleton
@@ -194,11 +204,12 @@ public abstract class WMShellBaseModule {
ShellTaskOrganizer organizer,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellMainThread Handler mainHandler) {
+ @ShellMainThread Handler mainHandler,
+ SyncTransactionQueue syncQueue) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, taskStackListener,
- uiEventLogger, organizer, displayController, mainExecutor, mainHandler));
+ uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
}
//
@@ -212,6 +223,13 @@ public abstract class WMShellBaseModule {
}
//
+ // Freeform (optional feature)
+ //
+
+ @BindsOptionalOf
+ abstract Optional<FreeformTaskListener> optionalFreeformTaskListener();
+
+ //
// Hide display cutout
//
@@ -327,9 +345,11 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
- Context context, @ShellMainThread ShellExecutor mainExecutor,
+ DisplayController displayController, Context context,
+ @ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor) {
- return new Transitions(organizer, pool, context, mainExecutor, animExecutor);
+ return new Transitions(organizer, pool, displayController, context, mainExecutor,
+ animExecutor);
}
//
@@ -424,8 +444,9 @@ public abstract class WMShellBaseModule {
@Provides
static TaskViewFactoryController provideTaskViewFactoryController(
ShellTaskOrganizer shellTaskOrganizer,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ SyncTransactionQueue syncQueue) {
+ return new TaskViewFactoryController(shellTaskOrganizer, mainExecutor, syncQueue);
}
//
@@ -440,7 +461,9 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,
+ static ShellInitImpl provideShellInitImpl(DisplayController displayController,
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<BubbleController> bubblesOptional,
@@ -449,10 +472,13 @@ public abstract class WMShellBaseModule {
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
+ Optional<Optional<FreeformTaskListener>> freeformTaskListener,
Transitions transitions,
StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
- return new ShellInitImpl(displayImeController,
+ return new ShellInitImpl(displayController,
+ displayImeController,
+ displayInsetsController,
dragAndDropController,
shellTaskOrganizer,
bubblesOptional,
@@ -461,6 +487,7 @@ public abstract class WMShellBaseModule {
appPairsOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
+ freeformTaskListener,
transitions,
startingWindow,
mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 36fd9bee80ab..83c2a9b1be33 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -28,6 +28,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -36,6 +37,7 @@ 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.freeform.FreeformTaskListener;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -48,6 +50,7 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -81,10 +84,23 @@ public class WMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
+ DisplayController displayController, DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainExecutor,
- transactionPool);
+ return new DisplayImeController(wmService, displayController, displayInsetsController,
+ mainExecutor, transactionPool);
+ }
+
+ //
+ // Freeform
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<FreeformTaskListener> provideFreeformTaskListener(
+ Context context,
+ SyncTransactionQueue syncQueue) {
+ return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue));
}
//
@@ -184,8 +200,15 @@ public class WMShellModule {
@WMSingleton
@Provides
+ static PipTransitionState providePipTransitionState() {
+ return new PipTransitionState();
+ }
+
+ @WMSingleton
+ @Provides
static PipTaskOrganizer providePipTaskOrganizer(Context context,
SyncTransactionQueue syncTransactionQueue,
+ PipTransitionState pipTransitionState,
PipBoundsState pipBoundsState,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipMenuController menuPhoneController,
@@ -197,7 +220,7 @@ public class WMShellModule {
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context,
- syncTransactionQueue, pipBoundsState, pipBoundsAlgorithm,
+ syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger,
shellTaskOrganizer, mainExecutor);
@@ -215,8 +238,9 @@ public class WMShellModule {
static PipTransitionController providePipTransitionController(Context context,
Transitions transitions, ShellTaskOrganizer shellTaskOrganizer,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PhonePipMenuController pipMenuController) {
- return new PipTransition(context, pipBoundsState, pipMenuController,
+ PipBoundsState pipBoundsState, PipTransitionState pipTransitionState,
+ PhonePipMenuController pipMenuController) {
+ return new PipTransition(context, pipBoundsState, pipTransitionState, pipMenuController,
pipBoundsAlgorithm, pipAnimationController, transitions, shellTaskOrganizer);
}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index f1c687ff3224..2c9c98032245 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -93,6 +93,13 @@
<activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
android:exported="false" />
+ <!-- started from UsbDeviceSettingsManager -->
+ <activity android:name=".usb.UsbPermissionActivityTest$UsbPermissionActivityTestable"
+ android:exported="false"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true" />
+
<provider
android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 06b0bb25e01c..ce65733ad5e5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,7 +19,6 @@ package com.android.keyguard;
import static org.junit.Assert.assertEquals;
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.never;
import static org.mockito.Mockito.times;
@@ -29,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -104,6 +104,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
private AnimatableClockView mLargeClockView;
@Mock
private FrameLayout mLargeClockFrame;
+ @Mock
+ private ViewGroup mSmartspaceContainer;
private final View mFakeSmartspaceView = new View(mContext);
@@ -123,6 +125,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame);
+ when(mView.findViewById(R.id.keyguard_smartspace_container))
+ .thenReturn(mSmartspaceContainer);
when(mClockView.getContext()).thenReturn(getContext());
when(mLargeClockView.getContext()).thenReturn(getContext());
@@ -210,7 +214,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Test
public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
- when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(true);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
@@ -219,7 +223,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Test
public void testSmartspaceDisabledShowsKeyguardStatusArea() {
- when(mSmartspaceController.isEnabled()).thenReturn(false);
+ when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(false);
mController.init();
assertEquals(View.VISIBLE, mStatusArea.getVisibility());
@@ -227,17 +231,16 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@Test
public void testDetachRemovesSmartspaceView() {
- when(mSmartspaceController.isEnabled()).thenReturn(true);
+ when(mSmartspaceController.isSmartspaceEnabled()).thenReturn(true);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
- verify(mView).addView(eq(mFakeSmartspaceView), anyInt(), any());
ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
- verify(mView).removeView(mFakeSmartspaceView);
+ verify(mSmartspaceContainer).removeAllViews();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 10ed1d75a533..ce02b8339422 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -19,6 +19,9 @@ package com.android.keyguard;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@@ -247,4 +250,36 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
verify(plugin).setStyle(style);
}
+
+ @Test
+ public void switchingToBigClock_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClock_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL);
+
+ mKeyguardClockSwitch.mClockInAnim.end();
+ mKeyguardClockSwitch.mClockOutAnim.end();
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index bb71bed84f43..8e1e42a9c9a2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.statusbar.policy.DevicePostureController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,6 +63,8 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
@Mock
private lateinit var mLockPatternView: LockPatternView
+ @Mock
+ private lateinit var mPostureController: DevicePostureController
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
@@ -78,7 +81,7 @@ class KeyguardPatternViewControllerTest : SysuiTestCase() {
mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
mLatencyTracker, mFalsingCollector, mEmergencyButtonController,
- mKeyguardMessageAreaControllerFactory)
+ mKeyguardMessageAreaControllerFactory, mPostureController)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index ca857c5dd05b..64bdc2e9dc5d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -19,11 +19,13 @@ package com.android.keyguard;
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -33,6 +35,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.MotionEvent;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
@@ -43,6 +46,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -58,6 +62,7 @@ import org.mockito.junit.MockitoRule;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
+ private static final int VIEW_WIDTH = 1600;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -100,6 +105,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
private EmergencyButtonController mEmergencyButtonController;
@Mock
private Resources mResources;
+ @Mock
+ private FalsingCollector mFalsingCollector;
private Configuration mConfiguration;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -112,7 +119,9 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mConfiguration.setToDefaults(); // Defaults to ORIENTATION_UNDEFINED.
when(mResources.getConfiguration()).thenReturn(mConfiguration);
+ when(mView.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
+ when(mView.getWidth()).thenReturn(VIEW_WIDTH);
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
@@ -131,7 +140,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mKeyguardSecurityViewFlipperController,
- mConfigurationController)
+ mConfigurationController, mFalsingCollector)
.create(mSecurityCallback);
}
@@ -169,18 +178,156 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
public void onResourcesUpdate_callsThroughOnRotationChange() {
// Rotation is the same, shouldn't cause an update
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(0)).updateLayoutForSecurityMode(any());
+ verify(mView, times(0)).setOneHandedMode(anyBoolean());
// Update rotation. Should trigger update
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
mKeyguardSecurityContainerController.updateResources();
- verify(mView, times(1)).updateLayoutForSecurityMode(any());
+ verify(mView, times(1)).setOneHandedMode(anyBoolean());
}
@Test
- public void updateKeyguardPosition_callsThroughToView() {
+ public void updateKeyguardPosition_callsThroughToViewInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ mKeyguardSecurityContainerController.updateKeyguardPosition(VIEW_WIDTH / 3f);
+ verify(mView).setOneHandedModeLeftAligned(true, false);
+
+ mKeyguardSecurityContainerController.updateKeyguardPosition((VIEW_WIDTH / 3f) * 2);
+ verify(mView).setOneHandedModeLeftAligned(false, false);
+ }
+
+ @Test
+ public void updateKeyguardPosition_ignoredInTwoHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(false);
mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f);
- verify(mView).updateKeyguardPosition(1.0f);
+ verify(mView, never()).setOneHandedModeLeftAligned(anyBoolean(), anyBoolean());
+ }
+
+ private void touchDownLeftSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */VIEW_WIDTH / 3f,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ private void touchDownRightSide() {
+ mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent(
+ MotionEvent.obtain(
+ /* downTime= */0,
+ /* eventTime= */0,
+ MotionEvent.ACTION_DOWN,
+ /* x= */(VIEW_WIDTH / 3f) * 2,
+ /* y= */0,
+ /* metaState= */0));
+ }
+
+ @Test
+ public void onInterceptTap_inhibitsFalsingInOneHandedMode() {
+ when(mView.isOneHandedMode()).thenReturn(true);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(true);
+
+ touchDownLeftSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ // Now on the right.
+ touchDownRightSide();
+ verify(mFalsingCollector).avoidGesture();
+
+ // Move and re-test
+ reset(mFalsingCollector);
+ when(mView.isOneHandedModeLeftAligned()).thenReturn(false);
+
+ // On the right...
+ touchDownRightSide();
+ verify(mFalsingCollector, never()).avoidGesture();
+
+ touchDownLeftSide();
+ verify(mFalsingCollector).avoidGesture();
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_deviceFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */false,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_sysUiFlagDisabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */false);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ @Test
+ public void showSecurityScreen_oneHandedMode_bothFlagsEnabled_oneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Pattern), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
+ verify(mView).setOneHandedMode(true);
+ }
+
+ @Test
+ public void showSecurityScreen_twoHandedMode_bothFlagsEnabled_noOneHandedMode() {
+ setUpKeyguardFlags(
+ /* deviceConfigCanUseOneHandedKeyguard= */true,
+ /* sysuiResourceCanUseOneHandedKeyguard= */true);
+
+ when(mKeyguardSecurityViewFlipperController.getSecurityView(
+ eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
+ .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
+
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
+ verify(mView).setOneHandedMode(false);
+ }
+
+ private void setUpKeyguardFlags(
+ boolean deviceConfigCanUseOneHandedKeyguard,
+ boolean sysuiResourceCanUseOneHandedKeyguard) {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_enableDynamicKeyguardPositioning))
+ .thenReturn(deviceConfigCanUseOneHandedKeyguard);
+ when(mResources.getBoolean(
+ R.bool.can_use_one_handed_bouncer))
+ .thenReturn(sysuiResourceCanUseOneHandedKeyguard);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f5916e748f04..02763235a8ca 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.testing.TestableResources;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -37,8 +36,6 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -57,11 +54,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int FAKE_MEASURE_SPEC =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH, View.MeasureSpec.EXACTLY);
- private static final SecurityMode ONE_HANDED_SECURITY_MODE = SecurityMode.PIN;
- private static final SecurityMode TWO_HANDED_SECURITY_MODE = SecurityMode.Password;
-
-
-
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
@@ -90,45 +82,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void onMeasure_usesFullWidthWithoutOneHandedMode() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
-
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithDeviceFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesFullWidthWithSysUIFlagDisabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
-
- mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
- }
-
- @Test
- public void onMeasure_usesHalfWidthWithFlagsEnabled() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- ONE_HANDED_SECURITY_MODE);
+ public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */true);
int halfWidthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -138,11 +93,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
@Test
- public void onMeasure_usesFullWidthForFullScreenIme() {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- TWO_HANDED_SECURITY_MODE);
+ public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -153,10 +105,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
int imeInsetAmount = 100;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -180,10 +129,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
int imeInsetAmount = 0;
int systemBarInsetAmount = 10;
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */false,
- /* sysuiResourceCanUseOneHandedKeyguard= */ false,
- ONE_HANDED_SECURITY_MODE);
+ mKeyguardSecurityContainer.setOneHandedMode(/* oneHandedMode= */false);
Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -201,56 +147,41 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, expectedHeightMeasureSpec);
}
- private void setupForUpdateKeyguardPosition(SecurityMode securityMode) {
- setUpKeyguard(
- /* deviceConfigCanUseOneHandedKeyguard= */true,
- /* sysuiResourceCanUseOneHandedKeyguard= */ true,
- securityMode);
+ private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+ mKeyguardSecurityContainer.setOneHandedMode(oneHandedMode);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(true, false);
mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
- // Start off left-aligned. This should happen anyway, but just do this to ensure
- // definitely move to the left.
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
-
// Clear any interactions with the mock so we know the interactions definitely come from the
// below testing.
reset(mSecurityViewFlipper);
}
@Test
- public void updateKeyguardPosition_movesKeyguard() {
- setupForUpdateKeyguardPosition(ONE_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_movesKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ true);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper).setTranslationX(0.0f);
}
@Test
- public void updateKeyguardPosition_doesntMoveTwoHandedKeyguard() {
- setupForUpdateKeyguardPosition(TWO_HANDED_SECURITY_MODE);
+ public void setIsLeftAligned_doesntMoveTwoHandedKeyguard() {
+ setupForUpdateKeyguardPosition(/* oneHandedMode= */ false);
- mKeyguardSecurityContainer.updateKeyguardPosition((SCREEN_WIDTH / 4f) * 3f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */false, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
- mKeyguardSecurityContainer.updateKeyguardPosition(0.0f);
+ mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
+ /* leftAligned= */true, /* animate= */false);
verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
}
-
- private void setUpKeyguard(
- boolean deviceConfigCanUseOneHandedKeyguard,
- boolean sysuiResourceCanUseOneHandedKeyguard,
- SecurityMode securityMode) {
- TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(
- com.android.internal.R.bool.config_enableDynamicKeyguardPositioning,
- deviceConfigCanUseOneHandedKeyguard);
- testableResources.addOverride(R.bool.can_use_one_handed_bouncer,
- sysuiResourceCanUseOneHandedKeyguard);
- mKeyguardSecurityContainer.updateLayoutForSecurityMode(securityMode);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 6ec14fe46223..9c2382d5aa6e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,6 +22,7 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -50,6 +51,8 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
+ private CommunalStateController mCommunalStateController;
+ @Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
ConfigurationController mConfigurationController;
@@ -76,6 +79,7 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase {
mKeyguardClockSwitchController,
mKeyguardStateController,
mKeyguardUpdateMonitor,
+ mCommunalStateController,
mConfigurationController,
mDozeParameters,
mKeyguardUnlockAnimationController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ec4dfba87af0..d3557d4b1809 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -86,8 +86,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.telephony.TelephonyListenerManager;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
new file mode 100644
index 000000000000..548b6551f586
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardVisibilityHelperTest.java
@@ -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.keyguard;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class KeyguardVisibilityHelperTest extends SysuiTestCase {
+ @Mock
+ private CommunalStateController mCommunalStateController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ com.android.systemui.statusbar.phone.DozeParameters mDozeParameters;
+ @Mock
+ UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock
+ ViewPropertyAnimator mViewPropertyAnimator;
+ @Mock
+ View mTargetView;
+
+ private KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mTargetView.animate()).thenReturn(mViewPropertyAnimator);
+ mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mTargetView,
+ mCommunalStateController, mKeyguardStateController, mDozeParameters,
+ mUnlockedScreenOffAnimationController, false);
+ }
+
+ @Test
+ public void testHideOnCommunal() {
+ // Verify view is hidden when communal is visible.
+ when(mCommunalStateController.getCommunalViewShowing()).thenReturn(true);
+ mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
+ false, StatusBarState.KEYGUARD);
+ verify(mTargetView).setVisibility(View.GONE);
+ verify(mTargetView).setAlpha(1.0f);
+
+ // Verify view is shown when communal is not visible.
+ when(mCommunalStateController.getCommunalViewShowing()).thenReturn(false);
+ mKeyguardVisibilityHelper.setViewVisibility(StatusBarState.KEYGUARD, false,
+ false, StatusBarState.KEYGUARD);
+ verify(mTargetView).setVisibility(View.VISIBLE);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 5617f1b6316b..fe5633eb9df8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -70,7 +70,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -90,7 +89,6 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
-@FlakyTest(bugId = 188890599)
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MagnificationModeSwitchTest extends SysuiTestCase {
@@ -507,6 +505,30 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
}
+ @Test
+ public void onScreenSizeChanged_buttonIsShowingOnTheRightSide_expectedPosition() {
+ final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ final Rect oldDraggableBounds = new Rect(mMagnificationModeSwitch.mDraggableWindowBounds);
+ final float windowHeightFraction =
+ (float) (mWindowManager.getLayoutParamsFromAttachedView().y
+ - oldDraggableBounds.top) / oldDraggableBounds.height();
+
+ // The window bounds and the draggable bounds are changed due to the screen size change.
+ final Rect tmpRect = new Rect(windowBounds);
+ tmpRect.scale(2);
+ final Rect newWindowBounds = new Rect(tmpRect);
+ mWindowManager.setWindowBounds(newWindowBounds);
+ mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+
+ final int expectedX = mMagnificationModeSwitch.mDraggableWindowBounds.right;
+ final int expectedY = (int) (windowHeightFraction
+ * mMagnificationModeSwitch.mDraggableWindowBounds.height())
+ + mMagnificationModeSwitch.mDraggableWindowBounds.top;
+ assertEquals(expectedX, mWindowManager.getLayoutParamsFromAttachedView().x);
+ assertEquals(expectedY, mWindowManager.getLayoutParamsFromAttachedView().y);
+ }
+
private void assertModeUnchanged(int expectedMode) {
final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
index 9621bedd9210..8bb9d423fa92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
@@ -16,9 +16,6 @@
package com.android.systemui.accessibility;
-import static android.view.WindowInsets.Type.systemGestures;
-
-import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.view.Display;
@@ -79,14 +76,11 @@ public class TestableWindowManager implements WindowManager {
@Override
public WindowMetrics getCurrentWindowMetrics() {
- final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
- final WindowInsets insets = new WindowInsets.Builder()
- .setInsets(systemGestures(), systemGesturesInsets)
- .build();
+ final WindowMetrics realMetrics = mWindowManager.getCurrentWindowMetrics();
final WindowMetrics windowMetrics = new WindowMetrics(
- mWindowBounds == null ? mWindowManager.getCurrentWindowMetrics().getBounds()
+ mWindowBounds == null ? realMetrics.getBounds()
: mWindowBounds,
- mWindowInsets == null ? insets : mWindowInsets);
+ mWindowInsets == null ? realMetrics.getWindowInsets() : mWindowInsets);
return windowMetrics;
}
@@ -106,10 +100,20 @@ public class TestableWindowManager implements WindowManager {
return (WindowManager.LayoutParams) mView.getLayoutParams();
}
+ /**
+ * Sets the given window bounds to current window metrics.
+ *
+ * @param bounds the window bounds
+ */
public void setWindowBounds(Rect bounds) {
mWindowBounds = bounds;
}
+ /**
+ * Sets the given window insets to the current window metics.
+ *
+ * @param insets the window insets.
+ */
public void setWindowInsets(WindowInsets insets) {
mWindowInsets = insets;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f62069d35d35..934aa3549343 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -45,6 +46,8 @@ import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
@@ -55,11 +58,11 @@ import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -140,7 +143,6 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- @FlakyTest(bugId = 188889181)
public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -184,6 +186,7 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+ setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -239,11 +242,13 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
});
}
@Test
- public void onOrientationChanged_enabled_updateDisplayRotationAndLayout() {
+ public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
final Display display = Mockito.spy(mContext.getDisplay());
when(display.getRotation()).thenReturn(Surface.ROTATION_90);
when(mContext.getDisplay()).thenReturn(display);
@@ -251,13 +256,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
});
+ final PointF expectedCenter = new PointF(mWindowMagnificationController.getCenterY(),
+ mWindowMagnificationController.getCenterX());
+ final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+ // Rotate the window 90 degrees.
+ windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+ windowBounds.right);
+ mWindowManager.setWindowBounds(windowBounds);
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
});
assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
- // The first invocation is called when the surface is created.
+ final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
+ mWindowMagnificationController.getCenterY());
+ assertEquals(expectedCenter, actualCenter);
verify(mWindowManager, times(2)).updateViewLayout(any(), any());
}
@@ -275,6 +289,33 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
+ public void onScreenSizeChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
+ // The default position is at the center of the screen.
+ final float expectedRatio = 0.5f;
+ final Rect testWindowBounds = new Rect(
+ mWindowManager.getCurrentWindowMetrics().getBounds());
+ testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+ testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ Float.NaN);
+ });
+ mWindowManager.setWindowBounds(testWindowBounds);
+
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+ });
+
+ // The ratio of center to window size should be the same.
+ assertEquals(expectedRatio,
+ mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
+ 0);
+ assertEquals(expectedRatio,
+ mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
+ 0);
+ }
+
+ @Test
public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -419,9 +460,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
}
@Test
- public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
- final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+ public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
+ final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+ setSystemGestureInsets();
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
Float.NaN);
@@ -447,4 +488,11 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
private boolean hasMagnificationOverlapFlag() {
return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
}
+
+ private void setSystemGestureInsets() {
+ final WindowInsets testInsets = new WindowInsets.Builder()
+ .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
+ .build();
+ mWindowManager.setWindowInsets(testInsets);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index 06e27b5dbf48..f09d7b78ea6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.View.OVER_SCROLL_ALWAYS;
import static android.view.View.OVER_SCROLL_NEVER;
import static android.view.WindowInsets.Type.ime;
@@ -134,7 +135,10 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase {
mMenuHalfHeight = menuHeight / 2;
mScreenHalfWidth = screenWidth / 2;
mScreenHalfHeight = mScreenHeight / 2;
- mMaxWindowX = screenWidth - margin - menuWidth;
+ int marginStartEnd =
+ mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT
+ ? margin : 0;
+ mMaxWindowX = screenWidth - marginStartEnd - menuWidth;
mMenuWindowHeight = menuHeight + margin * 2;
mMaxWindowY = mScreenHeight - mMenuWindowHeight;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 14f112b8b071..cc35a8f9e1b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -196,7 +196,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
return RemoteAnimationTarget(
0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
- taskInfo
+ taskInfo, false
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
new file mode 100644
index 000000000000..1d038a43ec8e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.battery;
+
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+
+import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.eq;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class BatteryMeterViewControllerTest extends SysuiTestCase {
+ @Mock
+ private BatteryMeterView mBatteryMeterView;
+
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private TunerService mTunerService;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private ContentResolver mContentResolver;
+ @Mock
+ private BatteryController mBatteryController;
+
+ private BatteryMeterViewController mController;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mBatteryMeterView.getContext()).thenReturn(mContext);
+ when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources());
+
+ mController = new BatteryMeterViewController(
+ mBatteryMeterView,
+ mConfigurationController,
+ mTunerService,
+ mBroadcastDispatcher,
+ mHandler,
+ mContentResolver,
+ mBatteryController
+ );
+ }
+
+ @Test
+ public void onViewAttached_callbacksRegistered() {
+ mController.onViewAttached();
+
+ verify(mConfigurationController).addCallback(any());
+ verify(mTunerService).addTunable(any(), any());
+ verify(mContentResolver).registerContentObserver(
+ eq(Settings.System.getUriFor(SHOW_BATTERY_PERCENT)), anyBoolean(), any(), anyInt()
+ );
+ verify(mContentResolver).registerContentObserver(
+ eq(Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)),
+ anyBoolean(),
+ any()
+ );
+ verify(mBatteryController).addCallback(any());
+ }
+
+ @Test
+ public void onViewDetached_callbacksUnregistered() {
+ // Set everything up first.
+ mController.onViewAttached();
+
+ mController.onViewDetached();
+
+ verify(mConfigurationController).removeCallback(any());
+ verify(mTunerService).removeTunable(any());
+ verify(mContentResolver).unregisterContentObserver(any());
+ verify(mBatteryController).removeCallback(any());
+ }
+
+ @Test
+ public void ignoreTunerUpdates_afterOnViewAttached_callbackUnregistered() {
+ // Start out receiving tuner updates
+ mController.onViewAttached();
+
+ mController.ignoreTunerUpdates();
+
+ verify(mTunerService).removeTunable(any());
+ }
+
+ @Test
+ public void ignoreTunerUpdates_beforeOnViewAttached_callbackNeverRegistered() {
+ mController.ignoreTunerUpdates();
+
+ mController.onViewAttached();
+
+ verify(mTunerService, never()).addTunable(any(), any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
new file mode 100644
index 000000000000..b4ff2a5e1fbf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -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.systemui.battery
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher
+import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class BatteryMeterViewTest : SysuiTestCase() {
+
+ private lateinit var mBatteryMeterView: BatteryMeterView
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mBatteryMeterView = BatteryMeterView(mContext, null)
+ }
+
+ @Test
+ fun updatePercentText_estimateModeAndNotCharging_estimateFetched() {
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+ mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+
+ mBatteryMeterView.updatePercentText()
+
+ assertThat(mBatteryMeterView.batteryPercentViewText).isEqualTo(ESTIMATE)
+ }
+
+ @Test
+ fun updatePercentText_noBatteryEstimateFetcher_noCrash() {
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+ mBatteryMeterView.updatePercentText()
+ // No assert needed
+ }
+
+ private class Fetcher : BatteryEstimateFetcher {
+ override fun fetchBatteryTimeRemainingEstimate(
+ completion: EstimateFetchCompletion) {
+ completion.onBatteryRemainingEstimateRetrieved(ESTIMATE)
+ }
+ }
+
+ private companion object {
+ const val ESTIMATE = "2 hours 2 minutes"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 2120b0ee4790..59010803db61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -69,6 +69,7 @@ import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.SystemClock;
import org.junit.Before;
import org.junit.Rule;
@@ -147,6 +148,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private Handler mHandler;
@Mock
private ConfigurationController mConfigurationController;
+ @Mock
+ private SystemClock mSystemClock;
private FakeExecutor mFgExecutor;
@@ -212,7 +215,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mWindowManager,
mStatusBarStateController,
mFgExecutor,
- mStatusBar,
+ Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
@@ -229,7 +232,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mKeyguardBypassController,
mDisplayManager,
mHandler,
- mConfigurationController);
+ mConfigurationController,
+ mSystemClock);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 0c03a51f816e..17730d960435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -18,8 +18,11 @@ package com.android.systemui.biometrics;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -41,6 +44,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -50,6 +54,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -80,6 +85,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
private ConfigurationController mConfigurationController;
@Mock
private UdfpsController mUdfpsController;
+ private FakeSystemClock mSystemClock = new FakeSystemClock();
private UdfpsKeyguardViewController mController;
@@ -106,7 +112,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mController = new UdfpsKeyguardViewController(
mView,
mStatusBarStateController,
- mStatusBar,
+ Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
mExecutor,
@@ -114,6 +120,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
+ mSystemClock,
mUdfpsController);
}
@@ -273,6 +280,59 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor);
}
+ @Test
+ public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() {
+ // GIVEN view is attached
+ mController.onViewAttached();
+ captureAltAuthInterceptor();
+
+ // GIVEN udfps bouncer isn't showing
+ mAltAuthInterceptor.hideAlternateAuthBouncer();
+
+ // WHEN touch is observed outside the view
+ mController.onTouchOutsideView();
+
+ // THEN bouncer / alt auth methods are never called
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ }
+
+ @Test
+ public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() {
+ // GIVEN view is attached
+ mController.onViewAttached();
+ captureAltAuthInterceptor();
+
+ // GIVEN udfps bouncer is showing
+ mAltAuthInterceptor.showAlternateAuthBouncer();
+
+ // WHEN touch is observed outside the view 200ms later (just within threshold)
+ mSystemClock.advanceTime(200);
+ mController.onTouchOutsideView();
+
+ // THEN bouncer / alt auth methods are never called because not enough time has passed
+ verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+ }
+
+ @Test
+ public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() {
+ // GIVEN view is attached
+ mController.onViewAttached();
+ captureAltAuthInterceptor();
+
+ // GIVEN udfps bouncer is showing
+ mAltAuthInterceptor.showAlternateAuthBouncer();
+
+ // WHEN touch is observed outside the view 205ms later
+ mSystemClock.advanceTime(205);
+ mController.onTouchOutsideView();
+
+ // THEN show the bouncer and reset alt auth
+ verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
+ verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
+ }
+
private void sendStatusBarStateChanged(int statusBarState) {
mStatusBarStateListener.onStateChanged(statusBarState);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
new file mode 100644
index 000000000000..990637955b24
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalHostViewControllerTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalHostViewControllerTest extends SysuiTestCase {
+ @Mock
+ private CommunalStateController mCommunalStateController;
+
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+
+ @Mock
+ private CommunalHostView mCommunalView;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ private CommunalHostViewController mController;
+
+ @Mock
+ private CommunalSource mCommunalSource;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mCommunalView.isAttachedToWindow()).thenReturn(true);
+
+ mController = new CommunalHostViewController(mFakeExecutor, mCommunalStateController,
+ mKeyguardUpdateMonitor, mKeyguardStateController, mStatusBarStateController,
+ mCommunalView);
+ mController.init();
+ mFakeExecutor.runAllReady();
+ Mockito.clearInvocations(mCommunalView);
+ }
+
+ @Test
+ public void testShow() {
+ ArgumentCaptor<KeyguardStateController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+
+ // Capture callback value for later use.
+ verify(mKeyguardStateController).addCallback(callbackCapture.capture());
+
+ // Verify the communal view is shown when the controller is initialized with keyguard
+ // showing (see setup).
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.VISIBLE);
+
+ // Trigger keyguard off to ensure visibility of communal view is changed accordingly.
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ callbackCapture.getValue().onKeyguardShowingChanged();
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.INVISIBLE);
+ }
+
+ @Test
+ public void testHideOnBouncer() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ // Capture callback value for later use.
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
+
+ // Establish a visible communal view.
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.VISIBLE);
+ Mockito.clearInvocations(mCommunalView);
+
+ // Trigger bouncer.
+ Mockito.clearInvocations(mCommunalView);
+ callbackCapture.getValue().onKeyguardBouncerChanged(true);
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.INVISIBLE);
+
+ // Hide bouncer
+ Mockito.clearInvocations(mCommunalView);
+ callbackCapture.getValue().onKeyguardBouncerChanged(false);
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void testHideOnOcclude() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ // Capture callback value for later use.
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
+
+ // Establish a visible communal view.
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.VISIBLE);
+ Mockito.clearInvocations(mCommunalView);
+
+ // Occlude.
+ Mockito.clearInvocations(mCommunalView);
+ callbackCapture.getValue().onKeyguardOccludedChanged(true);
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.INVISIBLE);
+
+ // Unocclude.
+ Mockito.clearInvocations(mCommunalView);
+ callbackCapture.getValue().onKeyguardOccludedChanged(false);
+ mFakeExecutor.runAllReady();
+ verify(mCommunalView).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void testReportOcclusion() {
+ // Ensure CommunalHostViewController reports view occluded when either the QS or Shade is
+ // expanded.
+ mController.updateShadeExpansion(0);
+ verify(mCommunalStateController).setCommunalViewOccluded(false);
+ clearInvocations(mCommunalStateController);
+ mController.updateQsExpansion(.5f);
+ verify(mCommunalStateController).setCommunalViewOccluded(true);
+ clearInvocations(mCommunalStateController);
+ mController.updateShadeExpansion(.7f);
+ verify(mCommunalStateController).setCommunalViewOccluded(true);
+ clearInvocations(mCommunalStateController);
+ mController.updateShadeExpansion(0);
+ verify(mCommunalStateController).setCommunalViewOccluded(true);
+ clearInvocations(mCommunalStateController);
+ mController.updateQsExpansion(0f);
+ verify(mCommunalStateController).setCommunalViewOccluded(false);
+ clearInvocations(mCommunalStateController);
+ }
+
+ @Test
+ public void testCommunalStateControllerHideNotified() {
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+
+ // Capture callback value for later use.
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
+
+ // Establish a visible communal view.
+ mController.show(new WeakReference<>(mCommunalSource));
+ mFakeExecutor.runAllReady();
+
+ // Occlude
+ clearInvocations(mCommunalStateController);
+ callbackCapture.getValue().onKeyguardOccludedChanged(true);
+ mFakeExecutor.runAllReady();
+
+ // Verify state controller is notified communal view is hidden.
+ verify(mCommunalStateController).setCommunalViewShowing(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
new file mode 100644
index 000000000000..68600de81085
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.FakeSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CommunalSourceMonitorTest extends SysuiTestCase {
+ private CommunalSourceMonitor mCommunalSourceMonitor;
+ private FakeSettings mSecureSettings;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mSecureSettings = new FakeSettings();
+ mCommunalSourceMonitor = new CommunalSourceMonitor(
+ Handler.createAsync(TestableLooper.get(this).getLooper()), mSecureSettings);
+ }
+
+ @Test
+ public void testSourceAddedBeforeCallbackAdded() {
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.setSource(source);
+ mCommunalSourceMonitor.addCallback(callback);
+
+ verifyOnSourceAvailableCalledWith(callback, source);
+ }
+
+ @Test
+ public void testRemoveCallback() {
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.addCallback(callback);
+ mCommunalSourceMonitor.removeCallback(callback);
+ mCommunalSourceMonitor.setSource(source);
+
+ verify(callback, never()).onSourceAvailable(any());
+ }
+
+ @Test
+ public void testAddCallbackWithDefaultSetting() {
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.addCallback(callback);
+ mCommunalSourceMonitor.setSource(source);
+
+ verifyOnSourceAvailableCalledWith(callback, source);
+ }
+
+ @Test
+ public void testAddCallbackWithSettingDisabled() {
+ setCommunalEnabled(false);
+
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.addCallback(callback);
+ mCommunalSourceMonitor.setSource(source);
+
+ verify(callback, never()).onSourceAvailable(any());
+ }
+
+ @Test
+ public void testSettingEnabledAfterCallbackAdded() {
+ setCommunalEnabled(false);
+
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.addCallback(callback);
+ mCommunalSourceMonitor.setSource(source);
+
+ // The callback should not have executed since communal is disabled.
+ verify(callback, never()).onSourceAvailable(any());
+
+ // The callback should execute when the user changes the setting to enabled.
+ setCommunalEnabled(true);
+ verifyOnSourceAvailableCalledWith(callback, source);
+ }
+
+ @Test
+ public void testSettingDisabledAfterCallbackAdded() {
+ final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
+ final CommunalSource source = mock(CommunalSource.class);
+
+ mCommunalSourceMonitor.addCallback(callback);
+ mCommunalSourceMonitor.setSource(source);
+ verifyOnSourceAvailableCalledWith(callback, source);
+
+ // The callback should execute again when the setting is disabled, with a value of null.
+ setCommunalEnabled(false);
+ verify(callback).onSourceAvailable(null);
+ }
+
+ private void setCommunalEnabled(boolean enabled) {
+ mSecureSettings.putIntForUser(
+ Settings.Secure.COMMUNAL_MODE_ENABLED,
+ enabled ? 1 : 0,
+ UserHandle.USER_SYSTEM);
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback,
+ CommunalSource source) {
+ final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
+ ArgumentCaptor.forClass(WeakReference.class);
+ verify(callback).onSourceAvailable(sourceCapture.capture());
+ assertThat(sourceCapture.getValue().get()).isEqualTo(source);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
new file mode 100644
index 000000000000..7f85c35af742
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalStateControllerTest extends SysuiTestCase {
+ @Mock
+ private CommunalStateController.Callback mCallback;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testDefaultCommunalViewShowingState() {
+ // The state controller should report the communal view as not showing by default.
+ final CommunalStateController stateController = new CommunalStateController();
+ assertThat(stateController.getCommunalViewShowing()).isFalse();
+ }
+
+ @Test
+ public void testNotifyCommunalSurfaceShow() {
+ final CommunalStateController stateController = new CommunalStateController();
+ stateController.addCallback(mCallback);
+
+ // Verify setting communal view to showing propagates to callback.
+ stateController.setCommunalViewShowing(true);
+ verify(mCallback).onCommunalViewShowingChanged();
+ assertThat(stateController.getCommunalViewShowing()).isTrue();
+
+ clearInvocations(mCallback);
+
+ // Verify setting communal view to not showing propagates to callback.
+ stateController.setCommunalViewShowing(false);
+ verify(mCallback).onCommunalViewShowingChanged();
+ assertThat(stateController.getCommunalViewShowing()).isFalse();
+ }
+
+ @Test
+ public void testCallbackRegistration() {
+ final CommunalStateController stateController = new CommunalStateController();
+ stateController.addCallback(mCallback);
+
+ // Verify setting communal view to showing propagates to callback.
+ stateController.setCommunalViewShowing(true);
+ verify(mCallback).onCommunalViewShowingChanged();
+
+ clearInvocations(mCallback);
+
+ stateController.removeCallback(mCallback);
+ clearInvocations(mCallback);
+
+ // Verify callback not invoked after removing from state controller.
+ stateController.setCommunalViewShowing(false);
+ verify(mCallback, never()).onCommunalViewShowingChanged();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSourcePrimerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSourcePrimerTest.java
new file mode 100644
index 000000000000..95ab5cbb585d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSourcePrimerTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal.service;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalSourceMonitor;
+import com.android.systemui.shared.communal.ICommunalSource;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+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.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSourcePrimerTest extends SysuiTestCase {
+ private static final String TEST_COMPONENT_NAME = "com.google.tests/.CommualService";
+ private static final ComponentName TEST_COMPONENT =
+ ComponentName.unflattenFromString(TEST_COMPONENT_NAME);
+ private static final int MAX_RETRIES = 5;
+ private static final int RETRY_DELAY_MS = 1000;
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private Resources mResources;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Mock
+ private CommunalSourceMonitor mCommunalSourceMonitor;
+
+ @Mock
+ private CommunalSourceImpl.Factory mCommunalSourceFactory;
+
+ @Mock
+ private CommunalSourceImpl mCommunalSourceImpl;
+
+ @Mock
+ private IBinder mServiceProxy;
+
+ private CommunalSourcePrimer mPrimer;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mResources.getInteger(R.integer.config_communalSourceMaxReconnectAttempts))
+ .thenReturn(MAX_RETRIES);
+ when(mResources.getInteger(R.integer.config_communalSourceReconnectBaseDelay))
+ .thenReturn(RETRY_DELAY_MS);
+ when(mResources.getString(R.string.config_communalSourceComponent))
+ .thenReturn(TEST_COMPONENT_NAME);
+ when(mCommunalSourceFactory.create(any(ICommunalSource.class)))
+ .thenReturn(mCommunalSourceImpl);
+
+ mPrimer = new CommunalSourcePrimer(mContext, mResources, mFakeExecutor,
+ mCommunalSourceMonitor, mCommunalSourceFactory);
+ }
+
+ @Test
+ public void testNoConnectWithEmptyComponent() {
+ when(mResources.getString(R.string.config_communalSourceComponent)).thenReturn(null);
+ final CommunalSourcePrimer emptyComponentPrimer = new CommunalSourcePrimer(mContext,
+ mResources, mFakeExecutor, mCommunalSourceMonitor, mCommunalSourceFactory);
+
+ emptyComponentPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+ // When there is no component, we should not register any broadcast receives or bind to
+ // any service
+ verify(mContext, times(0))
+ .registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+ verify(mContext, times(0)).bindService(any(Intent.class), anyInt(),
+ any(Executor.class), any(ServiceConnection.class));
+ }
+
+ private ServiceConnection givenOnBootCompleted(boolean bindSucceed) {
+ ArgumentCaptor<ServiceConnection> connectionCapture =
+ ArgumentCaptor.forClass(ServiceConnection.class);
+
+ when(mContext.bindService(any(Intent.class), anyInt(), any(Executor.class),
+ any(ServiceConnection.class))).thenReturn(bindSucceed);
+
+ mPrimer.onBootCompleted();
+ mFakeExecutor.runAllReady();
+
+ verify(mContext).bindService(any(Intent.class), anyInt(), any(Executor.class),
+ connectionCapture.capture());
+
+ // Simulate successful connection.
+ return connectionCapture.getValue();
+ }
+
+ @Test
+ public void testConnect() {
+ final ServiceConnection connection = givenOnBootCompleted(true);
+
+ // Simulate successful connection.
+ connection.onServiceConnected(TEST_COMPONENT, mServiceProxy);
+
+ // Verify source created and monitor informed.
+ verify(mCommunalSourceFactory).create(any(ICommunalSource.class));
+ verify(mCommunalSourceMonitor).setSource(mCommunalSourceImpl);
+ }
+
+ @Test
+ public void testRetryOnBindFailure() {
+ // Fail to bind on connection.
+ givenOnBootCompleted(false);
+
+ // Verify attempts happen. Note that we account for the retries plus initial attempt, which
+ // is not scheduled.
+ for (int attemptCount = 0; attemptCount < MAX_RETRIES + 1; attemptCount++) {
+ verify(mContext, times(1)).bindService(any(Intent.class),
+ anyInt(), any(Executor.class), any(ServiceConnection.class));
+ clearInvocations(mContext);
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ }
+
+ // Verify no more attempts occur.
+ verify(mContext, times(0)).bindService(any(Intent.class), anyInt(),
+ any(Executor.class), any(ServiceConnection.class));
+
+ // Verify source is not created and monitor is not informed.
+ verify(mCommunalSourceFactory, times(0))
+ .create(any(ICommunalSource.class));
+ verify(mCommunalSourceMonitor, times(0))
+ .setSource(any(CommunalSourceImpl.class));
+ }
+
+ @Test
+ public void testAttemptOnPackageChange() {
+ ArgumentCaptor<BroadcastReceiver> receiverCapture =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+ // Fail to bind initially.
+ givenOnBootCompleted(false);
+
+ // Capture broadcast receiver.
+ verify(mContext).registerReceiver(receiverCapture.capture(), any(IntentFilter.class));
+
+ clearInvocations(mContext);
+
+ // Inform package has been added.
+ receiverCapture.getValue().onReceive(mContext, new Intent());
+
+ // Verify bind has been attempted.
+ verify(mContext, times(1)).bindService(any(Intent.class), anyInt(),
+ any(Executor.class), any(ServiceConnection.class));
+ }
+
+ @Test
+ public void testRetryOnServiceDisconnected() {
+ verifyConnectionFailureReconnect(v -> v.onServiceDisconnected(TEST_COMPONENT));
+ }
+
+ @Test
+ public void testRetryOnBindingDied() {
+ verifyConnectionFailureReconnect(v -> v.onBindingDied(TEST_COMPONENT));
+ }
+
+ private void verifyConnectionFailureReconnect(ConnectionHandler connectionHandler) {
+ // Fail to bind on connection.
+ final ServiceConnection connection = givenOnBootCompleted(false);
+
+ clearInvocations(mContext, mCommunalSourceMonitor);
+
+ connectionHandler.onConnectionMade(connection);
+
+ // Ensure source is cleared.
+ verify(mCommunalSourceMonitor).setSource(null);
+
+ // Ensure request made to bind. This is not a reattempt so it should happen in the same
+ // execution loop.
+ verify(mContext).bindService(any(Intent.class), anyInt(), any(Executor.class),
+ any(ServiceConnection.class));
+ }
+
+ interface ConnectionHandler {
+ void onConnectionMade(ServiceConnection connection);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
new file mode 100644
index 000000000000..a31e54350d7e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/service/CommunalSurfaceViewControllerTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.communal.service;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
+import android.view.Display;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import com.google.common.util.concurrent.SettableFuture;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
+ private static final int MEASURED_HEIGHT = 200;
+ private static final int MEASURED_WIDTH = 500;
+ private static final int DISPLAY_ID = 3;
+
+ @Mock
+ private Display mDisplay;
+
+ @Mock
+ private IBinder mHostToken;
+
+ @Mock
+ private SurfaceView mSurfaceView;
+
+ @Mock
+ private SurfaceHolder mSurfaceHolder;
+
+ @Mock
+ private CommunalSourceImpl mCommunalSource;
+
+ @Mock
+ private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
+ @Mock
+ private CommunalStateController mCommunalStateController;
+
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ private SurfaceHolder.Callback mCallback;
+
+ private CommunalSurfaceViewController mController;
+
+ private SettableFuture<SurfaceControlViewHost.SurfacePackage> mPackageFuture;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
+ ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
+ when(mSurfaceView.getHolder()).thenReturn(mSurfaceHolder);
+ when(mSurfaceView.getDisplay()).thenReturn(mDisplay);
+ when(mDisplay.getDisplayId()).thenReturn(DISPLAY_ID);
+ when(mSurfaceView.getHostToken()).thenReturn(mHostToken);
+ when(mSurfaceView.getMeasuredWidth()).thenReturn(MEASURED_WIDTH);
+ when(mSurfaceView.getMeasuredHeight()).thenReturn(MEASURED_HEIGHT);
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(false);
+ mController = new CommunalSurfaceViewController(mSurfaceView, mFakeExecutor,
+ mCommunalStateController, mCommunalSource);
+ mController.init();
+ verify(mSurfaceHolder).addCallback(callbackCapture.capture());
+ mCallback = callbackCapture.getValue();
+
+ mPackageFuture = SettableFuture.create();
+
+ when(mCommunalSource.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(mPackageFuture);
+ }
+
+ @Test
+ public void testSetSurfacePackage() {
+ // There should be no requests without the proper state.
+ verify(mCommunalSource, times(0))
+ .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+
+ // The full state must be present to make a request.
+ mController.onViewAttached();
+ verify(mCommunalSource, times(0))
+ .requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
+
+ clearInvocations(mSurfaceView);
+
+ // Request surface view once all conditions are met.
+ mCallback.surfaceCreated(mSurfaceHolder);
+ verify(mCommunalSource)
+ .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
+
+ // Respond to request.
+ mPackageFuture.set(mSurfacePackage);
+ mFakeExecutor.runAllReady();
+
+
+ // Make sure SurfaceView is set.
+ verify(mSurfaceView).setChildSurfacePackage(mSurfacePackage);
+ verify(mSurfaceView).setZOrderOnTop(true);
+ verify(mSurfaceView).setWillNotDraw(false);
+ }
+
+ @Test
+ public void testCommunalStateControllerShowNotified() {
+ // Move CommunalSurfaceView to show
+ mController.onViewAttached();
+ mCallback.surfaceCreated(mSurfaceHolder);
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
+ mPackageFuture.set(mSurfacePackage);
+ mFakeExecutor.runAllReady();
+
+ // Ensure state controller is informed that the communal view is showing.
+ verify(mCommunalStateController).setCommunalViewShowing(true);
+ }
+
+ // Invoked to setup surface view package.
+ private void givenSurfacePresent() {
+ mController.onViewAttached();
+ mCallback.surfaceCreated(mSurfaceHolder);
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
+ mPackageFuture.set(mSurfacePackage);
+ mFakeExecutor.runAllReady();
+ clearInvocations(mSurfaceView);
+ }
+
+ @Test
+ public void testClearOnDetach() {
+ givenSurfacePresent();
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(false);
+ mController.onViewDetached();
+ verify(mSurfaceView).setWillNotDraw(true);
+ }
+
+ @Test
+ public void testClearOnSurfaceDestroyed() {
+ givenSurfacePresent();
+ mCallback.surfaceDestroyed(mSurfaceHolder);
+ verify(mSurfaceView).setWillNotDraw(true);
+ }
+
+ @Test
+ public void testCancelRequest() {
+ mController.onViewAttached();
+ mCallback.surfaceCreated(mSurfaceHolder);
+ when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
+ mFakeExecutor.runAllReady();
+ clearInvocations(mSurfaceView);
+
+ verify(mCommunalSource, times(1))
+ .requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
+
+ mController.onViewDetached();
+ assertTrue(mPackageFuture.isCancelled());
+ verify(mSurfaceView).setWillNotDraw(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index d6226aa53f67..a32cb9b6baa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -43,6 +43,7 @@ public class DozeConfigurationUtil {
when(params.singleTapUsesProx()).thenReturn(true);
when(params.longPressUsesProx()).thenReturn(true);
when(params.getQuickPickupAodDuration()).thenReturn(500);
+ when(params.brightnessUsesProx()).thenReturn(true);
doneHolder[0] = true;
return params;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 4e8b59c95681..deb7d31d87a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -29,6 +29,7 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -47,6 +48,7 @@ import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -82,6 +84,8 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
DozeParameters mDozeParameters;
+ @Mock
+ DockManager mDockManager;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
@@ -109,7 +113,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mSensor = fakeSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */,
- mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+ mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
mScreen.onScreenState(Display.STATE_ON);
}
@@ -157,6 +161,67 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
+ public void testAodDocked_doNotSelectivelyUseProx_usesLightSensor() {
+ // GIVEN the device doesn't need to selectively register for prox sensors and
+ // brightness sensor uses prox
+ when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
+ when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
+
+ // GIVEN the device is docked and the display state changes to ON
+ when(mDockManager.isDocked()).thenReturn(true);
+ mScreen.onScreenState(Display.STATE_ON);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is updated
+ assertEquals(3, mServiceFake.screenBrightness);
+ }
+
+ @Test
+ public void testAodDocked_brightnessDoesNotUseProx_usesLightSensor() {
+ // GIVEN the device doesn't need to selectively register for prox sensors but
+ // the brightness sensor doesn't use prox
+ when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
+ when(mDozeParameters.brightnessUsesProx()).thenReturn(false);
+
+ // GIVEN the device is docked and the display state changes to ON
+ when(mDockManager.isDocked()).thenReturn(true);
+ mScreen.onScreenState(Display.STATE_ON);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is updated
+ assertEquals(3, mServiceFake.screenBrightness);
+ }
+
+
+ @Test
+ public void testAodDocked_noProx_brightnessUsesProx_doNotUseLightSensor() {
+ final int startBrightness = mServiceFake.screenBrightness;
+
+ // GIVEN the device needs to selectively register for prox sensors and
+ // the brightness sensor uses prox
+ when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
+ when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
+
+ // GIVEN the device is docked and the display state is on
+ when(mDockManager.isDocked()).thenReturn(true);
+ mScreen.onScreenState(Display.STATE_ON);
+ waitForSensorManager();
+
+ // WHEN new sensor event sent
+ mSensor.sendSensorEvent(3);
+
+ // THEN brightness is NOT changed
+ assertNotSame(3, mServiceFake.screenBrightness);
+ assertEquals(startBrightness, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testPausingAod_doesNotResetBrightness() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -175,7 +240,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
- mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+ mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE);
reset(mDozeHost);
@@ -216,7 +281,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
- mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+ mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
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 223714cfda30..7bc5f86510a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.wrapper.BuildInfo;
import org.junit.Before;
@@ -43,6 +44,7 @@ import org.mockito.MockitoAnnotations;
public class FeatureFlagReaderTest extends SysuiTestCase {
@Mock private Resources mResources;
@Mock private BuildInfo mBuildInfo;
+ @Mock private PluginManager mPluginManager;
@Mock private SystemPropertiesHelper mSystemPropertiesHelper;
private FeatureFlagReader mReader;
@@ -63,7 +65,8 @@ public class FeatureFlagReaderTest extends SysuiTestCase {
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, mSystemPropertiesHelper);
+ mReader = new FeatureFlagReader(
+ mResources, mBuildInfo, mPluginManager, mSystemPropertiesHelper);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
new file mode 100644
index 000000000000..1a961787ee79
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FlagReaderPlugin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class FeatureFlagsTest extends SysuiTestCase {
+
+ @Mock FeatureFlagReader mFeatureFlagReader;
+
+ private FeatureFlags mFeatureFlags;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mFeatureFlags = new FeatureFlags(mFeatureFlagReader, getContext());
+ }
+
+ @Test
+ public void testAddListener() {
+ Flag<?> flag = new BooleanFlag(1);
+ mFeatureFlags.addFlag(flag);
+
+ // Assert and capture that a plugin listener was added.
+ ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
+ ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
+
+ verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
+ FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
+
+ // Signal a change. No listeners, so no real effect.
+ pluginListener.onFlagChanged(flag.getId());
+
+ // Add a listener for the flag
+ final Flag<?>[] changedFlag = {null};
+ FeatureFlags.Listener listener = f -> changedFlag[0] = f;
+ mFeatureFlags.addFlagListener(flag, listener);
+
+ // No changes seen yet.
+ assertThat(changedFlag[0]).isNull();
+
+ // Signal a change.
+ pluginListener.onFlagChanged(flag.getId());
+
+ // Assert that the change was for the correct flag.
+ assertThat(changedFlag[0]).isEqualTo(flag);
+ }
+
+ @Test
+ public void testRemoveListener() {
+ Flag<?> flag = new BooleanFlag(1);
+ mFeatureFlags.addFlag(flag);
+
+ // Assert and capture that a plugin listener was added.
+ ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
+ ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
+
+ verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
+ FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
+
+ // Add a listener for the flag
+ final Flag<?>[] changedFlag = {null};
+ FeatureFlags.Listener listener = f -> changedFlag[0] = f;
+ mFeatureFlags.addFlagListener(flag, listener);
+
+ // Signal a change.
+ pluginListener.onFlagChanged(flag.getId());
+
+ // Assert that the change was for the correct flag.
+ assertThat(changedFlag[0]).isEqualTo(flag);
+
+ changedFlag[0] = null;
+
+ // Now remove the listener.
+ mFeatureFlags.removeFlagListener(flag, listener);
+ // Signal a change.
+ pluginListener.onFlagChanged(flag.getId());
+ // Assert that the change was not triggered
+ assertThat(changedFlag[0]).isNull();
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
new file mode 100644
index 000000000000..25c302885e07
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.flags;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+public class FlagsTest extends SysuiTestCase {
+
+ @Test
+ public void testDuplicateFlagIdCheckWorks() {
+ List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
+ Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+ assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size()).isEqualTo(2);
+ }
+
+ @Test
+ public void testNoDuplicateFlagIds() {
+ List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
+ Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+ assertWithMessage(generateAssertionMessage(duplicates))
+ .that(duplicates.size()).isEqualTo(0);
+ }
+
+ private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("Duplicate flag keys found: {");
+ for (int id : duplicates.keySet()) {
+ stringBuilder
+ .append(" ")
+ .append(id)
+ .append(": [")
+ .append(String.join(", ", duplicates.get(id)))
+ .append("]");
+ }
+ stringBuilder.append(" }");
+
+ return stringBuilder.toString();
+ }
+
+ private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
+ List<Pair<String, Flag<?>>> flags = new ArrayList<>();
+
+ Field[] fields = clz.getFields();
+
+ for (Field field : fields) {
+ Class<?> t = field.getType();
+ if (Flag.class.isAssignableFrom(t)) {
+ try {
+ flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
+ } catch (IllegalAccessException e) {
+ // no-op
+ }
+ }
+ }
+
+ return flags;
+ }
+
+ private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
+ Map<Integer, List<String>> grouping = new HashMap<>();
+
+ for (Pair<String, Flag<?>> flag : flags) {
+ grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
+ grouping.get(flag.second.getId()).add(flag.first);
+ }
+
+ Map<Integer, List<String>> result = new HashMap<>();
+ for (Integer id : grouping.keySet()) {
+ if (grouping.get(id).size() > 1) {
+ result.put(id, grouping.get(id));
+ }
+ }
+
+ return result;
+ }
+
+ private static class DuplicateFlagContainer {
+ public static final BooleanFlag A_FLAG = new BooleanFlag(0);
+ public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+ public static final StringFlag C_FLAG = new StringFlag(0);
+
+ public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+
+ public static final DoubleFlag E_FLAG = new DoubleFlag(3);
+ public static final DoubleFlag F_FLAG = new DoubleFlag(3);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 578c2d976cce..3b1c5f32a772 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -51,6 +52,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -74,6 +76,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.Executor;
@SmallTest
@@ -104,7 +107,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor;
@Mock private UiEventLogger mUiEventLogger;
- @Mock private GlobalActionsInfoProvider mInfoProvider;
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private SysUiState mSysUiState;
@@ -112,6 +114,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private StatusBar mStatusBar;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableLooper mTestableLooper;
@@ -151,12 +154,12 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mWindowManager,
mBackgroundExecutor,
mUiEventLogger,
- mInfoProvider,
mRingerModeTracker,
mSysUiState,
mHandler,
mPackageManager,
- mStatusBar
+ Optional.of(mStatusBar),
+ mKeyguardUpdateMonitor
);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -170,14 +173,14 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
public void testShouldLogShow() {
mGlobalActionsDialogLite.onShow(null);
mTestableLooper.processAllMessages();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_OPEN);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_POWER_MENU_OPEN);
}
@Test
public void testShouldLogDismiss() {
mGlobalActionsDialogLite.onDismiss(mGlobalActionsDialogLite.mDialog);
mTestableLooper.processAllMessages();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_CLOSE);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_POWER_MENU_CLOSE);
}
@Test
@@ -187,16 +190,16 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
dialog.onBackPressed();
mTestableLooper.processAllMessages();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_BACK);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_BACK);
}
@Test
@@ -206,17 +209,17 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
gestureListener.onSingleTapConfirmed(null);
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
}
@Test
@@ -227,10 +230,10 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
doReturn(true).when(mStatusBar).isKeyguardShowing();
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
@@ -239,7 +242,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
verify(mStatusBar).animateExpandSettingsPanel(null);
}
@@ -251,10 +254,10 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
doReturn(false).when(mStatusBar).isKeyguardShowing();
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
@@ -263,40 +266,40 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
verify(mStatusBar).animateExpandNotificationsPanel();
}
@Test
public void testShouldLogBugreportPress() throws InterruptedException {
- GlobalActionsDialog.BugReportAction bugReportAction =
+ GlobalActionsDialogLite.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
bugReportAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_BUGREPORT_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_BUGREPORT_PRESS);
}
@Test
public void testShouldLogBugreportLongPress() {
- GlobalActionsDialog.BugReportAction bugReportAction =
+ GlobalActionsDialogLite.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
bugReportAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
}
@Test
public void testShouldLogEmergencyDialerPress() {
- GlobalActionsDialog.EmergencyDialerAction emergencyDialerAction =
+ GlobalActionsDialogLite.EmergencyDialerAction emergencyDialerAction =
mGlobalActionsDialogLite.makeEmergencyDialerActionForTesting();
emergencyDialerAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
}
@Test
public void testShouldLogScreenshotPress() {
- GlobalActionsDialog.ScreenshotAction screenshotAction =
+ GlobalActionsDialogLite.ScreenshotAction screenshotAction =
mGlobalActionsDialogLite.makeScreenshotActionForTesting();
screenshotAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SCREENSHOT_PRESS);
}
@Test
@@ -305,7 +308,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
com.android.internal.R.integer.config_navBarInteractionMode,
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON);
- GlobalActionsDialog.ScreenshotAction screenshotAction =
+ GlobalActionsDialogLite.ScreenshotAction screenshotAction =
mGlobalActionsDialogLite.makeScreenshotActionForTesting();
assertThat(screenshotAction.shouldShow()).isTrue();
}
@@ -316,12 +319,12 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
com.android.internal.R.integer.config_navBarInteractionMode,
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
- GlobalActionsDialog.ScreenshotAction screenshotAction =
+ GlobalActionsDialogLite.ScreenshotAction screenshotAction =
mGlobalActionsDialogLite.makeScreenshotActionForTesting();
assertThat(screenshotAction.shouldShow()).isFalse();
}
- private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) {
+ private void verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent event) {
mTestableLooper.processAllMessages();
verify(mUiEventLogger, times(1))
.log(event);
@@ -342,19 +345,19 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
mGlobalActionsDialogLite.createActionItems();
assertItemsOfType(mGlobalActionsDialogLite.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.LockDownAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
+ GlobalActionsDialogLite.EmergencyAction.class,
+ GlobalActionsDialogLite.LockDownAction.class,
+ GlobalActionsDialogLite.ShutDownAction.class,
+ GlobalActionsDialogLite.RestartAction.class);
assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
}
@@ -366,19 +369,19 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
// make sure lockdown action will NOT be shown
doReturn(false).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
// lockdown action not allowed
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
mGlobalActionsDialogLite.createActionItems();
assertItemsOfType(mGlobalActionsDialogLite.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
+ GlobalActionsDialogLite.EmergencyAction.class,
+ GlobalActionsDialogLite.ShutDownAction.class,
+ GlobalActionsDialogLite.RestartAction.class);
assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
}
@@ -388,7 +391,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.LockDownAction lockDownAction =
mGlobalActionsDialogLite.new LockDownAction();
lockDownAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_LOCKDOWN_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_LOCKDOWN_PRESS);
}
@Test
@@ -396,7 +399,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.ShutDownAction shutDownAction =
mGlobalActionsDialogLite.new ShutDownAction();
shutDownAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
}
@Test
@@ -404,7 +407,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.ShutDownAction shutDownAction =
mGlobalActionsDialogLite.new ShutDownAction();
shutDownAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
}
@Test
@@ -412,7 +415,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.RestartAction restartAction =
mGlobalActionsDialogLite.new RestartAction();
restartAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_REBOOT_PRESS);
}
@Test
@@ -420,6 +423,33 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
GlobalActionsDialogLite.RestartAction restartAction =
mGlobalActionsDialogLite.new RestartAction();
restartAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
+ verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
+ }
+
+ @Test
+ public void testOnLockScreen_disableSmartLock() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(false).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+ // When entering power menu from lockscreen, with smart lock enabled
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+ mGlobalActionsDialogLite.showOrHideDialog(true, true);
+
+ // Then smart lock will be disabled
+ verify(mLockPatternUtils).requireCredentialEntry(eq(user));
+
+ // hide dialog again
+ mGlobalActionsDialogLite.showOrHideDialog(true, true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
deleted file mode 100644
index 2fa67cc0be60..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.globalactions;
-
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.trust.TrustManager;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.graphics.Color;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.service.dreams.IDreamManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.IWindowManager;
-import android.view.View;
-import android.view.WindowManagerPolicyConstants;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.telephony.TelephonyListenerManager;
-import com.android.systemui.util.RingerModeLiveData;
-import com.android.systemui.util.RingerModeTracker;
-import com.android.systemui.util.settings.GlobalSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.regex.Pattern;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class GlobalActionsDialogTest extends SysuiTestCase {
- private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
- private static final Pattern CANCEL_BUTTON =
- Pattern.compile("cancel", Pattern.CASE_INSENSITIVE);
-
- private GlobalActionsDialog mGlobalActionsDialog;
-
- @Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs;
- @Mock private AudioManager mAudioManager;
- @Mock private IDreamManager mDreamManager;
- @Mock private DevicePolicyManager mDevicePolicyManager;
- @Mock private LockPatternUtils mLockPatternUtils;
- @Mock private BroadcastDispatcher mBroadcastDispatcher;
- @Mock private TelephonyListenerManager mTelephonyListenerManager;
- @Mock private GlobalSettings mGlobalSettings;
- @Mock private Resources mResources;
- @Mock private ConfigurationController mConfigurationController;
- @Mock private ActivityStarter mActivityStarter;
- @Mock private KeyguardStateController mKeyguardStateController;
- @Mock private UserManager mUserManager;
- @Mock private TrustManager mTrustManager;
- @Mock private IActivityManager mActivityManager;
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private SysuiColorExtractor mColorExtractor;
- @Mock private IStatusBarService mStatusBarService;
- @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
- @Mock private IWindowManager mWindowManager;
- @Mock private Executor mBackgroundExecutor;
- @Mock private UiEventLogger mUiEventLogger;
- @Mock private RingerModeTracker mRingerModeTracker;
- @Mock private RingerModeLiveData mRingerModeLiveData;
- @Mock private SysUiState mSysUiState;
- @Mock GlobalActionsPanelPlugin mWalletPlugin;
- @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
- @Mock private Handler mHandler;
- @Mock private UserTracker mUserTracker;
- @Mock private PackageManager mPackageManager;
- @Mock private SecureSettings mSecureSettings;
- @Mock private StatusBar mStatusBar;
-
- private TestableLooper mTestableLooper;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mTestableLooper = TestableLooper.get(this);
- allowTestableLooperAsMainThread();
-
- when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
- when(mResources.getConfiguration()).thenReturn(
- getContext().getResources().getConfiguration());
-
- mGlobalActionsDialog = new GlobalActionsDialog(mContext,
- mWindowManagerFuncs,
- mAudioManager,
- mDreamManager,
- mDevicePolicyManager,
- mLockPatternUtils,
- mBroadcastDispatcher,
- mTelephonyListenerManager,
- mGlobalSettings,
- mSecureSettings,
- null,
- mResources,
- mConfigurationController,
- mActivityStarter,
- mKeyguardStateController,
- mUserManager,
- mTrustManager,
- mActivityManager,
- null,
- mMetricsLogger,
- mColorExtractor,
- mStatusBarService,
- mNotificationShadeWindowController,
- mWindowManager,
- mBackgroundExecutor,
- mUiEventLogger,
- mRingerModeTracker,
- mSysUiState,
- mHandler,
- mPackageManager,
- mStatusBar
- );
- mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
-
- ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
- backdropColors.setMainColor(Color.BLACK);
- when(mColorExtractor.getNeutralColors()).thenReturn(backdropColors);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
- }
-
- @Test
- public void testShouldLogShow() {
- mGlobalActionsDialog.onShow(null);
- mTestableLooper.processAllMessages();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_OPEN);
- }
-
- @Test
- public void testShouldLogDismiss() {
- mGlobalActionsDialog.onDismiss(mGlobalActionsDialog.mDialog);
- mTestableLooper.processAllMessages();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_CLOSE);
- }
-
- @Test
- public void testShouldLogBugreportPress() throws InterruptedException {
- GlobalActionsDialog.BugReportAction bugReportAction =
- mGlobalActionsDialog.makeBugReportActionForTesting();
- bugReportAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_BUGREPORT_PRESS);
- }
-
- @Test
- public void testShouldLogBugreportLongPress() {
- GlobalActionsDialog.BugReportAction bugReportAction =
- mGlobalActionsDialog.makeBugReportActionForTesting();
- bugReportAction.onLongPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
- }
-
- @Test
- public void testShouldLogEmergencyDialerPress() {
- GlobalActionsDialog.EmergencyDialerAction emergencyDialerAction =
- mGlobalActionsDialog.makeEmergencyDialerActionForTesting();
- emergencyDialerAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
- }
-
- @Test
- public void testShouldLogScreenshotPress() {
- GlobalActionsDialog.ScreenshotAction screenshotAction =
- mGlobalActionsDialog.makeScreenshotActionForTesting();
- screenshotAction.onPress();
- verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_PRESS);
- }
-
- @Test
- public void testShouldShowScreenshot() {
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.integer.config_navBarInteractionMode,
- WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON);
-
- GlobalActionsDialog.ScreenshotAction screenshotAction =
- mGlobalActionsDialog.makeScreenshotActionForTesting();
- assertThat(screenshotAction.shouldShow()).isTrue();
- }
-
- @Test
- public void testShouldNotShowScreenshot() {
- mContext.getOrCreateTestableResources().addOverride(
- com.android.internal.R.integer.config_navBarInteractionMode,
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON);
-
- GlobalActionsDialog.ScreenshotAction screenshotAction =
- mGlobalActionsDialog.makeScreenshotActionForTesting();
- assertThat(screenshotAction.shouldShow()).isFalse();
- }
-
- private void verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent event) {
- mTestableLooper.processAllMessages();
- verify(mUiEventLogger, times(1))
- .log(event);
- }
-
- @SafeVarargs
- private static <T> void assertItemsOfType(List<T> stuff, Class<? extends T>... classes) {
- assertThat(stuff).hasSize(classes.length);
- for (int i = 0; i < stuff.size(); i++) {
- assertThat(stuff.get(i)).isInstanceOf(classes[i]);
- }
- }
-
- @Test
- public void testCreateActionItems_maxThree_noOverflow() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertThat(mGlobalActionsDialog.mPowerItems).isEmpty();
- }
-
- @Test
- public void testCreateActionItems_maxThree_condensePower() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- // make sure lockdown action will be shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.LockDownAction.class,
- GlobalActionsDialog.PowerOptionsAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertItemsOfType(mGlobalActionsDialog.mPowerItems,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- }
-
- @Test
- public void testCreateActionItems_maxThree_condensePower_splitPower() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // make sure lockdown action will be shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- // make sure bugreport also shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayBugReport(any());
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_BUGREPORT,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.LockDownAction.class,
- GlobalActionsDialog.PowerOptionsAction.class);
- assertItemsOfType(mGlobalActionsDialog.mOverflowItems,
- GlobalActionsDialog.BugReportAction.class);
- assertItemsOfType(mGlobalActionsDialog.mPowerItems,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- }
-
- @Test
- public void testCreateActionItems_maxFour_condensePower() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow 3 items to be shown
- doReturn(4).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // make sure lockdown action will be shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.LockDownAction.class,
- GlobalActionsDialog.PowerOptionsAction.class,
- GlobalActionsDialog.ScreenshotAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertItemsOfType(mGlobalActionsDialog.mPowerItems,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- }
-
- @Test
- public void testCreateActionItems_maxThree_doNotCondensePower() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // make sure lockdown action will be shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- // make sure bugreport is also shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayBugReport(any());
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_BUGREPORT,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.BugReportAction.class);
- assertItemsOfType(mGlobalActionsDialog.mOverflowItems,
- GlobalActionsDialog.LockDownAction.class);
- assertThat(mGlobalActionsDialog.mPowerItems).isEmpty();
- }
-
- @Test
- public void testCreateActionItems_maxAny() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow any number of power menu items to be shown
- doReturn(Integer.MAX_VALUE).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // ensure items are not blocked by keyguard or device provisioning
- doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
- // make sure lockdown action will be shown
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class,
- GlobalActionsDialog.LockDownAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertThat(mGlobalActionsDialog.mPowerItems).isEmpty();
- }
-
- @Test
- public void testCreateActionItems_maxThree_lockdownDisabled_doesNotShowLockdown() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow only 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- // make sure lockdown action will NOT be shown
- doReturn(false).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- // lockdown action not allowed
- GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertThat(mGlobalActionsDialog.mPowerItems).isEmpty();
- }
-
- @Test
- public void testCreateActionItems_shouldShowAction_excludeBugReport() {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- // allow only 3 items to be shown
- doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
- doReturn(true).when(mGlobalActionsDialog).shouldDisplayBugReport(any());
- // exclude bugreport in shouldShowAction to demonstrate how any button can be removed
- doAnswer(
- invocation -> !(invocation.getArgument(0)
- instanceof GlobalActionsDialog.BugReportAction))
- .when(mGlobalActionsDialog).shouldShowAction(any());
-
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- // bugreport action not allowed
- GlobalActionsDialog.GLOBAL_ACTION_KEY_BUGREPORT,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- mGlobalActionsDialog.createActionItems();
-
- assertItemsOfType(mGlobalActionsDialog.mItems,
- GlobalActionsDialog.EmergencyAction.class,
- GlobalActionsDialog.ShutDownAction.class,
- GlobalActionsDialog.RestartAction.class);
- assertThat(mGlobalActionsDialog.mOverflowItems).isEmpty();
- assertThat(mGlobalActionsDialog.mPowerItems).isEmpty();
- }
-
- @Test
- public void testShouldShowLockScreenMessage() throws RemoteException {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- mGlobalActionsDialog.mDialog = null;
- when(mKeyguardStateController.isUnlocked()).thenReturn(false);
- when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
- when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
- mGlobalActionsDialog.mShowLockScreenCards = false;
- setupDefaultActions();
- when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
- when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
-
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
-
- GlobalActionsDialog.ActionsDialog dialog =
- (GlobalActionsDialog.ActionsDialog) mGlobalActionsDialog.mDialog;
- assertThat(dialog).isNotNull();
- assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.VISIBLE);
-
- // Dismiss the dialog so that it does not pollute other tests
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
- }
-
- @Test
- public void testShouldNotShowLockScreenMessage_whenWalletShownOnLockScreen()
- throws RemoteException {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- mGlobalActionsDialog.mDialog = null;
- when(mKeyguardStateController.isUnlocked()).thenReturn(false);
- when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
- when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
- mGlobalActionsDialog.mShowLockScreenCards = true;
- setupDefaultActions();
- when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
- when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
-
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
-
- GlobalActionsDialog.ActionsDialog dialog =
- (GlobalActionsDialog.ActionsDialog) mGlobalActionsDialog.mDialog;
- assertThat(dialog).isNotNull();
- assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
-
- // Dismiss the dialog so that it does not pollute other tests
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
- }
-
- @Test
- public void testShouldNotShowLockScreenMessage_whenWalletBothDisabled()
- throws RemoteException {
- mGlobalActionsDialog = spy(mGlobalActionsDialog);
- mGlobalActionsDialog.mDialog = null;
- when(mKeyguardStateController.isUnlocked()).thenReturn(false);
-
- when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
- when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
- mGlobalActionsDialog.mShowLockScreenCards = true;
- setupDefaultActions();
- when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
- when(mWalletController.getPanelContent()).thenReturn(null);
-
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
-
- GlobalActionsDialog.ActionsDialog dialog =
- (GlobalActionsDialog.ActionsDialog) mGlobalActionsDialog.mDialog;
- assertThat(dialog).isNotNull();
- assertThat(dialog.mLockMessageContainer.getVisibility()).isEqualTo(View.GONE);
-
- // Dismiss the dialog so that it does not pollute other tests
- mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
- }
-
- private UserInfo newUserInfo() {
- return new UserInfo(0, null, null, UserInfo.FLAG_PRIMARY, null);
- }
-
- private void setupDefaultActions() {
- String[] actions = {
- GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- };
- doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
deleted file mode 100644
index 302a8d3f2efa..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsInfoProviderTest.kt
+++ /dev/null
@@ -1,134 +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.google.android.systemui.globalactions
-
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.res.Configuration
-import android.content.res.Resources
-import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.testing.AndroidTestingRunner
-import android.view.ViewGroup
-import androidx.test.filters.SmallTest
-import com.android.systemui.R
-import com.android.systemui.controls.controller.ControlsController
-import com.android.systemui.globalactions.GlobalActionsInfoProvider
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.SysuiTestCase
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyObject
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.Mockito.`when` as whenever
-
-private const val PREFERENCE = "global_actions_info_prefs"
-private const val KEY_VIEW_COUNT = "view_count"
-
-private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class GlobalActionsInfoProviderTest : SysuiTestCase() {
-
- @Mock private lateinit var walletClient: QuickAccessWalletClient
- @Mock private lateinit var controlsController: ControlsController
- @Mock private lateinit var activityStarter: ActivityStarter
- @Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockResources: Resources
- @Mock private lateinit var sharedPrefs: SharedPreferences
- @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
-
- private lateinit var infoProvider: GlobalActionsInfoProvider
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- mockContext = spy(context)
- mockResources = spy(context.resources)
- whenever(mockContext.resources).thenReturn(mockResources)
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(true)
- whenever(mockContext.getSharedPreferences(eq(PREFERENCE), anyInt()))
- .thenReturn(sharedPrefs)
- whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putInt(anyString(), anyInt())).thenReturn(sharedPrefsEditor)
- whenever(sharedPrefsEditor.putBoolean(anyString(), anyBoolean()))
- .thenReturn(sharedPrefsEditor)
-
- infoProvider = GlobalActionsInfoProvider(
- mockContext,
- walletClient,
- controlsController,
- activityStarter
- )
- }
-
- @Test
- fun testIsEligible_noCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testIsEligible_hasCards() {
- whenever(sharedPrefs.contains(eq(KEY_VIEW_COUNT))).thenReturn(false)
- whenever(walletClient.isWalletFeatureAvailable).thenReturn(true)
-
- assertTrue(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testNotEligible_shouldNotShow() {
- whenever(mockResources.getBoolean(R.bool.global_actions_show_change_info))
- .thenReturn(false)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-
- @Test
- fun testTooManyButtons_doesNotAdd() {
- val configuration = Configuration()
- configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
- whenever(mockResources.configuration).thenReturn(configuration)
-
- val parent = mock(ViewGroup::class.java)
- infoProvider.addPanel(mockContext, parent, 5, { })
-
- verify(parent, never()).addView(anyObject(), anyInt())
- }
-
- @Test
- fun testLimitTimesShown() {
- whenever(sharedPrefs.getInt(eq(KEY_VIEW_COUNT), anyInt())).thenReturn(4)
-
- assertFalse(infoProvider.shouldShowMessage())
- }
-} \ No newline at end of file
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 ad0878031679..31d70f5c811f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -67,7 +67,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@TestableLooper.RunWithLooper
@SmallTest
public class KeyguardViewMediatorTest extends SysuiTestCase {
private KeyguardViewMediator mViewMediator;
@@ -126,7 +126,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mUnlockedScreenOffAnimationController,
() -> mNotificationShadeDepthController);
mViewMediator.start();
- mViewMediator.onSystemReady();
}
@Test
@@ -165,8 +164,10 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
}
@Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway() {
// When showing and provisioned
+ mViewMediator.onSystemReady();
when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
mViewMediator.setShowingLocked(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 25ae67baa9be..8cc2776bc16b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -16,13 +16,12 @@
package com.android.systemui.media
+import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.FrameLayout
-import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -52,8 +51,7 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock
private lateinit var configurationController: ConfigurationController
- @Mock
- private lateinit var featureFlags: FeatureFlags
+
@Mock
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
@JvmField @Rule
@@ -71,17 +69,17 @@ class KeyguardMediaControllerTest : SysuiTestCase() {
whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
.thenReturn(true)
whenever(mediaHost.hostView).thenReturn(hostView)
-
+ hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
keyguardMediaController = KeyguardMediaController(
mediaHost,
bypassController,
statusBarStateController,
notificationLockscreenUserManager,
- featureFlags,
context,
configurationController
)
keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
+ keyguardMediaController.useSplitShade = false
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index d2527c679a13..3ea57be98be0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -81,6 +81,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
+ private CommandQueue mCommandQueue = mock(CommandQueue.class);
+
private static final int SECONDARY_DISPLAY = 1;
@Before
@@ -99,11 +101,11 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(StatusBarStateController.class),
mock(SysUiState.class),
mock(BroadcastDispatcher.class),
- mock(CommandQueue.class),
+ mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -112,6 +114,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
mock(ConfigurationController.class),
+ mock(NavigationBarA11yHelper.class),
+ mock(TaskbarDelegate.class),
mock(UserTracker.class)));
initializeNavigationBars();
}
@@ -138,6 +142,8 @@ public class NavigationBarControllerTest extends SysuiTestCase {
@Test
public void testCreateNavigationBarsIncludeDefaultTrue() {
+ // Tablets may be using taskbar and the logic is different
+ mNavigationBarController.mIsTablet = false;
doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
mNavigationBarController.createNavigationBars(true, null);
@@ -275,4 +281,9 @@ public class NavigationBarControllerTest extends SysuiTestCase {
verify(mSecondaryNavBar).disableAnimationsDuringHide(eq(500L));
}
+
+ @Test
+ public void test3ButtonTaskbarFlagDisabledNoRegister() {
+ verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
index eac68f6e76f4..a6ff2e8d2e15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarRotationContextTest.java
@@ -31,9 +31,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
-import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
-import com.android.systemui.navigationbar.RotationButton;
-import com.android.systemui.navigationbar.RotationButtonController;
import com.android.systemui.statusbar.policy.RotationLockController;
import org.junit.Before;
@@ -46,7 +43,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NavigationBarRotationContextTest extends SysuiTestCase {
- static final int RES_UNDEF = 0;
static final int DEFAULT_ROTATE = 0;
@Rule
@@ -63,10 +59,18 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
final View view = new View(mContext);
mRotationButton = mock(RotationButton.class);
mRotationButtonController = new RotationButtonController(mContext, 0, 0);
- mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
+ mRotationButtonController.setRotationButton(mRotationButton,
+ new RotationButton.RotationButtonUpdatesCallback() {
+ @Override
+ public void onVisibilityChanged(boolean isVisible) {
+ }
+
+ @Override
+ public void onPositionChanged() {
+ }
+ });
// Due to a mockito issue, only spy the object after setting the initial state
mRotationButtonController = spy(mRotationButtonController);
- final KeyButtonDrawable kbd = mock(KeyButtonDrawable.class);
doReturn(view).when(mRotationButton).getCurrentView();
doReturn(true).when(mRotationButton).acceptRotationProposal();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 1968f7fd3457..ed3c473aacf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -279,7 +279,7 @@ public class NavigationBarTest extends SysuiTestCase {
Optional.of(mock(Pip.class)),
Optional.of(mock(LegacySplitScreen.class)),
Optional.of(mock(Recents.class)),
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
@@ -287,6 +287,7 @@ public class NavigationBarTest extends SysuiTestCase {
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
+ mock(NavigationBarA11yHelper.class),
mock(UserTracker.class)));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
new file mode 100644
index 000000000000..0a2000107053
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -0,0 +1,127 @@
+package com.android.systemui.navigationbar.gestural
+
+import android.view.Gravity
+import android.view.Surface
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+@SmallTest
+internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase)
+ : SysuiTestCase() {
+
+ private val calculator = FloatingRotationButtonPositionCalculator(
+ MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM
+ )
+
+ @Test
+ fun calculatePosition() {
+ val position = calculator.calculatePosition(
+ testCase.rotation,
+ testCase.taskbarVisible,
+ testCase.taskbarStashed
+ )
+
+ assertThat(position).isEqualTo(testCase.expectedPosition)
+ }
+
+ internal class TestCase(
+ val rotation: Int,
+ val taskbarVisible: Boolean,
+ val taskbarStashed: Boolean,
+ val expectedPosition: Position
+ ) {
+ override fun toString(): String =
+ "when rotation = $rotation, " +
+ "taskbarVisible = $taskbarVisible, " +
+ "taskbarStashed = $taskbarStashed - " +
+ "expected $expectedPosition"
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<TestCase> =
+ listOf(
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_90,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.RIGHT,
+ translationX = -MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_180,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.TOP or Gravity.RIGHT,
+ translationX = -MARGIN_DEFAULT,
+ translationY = MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_270,
+ taskbarVisible = false,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.TOP or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = true,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_TASKBAR_LEFT,
+ translationY = -MARGIN_TASKBAR_BOTTOM
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_0,
+ taskbarVisible = true,
+ taskbarStashed = true,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.LEFT,
+ translationX = MARGIN_DEFAULT,
+ translationY = -MARGIN_DEFAULT
+ )
+ ),
+ TestCase(
+ rotation = Surface.ROTATION_90,
+ taskbarVisible = true,
+ taskbarStashed = false,
+ expectedPosition = Position(
+ gravity = Gravity.BOTTOM or Gravity.RIGHT,
+ translationX = -MARGIN_TASKBAR_LEFT,
+ translationY = -MARGIN_TASKBAR_BOTTOM
+ )
+ )
+ )
+
+ private const val MARGIN_DEFAULT = 10
+ private const val MARGIN_TASKBAR_LEFT = 20
+ private const val MARGIN_TASKBAR_BOTTOM = 30
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 35620329467b..e73e5ff49dd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -58,6 +58,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.Duration;
+import java.util.Optional;
import java.util.concurrent.TimeUnit;
import dagger.Lazy;
@@ -89,7 +90,7 @@ public class PowerUITest extends SysuiTestCase {
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
- @Mock private Lazy<StatusBar> mStatusBarLazy;
+ @Mock private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
@Mock private StatusBar mStatusBar;
@Before
@@ -98,7 +99,7 @@ public class PowerUITest extends SysuiTestCase {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
- when(mStatusBarLazy.get()).thenReturn(mStatusBar);
+ when(mStatusBarOptionalLazy.get()).thenReturn(Optional.of(mStatusBar));
mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
@@ -688,7 +689,8 @@ public class PowerUITest extends SysuiTestCase {
}
private void createPowerUi() {
- mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarLazy);
+ mPowerUI = new PowerUI(
+ mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarOptionalLazy);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index de7abf866f6a..922c6b648aab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -14,13 +14,19 @@
package com.android.systemui.qs;
-import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
+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.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.os.UserHandle;
import android.provider.Settings.Secure;
import android.testing.AndroidTestingRunner;
@@ -28,13 +34,24 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -43,42 +60,38 @@ public class AutoAddTrackerTest extends SysuiTestCase {
private static final int USER = 0;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private QSHost mQSHost;
+ @Mock
+ private DumpManager mDumpManager;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
+
+ private Executor mBackgroundExecutor = Runnable::run; // Direct executor
private AutoAddTracker mAutoTracker;
+ private SecureSettings mSecureSettings;
@Before
public void setUp() {
- Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, "");
- }
+ MockitoAnnotations.initMocks(this);
- @Test
- public void testMigration() {
- Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
- Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
+ mSecureSettings = new FakeSettings();
- assertTrue(mAutoTracker.isAdded(SAVER));
- assertTrue(mAutoTracker.isAdded(WORK));
- assertFalse(mAutoTracker.isAdded(INVERSION));
+ mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER);
- // These keys have been removed; retrieving their values should always return the default.
- assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true ));
- assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false));
- assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true));
- assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false));
-
- mAutoTracker.destroy();
+ mAutoTracker = createAutoAddTracker(USER);
+ mAutoTracker.initialize();
}
@Test
public void testChangeFromBackup() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
-
assertFalse(mAutoTracker.isAdded(SAVER));
- Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, SAVER);
- mAutoTracker.mObserver.onChange(false);
+ mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER);
assertTrue(mAutoTracker.isAdded(SAVER));
@@ -87,9 +100,6 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testSetAdded() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
-
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
@@ -100,14 +110,12 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testPersist() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
-
assertFalse(mAutoTracker.isAdded(SAVER));
mAutoTracker.setTileAdded(SAVER);
mAutoTracker.destroy();
- mAutoTracker = new AutoAddTracker(mContext, USER);
+ mAutoTracker = createAutoAddTracker(USER);
+ mAutoTracker.initialize();
assertTrue(mAutoTracker.isAdded(SAVER));
@@ -116,22 +124,158 @@ public class AutoAddTrackerTest extends SysuiTestCase {
@Test
public void testIndependentUsers() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
mAutoTracker.setTileAdded(SAVER);
- mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+ mAutoTracker = createAutoAddTracker(USER + 1);
+ mAutoTracker.initialize();
assertFalse(mAutoTracker.isAdded(SAVER));
}
@Test
public void testChangeUser() {
- mAutoTracker = new AutoAddTracker(mContext, USER);
- mAutoTracker.initialize();
mAutoTracker.setTileAdded(SAVER);
- mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+ mAutoTracker = createAutoAddTracker(USER + 1);
mAutoTracker.changeUser(UserHandle.of(USER));
assertTrue(mAutoTracker.isAdded(SAVER));
}
+
+ @Test
+ public void testBroadcastReceiverRegistered() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)));
+
+ assertTrue(
+ mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED));
+ }
+
+ @Test
+ public void testBroadcastReceiverChangesWithUser() {
+ mAutoTracker.changeUser(UserHandle.of(USER + 1));
+
+ InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher);
+ inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any());
+ inOrder.verify(mBroadcastDispatcher)
+ .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1)));
+ }
+
+ @Test
+ public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() {
+ verify(mBroadcastDispatcher).registerReceiver(
+ mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+ // These tiles were present in the original device
+ String restoredTiles = "saver,work,internet,cast";
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ // And these tiles have been auto-added in the original device
+ // (no auto-added before restore)
+ String restoredAutoAddTiles = "work";
+ Intent restoreAutoAddTilesIntent =
+ makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+ // Then, don't remove any current tiles
+ verify(mQSHost, never()).removeTiles(any());
+ assertEquals(restoredAutoAddTiles,
+ mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+ }
+
+ @Test
+ public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() {
+ verify(mBroadcastDispatcher)
+ .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+ // These tiles were present in the original device
+ String restoredTiles = "saver,internet,cast";
+ Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ // And these tiles have been auto-added in the original device
+ // (no auto-added before restore)
+ String restoredAutoAddTiles = "work";
+ Intent restoreAutoAddTilesIntent =
+ makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+ // Then, remove work tile
+ verify(mQSHost).removeTiles(List.of("work"));
+ assertEquals(restoredAutoAddTiles,
+ mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+ }
+
+ @Test
+ public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() {
+ verify(mBroadcastDispatcher)
+ .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+ // These tiles were present in the original device
+ String restoredTiles = "saver,internet,cast";
+ Intent restoreTilesIntent =
+ makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ // And these tiles have been auto-added in the original device
+ // (no auto-added before restore)
+ String restoredAutoAddTiles = "work";
+ Intent restoreAutoAddTilesIntent =
+ makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+ // Then, remove work tile
+ verify(mQSHost).removeTiles(List.of("work"));
+ assertEquals(restoredAutoAddTiles,
+ mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+ }
+
+ @Test
+ public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() {
+ verify(mBroadcastDispatcher)
+ .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+ // These tiles were present in the original device
+ String restoredTiles = "saver,internet,cast";
+ Intent restoreTilesIntent =
+ makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+ // And these tiles have been auto-added in the original device
+ // (no auto-added before restore)
+ String restoredAutoAddTiles = "work";
+ Intent restoreAutoAddTilesIntent =
+ makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles);
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+ // Then, remove work tile
+ verify(mQSHost).removeTiles(List.of("work"));
+
+ String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER);
+ assertEquals(2, setting.split(",").length);
+ assertTrue(setting.contains("work"));
+ assertTrue(setting.contains("inversion"));
+ }
+
+
+ private Intent makeRestoreIntent(
+ String settingName, String previousValue, String restoredValue) {
+ Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED);
+ intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName);
+ intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue);
+ intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue);
+ return intent;
+ }
+
+ private AutoAddTracker createAutoAddTracker(int user) {
+ // Null handler wil dispatch sync.
+ return new AutoAddTracker(
+ mSecureSettings,
+ mBroadcastDispatcher,
+ mQSHost,
+ mDumpManager,
+ null,
+ mBackgroundExecutor,
+ user
+ );
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
new file mode 100644
index 000000000000..e54a6ec46b9c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.qs
+
+import com.android.systemui.R
+import android.os.UserManager
+import android.view.LayoutInflater
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.FakeMetricsLogger
+import com.android.systemui.Dependency
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.utils.leaks.FakeTunerService
+import com.android.systemui.utils.leaks.LeakCheckedTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+class FooterActionsControllerTest : LeakCheckedTest() {
+ @Mock
+ private lateinit var userManager: UserManager
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock
+ private lateinit var userInfoController: UserInfoController
+ @Mock
+ private lateinit var qsPanelController: QSPanelController
+ @Mock
+ private lateinit var multiUserSwitchController: MultiUserSwitchController
+ @Mock
+ private lateinit var globalActionsDialog: GlobalActionsDialogLite
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
+ @Mock
+ private lateinit var controller: FooterActionsController
+
+ private val metricsLogger: MetricsLogger = FakeMetricsLogger()
+ private lateinit var view: FooterActionsView
+ private val falsingManager: FalsingManagerFake = FalsingManagerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
+ val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
+
+ view = LayoutInflater.from(context)
+ .inflate(R.layout.footer_actions, null) as FooterActionsView
+
+ controller = FooterActionsController(view, qsPanelController, activityStarter,
+ userManager, userInfoController, multiUserSwitchController,
+ deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
+ globalActionsDialog, uiEventLogger, showPMLiteButton = true,
+ buttonsVisibleState = ExpansionState.EXPANDED)
+ controller.init()
+ controller.onViewAttached()
+ }
+
+ @Test
+ fun testLogPowerMenuClick() {
+ controller.expanded = true
+ falsingManager.setFalseTap(false)
+
+ view.findViewById<View>(R.id.pm_lite).performClick()
+ // Verify clicks are logged
+ verify(uiEventLogger, Mockito.times(1))
+ .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+ }
+
+ @Test
+ fun testSettings_UserNotSetup() {
+ whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
+ view.findViewById<View>(R.id.settings_button).performClick()
+ // Verify Settings wasn't launched.
+ verify<ActivityStarter>(activityStarter, Mockito.never()).startActivity(any(), anyBoolean())
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 6f7bf3b09daa..8b19c50f915e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -18,16 +18,11 @@ package com.android.systemui.qs;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-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.content.ClipData;
import android.content.ClipboardManager;
-import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -35,21 +30,8 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.globalactions.GlobalActionsDialogLite;
-import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.MultiUserSwitchController;
-import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.utils.leaks.FakeTunerService;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -67,14 +49,6 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
@Mock
private QSFooterView mView;
@Mock
- private UserManager mUserManager;
- @Mock
- private ActivityStarter mActivityStarter;
- @Mock
- private DeviceProvisionedController mDeviceProvisionedController;
- @Mock
- private UserInfoController mUserInfoController;
- @Mock
private UserTracker mUserTracker;
@Mock
private QSPanelController mQSPanelController;
@@ -82,36 +56,19 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
private ClipboardManager mClipboardManager;
@Mock
private QuickQSPanelController mQuickQSPanelController;
- private FakeTunerService mFakeTunerService;
- private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
- private FalsingManagerFake mFalsingManager;
-
- @Mock
- private SettingsButton mSettingsButton;
@Mock
private TextView mBuildText;
@Mock
- private View mEdit;
- @Mock
- private MultiUserSwitchController mMultiUserSwitchController;
- @Mock
- private View mPowerMenuLiteView;
- @Mock
- private GlobalActionsDialogLite mGlobalActionsDialog;
- @Mock
- private UiEventLogger mUiEventLogger;
+ private FooterActionsController mFooterActionsController;
private QSFooterViewController mController;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- mFalsingManager = new FalsingManagerFake();
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class);
-
mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
when(mView.getContext()).thenReturn(mContext);
@@ -119,16 +76,10 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
when(mUserTracker.getUserContext()).thenReturn(mContext);
when(mView.isAttachedToWindow()).thenReturn(true);
- when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton);
when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
- when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
- when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView);
- mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
- mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
- mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
- mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
- mUiEventLogger);
+ mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
+ mQuickQSPanelController, mFooterActionsController);
mController.init();
}
@@ -148,40 +99,4 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
verify(mClipboardManager).setPrimaryClip(captor.capture());
assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
}
-
- @Test
- public void testSettings_UserNotSetup() {
- ArgumentCaptor<View.OnClickListener> onClickCaptor =
- ArgumentCaptor.forClass(View.OnClickListener.class);
- verify(mSettingsButton).setOnClickListener(onClickCaptor.capture());
-
- when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
-
- onClickCaptor.getValue().onClick(mSettingsButton);
- // Verify Settings wasn't launched.
- verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
- }
-
- @Test
- public void testLogPowerMenuClick() {
- // Enable power menu button
- mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
- mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
- mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
- mMetricsLogger, new FalsingManagerFake(), true, mGlobalActionsDialog,
- mUiEventLogger);
- mController.init();
- mController.setExpanded(true);
- mFalsingManager.setFalseTap(false);
-
- ArgumentCaptor<View.OnClickListener> onClickCaptor =
- ArgumentCaptor.forClass(View.OnClickListener.class);
- verify(mPowerMenuLiteView).setOnClickListener(onClickCaptor.capture());
-
- onClickCaptor.getValue().onClick(mPowerMenuLiteView);
-
- // Verify clicks are logged
- verify(mUiEventLogger, times(1))
- .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 3ee3e55c749a..1ed34d9d534e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -48,12 +48,12 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.external.CustomTileStatePersister;
+import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -95,9 +95,11 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Mock
private KeyguardBypassController mBypassController;
@Mock
- private FeatureFlags mFeatureFlags;
- @Mock
private FalsingManager mFalsingManager;
+ @Mock
+ private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
+ @Mock
+ private TileServiceRequestController mTileServiceRequestController;
public QSFragmentTest() {
super(QSFragment.class);
@@ -111,6 +113,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
+ when(mTileServiceRequestControllerBuilder.create(any()))
+ .thenReturn(mTileServiceRequestController);
+
mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
new LayoutInflaterBuilder(mContext)
@@ -139,7 +144,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
- mock(SecureSettings.class), mock(CustomTileStatePersister.class));
+ mock(SecureSettings.class), mock(CustomTileStatePersister.class),
+ mTileServiceRequestControllerBuilder);
qs.setHost(host);
qs.setListening(true);
@@ -189,7 +195,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mQQSMediaHost,
mBypassController,
mQsComponentFactory,
- mFeatureFlags,
mFalsingManager,
mock(DumpManager.class));
}
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 65e5f9703d84..6ff5aa0a2fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -49,7 +49,6 @@ 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;
@@ -93,8 +92,6 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
@Mock
PagedTileLayout mPagedTileLayout;
@Mock
- FeatureFlags mFeatureFlags;
- @Mock
Resources mResources;
@Mock
Configuration mConfiguration;
@@ -108,9 +105,9 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, FeatureFlags featureFlags) {
+ DumpManager dumpManager) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager, featureFlags);
+ qsLogger, dumpManager);
}
@Override
@@ -140,7 +137,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
reset(mQSTileRevealController);
@@ -152,7 +149,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -241,18 +238,17 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
when(mMediaHost.getVisible()).thenReturn(true);
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
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 bf6c981bf05c..1a87975f0e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -42,7 +42,6 @@ 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;
@@ -96,8 +95,6 @@ public class QSPanelControllerTest extends SysuiTestCase {
@Mock
PagedTileLayout mPagedTileLayout;
FalsingManagerFake mFalsingManager = new FalsingManagerFake();
- @Mock
- FeatureFlags mFeatureFlags;
private QSPanelController mController;
@@ -109,6 +106,7 @@ public class QSPanelControllerTest extends SysuiTestCase {
when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
+ when(mQSPanel.getResources()).thenReturn(mContext.getResources());
when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
when(mToggleSliderViewControllerFactory.create(any(), any()))
@@ -123,7 +121,7 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFalsingManager, mFeatureFlags
+ mFalsingManager
);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
deleted file mode 100644
index 16d4dddd67aa..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.util.animation.UniqueObjectHostView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Collections;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSPanelTest extends SysuiTestCase {
-
- private TestableLooper mTestableLooper;
- private QSPanel mQsPanel;
- @Mock
- private QSTileHost mHost;
- @Mock
- private QSTileImpl dndTile;
- @Mock
- private QSPanelControllerBase.TileRecord mDndTileRecord;
- private ViewGroup mParentView;
- @Mock
- private QSDetail.Callback mCallback;
- @Mock
- private QSTileView mQSTileView;
-
- private UniqueObjectHostView mMediaView;
- private View mSecurityFooter;
- @Mock
- private FrameLayout mHeaderContainer;
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
- mTestableLooper = TestableLooper.get(this);
-
- mDndTileRecord.tile = dndTile;
- mDndTileRecord.tileView = mQSTileView;
-
- mMediaView = new UniqueObjectHostView(mContext);
- mSecurityFooter = new View(mContext);
-
- mTestableLooper.runWithLooper(() -> {
- mQsPanel = new QSPanel(mContext, null);
- mQsPanel.initialize();
- mQsPanel.onFinishInflate();
- // Provides a parent with non-zero size for QSPanel
- mParentView = new FrameLayout(mContext);
- mParentView.addView(mQsPanel);
-
- when(dndTile.getTileSpec()).thenReturn("dnd");
- when(mHost.getTiles()).thenReturn(Collections.emptyList());
- when(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView);
- mQsPanel.addTile(mDndTileRecord);
- mQsPanel.setCallback(mCallback);
- mQsPanel.setHeaderContainer(mHeaderContainer);
- });
- }
-
- @Test
- public void testOpenDetailsWithExistingTile_NoException() {
- mTestableLooper.processAllMessages();
- mQsPanel.openDetails(dndTile);
- mTestableLooper.processAllMessages();
-
- verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
- }
-
- @Test
- public void testOpenDetailsWithNullParameter_NoException() {
- mTestableLooper.processAllMessages();
- mQsPanel.openDetails(null);
- mTestableLooper.processAllMessages();
-
- verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
- }
-
- @Test
- public void testSecurityFooterAtEndNoMedia_portrait() {
- mTestableLooper.processAllMessages();
-
- mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-
- mQsPanel.setSecurityFooter(mSecurityFooter);
-
- int children = mQsPanel.getChildCount();
- assertEquals(children - 1, mQsPanel.indexOfChild(mSecurityFooter));
- }
-
- @Test
- public void testSecurityFooterRightBeforeMedia_portrait() {
- mTestableLooper.processAllMessages();
-
- mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-
- mQsPanel.addView(mMediaView);
-
- mQsPanel.setSecurityFooter(mSecurityFooter);
-
- int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
- int mediaIndex = mQsPanel.indexOfChild(mMediaView);
-
- assertEquals(mediaIndex - 1, securityFooterIndex);
- }
-
- @Test
- public void testSecurityFooterRightBeforeMedia_portrait_configChange() {
- mTestableLooper.processAllMessages();
-
- mContext.getResources().getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
-
- mQsPanel.addView(mMediaView);
-
- mQsPanel.setSecurityFooter(mSecurityFooter);
-
- mQsPanel.onConfigurationChanged(mContext.getResources().getConfiguration());
-
- int securityFooterIndex = mQsPanel.indexOfChild(mSecurityFooter);
- int mediaIndex = mQsPanel.indexOfChild(mMediaView);
-
- assertEquals(mediaIndex - 1, securityFooterIndex);
- }
-
- @Test
- public void testSecurityFooterInHeader_landscape() {
- mTestableLooper.processAllMessages();
-
- mContext.getResources().getConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
-
- mQsPanel.setSecurityFooter(mSecurityFooter);
-
- verify(mHeaderContainer).addView(mSecurityFooter, 0);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
new file mode 100644
index 000000000000..3500c183de39
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES 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
+
+import android.content.res.Configuration
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class QSPanelTest : SysuiTestCase() {
+ private lateinit var mTestableLooper: TestableLooper
+ private lateinit var mQsPanel: QSPanel
+
+ @Mock
+ private lateinit var mHost: QSTileHost
+
+ @Mock
+ private lateinit var dndTile: QSTileImpl<*>
+
+ @Mock
+ private lateinit var mDndTileRecord: TileRecord
+
+ @Mock
+ private lateinit var mQSLogger: QSLogger
+ private lateinit var mParentView: ViewGroup
+
+ @Mock
+ private lateinit var mCallback: QSDetail.Callback
+
+ @Mock
+ private lateinit var mQSTileView: QSTileView
+
+ private lateinit var mFooter: View
+
+ @Before
+ @Throws(Exception::class)
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ mTestableLooper = TestableLooper.get(this)
+
+ mDndTileRecord.tile = dndTile
+ mDndTileRecord.tileView = mQSTileView
+ mTestableLooper.runWithLooper {
+ mQsPanel = QSPanel(mContext, null)
+ mQsPanel.initialize()
+ // QSPanel inflates a footer inside of it, mocking it here
+ mFooter = LinearLayout(mContext).apply { id = R.id.qs_footer }
+ mQsPanel.addView(mFooter)
+ mQsPanel.onFinishInflate()
+ mQsPanel.setSecurityFooter(View(mContext), false)
+ mQsPanel.setHeaderContainer(LinearLayout(mContext))
+ // Provides a parent with non-zero size for QSPanel
+ mParentView = FrameLayout(mContext).apply {
+ addView(mQsPanel)
+ }
+
+ whenever(dndTile.tileSpec).thenReturn("dnd")
+ whenever(mHost.tiles).thenReturn(emptyList())
+ whenever(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView)
+ mQsPanel.addTile(mDndTileRecord)
+ mQsPanel.setCallback(mCallback)
+ }
+ }
+
+ @Test
+ fun testOpenDetailsWithExistingTile_NoException() {
+ mTestableLooper.runWithLooper {
+ mQsPanel.openDetails(dndTile)
+ }
+
+ verify(mCallback).onShowingDetail(any(), anyInt(), anyInt())
+ }
+
+ @Test
+ fun testOpenDetailsWithNullParameter_NoException() {
+ mTestableLooper.runWithLooper {
+ mQsPanel.openDetails(null)
+ }
+
+ verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt())
+ }
+
+ @Test
+ fun testSecurityFooter_appearsOnBottomOnSplitShade() {
+ mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
+ mQsPanel.switchSecurityFooter(true)
+
+ mTestableLooper.runWithLooper {
+ mQsPanel.isExpanded = true
+ }
+
+ // After mFooter
+ assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(
+ mQsPanel.indexOfChild(mFooter) + 1
+ )
+ }
+
+ @Test
+ fun testSecurityFooter_appearsOnBottomIfPortrait() {
+ mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_PORTRAIT))
+ mQsPanel.switchSecurityFooter(false)
+
+ mTestableLooper.runWithLooper {
+ mQsPanel.isExpanded = true
+ }
+
+ // After mFooter
+ assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(
+ mQsPanel.indexOfChild(mFooter) + 1
+ )
+ }
+
+ @Test
+ fun testSecurityFooter_appearsOnTopIfSmallScreenAndLandscape() {
+ mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
+ mQsPanel.switchSecurityFooter(false)
+
+ mTestableLooper.runWithLooper {
+ mQsPanel.isExpanded = true
+ }
+
+ // -1 means that it is part of the mHeaderContainer
+ assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
+ }
+
+ private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
+ context.resources.configuration.apply { orientation = newOrientation }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 9e97f801be3e..fbb0a9555df5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -18,14 +18,12 @@ package com.android.systemui.qs;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.assertFalse;
-
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -59,6 +57,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.external.TileServiceKey;
+import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
@@ -67,12 +66,12 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
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;
@@ -120,10 +119,13 @@ public class QSTileHostTest extends SysuiTestCase {
private UiEventLogger mUiEventLogger;
@Mock
private UserTracker mUserTracker;
- @Mock
private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
+ @Mock
+ private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
+ @Mock
+ private TileServiceRequestController mTileServiceRequestController;
private Handler mHandler;
private TestableLooper mLooper;
@@ -134,14 +136,17 @@ public class QSTileHostTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mLooper = TestableLooper.get(this);
mHandler = new Handler(mLooper.getLooper());
+ when(mTileServiceRequestControllerBuilder.create(any()))
+ .thenReturn(mTileServiceRequestController);
+
+ mSecureSettings = new FakeSettings();
+ mSecureSettings.putStringForUser(
+ QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings, mCustomTileStatePersister);
+ mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder);
setUpTileFactory();
-
- when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
- .thenReturn("");
}
private void setUpTileFactory() {
@@ -364,6 +369,16 @@ public class QSTileHostTest extends SysuiTestCase {
.removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
}
+ @Test
+ public void testRemoveTiles() {
+ List<String> tiles = List.of("spec1", "spec2", "spec3");
+ mQSTileHost.saveTilesToSettings(tiles);
+
+ mQSTileHost.removeTiles(List.of("spec1", "spec2"));
+
+ assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
+ }
+
private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
@@ -371,11 +386,12 @@ public class QSTileHostTest extends SysuiTestCase {
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
- SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
+ SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister);
+ customTileStatePersister, tileServiceRequestControllerBuilder);
}
@Override
@@ -389,14 +405,11 @@ public class QSTileHostTest extends SysuiTestCase {
@Override
void saveTilesToSettings(List<String> tileSpecs) {
super.saveTilesToSettings(tileSpecs);
-
- ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
- verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING),
- specs.capture(), isNull(), eq(false), anyInt(), eq(true));
-
// After tiles are changed, make sure to call onTuningChanged with the new setting if it
// changed
- onTuningChanged(TILES_SETTING, specs.getValue());
+ String specs = mSecureSettings.getStringForUser(
+ QSTileHost.TILES_SETTING, mUserTracker.getUserId());
+ onTuningChanged(TILES_SETTING, specs);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
new file mode 100644
index 000000000000..de1d86b08785
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.never
+import org.mockito.Mockito.mock
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+class QuickQSBrightnessControllerTest : SysuiTestCase() {
+
+ @Mock
+ lateinit var brightnessController: BrightnessController
+ @get:Rule
+ val mockito = MockitoJUnit.rule()
+
+ lateinit var quickQSBrightnessController: QuickQSBrightnessController
+
+ @Before
+ fun setUp() {
+ quickQSBrightnessController = QuickQSBrightnessController(
+ brightnessControllerFactory = { brightnessController })
+ }
+
+ @Test
+ fun testSliderIsShownWhenInitializedInSplitShade() {
+ quickQSBrightnessController.init(shouldUseSplitNotificationShade = true)
+
+ verify(brightnessController).showSlider()
+ }
+
+ @Test
+ fun testSliderIsShownWhenRefreshedInSplitShade() {
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+ verify(brightnessController, times(1)).showSlider()
+ }
+
+ @Test
+ fun testSliderIsHiddenWhenRefreshedInNonSplitShade() {
+ // needs to be shown first
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+
+ verify(brightnessController).hideSlider()
+ }
+
+ @Test
+ fun testSliderChangesVisibilityWhenRotating() {
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+ verify(brightnessController, times(1)).showSlider()
+
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+ verify(brightnessController, times(1)).hideSlider()
+ }
+
+ @Test
+ fun testCallbacksAreRegisteredOnlyOnce() {
+ // this flow simulates expanding shade in portrait...
+ quickQSBrightnessController.setListening(true)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+ // ... and rotating to landscape/split shade where slider is visible
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+ verify(brightnessController, times(1)).registerCallbacks()
+ }
+
+ @Test
+ fun testCallbacksAreRegisteredOnlyOnceWhenRotatingPhone() {
+ quickQSBrightnessController.setListening(true)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+ verify(brightnessController, times(1)).registerCallbacks()
+ }
+
+ @Test
+ fun testCallbacksAreNotRegisteredWhenSliderNotVisible() {
+ quickQSBrightnessController.setListening(true)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = false)
+
+ verify(brightnessController, never()).registerCallbacks()
+ }
+
+ @Test
+ fun testMirrorIsSetWhenSliderIsShown() {
+ val mirrorController = mock(BrightnessMirrorController::class.java)
+ quickQSBrightnessController.setMirror(mirrorController)
+ quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+ verify(brightnessController).setMirror(mirrorController)
+ }
+} \ No newline at end of file
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 0604e1b42c9d..912bea2f4c97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.qs
+import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -27,7 +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 com.android.systemui.flags.FeatureFlags
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -66,6 +66,10 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
private lateinit var tileView: QSTileView
@Mock
private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var quickQsBrightnessController: QuickQSBrightnessController
+ @Mock
+ private lateinit var footerActionsController: FooterActionsController
private lateinit var controller: QuickQSPanelController
@@ -75,6 +79,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
`when`(quickQSPanel.tileLayout).thenReturn(tileLayout)
`when`(quickQSPanel.dumpableTag).thenReturn("")
+ `when`(quickQSPanel.resources).thenReturn(mContext.resources)
`when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
controller = QuickQSPanelController(
@@ -87,7 +92,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
uiEventLogger,
qsLogger,
dumpManager,
- featureFlags
+ quickQsBrightnessController,
+ footerActionsController
)
controller.init()
@@ -117,4 +123,4 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
verify(quickQSPanel, times(limit)).addTile(any())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 35360bd19393..b34433c55c36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -23,8 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyDialogController
@@ -32,10 +34,11 @@ import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.VariableDateView
+import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -47,9 +50,9 @@ import org.junit.runner.RunWith
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -87,8 +90,16 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
@Mock
private lateinit var privacyDialogController: PrivacyDialogController
@Mock
+ private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
+ @Mock
+ private lateinit var variableDateViewController: VariableDateViewController
+ @Mock
+ private lateinit var batteryMeterViewController: BatteryMeterViewController
+ @Mock
private lateinit var clock: Clock
@Mock
+ private lateinit var variableDateView: VariableDateView
+ @Mock
private lateinit var mockView: View
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
@@ -109,6 +120,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
stubViews()
`when`(iconContainer.context).thenReturn(context)
`when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+ `when`(variableDateViewControllerFactory.create(any()))
+ .thenReturn(variableDateViewController)
`when`(view.resources).thenReturn(mContext.resources)
`when`(view.isAttachedToWindow).thenReturn(true)
`when`(view.context).thenReturn(context)
@@ -133,7 +146,9 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
colorExtractor,
privacyDialogController,
qsExpansionPathInterpolator,
- featureFlags
+ featureFlags,
+ variableDateViewControllerFactory,
+ batteryMeterViewController
)
}
@@ -274,6 +289,8 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
`when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
`when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
`when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
+ `when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
+ `when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
}
private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
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 72c7ddd3e5a5..126b332af30d 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
@@ -37,8 +37,8 @@ import android.widget.TextView;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
new file mode 100644
index 000000000000..d49673d0f572
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.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.systemui.qs.external
+
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTileView
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class TileRequestDialogTest : SysuiTestCase() {
+
+ companion object {
+ private const val APP_NAME = "App name"
+ private const val LABEL = "Label"
+ }
+
+ private lateinit var dialog: TileRequestDialog
+
+ @Before
+ fun setUp() {
+ // Create in looper so we can make sure that the tile is fully updated
+ TestableLooper.get(this).runWithLooper {
+ dialog = TileRequestDialog(mContext)
+ }
+ }
+
+ @After
+ fun teardown() {
+ if (this::dialog.isInitialized) {
+ dialog.dismiss()
+ }
+ }
+
+ @Test
+ fun useCorrectTheme() {
+ assertThat(dialog.context.themeResId).isEqualTo(R.style.TileRequestDialog)
+ }
+
+ @Test
+ fun setTileData_hasCorrectViews() {
+ val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+ val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+ dialog.setTileData(tileData)
+ dialog.show()
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+
+ assertThat(content.childCount).isEqualTo(2)
+ assertThat(content.getChildAt(0)).isInstanceOf(TextView::class.java)
+ assertThat(content.getChildAt(1)).isInstanceOf(QSTileView::class.java)
+ }
+
+ @Test
+ fun setTileData_hasCorrectAppName() {
+ val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+ val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+ dialog.setTileData(tileData)
+ dialog.show()
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val text = content.getChildAt(0) as TextView
+ assertThat(text.text.toString()).contains(APP_NAME)
+ }
+
+ @Test
+ fun setTileData_hasCorrectLabel() {
+ val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+ val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+ dialog.setTileData(tileData)
+ dialog.show()
+
+ TestableLooper.get(this).processAllMessages()
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val tile = content.getChildAt(1) as QSTileView
+ assertThat((tile.label as TextView).text.toString()).isEqualTo(LABEL)
+ }
+
+ @Test
+ fun setTileData_hasIcon() {
+ val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+ val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+ dialog.setTileData(tileData)
+ dialog.show()
+
+ TestableLooper.get(this).processAllMessages()
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val tile = content.getChildAt(1) as QSTileView
+ assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
+ }
+
+ @Test
+ fun setTileData_nullIcon_hasIcon() {
+ val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, null)
+
+ dialog.setTileData(tileData)
+ dialog.show()
+
+ TestableLooper.get(this).processAllMessages()
+
+ val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+ val tile = content.getChildAt(1) as QSTileView
+ assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
new file mode 100644
index 000000000000..ce8e58ca9e61
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.external
+
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.graphics.drawable.Icon
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TileServiceRequestControllerTest : SysuiTestCase() {
+
+ companion object {
+ private val TEST_COMPONENT = ComponentName("test_pkg", "test_cls")
+ private const val TEST_APP_NAME = "App"
+ private const val TEST_LABEL = "Label"
+ }
+
+ @Mock
+ private lateinit var tileRequestDialog: TileRequestDialog
+ @Mock
+ private lateinit var qsTileHost: QSTileHost
+ @Mock
+ private lateinit var commandRegistry: CommandRegistry
+ @Mock
+ private lateinit var icon: Icon
+
+ private lateinit var controller: TileServiceRequestController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // Tile not present by default
+ `when`(qsTileHost.indexOf(anyString())).thenReturn(-1)
+
+ controller = TileServiceRequestController(qsTileHost, commandRegistry) {
+ tileRequestDialog
+ }
+
+ controller.init()
+ }
+
+ @Test
+ fun requestTileAdd_dataIsPassedToDialog() {
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+
+ verify(tileRequestDialog).setTileData(
+ TileRequestDialog.TileData(TEST_APP_NAME, TEST_LABEL, icon)
+ )
+ }
+
+ @Test
+ fun tileAlreadyAdded_correctResult() {
+ `when`(qsTileHost.indexOf(CustomTile.toSpec(TEST_COMPONENT))).thenReturn(2)
+
+ val callback = Callback()
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+
+ assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.TILE_ALREADY_ADDED)
+ verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+ }
+
+ @Test
+ fun showAllUsers_set() {
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ verify(tileRequestDialog).setShowForAllUsers(true)
+ }
+
+ @Test
+ fun cancelOnTouchOutside_set() {
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, Callback())
+ verify(tileRequestDialog).setCanceledOnTouchOutside(true)
+ }
+
+ @Test
+ fun cancelListener_dismissResult() {
+ val cancelListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java)
+
+ val callback = Callback()
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ verify(tileRequestDialog).setOnCancelListener(capture(cancelListenerCaptor))
+
+ cancelListenerCaptor.value.onCancel(tileRequestDialog)
+ assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DISMISSED)
+ verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+ }
+
+ @Test
+ fun positiveActionListener_tileAddedResult() {
+ val clickListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
+
+ val callback = Callback()
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ verify(tileRequestDialog).setPositiveButton(anyInt(), capture(clickListenerCaptor))
+
+ clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_POSITIVE)
+
+ assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.ADD_TILE)
+ verify(qsTileHost).addTile(TEST_COMPONENT, /* end */ true)
+ }
+
+ @Test
+ fun negativeActionListener_tileNotAddedResult() {
+ val clickListenerCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener::class.java)
+
+ val callback = Callback()
+ controller.requestTileAdd(TEST_COMPONENT, TEST_APP_NAME, TEST_LABEL, icon, callback)
+ verify(tileRequestDialog).setNegativeButton(anyInt(), capture(clickListenerCaptor))
+
+ clickListenerCaptor.value.onClick(tileRequestDialog, DialogInterface.BUTTON_NEGATIVE)
+
+ assertThat(callback.lastAccepted).isEqualTo(TileServiceRequestController.DONT_ADD_TILE)
+ verify(qsTileHost, never()).addTile(any(ComponentName::class.java), anyBoolean())
+ }
+
+ private class Callback : Consumer<Int> {
+ var lastAccepted: Int? = null
+ private set
+ override fun accept(t: Int) {
+ lastAccepted = t
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2b1840462291..e027ab72f2ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -98,12 +98,20 @@ public class TileServicesTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
+ @Mock
+ private TileServiceRequestController mTileServiceRequestController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDependency.injectMockDependency(BluetoothController.class);
mManagers = new ArrayList<>();
+
+ when(mTileServiceRequestControllerBuilder.create(any()))
+ .thenReturn(mTileServiceRequestController);
+
QSTileHost host = new QSTileHost(mContext,
mStatusBarIconController,
mQSFactory,
@@ -119,7 +127,8 @@ public class TileServicesTest extends SysuiTestCase {
mUiEventLogger,
mUserTracker,
mSecureSettings,
- mock(CustomTileStatePersister.class));
+ mock(CustomTileStatePersister.class),
+ mTileServiceRequestControllerBuilder);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d44a52607707..e939411e4a2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles;
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -327,4 +328,77 @@ public class CastTileTest extends SysuiTestCase {
assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
}
+
+ @Test
+ public void testExpandView_wifiNotConnected() {
+ mCastTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_wifiEnabledNotCasting() {
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_projection() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastController.CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_projection() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertFalse(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_casting_mediaRoute() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ device.tag = mock(MediaRouter.RouteInfo.class);
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
+
+ @Test
+ public void testExpandView_connecting_mediaRoute() {
+ CastController.CastDevice connecting = new CastController.CastDevice();
+ connecting.state = CastDevice.STATE_CONNECTING;
+ connecting.tag = mock(RouteInfo.class);
+ connecting.name = "Test Casting Device";
+
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(connecting);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ enableWifiAndProcessMessages();
+
+ assertTrue(mCastTile.getState().forceExpandIcon);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index a70c2be4954e..8922b43b7447 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -228,7 +228,9 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mTile.handleClick(null /* view */);
mTestableLooper.processAllMessages();
- verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+ verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */,
+ (ActivityLaunchAnimator.Controller) eq(null),
+ eq(true) /* showOverLockscreenWhenLocked */);
Intent nextStartedIntent = mIntentCaptor.getValue();
String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
@@ -246,7 +248,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */,
- (ActivityLaunchAnimator.Controller) eq(null));
+ (ActivityLaunchAnimator.Controller) eq(null),
+ eq(true) /* showOverLockscreenWhenLocked */);
Intent nextStartedIntent = mIntentCaptor.getValue();
String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
new file mode 100644
index 000000000000..fa5f70c5a2fc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -0,0 +1,127 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class InternetAdapterTest extends SysuiTestCase {
+
+ private static final String WIFI_TITLE = "Wi-Fi Title";
+ private static final String WIFI_SUMMARY = "Wi-Fi Summary";
+
+ @Mock
+ private WifiEntry mInternetWifiEntry;
+ @Mock
+ private List<WifiEntry> mWifiEntries;
+ @Mock
+ private WifiEntry mWifiEntry;
+ @Mock
+ private InternetDialogController mInternetDialogController;
+ @Mock
+ private WifiUtils.InternetIconInjector mWifiIconInjector;
+
+ private InternetAdapter mInternetAdapter;
+ private InternetAdapter.InternetViewHolder mViewHolder;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+ when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+ when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+
+ mInternetAdapter = new InternetAdapter(mInternetDialogController);
+ mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0);
+ mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */);
+ mViewHolder.mWifiIconInjector = mWifiIconInjector;
+ }
+
+ @Test
+ public void getItemCount_returnWifiEntriesCount() {
+ for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) {
+ mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */);
+
+ assertThat(mInternetAdapter.getItemCount()).isEqualTo(i);
+ }
+ }
+
+ @Test
+ public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() {
+ when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() {
+ when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void onBindViewHolder_wifiLevelUnreachable_shouldNotGetWifiIcon() {
+ reset(mWifiIconInjector);
+ when(mWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE);
+
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ verify(mWifiIconInjector, never()).getIcon(anyBoolean(), anyInt());
+ }
+
+ @Test
+ public void onBindViewHolder_shouldNotShowXLevelIcon_getIconWithInternet() {
+ when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(false);
+
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ verify(mWifiIconInjector).getIcon(eq(false) /* noInternet */, anyInt());
+ }
+
+ @Test
+ public void onBindViewHolder_shouldShowXLevelIcon_getIconWithNoInternet() {
+ when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
+
+ mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+ verify(mWifiIconInjector).getIcon(eq(true) /* noInternet */, anyInt());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
new file mode 100644
index 000000000000..cacc4095d141
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -0,0 +1,491 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+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.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogControllerTest extends SysuiTestCase {
+
+ private static final int SUB_ID = 1;
+ private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
+ private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
+
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private GlobalSettings mGlobalSettings;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private NetworkController.AccessPointController mAccessPointController;
+ @Mock
+ private WifiEntry mConnectedEntry;
+ @Mock
+ private WifiEntry mWifiEntry1;
+ @Mock
+ private WifiEntry mWifiEntry2;
+ @Mock
+ private WifiEntry mWifiEntry3;
+ @Mock
+ private WifiEntry mWifiEntry4;
+ @Mock
+ private ServiceState mServiceState;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private WifiUtils.InternetIconInjector mWifiIconInjector;
+ @Mock
+ InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+
+ private MockInternetDialogController mInternetDialogController;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private List<WifiEntry> mAccessPoints = new ArrayList<>();
+ private List<WifiEntry> mWifiEntries = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+ when(mConnectedEntry.isDefaultNetwork()).thenReturn(true);
+ when(mConnectedEntry.hasInternetAccess()).thenReturn(true);
+ when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+ when(mWifiEntry2.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+ when(mWifiEntry3.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+ when(mWifiEntry4.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+ mAccessPoints.add(mConnectedEntry);
+ mAccessPoints.add(mWifiEntry1);
+ when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
+
+ mInternetDialogController = new MockInternetDialogController(mContext,
+ mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
+ mSubscriptionManager, mTelephonyManager, mWifiManager,
+ mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
+ mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController);
+ mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+ mInternetDialogController.mOnSubscriptionsChangedListener);
+ mInternetDialogController.onStart(mInternetDialogCallback, true);
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+ mInternetDialogController.mActivityStarter = mActivityStarter;
+ mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
+ }
+
+ @Test
+ public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
+ mInternetDialogController.setAirplaneModeEnabled(true);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+ getResourcesString("airplane_mode")));
+ }
+
+ @Test
+ public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+ getResourcesString("quick_settings_internet_label")));
+ }
+
+ @Test
+ public void getSubtitleText_withAirplaneModeOn_returnNull() {
+ mInternetDialogController.setAirplaneModeEnabled(true);
+
+ assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+ }
+
+ @Test
+ public void getSubtitleText_withWifiOff_returnWifiIsOff() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("wifi_is_off"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("wifi_is_off"));
+ }
+
+ @Test
+ public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ assertThat(mInternetDialogController.getSubtitleText(true))
+ .isEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(true))
+ .isNotEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
+ }
+
+ @Test
+ public void getSubtitleText_withWifiEntry_returnTapToConnect() {
+ // The preconditions WiFi Entries is already in setUp()
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("tap_a_network_to_connect"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("tap_a_network_to_connect"));
+ }
+
+ @Test
+ public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("unlock_to_view_networks")));
+ }
+
+ @Test
+ public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+ doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+ doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+
+ assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+ getResourcesString("all_network_unavailable")));
+ }
+
+ @Test
+ public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
+ mInternetDialogController.setAirplaneModeEnabled(false);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+ doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+
+ when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
+
+ // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+ mInternetDialogController.mCanConfigWifi = false;
+
+ assertThat(mInternetDialogController.getSubtitleText(false))
+ .isNotEqualTo(getResourcesString("non_carrier_network_unavailable"));
+ }
+
+ @Test
+ public void getWifiDetailsSettingsIntent_withNoConnectedEntry_returnNull() {
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+ }
+
+ @Test
+ public void getWifiDetailsSettingsIntent_withNoConnectedEntryKey_returnNull() {
+ when(mConnectedEntry.getKey()).thenReturn(null);
+
+ assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+ }
+
+ @Test
+ public void getWifiDetailsSettingsIntent_withConnectedEntryKey_returnIntent() {
+ when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+ assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNotNull();
+ }
+
+ @Test
+ public void getWifiDrawable_withConnectedEntry_returnIntentIconWithCorrectColor() {
+ final Drawable drawable = mock(Drawable.class);
+ when(mWifiIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(drawable);
+
+ mInternetDialogController.getInternetWifiDrawable(mConnectedEntry);
+
+ verify(mWifiIconInjector).getIcon(eq(false), anyInt());
+ verify(drawable).setTint(mContext.getColor(R.color.connected_network_primary_color));
+ }
+
+ @Test
+ public void launchWifiNetworkDetailsSetting_withNoConnectedEntry_doNothing() {
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+ verify(mActivityStarter, never())
+ .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+ }
+
+ @Test
+ public void launchWifiNetworkDetailsSetting_withConnectedEntryKey_startActivity() {
+ when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+ mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+ verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+ }
+
+ @Test
+ public void isDeviceLocked_keyguardIsUnlocked_returnFalse() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+
+ assertThat(mInternetDialogController.isDeviceLocked()).isFalse();
+ }
+
+ @Test
+ public void isDeviceLocked_keyguardIsLocked_returnTrue() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+ assertThat(mInternetDialogController.isDeviceLocked()).isTrue();
+ }
+
+ @Test
+ public void onAccessPointsChanged_canNotConfigWifi_doNothing() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.mCanConfigWifi = false;
+
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any());
+ }
+
+ @Test
+ public void onAccessPointsChanged_nullAccessPoints_callbackBothNull() {
+ reset(mInternetDialogCallback);
+
+ mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+ }
+
+ @Test
+ public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mConnectedEntry);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ }
+
+ @Test
+ public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mWifiEntry1);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ verify(mInternetDialogCallback)
+ .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+ }
+
+ @Test
+ public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mConnectedEntry);
+ mAccessPoints.add(mWifiEntry1);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ }
+
+ @Test
+ public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mConnectedEntry);
+ mAccessPoints.add(mWifiEntry1);
+ mAccessPoints.add(mWifiEntry2);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ mWifiEntries.add(mWifiEntry2);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ }
+
+ @Test
+ public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mConnectedEntry);
+ mAccessPoints.add(mWifiEntry1);
+ mAccessPoints.add(mWifiEntry2);
+ mAccessPoints.add(mWifiEntry3);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ mWifiEntries.add(mWifiEntry2);
+ mWifiEntries.add(mWifiEntry3);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+
+ // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry3);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ }
+
+ @Test
+ public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(true);
+ mAccessPoints.clear();
+ mAccessPoints.add(mConnectedEntry);
+ mAccessPoints.add(mWifiEntry1);
+ mAccessPoints.add(mWifiEntry2);
+ mAccessPoints.add(mWifiEntry3);
+ mAccessPoints.add(mWifiEntry4);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.clear();
+ mWifiEntries.add(mWifiEntry1);
+ mWifiEntries.add(mWifiEntry2);
+ mWifiEntries.add(mWifiEntry3);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+
+ // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+ reset(mInternetDialogCallback);
+ mInternetDialogController.setAirplaneModeEnabled(false);
+
+ mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+ mWifiEntries.remove(mWifiEntry3);
+ verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+ }
+
+ private String getResourcesString(String name) {
+ return mContext.getResources().getString(getResourcesId(name));
+ }
+
+ private int getResourcesId(String name) {
+ return mContext.getResources().getIdentifier(name, "string",
+ mContext.getPackageName());
+ }
+
+ private class MockInternetDialogController extends InternetDialogController {
+
+ private GlobalSettings mGlobalSettings;
+ private boolean mIsAirplaneModeOn;
+
+ MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
+ ActivityStarter starter, AccessPointController accessPointController,
+ SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+ @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+ @Main Handler handler, @Main Executor mainExecutor,
+ BroadcastDispatcher broadcastDispatcher,
+ KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
+ KeyguardStateController keyguardStateController) {
+ super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
+ telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
+ broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
+ keyguardStateController);
+ mGlobalSettings = globalSettings;
+ }
+
+ @Override
+ boolean isAirplaneModeEnabled() {
+ return mIsAirplaneModeOn;
+ }
+
+ public void setAirplaneModeEnabled(boolean enabled) {
+ mIsAirplaneModeOn = enabled;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
new file mode 100644
index 000000000000..fa9c0538e9bd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -0,0 +1,292 @@
+package com.android.systemui.qs.tiles.dialog;
+
+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.anyLong;
+import static org.mockito.ArgumentMatchers.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.when;
+
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogTest extends SysuiTestCase {
+
+ private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+ private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+ private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+ private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+ @Mock
+ private Handler mHandler;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private WifiEntry mInternetWifiEntry;
+ @Mock
+ private List<WifiEntry> mWifiEntries;
+ @Mock
+ private InternetAdapter mInternetAdapter;
+ @Mock
+ private InternetDialogController mInternetDialogController;
+
+ private InternetDialog mInternetDialog;
+ private View mDialogView;
+ private View mSubTitle;
+ private LinearLayout mMobileDataToggle;
+ private LinearLayout mWifiToggle;
+ private LinearLayout mConnectedWifi;
+ private RecyclerView mWifiList;
+ private LinearLayout mSeeAll;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+ when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+ when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+ when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+ when(mWifiEntries.size()).thenReturn(1);
+
+ when(mInternetDialogController.getMobileNetworkTitle()).thenReturn(MOBILE_NETWORK_TITLE);
+ when(mInternetDialogController.getMobileNetworkSummary())
+ .thenReturn(MOBILE_NETWORK_SUMMARY);
+ when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager);
+
+ mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
+ mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler);
+ mInternetDialog.mAdapter = mInternetAdapter;
+ mInternetDialog.onAccessPointsChanged(mWifiEntries, mInternetWifiEntry);
+ mInternetDialog.show();
+
+ mDialogView = mInternetDialog.mDialogView;
+ mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+ mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
+ mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+ mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
+ mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
+ mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
+ }
+
+ @After
+ public void tearDown() {
+ mInternetDialog.dismissDialog();
+ }
+
+ @Test
+ public void hideWifiViews_WifiViewsGone() {
+ mInternetDialog.hideWifiViews();
+
+ assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_withApmOn_mobileDataLayoutGone() {
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
+ // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialog.onAccessPointsChanged(mWifiEntries, null /* connectedEntry*/);
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() {
+ // The precondition WiFi ON is already in setUp()
+ mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, mInternetWifiEntry);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+ // The preconditions WiFi ON and WiFi entries are already in setUp()
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndHasInternetWifi_showHighlightWifiToggle() {
+ // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mWifiToggle.getBackground()).isNotNull();
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndHasInternetWifi_hideConnectedWifi() {
+ // The preconditions WiFi ON and Internet WiFi are already in setUp()
+ when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_deviceLockedAndHasWifiList_hideWifiListAndSeeAll() {
+ // The preconditions WiFi entries are already in setUp()
+ when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+ mInternetDialog.updateDialog();
+
+ assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+ mSeeAll.performClick();
+
+ verify(mInternetDialogController).launchNetworkSetting();
+ }
+
+ @Test
+ public void showProgressBar_wifiDisabled_hideProgressBar() {
+ Mockito.reset(mHandler);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+ mInternetDialog.showProgressBar();
+
+ assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+ }
+
+ @Test
+ public void showProgressBar_deviceLocked_hideProgressBar() {
+ Mockito.reset(mHandler);
+ when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+ mInternetDialog.showProgressBar();
+
+ assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+ }
+
+ @Test
+ public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() {
+ Mockito.reset(mHandler);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ mInternetDialog.showProgressBar();
+
+ // Show progress bar
+ assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mHandler).postDelayed(runnableCaptor.capture(),
+ eq(InternetDialog.PROGRESS_DELAY_MS));
+ runnableCaptor.getValue().run();
+
+ // Then hide progress bar
+ assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ }
+
+ @Test
+ public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() {
+ Mockito.reset(mHandler);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+ mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry*/);
+
+ mInternetDialog.showProgressBar();
+
+ // Show progress bar
+ assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mHandler).postDelayed(runnableCaptor.capture(),
+ eq(InternetDialog.PROGRESS_DELAY_MS));
+ runnableCaptor.getValue().run();
+
+ // Then hide searching sub-title only
+ assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+ assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
index 10c878a92745..6f081c759df7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
@@ -34,6 +34,7 @@ import android.view.ScrollCaptureResponse;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
@@ -274,7 +275,8 @@ public class ScrollCaptureControllerTest extends SysuiTestCase {
when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
.thenReturn(immediateFuture(session));
return new ScrollCaptureController(context, context.getMainExecutor(),
- client, new ImageTileSet(context.getMainThreadHandler()));
+ client, new ImageTileSet(context.getMainThreadHandler()),
+ new UiEventLoggerFake());
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index e0187bdf6ada..bceb92894609 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -108,15 +108,6 @@ class BrightnessSliderTest : SysuiTestCase() {
}
@Test
- fun testNullMirrorControllerNotTrackingTouch() {
- mController.setMirrorControllerAndMirror(null)
-
- verify(brightnessSliderView, never()).max
- verify(brightnessSliderView, never()).value
- verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
- }
-
- @Test
fun testNullMirrorNotTrackingTouch() {
whenever(mirrorController.toggleSlider).thenReturn(null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
new file mode 100644
index 000000000000..a9c6a5353350
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.shared.animation
+
+import android.graphics.Point
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_90
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var windowManager: WindowManager
+
+ @get:Rule
+ val mockito = MockitoJUnit.rule()
+
+ private lateinit var animator: UnfoldMoveFromCenterAnimator
+
+ @Before
+ fun before() {
+ animator = UnfoldMoveFromCenterAnimator(windowManager)
+ }
+
+ @Test
+ fun testRegisterViewOnTheLeftOfVerticalFold_halfProgress_viewTranslatedToTheRight() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+
+ animator.onTransitionProgress(0.5f)
+
+ // Positive translationX -> translated to the right
+ assertThat(view.translationX).isWithin(0.1f).of(3.75f)
+ }
+
+ @Test
+ fun testRegisterViewOnTheLeftOfVerticalFold_zeroProgress_viewTranslatedToTheRight() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+
+ animator.onTransitionProgress(0f)
+
+ // Positive translationX -> translated to the right
+ assertThat(view.translationX).isWithin(0.1f).of(7.5f)
+ }
+
+ @Test
+ fun testRegisterViewOnTheLeftOfVerticalFold_fullProgress_viewTranslatedToTheOriginalPosition() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+
+ animator.onTransitionProgress(1f)
+
+ assertThat(view.translationX).isEqualTo(0f)
+ }
+
+ @Test
+ fun testRegisterViewAndUnregister_halfProgress_viewIsNotUpdated() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+ animator.clearRegisteredViews()
+
+ animator.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isEqualTo(0f)
+ }
+
+ @Test
+ fun testRegisterViewUpdateProgressAndUnregister_halfProgress_viewIsNotUpdated() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+ animator.onTransitionProgress(0.2f)
+ animator.clearRegisteredViews()
+
+ animator.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isEqualTo(0f)
+ }
+
+ @Test
+ fun testRegisterViewOnTheTopOfHorizontalFold_halfProgress_viewTranslatedToTheBottom() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_90)
+ val view = createView(y = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+
+ animator.onTransitionProgress(0.5f)
+
+ // Positive translationY -> translated to the bottom
+ assertThat(view.translationY).isWithin(0.1f).of(3.75f)
+ }
+
+ private fun createView(
+ x: Int = 0,
+ y: Int = 0,
+ width: Int = 10,
+ height: Int = 10,
+ translationX: Float = 0f,
+ translationY: Float = 0f
+ ): View {
+ val view = spy(View(context))
+ doAnswer {
+ val location = (it.arguments[0] as IntArray)
+ location[0] = x
+ location[1] = y
+ Unit
+ }.`when`(view).getLocationOnScreen(any())
+
+ whenever(view.width).thenReturn(width)
+ whenever(view.height).thenReturn(height)
+
+ return view.apply {
+ setTranslationX(translationX)
+ setTranslationY(translationY)
+ }
+ }
+
+ private fun givenScreen(width: Int = 100,
+ height: Int = 100,
+ rotation: Int = ROTATION_0) {
+ val display = mock(Display::class.java)
+ whenever(display.getSize(any())).thenAnswer {
+ val size = (it.arguments[0] as Point)
+ size.set(width, height)
+ Unit
+ }
+ whenever(display.rotation).thenReturn(rotation)
+ whenever(windowManager.defaultDisplay).thenReturn(display)
+
+ animator.updateDisplayProperties()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 325d540ad741..790b4dd11825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -15,7 +15,6 @@
package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
@@ -32,33 +31,32 @@ import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.HandlerThread;
import android.test.suitebuilder.annotation.SmallTest;
-import androidx.test.annotation.UiThreadTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.Requires;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Collections;
@@ -68,63 +66,55 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class PluginInstanceManagerTest extends SysuiTestCase {
- private static final String WHITELISTED_PACKAGE = "com.android.systemui";
- // Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
- private static Plugin sMockPlugin;
+ private static final String PRIVILEGED_PACKAGE = "com.android.systemui.shared.plugins";
+ private TestPlugin mMockPlugin;
- private HandlerThread mHandlerThread;
- private Context mContextWrapper;
private PackageManager mMockPm;
- private PluginListener mMockListener;
- private PluginInstanceManager mPluginInstanceManager;
- private PluginManagerImpl mMockManager;
+ private PluginListener<Plugin> mMockListener;
+ private PluginInstanceManager<Plugin> mPluginInstanceManager;
private VersionInfo mMockVersionInfo;
private PluginEnabler mMockEnabler;
ComponentName mTestPluginComponentName =
- new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
+ new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName());
+ private PluginInitializer mInitializer;
+ private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ NotificationManager mNotificationManager;
+ private PluginInstanceManager.Factory mInstanceManagerFactory;
+ private final PluginInstanceManager.InstanceFactory<Plugin> mPluginInstanceFactory =
+ new PluginInstanceManager.InstanceFactory<Plugin>() {
+ @Override
+ Plugin create(Class cls) {
+ return mMockPlugin;
+ }
+ };
@Before
public void setup() throws Exception {
- mHandlerThread = new HandlerThread("test_thread");
- mHandlerThread.start();
- mContextWrapper = new MyContextWrapper(getContext());
+ mContext = new MyContextWrapper(mContext);
mMockPm = mock(PackageManager.class);
mMockListener = mock(PluginListener.class);
- mMockManager = mock(PluginManagerImpl.class);
- when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
mMockEnabler = mock(PluginEnabler.class);
- when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
mMockVersionInfo = mock(VersionInfo.class);
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, true, new String[0]);
- sMockPlugin = mock(Plugin.class);
- when(sMockPlugin.getVersion()).thenReturn(1);
- }
-
- @After
- public void tearDown() throws Exception {
- mHandlerThread.quit();
- sMockPlugin = null;
- }
-
- @UiThreadTest
- @Test
- public void testGetPlugin() throws Exception {
- setupFakePmQuery();
- PluginInfo p = mPluginInstanceManager.getPlugin();
- assertNotNull(p.mPlugin);
- verify(sMockPlugin).onCreate(any(), any());
+ mInitializer = mock(PluginInitializer.class);
+ mNotificationManager = mock(NotificationManager.class);
+ mMockPlugin = mock(TestPlugin.class);
+ mInstanceManagerFactory = new PluginInstanceManager.Factory(getContext(), mMockPm,
+ mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler,
+ new ArrayList<>())
+ .setInstanceFactory(mPluginInstanceFactory);
+
+ mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+ true, mMockVersionInfo, true);
+ when(mMockPlugin.getVersion()).thenReturn(1);
}
@Test
- public void testNoPlugins() throws Exception {
+ public void testNoPlugins() {
when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn(
Collections.emptyList());
mPluginInstanceManager.loadAll();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
verify(mMockListener, never()).onPluginConnected(any(), any());
}
@@ -134,7 +124,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
createPlugin();
// Verify startup lifecycle
- verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+ verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
ArgumentCaptor.forClass(Context.class).capture());
verify(mMockListener).onPluginConnected(any(), any());
}
@@ -145,45 +135,41 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.destroy();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
+
// Verify shutdown lifecycle
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
- verify(sMockPlugin).onDestroy();
+ verify(mMockPlugin).onDestroy();
}
@Test
public void testIncorrectVersion() throws Exception {
- NotificationManager nm = mock(NotificationManager.class);
- mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm);
setupFakePmQuery();
doThrow(new InvalidVersionException("", false)).when(mMockVersionInfo).checkVersion(any());
mPluginInstanceManager.loadAll();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
// Plugin shouldn't be connected because it is the wrong version.
verify(mMockListener, never()).onPluginConnected(any(), any());
- verify(nm).notify(eq(SystemMessage.NOTE_PLUGIN), any());
+ verify(mNotificationManager).notify(eq(SystemMessage.NOTE_PLUGIN), any());
}
@Test
public void testReloadOnChange() throws Exception {
createPlugin(); // Get into valid created state.
- mPluginInstanceManager.onPackageChange("com.android.systemui");
+ mPluginInstanceManager.onPackageChange(PRIVILEGED_PACKAGE);
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
// Verify the old one was destroyed.
verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
- verify(sMockPlugin).onDestroy();
+ verify(mMockPlugin).onDestroy();
// Also verify we got a second onCreate.
- verify(sMockPlugin, Mockito.times(2)).onCreate(
+ verify(mMockPlugin, Mockito.times(2)).onCreate(
ArgumentCaptor.forClass(Context.class).capture(),
ArgumentCaptor.forClass(Context.class).capture());
verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any());
@@ -192,35 +178,35 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
@Test
public void testNonDebuggable() throws Exception {
// Create a version that thinks the build is not debuggable.
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, false, new String[0]);
+ mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);;
+ mFakeExecutor.runAllReady();
// Non-debuggable build should receive no plugins.
verify(mMockListener, never()).onPluginConnected(any(), any());
}
@Test
- public void testNonDebuggable_whitelist() throws Exception {
+ public void testNonDebuggable_privileged() throws Exception {
// Create a version that thinks the build is not debuggable.
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+ PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+ mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+ mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+ factory.setInstanceFactory(mPluginInstanceFactory);
+ mPluginInstanceManager = factory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
setupFakePmQuery();
mPluginInstanceManager.loadAll();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
// Verify startup lifecycle
- verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+ verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
ArgumentCaptor.forClass(Context.class).capture());
verify(mMockListener).onPluginConnected(any(), any());
}
@@ -253,9 +239,13 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
@Test
public void testDisableWhitelisted() throws Exception {
- mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
- mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
- mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+ PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+ mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+ mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+ factory.setInstanceFactory(mPluginInstanceFactory);
+ mPluginInstanceManager = factory.create("myAction", mMockListener,
+ true, mMockVersionInfo, false);
+
createPlugin(); // Get into valid created state.
mPluginInstanceManager.disableAll();
@@ -280,9 +270,12 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn(
PackageManager.PERMISSION_GRANTED);
- ApplicationInfo appInfo = getContext().getApplicationInfo();
- when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
- appInfo);
+ when(mMockPm.getApplicationInfo(Mockito.anyString(), anyInt())).thenAnswer(
+ (Answer<ApplicationInfo>) invocation -> {
+ ApplicationInfo appInfo = getContext().getApplicationInfo();
+ appInfo.packageName = invocation.getArgument(0);
+ return appInfo;
+ });
when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
}
@@ -291,12 +284,11 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
mPluginInstanceManager.loadAll();
- waitForIdleSync(mPluginInstanceManager.mPluginHandler);
- waitForIdleSync(mPluginInstanceManager.mMainHandler);
+ mFakeExecutor.runAllReady();
}
// Real context with no registering/unregistering of receivers.
- private static class MyContextWrapper extends ContextWrapper {
+ private static class MyContextWrapper extends SysuiTestableContext {
public MyContextWrapper(Context base) {
super(base);
}
@@ -322,17 +314,15 @@ public class PluginInstanceManagerTest extends SysuiTestCase {
public static class TestPlugin implements Plugin {
@Override
public int getVersion() {
- return sMockPlugin.getVersion();
+ return 1;
}
@Override
public void onCreate(Context sysuiContext, Context pluginContext) {
- sMockPlugin.onCreate(sysuiContext, pluginContext);
}
@Override
public void onDestroy() {
- sMockPlugin.onDestroy();
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 536c043bd7ae..4590dd829550 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -13,10 +13,8 @@
*/
package com.android.systemui.shared.plugins;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -30,19 +28,13 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
import org.junit.Before;
import org.junit.Test;
@@ -51,19 +43,24 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Optional;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class PluginManagerTest extends SysuiTestCase {
- private static final String WHITELISTED_PACKAGE = "com.android.systemui";
+ private static final String PRIVILEGED_PACKAGE = "com.android.systemui";
- private PluginInstanceManagerFactory mMockFactory;
- private PluginInstanceManager mMockPluginInstance;
+ private PluginInstanceManager.Factory mMockFactory;
+ private PluginInstanceManager<Plugin> mMockPluginInstance;
private PluginManagerImpl mPluginManager;
- private PluginListener mMockListener;
+ private PluginListener<?> mMockListener;
private PackageManager mMockPackageManager;
+ private PluginEnabler mPluginEnabler;
+ private PluginPrefs mPluginPrefs;
private UncaughtExceptionHandler mRealExceptionHandler;
private UncaughtExceptionHandler mMockExceptionHandler;
@@ -71,44 +68,25 @@ public class PluginManagerTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
- mDependency.injectTestDependency(Dependency.BG_LOOPER,
- TestableLooper.get(this).getLooper());
mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
- mMockFactory = mock(PluginInstanceManagerFactory.class);
+ mMockFactory = mock(PluginInstanceManager.Factory.class);
mMockPluginInstance = mock(PluginInstanceManager.class);
- when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
- Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
+ mPluginEnabler = mock(PluginEnabler.class);
+ mPluginPrefs = mock(PluginPrefs.class);
+ when(mMockFactory.create(any(), any(), Mockito.anyBoolean(), any(), Mockito.anyBoolean()))
.thenReturn(mMockPluginInstance);
mMockPackageManager = mock(PackageManager.class);
mPluginManager = new PluginManagerImpl(
getContext(), mMockFactory, true,
- mMockExceptionHandler, new PluginInitializerImpl() {
- @Override
- public String[] getWhitelistedPlugins(Context context) {
- return new String[0];
- }
-
- @Override
- public PluginEnabler getPluginEnabler(Context context) {
- return new PluginEnablerImpl(context, mMockPackageManager);
- }
- });
+ Optional.of(mMockExceptionHandler), mPluginEnabler,
+ mPluginPrefs, new ArrayList<>());
+
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
}
- @RunWithLooper(setAsMainLooper = true)
- @Test
- public void testOneShot() {
- Plugin mockPlugin = mock(Plugin.class);
- when(mMockPluginInstance.getPlugin()).thenReturn(new PluginInfo(null, null, mockPlugin,
- null, null));
- Plugin result = mPluginManager.getOneShotPlugin("myAction", TestPlugin.class);
- assertSame(mockPlugin, result);
- }
-
@Test
public void testAddListener() {
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
@@ -126,53 +104,49 @@ public class PluginManagerTest extends SysuiTestCase {
@Test
@RunWithLooper(setAsMainLooper = true)
- public void testNonDebuggable_noWhitelist() {
- mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- mMockExceptionHandler, new PluginInitializerImpl() {
- @Override
- public String[] getWhitelistedPlugins(Context context) {
- return new String[0];
- }
- });
+ public void testNonDebuggable_nonPrivileged() {
+ mPluginManager = new PluginManagerImpl(
+ getContext(), mMockFactory, false,
+ Optional.of(mMockExceptionHandler), mPluginEnabler,
+ mPluginPrefs, new ArrayList<>());
resetExceptionHandler();
String sourceDir = "myPlugin";
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.sourceDir = sourceDir;
- applicationInfo.packageName = WHITELISTED_PACKAGE;
+ applicationInfo.packageName = PRIVILEGED_PACKAGE;
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
- assertNull(mPluginManager.getClassLoader(applicationInfo));
+ verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+ any(VersionInfo.class), eq(false));
+ verify(mMockPluginInstance).loadAll();
}
@Test
@RunWithLooper(setAsMainLooper = true)
- public void testNonDebuggable_whitelistedPkg() {
- mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- mMockExceptionHandler, new PluginInitializerImpl() {
- @Override
- public String[] getWhitelistedPlugins(Context context) {
- return new String[] {WHITELISTED_PACKAGE};
- }
- });
+ public void testNonDebuggable_privilegedPackage() {
+ mPluginManager = new PluginManagerImpl(
+ getContext(), mMockFactory, false,
+ Optional.of(mMockExceptionHandler), mPluginEnabler,
+ mPluginPrefs, Collections.singletonList(PRIVILEGED_PACKAGE));
resetExceptionHandler();
String sourceDir = "myPlugin";
- ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
- whiteListedApplicationInfo.sourceDir = sourceDir;
- whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+ ApplicationInfo privilegedApplicationInfo = new ApplicationInfo();
+ privilegedApplicationInfo.sourceDir = sourceDir;
+ privilegedApplicationInfo.packageName = PRIVILEGED_PACKAGE;
ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
invalidApplicationInfo.sourceDir = sourceDir;
invalidApplicationInfo.packageName = "com.android.invalidpackage";
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
- assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
+ verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+ any(VersionInfo.class), eq(false));
+ verify(mMockPluginInstance).loadAll();
}
@Test
public void testExceptionHandler_foundPlugin() {
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(true);
+ when(mMockPluginInstance.checkAndDisable(any())).thenReturn(true);
mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
@@ -187,7 +161,7 @@ public class PluginManagerTest extends SysuiTestCase {
@Test
public void testExceptionHandler_noFoundPlugin() {
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
- when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(false);
+ when(mMockPluginInstance.checkAndDisable(any())).thenReturn(false);
mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
@@ -211,9 +185,7 @@ public class PluginManagerTest extends SysuiTestCase {
intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
mPluginManager.onReceive(mContext, intent);
verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
- verify(mMockPackageManager).setComponentEnabledSetting(eq(testComponent),
- eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
- eq(PackageManager.DONT_KILL_APP));
+ verify(mPluginEnabler).setDisabled(testComponent, PluginEnabler.DISABLED_INVALID_VERSION);
}
private void resetExceptionHandler() {
@@ -223,8 +195,8 @@ public class PluginManagerTest extends SysuiTestCase {
}
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
- public static interface TestPlugin extends Plugin {
- public static final String ACTION = "testAction";
- public static final int VERSION = 1;
+ public interface TestPlugin extends Plugin {
+ String ACTION = "testAction";
+ int VERSION = 1;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 21c6292c151f..be7917a30fc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Bundle;
+import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -124,24 +125,25 @@ public class CommandQueueTest extends SysuiTestCase {
public void testOnSystemBarAttributesChanged() {
doTestOnSystemBarAttributesChanged(DEFAULT_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test");
}
@Test
public void testOnSystemBarAttributesChangedForSecondaryDisplay() {
doTestOnSystemBarAttributesChanged(SECONDARY_DISPLAY, 1,
new AppearanceRegion[]{new AppearanceRegion(2, new Rect())}, false,
- BEHAVIOR_DEFAULT, false);
+ BEHAVIOR_DEFAULT, new InsetsVisibilities(), "test");
}
private void doTestOnSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
mCommandQueue.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
waitForIdleSync();
verify(mCallbacks).onSystemBarAttributesChanged(eq(displayId), eq(appearance),
- eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior), eq(isFullscreen));
+ eq(appearanceRegions), eq(navbarColorManagedByIme), eq(behavior),
+ eq(requestedVisibilities), eq(packageName));
}
@Test
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 f5ce673c249e..4c9006316175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -111,6 +111,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
"bar");
+ private String mKeyguardTryFingerprintMsg;
private String mDisclosureWithOrganization;
private String mDisclosureGeneric;
private String mFinancedDisclosureWithOrganization;
@@ -182,6 +183,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mContext.addMockSystemService(UserManager.class, mUserManager);
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
+ mKeyguardTryFingerprintMsg = mContext.getString(R.string.keyguard_try_fingerprint);
mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
ORGANIZATION_NAME);
mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic);
@@ -677,6 +679,34 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
verifyTransientMessage(message);
}
+ @Test
+ public void faceAuthMessageSuppressed() {
+ createController();
+ String faceHelpMsg = "Face auth help message";
+
+ // GIVEN state of showing message when keyguard screen is on
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
+
+ // GIVEN fingerprint is also running (not udfps)
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+
+ mController.setVisible(true);
+
+ // WHEN a face help message comes in
+ mController.getKeyguardCallback().onBiometricHelp(
+ KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, faceHelpMsg,
+ BiometricSourceType.FACE);
+
+ // THEN "try fingerprint" message appears (and not the face help message)
+ verifyTransientMessage(mKeyguardTryFingerprintMsg);
+
+ // THEN the face help message is still announced for a11y
+ verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg));
+ }
+
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
new file mode 100644
index 000000000000..97fe25d9a619
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LightRevealScrimTest : SysuiTestCase() {
+
+ private lateinit var scrim: LightRevealScrim
+ private var isOpaque = false
+
+ @Before
+ fun setUp() {
+ scrim = LightRevealScrim(context, null)
+ scrim.isScrimOpaqueChangedListener = Consumer { opaque ->
+ isOpaque = opaque
+ }
+ scrim.revealAmount = 0f
+ assertTrue("Scrim is not opaque in initial setup", scrim.isScrimOpaque)
+ }
+
+ @Test
+ fun testAlphaSetsOpaque() {
+ scrim.alpha = 0.5f
+ assertFalse("Scrim is opaque even though alpha is set", scrim.isScrimOpaque)
+ }
+
+ @Test
+ fun testVisibilitySetsOpaque() {
+ scrim.visibility = View.INVISIBLE
+ assertFalse("Scrim is opaque even though it's invisible", scrim.isScrimOpaque)
+ scrim.visibility = View.GONE
+ assertFalse("Scrim is opaque even though it's gone", scrim.isScrimOpaque)
+ }
+
+ @Test
+ fun testRevealSetsOpaque() {
+ scrim.revealAmount = 0.5f
+ assertFalse("Scrim is opaque even though it's revealed", scrim.isScrimOpaque)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 18cf1c8ebaa6..c50296be94f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,10 +1,10 @@
package com.android.systemui.statusbar
+import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.util.DisplayMetrics
-import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -67,7 +67,6 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
- @Mock lateinit var featureFlags: FeatureFlags
@Mock lateinit var depthController: NotificationShadeDepthController
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@Mock lateinit var expandHelperCallback: ExpandHelper.Callback
@@ -92,11 +91,10 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
displayMetrics = displayMetrics,
mediaHierarchyManager = mediaHierarchyManager,
scrimController = scrimController,
- featureFlags = featureFlags,
+ depthController = depthController,
context = context,
configurationController = configurationController,
- falsingManager = falsingManager,
- depthController = depthController
+ falsingManager = falsingManager
)
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 8e949e7d1e37..8b7c76a9727a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -44,6 +44,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
import dagger.Lazy;
@SmallTest
@@ -80,7 +82,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext,
mLockscreenUserManager, mSmartReplyController, mEntryManager,
- () -> mock(StatusBar.class),
+ () -> Optional.of(mock(StatusBar.class)),
mStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
@@ -265,7 +267,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
- Lazy<StatusBar> statusBarLazy,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -276,7 +278,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
lockscreenUserManager,
smartReplyController,
notificationEntryManager,
- statusBarLazy,
+ statusBarOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 7cbc4e4e2c62..659d96e92f34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -51,6 +51,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -90,7 +92,7 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mRemoteInputManager = new NotificationRemoteInputManager(mContext,
mock(NotificationLockscreenUserManager.class), mSmartReplyController,
- mNotificationEntryManager, () -> mock(StatusBar.class),
+ mNotificationEntryManager, () -> Optional.of(mock(StatusBar.class)),
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 85ec3faa4013..f2671b763b56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -22,7 +22,7 @@ import android.view.WindowManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 116f807a888d..756e9844e6c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -41,7 +41,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.FakeExecution
@@ -389,7 +389,6 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
verify(userTracker).removeCallback(userListener)
verify(contentResolver).unregisterContentObserver(settingsObserver)
verify(configurationController).removeCallback(configChangeListener)
- verify(statusBarStateController).removeCallback(statusBarStateListener)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 1be14b66e968..8a32ce6c4f43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -64,7 +64,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 9a5482c33501..39d794dc0bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -67,7 +67,7 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.LogBufferEulogizer;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
new file mode 100644
index 000000000000..01e4cce0cc30
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/CommunalCoordinatorTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@SmallTest
+public class CommunalCoordinatorTest extends SysuiTestCase {
+ @Mock
+ CommunalStateController mCommunalStateController;
+ @Mock
+ NotificationEntryManager mNotificationEntryManager;
+ @Mock
+ NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+ @Mock
+ NotifPipeline mNotifPipeline;
+ @Mock
+ NotificationEntry mNotificationEntry;
+ @Mock
+ Pluggable.PluggableListener mFilterListener;
+
+ CommunalCoordinator mCoordinator;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mCoordinator = new CommunalCoordinator(mNotificationEntryManager,
+ mNotificationLockscreenUserManager, mCommunalStateController);
+ }
+
+ @Test
+ public void testNotificationSuppressionInCommunal() {
+ mCoordinator.attach(mNotifPipeline);
+ final ArgumentCaptor<CommunalStateController.Callback> stateCallbackCaptor =
+ ArgumentCaptor.forClass(CommunalStateController.Callback.class);
+ verify(mCommunalStateController).addCallback(stateCallbackCaptor.capture());
+
+ final CommunalStateController.Callback stateCallback = stateCallbackCaptor.getValue();
+
+ final ArgumentCaptor<NotifFilter> filterCaptor =
+ ArgumentCaptor.forClass(NotifFilter.class);
+ verify(mNotifPipeline).addPreGroupFilter(filterCaptor.capture());
+
+ final NotifFilter filter = filterCaptor.getValue();
+
+ // Verify that notifications are not filtered out by default.
+ assert (!filter.shouldFilterOut(mNotificationEntry, 0));
+
+ filter.setInvalidationListener(mFilterListener);
+
+ // Verify that notifications are filtered out when communal is showing and that the filter
+ // pipeline is notified.
+ stateCallback.onCommunalViewShowingChanged();
+ verify(mFilterListener).onPluggableInvalidated(any());
+ verify(mNotificationEntryManager).updateNotifications(any());
+ assert (filter.shouldFilterOut(mNotificationEntry, 0));
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
index 7e771cecaf66..a3569e4060d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -31,7 +31,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -72,7 +71,6 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
@Mock private HeadsUpViewBinder mHeadsUpViewBinder;
@Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private RemoteInputController mRemoteInputController;
@Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
@Mock private NodeController mHeaderController;
@@ -81,7 +79,6 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mCoordinator = new HeadsUpCoordinator(
mHeadsUpManager,
@@ -215,7 +212,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase {
// WHEN mEntry is removed from the notification collection
mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
- when(mRemoteInputController.isSpinning(any())).thenReturn(false);
+ when(mRemoteInputManager.isSpinning(any())).thenReturn(false);
// THEN heads up manager should remove the entry
verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
new file mode 100644
index 000000000000..24a0ad3de196
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import static android.view.DragEvent.ACTION_DRAG_STARTED;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.DragEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
+
+ private ExpandableNotificationRow mRow;
+ private ExpandableNotificationRow mGroupRow;
+ private ExpandableNotificationRowDragController mController;
+ private NotificationTestHelper mNotificationTestHelper;
+
+ private NotificationGutsManager mGutsManager = mock(NotificationGutsManager.class);
+ private HeadsUpManager mHeadsUpManager = mock(HeadsUpManager.class);
+ private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
+ private NotificationMenuRowPlugin.MenuItem mMenuItem =
+ mock(NotificationMenuRowPlugin.MenuItem.class);
+
+ @Before
+ public void setUp() throws Exception {
+ allowTestableLooperAsMainThread();
+
+ mDependency.injectMockDependency(ShadeController.class);
+
+ mNotificationTestHelper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ mRow = mNotificationTestHelper.createRow();
+ mGroupRow = mNotificationTestHelper.createGroup(4);
+ when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
+
+ mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager);
+ }
+
+ @Test
+ public void testDoStartDragHeadsUpNotif_startDragAndDrop() throws Exception {
+ ExpandableNotificationRowDragController controller = createSpyController();
+ mRow.setDragController(controller);
+ mRow.setHeadsUp(true);
+ mRow.setPinned(true);
+
+ mRow.doLongClickCallback(0, 0);
+ mRow.doDragCallback(0, 0);
+ verify(controller).startDragAndDrop(mRow);
+
+ // Simulate the drag start
+ mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
+ null, null, false));
+ verify(mHeadsUpManager, times(1)).releaseAllImmediately();
+ }
+
+ @Test
+ public void testDoStartDragNotif() throws Exception {
+ ExpandableNotificationRowDragController controller = createSpyController();
+ mRow.setDragController(controller);
+
+ mDependency.get(ShadeController.class).instantExpandNotificationsPanel();
+ mRow.doDragCallback(0, 0);
+ verify(controller).startDragAndDrop(mRow);
+
+ // Simulate the drag start
+ mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
+ null, null, false));
+ verify(mDependency.get(ShadeController.class)).animateCollapsePanels(0, true);
+ }
+
+ private ExpandableNotificationRowDragController createSpyController() {
+ return spy(mController);
+ }
+}
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 cea49b71f009..4562e4f5954d 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
@@ -47,11 +47,11 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -265,7 +265,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
new FalsingManagerFake(),
new FalsingCollectorFake(),
mPeopleNotificationIdentifier,
- Optional.of(mock(BubblesManager.class))
+ Optional.of(mock(BubblesManager.class)),
+ mock(ExpandableNotificationRowDragController.class)
));
when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 9f537f5b6afc..fc44669c5642 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -91,7 +91,6 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
@@ -99,8 +98,6 @@ import org.mockito.junit.MockitoRule;
import java.util.Optional;
-import javax.inject.Provider;
-
/**
* Tests for {@link NotificationGutsManager}.
*/
@@ -157,11 +154,12 @@ public class NotificationGutsManagerTest extends SysuiTestCase {
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext,
- () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider,
- mINotificationManager, mNotificationEntryManager, mPeopleSpaceWidgetManager,
- mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker,
- mAssistantFeedbackController, Optional.of(mBubblesManager),
- new UiEventLoggerFake(), mOnUserInteractionCallback, mShadeController);
+ () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager,
+ mHighPriorityProvider, mINotificationManager, mNotificationEntryManager,
+ mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
+ mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
+ Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
+ mShadeController);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 0772c03d10d0..d3c1dc9db218 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -37,14 +37,12 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.util.DeviceConfigProxy;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashSet;
@@ -59,8 +57,6 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
- @Mock
- private KeyguardBypassController mBypassController;
private float mSmallRadiusRatio;
@Before
@@ -70,7 +66,6 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
/ resources.getDimension(R.dimen.notification_corner_radius);
mRoundnessManager = new NotificationRoundnessManager(
- mBypassController,
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(
@@ -90,6 +85,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
createSection(null, null)
});
mRoundnessManager.setExpanded(1.0f, 1.0f);
+ mRoundnessManager.setShouldRoundPulsingViews(true);
reset(mRoundnessCallback);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f376e88b2cb1..07ebaea8bdda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -45,11 +46,11 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -92,7 +93,7 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class NotificationStackScrollerControllerTest extends SysuiTestCase {
+public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@@ -128,7 +129,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
@Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
@Mock private LayoutInflater mLayoutInflater;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private RemoteInputController mRemoteInputController;
@Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private ShadeController mShadeController;
@@ -145,7 +145,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
when(mFgServicesSectionController.createView(mLayoutInflater))
.thenReturn(mForegroundServiceDungeonView);
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
mController = new NotificationStackScrollLayoutController(
true,
@@ -232,16 +231,15 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
-
- true /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ true);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- true /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ true);
}
@Test
@@ -257,15 +255,42 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- true /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
setupShowEmptyShadeViewState(stateListener, false);
reset(mNotificationStackScrollLayout);
mController.updateShowEmptyShadeView();
verify(mNotificationStackScrollLayout).updateEmptyShadeView(
- false /* visible */,
- false /* notifVisibleInShade */);
+ /* visible= */ false,
+ /* notifVisibleInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mSysuiStatusBarStateController).addCallback(
+ mStateListenerArgumentCaptor.capture(), anyInt());
+ StatusBarStateController.StateListener stateListener =
+ mStateListenerArgumentCaptor.getValue();
+ when(mNotificationStackScrollLayout.isUsingSplitNotificationShade()).thenReturn(true);
+ stateListener.onStateChanged(SHADE);
+ mController.getView().removeAllViews();
+
+ mController.setQsExpanded(false);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
+
+ mController.setQsExpanded(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* notifVisibleInShade= */ false);
}
@Test
@@ -376,6 +401,19 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase {
any(ForegroundServiceDungeonView.class));
}
+ @Test
+ public void testUpdateFooter_remoteInput() {
+ ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
+ ArgumentCaptor.forClass(RemoteInputController.Callback.class);
+ doNothing().when(mRemoteInputManager).addControllerCallback(callbackCaptor.capture());
+ when(mRemoteInputManager.isRemoteInputActive()).thenReturn(false);
+ mController.attach(mNotificationStackScrollLayout);
+ verify(mNotificationStackScrollLayout).setIsRemoteInputActive(false);
+ RemoteInputController.Callback callback = callbackCaptor.getValue();
+ callback.onRemoteInputActive(true);
+ verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true);
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b03df880f0ba..4e76b1695f0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -52,18 +52,14 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -98,17 +94,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@Mock private ExpandHelper mExpandHelper;
@Mock private EmptyShadeView mEmptyShadeView;
- @Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private RemoteInputController mRemoteInputController;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
- @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@Mock private KeyguardBypassController mBypassController;
@Mock private NotificationSectionsManager mNotificationSectionsManager;
@Mock private NotificationSection mNotificationSection;
- @Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
- @Mock private FeatureFlags mFeatureFlags;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Before
@@ -131,7 +122,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
new NotificationSection[]{
mNotificationSection
});
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
// Interact with real instance of AmbientState.
mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController);
@@ -148,10 +138,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mGroupMembershipManger,
mGroupExpansionManager,
mAmbientState,
- mFeatureFlags,
mUnlockedScreenOffAnimationController);
- mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
- mNotificationSwipeHelper);
+ mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController);
mStackScroller.setStatusBar(mBar);
@@ -159,7 +147,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
when(mStackScrollLayoutController.getNoticationRoundessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
- mStackScroller.setRemoteInputManager(mRemoteInputManager);
// Stub out functionality that isn't necessary to test.
doNothing().when(mBar)
@@ -233,21 +220,24 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
@UiThreadTest
public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
final int[] expectedStackHeight = {0};
mStackScroller.addOnExpandedHeightChangedListener((expandedHeight, appear) -> {
assertWithMessage("Given shade enabled: %s",
- mFeatureFlags.isTwoColumnNotificationShadeEnabled())
+ true)
.that(mStackScroller.getHeight())
.isEqualTo(expectedStackHeight[0]);
});
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
expectedStackHeight[0] = 0;
mStackScroller.setExpandedHeight(100f);
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
expectedStackHeight[0] = 100;
mStackScroller.setExpandedHeight(100f);
}
@@ -304,7 +294,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
when(row.canViewBeDismissed()).thenReturn(true);
when(mStackScroller.getChildCount()).thenReturn(1);
when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
- when(mRemoteInputController.isRemoteInputActive()).thenReturn(true);
+ mStackScroller.setIsRemoteInputActive(true);
when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
.thenReturn(true);
when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index bc5307450ed5..e159caebd15c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
@@ -37,9 +38,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -51,6 +53,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.Optional;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -61,6 +65,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private OngoingCallController mOngoingCallController;
private SystemStatusAnimationScheduler mAnimationScheduler;
private StatusBarLocationPublisher mLocationPublisher;
+
// Set in instantiate()
private StatusBarIconController mStatusBarIconController;
private NetworkController mNetworkController;
@@ -69,6 +74,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private final StatusBar mStatusBar = mock(StatusBar.class);
private final CommandQueue mCommandQueue = mock(CommandQueue.class);
+ private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+ private OperatorNameViewController mOperatorNameViewController;
+
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -76,6 +84,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Before
public void setup() {
+ mStatusBarStateController = mDependency
+ .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
when(mStatusBar.getPanelController()).thenReturn(
mock(NotificationPanelViewController.class));
@@ -237,6 +247,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mNetworkController = mock(NetworkController.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mKeyguardStateController = mock(KeyguardStateController.class);
+ mOperatorNameViewController = mock(OperatorNameViewController.class);
+ mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
+ when(mOperatorNameViewControllerFactory.create(any()))
+ .thenReturn(mOperatorNameViewController);
+
setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment(
mOngoingCallController,
@@ -248,10 +263,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mKeyguardStateController,
mNetworkController,
mStatusBarStateController,
- mStatusBar,
- mCommandQueue);
+ () -> Optional.of(mStatusBar),
+ mCommandQueue,
+ mOperatorNameViewControllerFactory);
}
+
private void setUpNotificationIconAreaController() {
mMockNotificationAreaController = mock(NotificationIconAreaController.class);
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 5bf1bb3c573f..7a0b3669991c 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
@@ -38,7 +38,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
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 690b8415762d..2e76bd76e823 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
@@ -38,35 +38,27 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
- private static final boolean HAS_CUSTOM_CLOCK = false;
- private static final boolean HAS_VISIBLE_NOTIFS = false;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition;
- private int mNotificationStackHeight;
private float mPanelExpansion;
private int mKeyguardStatusHeight;
private float mDark;
- private boolean mHasCustomClock;
- private boolean mHasVisibleNotifs;
private float mQsExpansion;
- private int mCutoutTopInset = 0; // in pixels
+ private int mCutoutTopInsetPx = 0;
+ private int mSplitShadeSmartspaceHeightPx = 0;
private boolean mIsSplitShade = false;
@Before
public void setUp() {
mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
mClockPosition = new KeyguardClockPositionAlgorithm.Result();
-
- mHasCustomClock = HAS_CUSTOM_CLOCK;
- mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
}
@Test
public void clockPositionTopOfScreenOnAOD() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -79,11 +71,10 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void clockPositionBelowCutout() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
- mCutoutTopInset = 300;
+ mCutoutTopInsetPx = 300;
// WHEN the clock position algorithm is run
positionClock();
// THEN the clock Y position is below the cutout
@@ -97,7 +88,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void clockPositionAdjustsForKeyguardStatusOnAOD() {
// GIVEN on AOD with a clock of height 100
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 100;
// WHEN the clock position algorithm is run
positionClock();
@@ -112,7 +102,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void clockPositionLargeClockOnAOD() {
// GIVEN on AOD with a full screen clock
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -125,9 +114,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void clockPositionTopOfScreenOnLockScreen() {
- // GIVEN on lock screen with stack scroll and clock of 0 height
+ // GIVEN on lock screen with clock of 0 height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -138,24 +126,9 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
}
@Test
- public void clockPositionWithStackScrollExpandOnLockScreen() {
- // GIVEN on lock screen with stack scroll of height 500
- givenLockScreen();
- mNotificationStackHeight = 500;
- mKeyguardStatusHeight = EMPTY_HEIGHT;
- // WHEN the clock position algorithm is run
- positionClock();
- // THEN the clock Y position stays to the top
- assertThat(mClockPosition.clockY).isEqualTo(0);
- // AND the clock is positioned on the left.
- assertThat(mClockPosition.clockX).isEqualTo(0);
- }
-
- @Test
public void clockPositionWithPartialDragOnLockScreen() {
// GIVEN dragging up on lock screen
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.5f;
// WHEN the clock position algorithm is run
@@ -171,7 +144,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void clockPositionWithFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
@@ -184,7 +156,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void largeClockOnLockScreenIsTransparent() {
// GIVEN on lock screen with a full screen clock
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the clock position algorithm is run
positionClock();
@@ -194,9 +165,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void notifPositionTopOfScreenOnAOD() {
- // GIVEN on AOD and both stack scroll and clock have 0 height
+ // GIVEN on AOD and clock has 0 height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -208,7 +178,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
// GIVEN on AOD and clock has a nonzero height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 100;
// WHEN the position algorithm is run
positionClock();
@@ -220,7 +189,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPositionWithLargeClockOnAOD() {
// GIVEN on AOD and clock has a nonzero height
givenAOD();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -230,9 +198,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void notifPositionMiddleOfScreenOnLockScreen() {
- // GIVEN on lock screen and both stack scroll and clock have 0 height
+ // GIVEN on lock screen and clock has 0 height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -241,58 +208,42 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
}
@Test
- public void notifPositionAdjustsForStackHeightOnLockScreen() {
- // GIVEN on lock screen and stack scroller has a nonzero height
- givenLockScreen();
- mNotificationStackHeight = 500;
- mKeyguardStatusHeight = EMPTY_HEIGHT;
- // WHEN the position algorithm is run
- positionClock();
- // THEN the notif padding adjusts for keyguard status height
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
- }
-
- @Test
public void notifPositionAdjustsForClockHeightOnLockScreen() {
// GIVEN on lock screen and stack scroller has a nonzero height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
- // THEN the notif padding adjusts for both clock and notif stack.
+
assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
}
@Test
- public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
- // GIVEN on lock screen and stack scroller has a nonzero height
+ public void notifPositionAlignedWithClockInSplitShadeMode() {
givenLockScreen();
- mNotificationStackHeight = 500;
+ mIsSplitShade = true;
mKeyguardStatusHeight = 200;
// WHEN the position algorithm is run
positionClock();
- // THEN the notifs are placed below the statusview
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
+ // THEN the notif padding DOESN'T adjust for keyguard status height.
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
}
@Test
- public void notifPositionAlignedWithClockInSplitShadeMode() {
- // GIVEN on lock screen and split shade mode
+ public void notifPositionAdjustedBySmartspaceHeightInSplitShadeMode() {
givenLockScreen();
+ mSplitShadeSmartspaceHeightPx = 200;
mIsSplitShade = true;
- mHasCustomClock = true;
// WHEN the position algorithm is run
positionClock();
- // THEN the notif padding DOESN'T adjust for keyguard status height.
- assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+
+ assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
}
@Test
public void notifPositionWithLargeClockOnLockScreen() {
// GIVEN on lock screen and clock has a nonzero height
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
// WHEN the position algorithm is run
positionClock();
@@ -304,7 +255,6 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPositionWithFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = EMPTY_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
@@ -317,19 +267,18 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
public void notifPositionWithLargeClockFullDragOnLockScreen() {
// GIVEN the lock screen is dragged up and a full screen clock
givenLockScreen();
- mNotificationStackHeight = EMPTY_HEIGHT;
mKeyguardStatusHeight = SCREEN_HEIGHT;
mPanelExpansion = 0.f;
// WHEN the clock position algorithm is run
positionClock();
- // THEN the notif padding is zero.
+
assertThat(mClockPosition.stackScrollerPadding).isEqualTo(
(int) (mKeyguardStatusHeight * .667f));
}
@Test
public void clockHiddenWhenQsIsExpanded() {
- // GIVEN on the lock screen with a custom clock and visible notifications
+ // GIVEN on the lock screen with visible notifications
givenLockScreen();
mQsExpansion = 1;
// WHEN the clock position algorithm is run
@@ -349,12 +298,12 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
}
private void positionClock() {
- mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
- mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
+ mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT,
+ mPanelExpansion, mKeyguardStatusHeight,
0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
- mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
+ mDark, ZERO_DRAG, false /* bypassEnabled */,
0 /* unlockedStackScrollerPadding */, mQsExpansion,
- mCutoutTopInset, mIsSplitShade);
+ mCutoutTopInsetPx, mSplitShadeSmartspaceHeightPx, mIsSplitShade);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
new file mode 100644
index 000000000000..85e17ba441fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.phone;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.CarrierTextController;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
+ @Mock
+ private CarrierTextController mCarrierTextController;
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private SystemStatusAnimationScheduler mAnimationScheduler;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ private UserInfoController mUserInfoController;
+ @Mock
+ private StatusBarIconController mStatusBarIconController;
+ @Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private BatteryMeterViewController mBatteryMeterViewController;
+
+ private KeyguardStatusBarView mKeyguardStatusBarView;
+ private KeyguardStatusBarViewController mController;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ allowTestableLooperAsMainThread();
+ TestableLooper.get(this).runWithLooper(() -> {
+ mKeyguardStatusBarView =
+ (KeyguardStatusBarView) LayoutInflater.from(mContext)
+ .inflate(R.layout.keyguard_status_bar, null);
+ });
+
+ mController = new KeyguardStatusBarViewController(
+ mKeyguardStatusBarView,
+ mCarrierTextController,
+ mConfigurationController,
+ mAnimationScheduler,
+ mBatteryController,
+ mUserInfoController,
+ mStatusBarIconController,
+ new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags),
+ mBatteryMeterViewController
+ );
+ }
+
+ @Test
+ public void onViewAttached_callbacksRegistered() {
+ mController.onViewAttached();
+
+ verify(mConfigurationController).addCallback(any());
+ verify(mAnimationScheduler).addCallback(any());
+ verify(mUserInfoController).addCallback(any());
+ verify(mStatusBarIconController).addIconGroup(any());
+ }
+
+ @Test
+ public void onViewDetached_callbacksUnregistered() {
+ // Set everything up first.
+ mController.onViewAttached();
+
+ mController.onViewDetached();
+
+ verify(mConfigurationController).removeCallback(any());
+ verify(mAnimationScheduler).removeCallback(any());
+ verify(mUserInfoController).removeCallback(any());
+ verify(mStatusBarIconController).removeIconGroup(any());
+ }
+
+ @Test
+ public void setBatteryListening_true_callbackAdded() {
+ mController.setBatteryListening(true);
+
+ verify(mBatteryController).addCallback(any());
+ }
+
+ @Test
+ public void setBatteryListening_false_callbackRemoved() {
+ // First set to true so that we know setting to false is a change in state.
+ mController.setBatteryListening(true);
+
+ mController.setBatteryListening(false);
+
+ verify(mBatteryController).removeCallback(any());
+ }
+
+ @Test
+ public void setBatteryListening_trueThenTrue_callbackAddedOnce() {
+ mController.setBatteryListening(true);
+ mController.setBatteryListening(true);
+
+ verify(mBatteryController).addCallback(any());
+ }
+
+ @Test
+ public void updateTopClipping_viewClippingUpdated() {
+ int viewTop = 20;
+ mKeyguardStatusBarView.setTop(viewTop);
+ int notificationPanelTop = 30;
+
+ mController.updateTopClipping(notificationPanelTop);
+
+ assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(
+ notificationPanelTop - viewTop);
+ }
+
+ @Test
+ public void setNotTopClipping_viewClippingUpdatedToZero() {
+ // Start out with some amount of top clipping.
+ mController.updateTopClipping(50);
+ assertThat(mKeyguardStatusBarView.getClipBounds().top).isGreaterThan(0);
+
+ mController.setNoTopClipping();
+
+ assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(0);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
new file mode 100644
index 000000000000..3108ed9e7b98
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardStatusBarViewTest extends SysuiTestCase {
+
+ private KeyguardStatusBarView mKeyguardStatusBarView;
+
+ @Before
+ public void setup() throws Exception {
+ allowTestableLooperAsMainThread();
+ TestableLooper.get(this).runWithLooper(() -> {
+ mKeyguardStatusBarView =
+ (KeyguardStatusBarView) LayoutInflater.from(mContext)
+ .inflate(R.layout.keyguard_status_bar, null);
+ });
+ }
+
+ @Test
+ public void setTopClipping_clippingUpdated() {
+ int topClipping = 40;
+
+ mKeyguardStatusBarView.setTopClipping(topClipping);
+
+ assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(topClipping);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index cdfab1eec609..74f08aba9cf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -102,7 +102,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedVisibilities */,
+ null /* packageName */);
assertTrue(mLightsOutNotifController.areLightsOut());
}
@@ -114,7 +115,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedVisibilities */,
+ null /* packageName */);
assertFalse(mLightsOutNotifController.areLightsOut());
}
@@ -144,7 +146,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedVisibilities */,
+ null /* packageName */);
// THEN we should show dot
assertTrue(mLightsOutNotifController.shouldShowDot());
@@ -163,7 +166,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedVisibilities */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
@@ -182,7 +186,8 @@ public class LightsOutNotifControllerTest extends SysuiTestCase {
null /* appearanceRegions */,
false /* navbarColorManagedByIme */,
BEHAVIOR_DEFAULT,
- false /* isFullscreen */);
+ null /* requestedVisibilities */,
+ null /* packageName */);
// THEN we shouldn't show the dot
assertFalse(mLightsOutNotifController.shouldShowDot());
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 d7661be21752..e9d698fd882c 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
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.keyguard.KeyguardClockSwitch.LARGE;
+import static com.android.keyguard.KeyguardClockSwitch.SMALL;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
@@ -35,6 +37,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
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;
@@ -60,6 +63,7 @@ import android.view.ViewPropertyAnimator;
import android.view.ViewStub;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
@@ -85,10 +89,18 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.communal.CommunalHostView;
+import com.android.systemui.communal.CommunalHostViewController;
+import com.android.systemui.communal.CommunalSource;
+import com.android.systemui.communal.CommunalSourceMonitor;
+import com.android.systemui.communal.CommunalStateController;
+import com.android.systemui.communal.dagger.CommunalViewComponent;
import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.idle.IdleHostViewController;
+import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -97,7 +109,6 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -106,11 +117,11 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.statusbar.notification.ConversationNotificationManager;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -121,7 +132,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
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.KeyguardUserSwitcherController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -136,6 +150,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import java.lang.ref.WeakReference;
import java.util.List;
@SmallTest
@@ -167,7 +182,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardStatusBarView mKeyguardStatusBar;
@Mock
- private View mUserSwitcherView;
+ private KeyguardUserSwitcherView mUserSwitcherView;
@Mock
private ViewStub mUserSwitcherStubView;
@Mock
@@ -236,8 +251,22 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
@Mock
+ private KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent;
+ @Mock
+ private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
+ @Mock
private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
@Mock
+ private KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent;
+ @Mock
+ private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
+ @Mock
+ private IdleViewComponent.Factory mIdleViewComponentFactory;
+ @Mock
+ private IdleViewComponent mIdleViewComponent;
+ @Mock
+ private IdleHostViewController mIdleHostViewController;
+ @Mock
private QSDetailDisplayer mQSDetailDisplayer;
@Mock
private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@@ -246,6 +275,21 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock
+ private CommunalViewComponent.Factory mCommunalViewComponentFactory;
+ @Mock
+ private CommunalViewComponent mCommunalViewComponent;
+ @Mock
+ private CommunalHostViewController mCommunalHostViewController;
+ @Mock
+ private CommunalSourceMonitor mCommunalSourceMonitor;
+ @Mock
+ private CommunalSource mCommunalSource;
+ @Mock
+ private CommunalHostView mCommunalHostView;
+ @Mock
+ private CommunalStateController mCommunalStateController;
+ private CommunalStateController.Callback mCommunalStateControllerCallback;
+ @Mock
private KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock
private KeyguardStatusViewController mKeyguardStatusViewController;
@@ -264,8 +308,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private MediaDataManager mMediaDataManager;
@Mock
- private FeatureFlags mFeatureFlags;
- @Mock
private AmbientState mAmbientState;
@Mock
private UserManager mUserManager;
@@ -282,6 +324,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
+ private SplitShadeHeaderController mSplitShadeHeaderController;
+ @Mock
private ContentResolver mContentResolver;
@Mock
private TapAgainViewController mTapAgainViewController;
@@ -296,11 +340,15 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Mock
private NotificationRemoteInputManager mNotificationRemoteInputManager;
@Mock
- private RemoteInputController mRemoteInputController;
- @Mock
private RecordingController mRecordingController;
@Mock
private ControlsComponent mControlsComponent;
+ @Mock
+ private LockscreenSmartspaceController mLockscreenSmartspaceController;
+ @Mock
+ private FrameLayout mSplitShadeSmartspaceContainer;
+ @Mock
+ private LockscreenGestureLogger mLockscreenGestureLogger;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -337,6 +385,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
+ when(mView.findViewById(R.id.communal_host)).thenReturn(mCommunalHostView);
when(mNotificationStackScrollLayout.getController())
.thenReturn(mNotificationStackScrollLayoutController);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
@@ -350,15 +399,26 @@ public class NotificationPanelViewTest extends SysuiTestCase {
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
+ when(mView.findViewById(R.id.split_shade_smartspace_container))
+ .thenReturn(mSplitShadeSmartspaceContainer);
mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
mNotificationContainerParent.addView(mKeyguardStatusView);
+ mNotificationContainerParent.onFinishInflate();
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
mDisplayMetrics);
+ when(mKeyguardQsUserSwitchComponentFactory.build(any()))
+ .thenReturn(mKeyguardQsUserSwitchComponent);
+ when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController())
+ .thenReturn(mKeyguardQsUserSwitchController);
+ when(mKeyguardUserSwitcherComponentFactory.build(any()))
+ .thenReturn(mKeyguardUserSwitcherComponent);
+ when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
+ .thenReturn(mKeyguardUserSwitcherController);
doAnswer((Answer<Void>) invocation -> {
mTouchHandler = invocation.getArgument(0);
@@ -393,12 +453,21 @@ public class NotificationPanelViewTest extends SysuiTestCase {
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
+ when(mCommunalViewComponentFactory.build(any()))
+ .thenReturn(mCommunalViewComponent);
+ when(mCommunalViewComponent.getCommunalHostViewController())
+ .thenReturn(mCommunalHostViewController);
+ when(mIdleViewComponentFactory.build(any()))
+ .thenReturn(mIdleViewComponent);
+ when(mIdleViewComponent.getIdleHostViewController())
+ .thenReturn(mIdleHostViewController);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
.thenReturn(mKeyguardStatusView);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean()))
+ .thenReturn(mUserSwitcherView);
when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
.thenReturn(mKeyguardBottomArea);
- when(mNotificationRemoteInputManager.getController()).thenReturn(mRemoteInputController);
- when(mRemoteInputController.isRemoteInputActive()).thenReturn(false);
+ when(mNotificationRemoteInputManager.isRemoteInputActive()).thenReturn(false);
reset(mView);
@@ -409,10 +478,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager,
- mKeyguardStateController, mStatusBarStateController, mDozeLog,
- mDozeParameters, mCommandQueue, mVibratorHelper,
+ mCommunalStateController, mKeyguardStateController, mStatusBarStateController,
+ mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
- mMetricsLogger, mActivityManager, mConfigurationController,
+ mCommunalSourceMonitor, mMetricsLogger, mActivityManager, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mBiometricUnlockController, mStatusBarKeyguardViewManager,
@@ -421,6 +490,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mKeyguardQsUserSwitchComponentFactory,
mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory,
+ mCommunalViewComponentFactory,
+ mIdleViewComponentFactory,
mLockscreenShadeTransitionController,
mQSDetailDisplayer,
mGroupManager,
@@ -432,7 +503,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mNotificationShadeDepthController,
mAmbientState,
mLockIconViewController,
- mFeatureFlags,
mKeyguardMediaController,
mPrivacyDotViewController,
mTapAgainViewController,
@@ -443,7 +513,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mRecordingController,
new FakeExecutor(new FakeSystemClock()),
mSecureSettings,
+ mSplitShadeHeaderController,
+ mLockscreenSmartspaceController,
mUnlockedScreenOffAnimationController,
+ mLockscreenGestureLogger,
mNotificationRemoteInputManager,
mControlsComponent);
mNotificationPanelViewController.initDependencies(
@@ -558,7 +631,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testAllChildrenOfNotificationContainer_haveIds() {
- enableSplitShade();
+ enableSplitShade(/* enabled= */ true);
mNotificationContainerParent.removeAllViews();
mNotificationContainerParent.addView(newViewWithId(1));
mNotificationContainerParent.addView(newViewWithId(View.NO_ID));
@@ -571,7 +644,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testSinglePaneShadeLayout_isAlignedToParent() {
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ enableSplitShade(/* enabled= */ false);
mNotificationPanelViewController.updateResources();
@@ -582,17 +655,19 @@ public class NotificationPanelViewTest extends SysuiTestCase {
}
@Test
- public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() {
- mNotificationPanelViewController.updateResources();
+ public void testKeyguardStatusViewInSplitShade_changesConstraintsDependingOnNotifications() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mNotificationPanelViewController.updateResources();
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(ConstraintSet.PARENT_ID);
+ .isEqualTo(R.id.qs_edge_guideline);
- enableSplitShade();
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mNotificationPanelViewController.updateResources();
-
assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
- .isEqualTo(R.id.qs_edge_guideline);
+ .isEqualTo(ConstraintSet.PARENT_ID);
}
@Test
@@ -629,7 +704,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testSplitShadeLayout_isAlignedToGuideline() {
- enableSplitShade();
+ enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.updateResources();
@@ -641,7 +716,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testSinglePaneShadeLayout_childrenHaveConstantWidth() {
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false);
+ enableSplitShade(/* enabled= */ false);
mNotificationPanelViewController.updateResources();
@@ -653,7 +728,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testSplitShadeLayout_childrenHaveZeroWidth() {
- enableSplitShade();
+ enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.updateResources();
@@ -665,7 +740,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
public void testOnDragDownEvent_horizontalTranslationIsZeroForSplitShade() {
when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f);
when(mView.getWidth()).thenReturn(800);
- enableSplitShade();
+ enableSplitShade(/* enabled= */ true);
onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN,
200f /* x position */, 0f, 0));
@@ -699,7 +774,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testCanCollapsePanelOnTouch_falseInDualPaneShade() {
mStatusBarStateController.setState(SHADE);
- enableSplitShade();
+ enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.setQsExpanded(true);
assertThat(mNotificationPanelViewController.canCollapsePanelOnTouch()).isFalse();
@@ -753,6 +828,109 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mTapAgainViewController).show();
}
+ @Test
+ public void testSwitchesToCorrectClockInSinglePaneShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ mNotificationPanelViewController.closeQs();
+ verify(mKeyguardStatusViewController).displayClock(SMALL);
+ }
+
+ @Test
+ public void testSwitchesToCorrectClockInSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ }
+
+ @Test
+ public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+
+ // one notification + media player visible
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController).displayClock(SMALL);
+
+ // only media player visible
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+ triggerPositionClockAndNotifications();
+ verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL);
+ verify(mKeyguardStatusViewController, never()).displayClock(LARGE);
+ }
+
+ @Test
+ public void testCommunalhostViewControllerInit() {
+ clearInvocations(mCommunalHostViewController);
+ givenViewAttached();
+ verify(mCommunalHostViewController).init();
+ }
+
+ @Test
+ public void testCommunalSourceListening() {
+ final ArgumentCaptor<CommunalSourceMonitor.Callback> monitorCallback =
+ ArgumentCaptor.forClass(CommunalSourceMonitor.Callback.class);
+
+ givenViewAttached();
+ verify(mCommunalSourceMonitor).addCallback(monitorCallback.capture());
+
+ final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
+ ArgumentCaptor.forClass(WeakReference.class);
+
+ monitorCallback.getValue().onSourceAvailable(new WeakReference<>(mCommunalSource));
+ verify(mCommunalHostViewController).show(sourceCapture.capture());
+ assertThat(sourceCapture.getValue().get()).isEqualTo(mCommunalSource);
+
+ clearInvocations(mCommunalHostViewController);
+ givenViewDetached();
+ verify(mCommunalSourceMonitor).removeCallback(any());
+ verify(mCommunalHostViewController).show(sourceCapture.capture());
+ assertThat(sourceCapture.getValue()).isEqualTo(null);
+ }
+
+ @Test
+ public void testKeyguardStatusViewUpdatedWithCommunalPresence() {
+ givenViewAttached();
+
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+ updateMultiUserSetting(true);
+
+ ArgumentCaptor<CommunalStateController.Callback> communalCallbackCapture =
+ ArgumentCaptor.forClass(CommunalStateController.Callback.class);
+ verify(mCommunalStateController).addCallback(communalCallbackCapture.capture());
+ final CommunalStateController.Callback communalStateControllerCallback =
+ communalCallbackCapture.getValue();
+
+ clearInvocations(mKeyguardStatusViewController, mKeyguardUserSwitcherController);
+ // Ensure changes in communal visibility leads to setting the keyguard status view
+ // visibility.
+ communalStateControllerCallback.onCommunalViewShowingChanged();
+ verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility(anyInt(),
+ anyBoolean(), anyBoolean(), anyInt());
+ verify(mKeyguardUserSwitcherController).setKeyguardUserSwitcherVisibility(anyInt(),
+ anyBoolean(), anyBoolean(), anyInt());
+ }
+
+ private void triggerPositionClockAndNotifications() {
+ mNotificationPanelViewController.closeQs();
+ }
+
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
@@ -767,6 +945,13 @@ public class NotificationPanelViewTest extends SysuiTestCase {
}
}
+ private void givenViewDetached() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewDetachedFromWindow(mView);
+ }
+ }
+
+
private View newViewWithId(int id) {
View view = new View(mContext);
view.setId(id);
@@ -783,9 +968,8 @@ public class NotificationPanelViewTest extends SysuiTestCase {
return constraintSet.getConstraint(id).layout;
}
- private void enableSplitShade() {
- when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
- when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+ private void enableSplitShade(boolean enabled) {
+ when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled);
mNotificationPanelViewController.updateResources();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index ddd78541d113..90b8a74d88be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -146,7 +146,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mNotificationShadeWindowController.attach();
clearInvocations(mWindowManager);
- mNotificationShadeWindowController.setLightRevealScrimAmount(0f);
+ mNotificationShadeWindowController.setLightRevealScrimOpaque(true);
verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) == 0).isTrue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 6c1a3c90d83d..bcc257dfa3d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -16,6 +16,11 @@
package com.android.systemui.statusbar.phone;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -55,6 +60,8 @@ import com.android.systemui.util.InjectionInflationController;
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;
@@ -91,6 +98,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
+ mInteractionEventHandlerCaptor;
+ private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -147,4 +158,49 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
verify(mDragDownHelper).onTouchEvent(ev);
ev.recycle();
}
+
+ @Test
+ public void testInterceptTouchWhenShowingAltAuth() {
+ captureInteractionEventHandler();
+
+ // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+ when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+ // THEN we should intercept touch
+ assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
+ }
+
+ @Test
+ public void testNoInterceptTouch() {
+ captureInteractionEventHandler();
+
+ // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false);
+ when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+ // THEN we shouldn't intercept touch
+ assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
+ }
+
+ @Test
+ public void testHandleTouchEventWhenShowingAltAuth() {
+ captureInteractionEventHandler();
+
+ // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+ when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+ // THEN we should handle the touch
+ assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class)));
+ }
+
+ private void captureInteractionEventHandler() {
+ verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture());
+ mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue();
+
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
new file mode 100644
index 000000000000..d63730d596d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+
+ private lateinit var view: PhoneStatusBarView
+ private lateinit var controller: PhoneStatusBarViewController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ view = PhoneStatusBarView(mContext, null)
+ controller = PhoneStatusBarViewController(view, commandQueue)
+ }
+
+ @Test
+ fun constructor_setsPanelEnabledProviderOnView() {
+ var providerUsed = false
+ `when`(commandQueue.panelsEnabled()).then {
+ providerUsed = true
+ true
+ }
+
+ // If the constructor correctly set a [PanelEnabledProvider], then it should be used
+ // when [PhoneStatusBarView.panelEnabled] is called.
+ view.panelEnabled()
+
+ assertThat(providerUsed).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
new file mode 100644
index 000000000000..49ab6ebbde93
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class PhoneStatusBarViewTest : SysuiTestCase() {
+
+ private lateinit var view: PhoneStatusBarView
+
+ @Before
+ fun setUp() {
+ view = PhoneStatusBarView(mContext, null)
+ }
+
+ @Test
+ fun panelEnabled_providerReturnsTrue_returnsTrue() {
+ view.setPanelEnabledProvider { true }
+
+ assertThat(view.panelEnabled()).isTrue()
+ }
+
+ @Test
+ fun panelEnabled_providerReturnsFalse_returnsFalse() {
+ view.setPanelEnabledProvider { false }
+
+ assertThat(view.panelEnabled()).isFalse()
+ }
+
+ @Test
+ fun panelEnabled_noProvider_noCrash() {
+ view.panelEnabled()
+ // No assert needed, just testing no crash
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 678b193073c2..195390347d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -83,7 +83,6 @@ public class ScrimControllerTest extends SysuiTestCase {
private ScrimView mScrimBehind;
private ScrimView mNotificationsScrim;
private ScrimView mScrimInFront;
- private ScrimView mScrimForBubble;
private ScrimState mScrimState;
private float mScrimBehindAlpha;
private GradientColors mScrimInFrontColor;
@@ -167,7 +166,6 @@ public class ScrimControllerTest extends SysuiTestCase {
endAnimation(mNotificationsScrim);
endAnimation(mScrimBehind);
endAnimation(mScrimInFront);
- endAnimation(mScrimForBubble);
assertEquals("Animators did not finish",
mAnimatorListener.getNumStarts(), mAnimatorListener.getNumEnds());
@@ -190,7 +188,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimBehind = spy(new ScrimView(getContext()));
mScrimInFront = new ScrimView(getContext());
- mScrimForBubble = new ScrimView(getContext());
mNotificationsScrim = new ScrimView(getContext());
mAlwaysOnEnabled = true;
mLooper = TestableLooper.get(this);
@@ -226,8 +223,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mUnlockedScreenOffAnimationController);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
- mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront,
- mScrimForBubble);
+ mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
mScrimController.setHasBackdrop(false);
@@ -257,8 +253,8 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false));
+ mScrimBehind, true
+ ));
}
@Test
@@ -274,8 +270,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, false,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -293,8 +288,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, false,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -309,8 +303,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
@@ -329,8 +322,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -369,8 +361,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -389,8 +380,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -510,8 +500,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
// ... and when ambient goes dark, front scrim should be semi-transparent
@@ -549,8 +538,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, false,
mScrimBehind, false,
- mNotificationsScrim, false,
- mScrimForBubble, false
+ mNotificationsScrim, false
));
}
@@ -570,8 +558,50 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mScrimInFront, false,
mScrimBehind, true,
- mNotificationsScrim, false,
- mScrimForBubble, false
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
+ public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(true);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(false);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, false,
+ mNotificationsScrim, false
+ ));
+ }
+
+ @Test
+ public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
+ mScrimController.setClipsQsScrim(false);
+ mScrimController.transitionTo(ScrimState.BOUNCER);
+
+ mScrimController.setClipsQsScrim(true);
+
+ finishAnimationsImmediately();
+ // Front scrim should be transparent
+ // Back scrim should be clipping QS
+ // Notif scrim should be visible without tint
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, OPAQUE,
+ mScrimBehind, OPAQUE));
+ assertScrimTinted(Map.of(
+ mScrimInFront, false,
+ mScrimBehind, true,
+ mNotificationsScrim, false
));
}
@@ -584,8 +614,7 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimBehind, TRANSPARENT));
assertScrimTinted(Map.of(
mScrimInFront, false,
- mScrimBehind, false,
- mScrimForBubble, false
+ mScrimBehind, false
));
}
@@ -602,8 +631,7 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimTinted(Map.of(
mNotificationsScrim, false,
mScrimInFront, false,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
// Back scrim should be visible after start dragging
@@ -614,27 +642,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimBehind, SEMI_TRANSPARENT));
}
- @Test
- public void transitionToBubbleExpanded() {
- mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
- finishAnimationsImmediately();
-
- assertScrimTinted(Map.of(
- mScrimInFront, false,
- mScrimBehind, false,
- mScrimForBubble, true
- ));
-
- // Front scrim should be transparent
- assertEquals(ScrimController.TRANSPARENT,
- mScrimInFront.getViewAlpha(), 0.0f);
- // Back scrim should be visible
- assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
- mScrimBehind.getViewAlpha(), 0.0f);
- // Bubble scrim should be visible
- assertEquals(ScrimController.BUBBLE_SCRIM_ALPHA,
- mScrimForBubble.getViewAlpha(), 0.0f);
- }
@Test
public void scrimStateCallback() {
@@ -744,8 +751,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// Immediately tinted black after the transition starts
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, true
+ mScrimBehind, true
));
finishAnimationsImmediately();
@@ -753,14 +759,12 @@ public class ScrimControllerTest extends SysuiTestCase {
// All scrims should be transparent at the end of fade transition.
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, TRANSPARENT,
- mScrimForBubble, TRANSPARENT));
+ mScrimBehind, TRANSPARENT));
// Make sure at the very end of the animation, we're reset to transparent
assertScrimTinted(Map.of(
mScrimInFront, false,
- mScrimBehind, true,
- mScrimForBubble, false
+ mScrimBehind, true
));
}
@@ -778,8 +782,7 @@ public class ScrimControllerTest extends SysuiTestCase {
+ mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
assertScrimTinted(Map.of(
mScrimInFront, true,
- mScrimBehind, true,
- mScrimForBubble, true
+ mScrimBehind, true
));
Assert.assertSame("Scrim should be visible during transition.",
mScrimVisibility, OPAQUE);
@@ -1032,8 +1035,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimInFront.getDefaultFocusHighlightEnabled());
Assert.assertFalse("Scrim shouldn't have focus highlight",
mScrimBehind.getDefaultFocusHighlightEnabled());
- Assert.assertFalse("Scrim shouldn't have focus highlight",
- mScrimForBubble.getDefaultFocusHighlightEnabled());
}
@Test
@@ -1043,7 +1044,7 @@ public class ScrimControllerTest extends SysuiTestCase {
HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
- ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
+ ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
for (ScrimState state : ScrimState.values()) {
if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
@@ -1138,6 +1139,19 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
+ public void testNotificationTransparency_unnocclusion() {
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ mScrimController.setUnocclusionAnimationRunning(true);
+
+ assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 0.0f);
+ assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.0f, /* expansion */ 1.0f);
+
+ // Verify normal behavior after
+ mScrimController.setUnocclusionAnimationRunning(false);
+ assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.2f, /* expansion */ 0.4f);
+ }
+
+ @Test
public void testNotificationTransparency_inKeyguardState() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1196,21 +1210,16 @@ public class ScrimControllerTest extends SysuiTestCase {
return "behind";
} else if (scrim == mNotificationsScrim) {
return "notifications";
- } else if (scrim == mScrimForBubble) {
- return "bubble";
}
return "unknown_scrim";
}
/**
- * If {@link #mScrimForBubble} or {@link #mNotificationsScrim} is not passed in the map
+ * If {@link #mNotificationsScrim} is not passed in the map
* we assume it must be transparent
*/
private void assertScrimAlpha(Map<ScrimView, Integer> scrimToAlpha) {
// Check single scrim visibility.
- if (!scrimToAlpha.containsKey(mScrimForBubble)) {
- assertScrimAlpha(mScrimForBubble, TRANSPARENT);
- }
if (!scrimToAlpha.containsKey(mNotificationsScrim)) {
assertScrimAlpha(mNotificationsScrim, TRANSPARENT);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
new file mode 100644
index 000000000000..52538c7f5496
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.phone;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.StatusBarManager;
+import android.os.PowerManager;
+import android.os.Vibrator;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
+ @Mock private StatusBar mStatusBar;
+ @Mock private ShadeController mShadeController;
+ @Mock private CommandQueue mCommandQueue;
+ @Mock private NotificationPanelViewController mNotificationPanelViewController;
+ @Mock private LegacySplitScreen mLegacySplitScreen;
+ @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
+ @Mock private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private AssistManager mAssistManager;
+ @Mock private DozeServiceHost mDozeServiceHost;
+ @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
+ @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+ @Mock private PowerManager mPowerManager;
+ @Mock private VibratorHelper mVibratorHelper;
+ @Mock private Vibrator mVibrator;
+ @Mock private LightBarController mLightBarController;
+
+ StatusBarCommandQueueCallbacks mSbcqCallbacks;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mSbcqCallbacks = new StatusBarCommandQueueCallbacks(
+ mStatusBar,
+ mContext,
+ mContext.getResources(),
+ mShadeController,
+ mCommandQueue,
+ mNotificationPanelViewController,
+ Optional.of(mLegacySplitScreen),
+ mRemoteInputQuickSettingsDisabler,
+ mMetricsLogger,
+ mKeyguardUpdateMonitor,
+ mKeyguardStateController,
+ mHeadsUpManager,
+ mWakefulnessLifecycle,
+ mDeviceProvisionedController,
+ mStatusBarKeyguardViewManager,
+ mAssistManager,
+ mDozeServiceHost,
+ mStatusBarStateController,
+ mNotificationShadeWindowView,
+ mNotificationStackScrollLayoutController,
+ mPowerManager,
+ mVibratorHelper,
+ Optional.of(mVibrator),
+ mLightBarController,
+ DEFAULT_DISPLAY);
+
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+ when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
+ .thenAnswer((Answer<Integer>) invocation -> invocation.getArgument(0));
+ }
+
+ @Test
+ public void testDisableNotificationShade() {
+ when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCommandQueue.panelsEnabled()).thenReturn(false);
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+
+ verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mShadeController).animateCollapsePanels();
+
+ // Trying to open it does nothing.
+ mSbcqCallbacks.animateExpandNotificationsPanel();
+ verify(mNotificationPanelViewController, never()).expandWithoutQs();
+ mSbcqCallbacks.animateExpandSettingsPanel(null);
+ verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+ }
+
+ @Test
+ public void testEnableNotificationShade() {
+ when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+ when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NONE, false);
+ verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mShadeController, never()).animateCollapsePanels();
+
+ // Can now be opened.
+ mSbcqCallbacks.animateExpandNotificationsPanel();
+ verify(mNotificationPanelViewController).expandWithoutQs();
+ mSbcqCallbacks.animateExpandSettingsPanel(null);
+ verify(mNotificationPanelViewController).expandWithQs();
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_suppress() {
+ mSbcqCallbacks.suppressAmbientDisplay(true);
+ verify(mDozeServiceHost).setDozeSuppressed(true);
+ }
+
+ @Test
+ public void testSuppressAmbientDisplay_unsuppress() {
+ mSbcqCallbacks.suppressAmbientDisplay(false);
+ verify(mDozeServiceHost).setDozeSuppressed(false);
+ }
+
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index e3263d4ca6b3..0f1c40bacb7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -30,8 +30,8 @@ import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarMobileView;
import com.android.systemui.statusbar.StatusBarWifiView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c39a9061f95d..38f36bfa8cfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -64,6 +64,8 @@ import org.mockito.MockitoAnnotations;
import java.util.Optional;
+import dagger.Lazy;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -103,6 +105,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private Lazy<ShadeController> mShadeController;
private WakefulnessLifecycle mWakefulnessLifecycle;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -133,7 +137,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mKeyguardBouncerFactory,
mWakefulnessLifecycle,
mUnlockedScreenOffAnimationController,
- mKeyguardMessageAreaFactory);
+ mKeyguardMessageAreaFactory,
+ mShadeController);
mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
mNotificationPanelView, mBiometrucUnlockController,
mNotificationContainer, mBypassController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 37a6d21b690e..72a3d664a6ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -53,15 +53,14 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationClickNotifier;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -111,8 +110,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Mock
private NotificationRemoteInputManager mRemoteInputManager;
@Mock
- private RemoteInputController mRemoteInputController;
- @Mock
private StatusBar mStatusBar;
@Mock
private KeyguardStateController mKeyguardStateController;
@@ -153,8 +150,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-
when(mContentIntent.isActivity()).thenReturn(true);
when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1));
when(mContentIntent.getIntent()).thenReturn(mContentIntentInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index ce45f2618f2b..c80c07249cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -31,9 +31,9 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
@@ -46,13 +46,11 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -60,6 +58,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
@@ -67,14 +66,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import java.util.ArrayList;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper()
public class StatusBarNotificationPresenterTest extends SysuiTestCase {
-
-
private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
mock(NotificationInterruptStateProvider.class);
@@ -87,29 +82,16 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
@Before
public void setup() {
- NotificationRemoteInputManager notificationRemoteInputManager =
- mock(NotificationRemoteInputManager.class);
- when(notificationRemoteInputManager.getController())
- .thenReturn(mock(RemoteInputController.class));
mMetricsLogger = new FakeMetricsLogger();
- mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+ LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger(
+ mMetricsLogger);
mCommandQueue = new CommandQueue(mContext);
mDependency.injectTestDependency(StatusBarStateController.class,
mock(SysuiStatusBarStateController.class));
mDependency.injectTestDependency(ShadeController.class, mShadeController);
- mDependency.injectTestDependency(NotificationRemoteInputManager.class,
- notificationRemoteInputManager);
- mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
- mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
- mDependency.injectMockDependency(NotificationMediaManager.class);
- mDependency.injectMockDependency(VisualStabilityManager.class);
- mDependency.injectMockDependency(NotificationGutsManager.class);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
mDependency.injectMockDependency(ForegroundServiceNotificationListener.class);
- NotificationEntryManager entryManager =
- mDependency.injectMockDependency(NotificationEntryManager.class);
- when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
NotificationShadeWindowView notificationShadeWindowView =
mock(NotificationShadeWindowView.class);
@@ -129,8 +111,19 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class), mStatusBar,
mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
- mCommandQueue, mInitController,
- mNotificationInterruptStateProvider);
+ mCommandQueue,
+ mock(NotificationViewHierarchyManager.class),
+ mock(NotificationLockscreenUserManager.class),
+ mock(SysuiStatusBarStateController.class),
+ mock(NotificationEntryManager.class),
+ mock(NotificationMediaManager.class),
+ mock(NotificationGutsManager.class),
+ mock(KeyguardUpdateMonitor.class),
+ lockscreenGestureLogger,
+ mInitController,
+ mNotificationInterruptStateProvider,
+ mock(NotificationRemoteInputManager.class),
+ mock(ConfigurationController.class));
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
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 2c2833a864b9..71c61ab7f9b7 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
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.view.Display.DEFAULT_DISPLAY;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -40,7 +39,7 @@ import static org.mockito.Mockito.when;
import android.app.IWallpaperManager;
import android.app.Notification;
-import android.app.StatusBarManager;
+import android.app.WallpaperManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -78,28 +77,25 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
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.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationListener;
@@ -109,16 +105,13 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SuperStatusBarViewFactory;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.charging.WiredChargingRippleController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -143,13 +136,16 @@ import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.MessageRouterImpl;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wmshell.BubblesManager;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
@@ -180,7 +176,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
- @Mock private StatusBarSignalPolicy mStatusBarSignalPolicy;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private KeyguardStateController mKeyguardStateController;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@@ -201,12 +196,10 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
- @Mock private RemoteInputController mRemoteInputController;
@Mock private StatusBarStateControllerImpl mStatusBarStateController;
@Mock private BatteryController mBatteryController;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
- @Mock private NotificationEntryListener mEntryListener;
@Mock private NotificationFilter mNotificationFilter;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
@Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@@ -214,10 +207,10 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private AssistManager mAssistManager;
+ @Mock private NotificationEntryManager mNotificationEntryManager;
@Mock private NotificationGutsManager mNotificationGutsManager;
@Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private NavigationBarController mNavigationBarController;
- @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
@Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private ColorExtractor.GradientColors mGradientColors;
@@ -229,7 +222,6 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
@Mock private UserSwitcherController mUserSwitcherController;
@Mock private NetworkController mNetworkController;
- @Mock private VibratorHelper mVibratorHelper;
@Mock private BubblesManager mBubblesManager;
@Mock private Bubbles mBubbles;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@@ -237,10 +229,10 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@Mock private DozeParameters mDozeParameters;
@Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+ @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock private LockscreenWallpaper mLockscreenWallpaper;
@Mock private DozeServiceHost mDozeServiceHost;
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
- @Mock private KeyguardLiftController mKeyguardLiftController;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
@Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
@@ -251,12 +243,10 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@Mock private LightsOutNotifController mLightsOutNotifController;
@Mock private ViewMediatorCallback mViewMediatorCallback;
- @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock private ScreenPinningRequest mScreenPinningRequest;
@Mock private StatusBarNotificationActivityStarter.Builder
mStatusBarNotificationActivityStarterBuilder;
- @Mock private DarkIconDispatcher mDarkIconDispatcher;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@@ -265,19 +255,26 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private DemoModeController mDemoModeController;
@Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
- @Mock private WiredChargingRippleController mWiredChargingRippleController;
+ @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
+ @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
@Mock private StatusBarIconController mIconController;
@Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
@Mock private FeatureFlags mFeatureFlags;
- @Mock private IWallpaperManager mWallpaperManager;
+ @Mock private WallpaperManager mWallpaperManager;
+ @Mock private IWallpaperManager mIWallpaperManager;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private TunerService mTunerService;
@Mock private StartingSurface mStartingSurface;
+ @Mock private OperatorNameViewController mOperatorNameViewController;
+ @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private ShadeController mShadeController;
- private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
+ private FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
private InitController mInitController = new InitController();
@Before
@@ -332,10 +329,8 @@ public class StatusBarTest extends SysuiTestCase {
return null;
}).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
- when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
-
WakefulnessLifecycle wakefulnessLifecycle =
- new WakefulnessLifecycle(mContext, mWallpaperManager);
+ new WakefulnessLifecycle(mContext, mIWallpaperManager);
wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
wakefulnessLifecycle.dispatchFinishedWakingUp();
@@ -354,7 +349,10 @@ public class StatusBarTest extends SysuiTestCase {
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> mStatusBar, () -> mAssistManager, Optional.of(mBubbles));
+ () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles));
+
+ when(mOperatorNameViewControllerFactory.create(any()))
+ .thenReturn(mOperatorNameViewController);
mStatusBar = new StatusBar(
mContext,
@@ -362,7 +360,6 @@ public class StatusBarTest extends SysuiTestCase {
mLightBarController,
mAutoHideController,
mKeyguardUpdateMonitor,
- mStatusBarSignalPolicy,
mPulseExpansionHandler,
mNotificationWakeUpCoordinator,
mKeyguardBypassController,
@@ -373,11 +370,7 @@ public class StatusBarTest extends SysuiTestCase {
new FalsingManagerFake(),
new FalsingCollectorFake(),
mBroadcastDispatcher,
- new RemoteInputQuickSettingsDisabler(
- mContext,
- configurationController,
- mCommandQueue
- ),
+ mNotificationEntryManager,
mNotificationGutsManager,
notificationLogger,
mNotificationInterruptStateProvider,
@@ -396,20 +389,18 @@ public class StatusBarTest extends SysuiTestCase {
new ScreenLifecycle(),
wakefulnessLifecycle,
mStatusBarStateController,
- mVibratorHelper,
Optional.of(mBubblesManager),
Optional.of(mBubbles),
mVisualStabilityManager,
mDeviceProvisionedController,
mNavigationBarController,
- mAccessibilityFloatingMenuController,
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
mDozeParameters,
mScrimController,
- mKeyguardLiftController,
mLockscreenWallpaperLazy,
+ mLockscreenGestureLogger,
mBiometricUnlockControllerLazy,
mDozeServiceHost,
mPowerManager, mScreenPinningRequest,
@@ -431,15 +422,16 @@ public class StatusBarTest extends SysuiTestCase {
mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
+ mOperatorNameViewControllerFactory,
mPhoneStatusBarPolicy,
mKeyguardIndicationController,
- mDismissCallbackRegistry,
mDemoModeController,
mNotificationShadeDepthControllerLazy,
mStatusBarTouchableRegionManager,
mNotificationIconAreaController,
mBrightnessSliderFactory,
- mWiredChargingRippleController,
+ mUnfoldTransitionConfig,
+ mUnfoldLightRevealOverlayAnimationLazy,
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
@@ -447,8 +439,13 @@ public class StatusBarTest extends SysuiTestCase {
mLockscreenTransitionController,
mFeatureFlags,
mKeyguardUnlockAnimationController,
+ new Handler(TestableLooper.get(this).getLooper()),
+ mMainExecutor,
+ new MessageRouterImpl(mMainExecutor),
+ mWallpaperManager,
mUnlockedScreenOffAnimationController,
- Optional.of(mStartingSurface));
+ Optional.of(mStartingSurface),
+ mTunerService);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(KeyguardBypassController.class)))
@@ -711,7 +708,7 @@ public class StatusBarTest extends SysuiTestCase {
} catch (RemoteException e) {
fail();
}
- TestableLooper.get(this).processAllMessages();
+ mMainExecutor.runAllReady();
}
@Test
@@ -730,7 +727,7 @@ public class StatusBarTest extends SysuiTestCase {
} catch (RemoteException e) {
fail();
}
- TestableLooper.get(this).processAllMessages();
+ mMainExecutor.runAllReady();
}
@Test
@@ -748,32 +745,7 @@ public class StatusBarTest extends SysuiTestCase {
} catch (RemoteException e) {
fail();
}
- TestableLooper.get(this).processAllMessages();
- }
-
- @Test
- public void testDisableExpandStatusBar() {
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
- mStatusBar.setUserSetupForTest(true);
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
-
- when(mCommandQueue.panelsEnabled()).thenReturn(false);
- mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
- StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- verify(mNotificationPanelViewController).setQsExpansionEnabledPolicy(false);
- mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController, never()).expand(anyBoolean());
- mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelViewController, never()).expand(anyBoolean());
-
- when(mCommandQueue.panelsEnabled()).thenReturn(true);
- mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
- StatusBarManager.DISABLE2_NONE, false);
- verify(mNotificationPanelViewController).setQsExpansionEnabledPolicy(true);
- mStatusBar.animateExpandNotificationsPanel();
- verify(mNotificationPanelViewController).expandWithoutQs();
- mStatusBar.animateExpandSettingsPanel(null);
- verify(mNotificationPanelViewController).expandWithQs();
+ mMainExecutor.runAllReady();
}
@Test
@@ -788,15 +760,6 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
- @RunWithLooper(setAsMainLooper = true)
- public void testUpdateKeyguardState_DoesNotCrash() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(
- new SparseArray<>());
- mStatusBar.onStateChanged(StatusBarState.SHADE);
- }
-
- @Test
public void testFingerprintNotification_UpdatesScrims() {
mStatusBar.notifyBiometricAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
@@ -812,6 +775,30 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
+ public void testTransitionLaunch_goesToUnlocked() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
+ when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+ }
+
+ @Test
+ public void testTransitionLaunch_noPreview_doesntGoUnlocked() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
+ when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+ }
+
+ @Test
public void testSetOccluded_propagatesToScrimController() {
mStatusBar.setOccluded(true);
verify(mScrimController).setKeyguardOccluded(eq(true));
@@ -903,18 +890,6 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
- public void testSuppressAmbientDisplay_suppress() {
- mStatusBar.suppressAmbientDisplay(true);
- verify(mDozeServiceHost).setDozeSuppressed(true);
- }
-
- @Test
- public void testSuppressAmbientDisplay_unsuppress() {
- mStatusBar.suppressAmbientDisplay(false);
- verify(mDozeServiceHost).setDozeSuppressed(false);
- }
-
- @Test
public void testUpdateResources_updatesBouncer() {
mStatusBar.updateResources();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index d26db4c69ece..4476fd879e24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -34,7 +34,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -238,7 +238,6 @@ class OngoingCallControllerTest : SysuiTestCase() {
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
-
@Test
fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
@@ -364,7 +363,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
// Update the process to visible.
uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_VISIBLE, 0, 0)
mainExecutor.advanceClockToLast()
- mainExecutor.runAllReady();
+ mainExecutor.runAllReady()
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
@@ -385,7 +384,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
// Update the process to invisible.
uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_INVISIBLE, 0, 0)
mainExecutor.advanceClockToLast()
- mainExecutor.runAllReady();
+ mainExecutor.runAllReady()
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
new file mode 100644
index 000000000000..30717f431f5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
+
+ private static final String[] DEFAULT_SETTINGS = new String[]{
+ "0:0",
+ "1:2"
+ };
+
+ private final FakeSettings mFakeSettings = new FakeSettings();
+ private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
+ @Mock DeviceStateManager mDeviceStateManager;
+ RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+ DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+ private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass= */ this);
+ TestableResources resources = mContext.getOrCreateTestableResources();
+
+ ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(
+ DeviceStateManager.DeviceStateCallback.class);
+
+ mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController(
+ mFakeSettings,
+ mFakeRotationPolicy,
+ mDeviceStateManager,
+ mFakeExecutor,
+ DEFAULT_SETTINGS
+ );
+
+ mDeviceStateRotationLockSettingController.setListening(true);
+ verify(mDeviceStateManager).registerCallback(any(),
+ deviceStateCallbackArgumentCaptor.capture());
+ mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue();
+ }
+
+ @Test
+ public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() {
+ mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "",
+ UserHandle.USER_CURRENT);
+
+ mDeviceStateRotationLockSettingController.initialize();
+
+ assertThat(mFakeSettings
+ .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:0:1:2");
+ }
+
+ @Test
+ public void whenNoSavedValueForDeviceState_assumeIgnored() {
+ mFakeSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"0:2:1:2",
+ UserHandle.USER_CURRENT);
+ mFakeRotationPolicy.setRotationLock(true);
+ mDeviceStateRotationLockSettingController.initialize();
+
+ mDeviceStateCallback.onStateChanged(1);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ // Settings only exist for state 0 and 1
+ mDeviceStateCallback.onStateChanged(2);
+
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+ }
+
+ @Test
+ public void whenDeviceStateSwitched_loadCorrectSetting() {
+ mFakeSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"0:2:1:1",
+ UserHandle.USER_CURRENT);
+ mFakeRotationPolicy.setRotationLock(true);
+ mDeviceStateRotationLockSettingController.initialize();
+
+ mDeviceStateCallback.onStateChanged(0);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ mDeviceStateCallback.onStateChanged(1);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+
+ }
+
+ @Test
+ public void whenUserChangesSetting_saveSettingForCurrentState() {
+ mFakeSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"0:1:1:2",
+ UserHandle.USER_CURRENT);
+ mFakeRotationPolicy.setRotationLock(true);
+ mDeviceStateRotationLockSettingController.initialize();
+
+ mDeviceStateCallback.onStateChanged(0);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+
+ mDeviceStateRotationLockSettingController
+ .onRotationLockStateChanged(/* rotationLocked= */false,
+ /* affordanceVisible= */ true);
+
+ assertThat(mFakeSettings
+ .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:2:1:2");
+ }
+
+
+ @Test
+ public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
+ mFakeSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"0:0:1:2",
+ UserHandle.USER_CURRENT);
+ mFakeRotationPolicy.setRotationLock(true);
+ mDeviceStateRotationLockSettingController.initialize();
+
+ mDeviceStateCallback.onStateChanged(1);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ mDeviceStateCallback.onStateChanged(0);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+ }
+
+ @Test
+ public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
+ mFakeSettings.putStringForUser(
+ Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ /* value= */"0:0:1:2",
+ UserHandle.USER_CURRENT);
+ mFakeRotationPolicy.setRotationLock(true);
+ mDeviceStateRotationLockSettingController.initialize();
+
+ mDeviceStateCallback.onStateChanged(1);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ mDeviceStateCallback.onStateChanged(0);
+ assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+ mDeviceStateRotationLockSettingController
+ .onRotationLockStateChanged(/* rotationLocked= */true,
+ /* affordanceVisible= */ true);
+
+ assertThat(mFakeSettings
+ .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+ UserHandle.USER_CURRENT))
+ .isEqualTo("0:0:1:1");
+ }
+
+ private static class FakeRotationPolicy implements RotationPolicyWrapper {
+
+ private boolean mRotationLock;
+
+ @Override
+ public void setRotationLock(boolean enabled) {
+ mRotationLock = enabled;
+ }
+
+ @Override
+ public void setRotationLockAtAngle(boolean enabled, int rotation) {
+ mRotationLock = enabled;
+ }
+
+ @Override
+ public int getRotationLockOrientation() {
+ throw new AssertionError("Not implemented");
+ }
+
+ @Override
+ public boolean isRotationLockToggleVisible() {
+ throw new AssertionError("Not implemented");
+ }
+
+ @Override
+ public boolean isRotationLocked() {
+ return mRotationLock;
+ }
+
+ @Override
+ public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
+ int userHandle) {
+ throw new AssertionError("Not implemented");
+ }
+
+ @Override
+ public void unregisterRotationPolicyListener(
+ RotationPolicy.RotationPolicyListener listener) {
+ throw new AssertionError("Not implemented");
+ }
+ }
+}
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 6c4ec223969a..21c4a1745a88 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
@@ -70,8 +70,7 @@ import com.android.settingslib.net.DataUsageController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
@@ -242,9 +241,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
- mFeatureFlags,
- mock(DumpManager.class)
- );
+ mFeatureFlags);
setupNetworkController();
// Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -312,8 +309,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mCarrierConfigTracker, mFeatureFlags,
- mock(DumpManager.class));
+ mCarrierConfigTracker, mFeatureFlags);
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 3433a14f54e7..bc4c2b69e3f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -21,7 +21,6 @@ import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -114,7 +113,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
- mock(CarrierConfigTracker.class), mFeatureFlags, mock(DumpManager.class));
+ mock(CarrierConfigTracker.class), mFeatureFlags);
setupNetworkController();
setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 4ff13011567b..5090b0dbc2a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -41,7 +41,6 @@ import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
-import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -68,8 +67,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mock(DumpManager.class));
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
setupNetworkController();
verifyLastMobileDataIndicators(false, -1, 0);
@@ -89,8 +87,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mock(DumpManager.class));
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
mNetworkController.registerListeners();
// Wait for the main looper to execute the previous command
@@ -158,8 +155,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
- mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
- mock(DumpManager.class));
+ mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
setupNetworkController();
// No Subscriptions.
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 57198dbe18bb..4a5770d12239 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
@@ -238,7 +238,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
mNetworkController.setNoNetworksAvailable(false);
setWifiStateForVcn(true, testSsid);
setWifiLevelForVcn(0);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
+ verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false);
mNetworkController.setNoNetworksAvailable(true);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
@@ -246,11 +246,11 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
setConnectivityViaCallbackInNetworkControllerForVcn(
NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
+ verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true);
setConnectivityViaCallbackInNetworkControllerForVcn(
NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
- verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+ verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index dd8354dedafd..0d4d8894c894 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.policy;
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -25,15 +27,19 @@ import static org.mockito.Mockito.spy;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ShortcutManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.ContentInfo;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -238,4 +244,39 @@ public class RemoteInputViewTest extends SysuiTestCase {
RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND.getId(),
mUiEventLoggerFake.eventId(1));
}
+
+ @Test
+ public void testUiEventLogging_openAndAttach() throws Exception {
+ NotificationTestHelper helper = new NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this));
+ ExpandableNotificationRow row = helper.createRow();
+ RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+
+ setTestPendingIntent(view);
+
+ // Open view, attach an image
+ view.focus();
+ EditText editText = view.findViewById(R.id.remote_input_text);
+ editText.setText(TEST_REPLY);
+ ClipDescription description = new ClipDescription("", new String[] {"image/png"});
+ // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+ ClipData clip = new ClipData(description, new ClipData.Item(
+ Uri.parse("android.resource://com.android.systemui/"
+ + R.drawable.default_thumbnail)));
+ ContentInfo payload =
+ new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
+ view.setAttachment(payload);
+ mReceiver.waitForIntent();
+
+ assertEquals(2, mUiEventLoggerFake.numLogs());
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_OPEN.getId(),
+ mUiEventLoggerFake.eventId(0));
+ assertEquals(
+ RemoteInputView.NotificationRemoteInputEvent
+ .NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE.getId(),
+ mUiEventLoggerFake.eventId(1));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
new file mode 100644
index 000000000000..0581264d18e2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+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;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RotationLockControllerImplTest extends SysuiTestCase {
+
+ private static final String[] DEFAULT_SETTINGS = new String[]{
+ "0:0",
+ "1:2"
+ };
+
+ @Mock RotationPolicyWrapper mRotationPolicyWrapper;
+ @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+
+ private TestableResources mResources;
+ private ArgumentCaptor<RotationPolicy.RotationPolicyListener>
+ mRotationPolicyListenerCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(/* testClass= */ this);
+ mResources = mContext.getOrCreateTestableResources();
+
+ mRotationPolicyListenerCaptor = ArgumentCaptor.forClass(
+ RotationPolicy.RotationPolicyListener.class);
+ }
+
+ @Test
+ public void whenFlagOff_doesntInteractWithDeviceStateRotationController() {
+ createRotationLockController(new String[0]);
+
+ verifyZeroInteractions(mDeviceStateRotationLockSettingController);
+ }
+
+ @Test
+ public void whenFlagOn_setListeningSetsListeningOnDeviceStateRotationController() {
+ createRotationLockController();
+
+ verify(mDeviceStateRotationLockSettingController).setListening(/* listening= */ true);
+ }
+
+ @Test
+ public void whenFlagOn_initializesDeviceStateRotationController() {
+ createRotationLockController();
+
+ verify(mDeviceStateRotationLockSettingController).initialize();
+ }
+
+ @Test
+ public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() {
+ createRotationLockController();
+ captureRotationPolicyListener().onChange();
+
+ verify(mDeviceStateRotationLockSettingController)
+ .onRotationLockStateChanged(anyBoolean(), anyBoolean());
+ }
+
+ private RotationPolicy.RotationPolicyListener captureRotationPolicyListener() {
+ verify(mRotationPolicyWrapper)
+ .registerRotationPolicyListener(mRotationPolicyListenerCaptor.capture(), anyInt());
+ return mRotationPolicyListenerCaptor.getValue();
+ }
+
+ private void createRotationLockController() {
+ createRotationLockController(DEFAULT_SETTINGS);
+ }
+ private void createRotationLockController(String[] deviceStateRotationLockDefaults) {
+ new RotationLockControllerImpl(
+ mRotationPolicyWrapper,
+ mDeviceStateRotationLockSettingController,
+ deviceStateRotationLockDefaults
+ );
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 3431a9d895d3..b1b9ff457286 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -100,8 +100,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
context.orCreateTestableResources.addOverride(
com.android.internal.R.bool.config_guestUserAutoCreated, false)
- context.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
- context.addMockSystemService(Context.FINGERPRINT_SERVICE,
+ mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java))
+ mContext.addMockSystemService(Context.FINGERPRINT_SERVICE,
mock(FingerprintManager::class.java))
`when`(userManager.canAddMoreUsers()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
new file mode 100644
index 000000000000..871a48c503be
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Date
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class VariableDateViewControllerTest : SysuiTestCase() {
+
+ companion object {
+ private const val TIME_STAMP = 1_500_000_000_000
+ private const val LONG_PATTERN = "EEEMMMd"
+ private const val SHORT_PATTERN = "MMMd"
+ private const val CHAR_WIDTH = 10f
+ }
+
+ @Mock
+ private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock
+ private lateinit var view: VariableDateView
+ @Captor
+ private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
+
+ private var lastText: String? = null
+
+ private lateinit var systemClock: FakeSystemClock
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var testableHandler: Handler
+ private lateinit var controller: VariableDateViewController
+
+ private lateinit var longText: String
+ private lateinit var shortText: String
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ testableHandler = Handler(testableLooper.looper)
+
+ systemClock = FakeSystemClock()
+ systemClock.setCurrentTimeMillis(TIME_STAMP)
+
+ `when`(view.longerPattern).thenReturn(LONG_PATTERN)
+ `when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
+ `when`(view.handler).thenReturn(testableHandler)
+
+ `when`(view.setText(anyString())).thenAnswer {
+ lastText = it.arguments[0] as? String
+ Unit
+ }
+ `when`(view.isAttachedToWindow).thenReturn(true)
+
+ val date = Date(TIME_STAMP)
+ longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
+ shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN))
+
+ // Assume some sizes for the text, the controller doesn't need to know if these sizes are
+ // the true ones
+ `when`(view.getDesiredWidthForText(any())).thenAnswer {
+ getTextLength(it.arguments[0] as CharSequence)
+ }
+
+ controller = VariableDateViewController(
+ systemClock,
+ broadcastDispatcher,
+ testableHandler,
+ view
+ )
+
+ controller.init()
+ testableLooper.processAllMessages()
+
+ verify(view).onAttach(capture(onMeasureListenerCaptor))
+ }
+
+ @Test
+ fun testViewStartsWithLongText() {
+ assertThat(lastText).isEqualTo(longText)
+ }
+
+ @Test
+ fun testListenerNotNull() {
+ assertThat(onMeasureListenerCaptor.value).isNotNull()
+ }
+
+ @Test
+ fun testLotsOfSpaceUseLongText() {
+ onMeasureListenerCaptor.value.onMeasureAction(10000)
+
+ testableLooper.processAllMessages()
+ assertThat(lastText).isEqualTo(longText)
+ }
+
+ @Test
+ fun testSmallSpaceUseEmpty() {
+ onMeasureListenerCaptor.value.onMeasureAction(1)
+ testableLooper.processAllMessages()
+
+ assertThat(lastText).isEmpty()
+ }
+
+ @Test
+ fun testSpaceInBetweenUseShortText() {
+ val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+
+ onMeasureListenerCaptor.value.onMeasureAction(average)
+ testableLooper.processAllMessages()
+
+ assertThat(lastText).isEqualTo(shortText)
+ }
+
+ @Test
+ fun testSwitchBackToLonger() {
+ onMeasureListenerCaptor.value.onMeasureAction(1)
+ testableLooper.processAllMessages()
+
+ onMeasureListenerCaptor.value.onMeasureAction(10000)
+ testableLooper.processAllMessages()
+
+ assertThat(lastText).isEqualTo(longText)
+ }
+
+ @Test
+ fun testNoSwitchingWhenFrozen() {
+ `when`(view.freezeSwitching).thenReturn(true)
+
+ val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+ onMeasureListenerCaptor.value.onMeasureAction(average)
+ testableLooper.processAllMessages()
+ assertThat(lastText).isEqualTo(longText)
+
+ onMeasureListenerCaptor.value.onMeasureAction(1)
+ testableLooper.processAllMessages()
+ assertThat(lastText).isEqualTo(longText)
+ }
+
+ private fun getTextLength(text: CharSequence): Float {
+ return text.length * CHAR_WIDTH
+ }
+} \ No newline at end of file
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 07d3fc20983f..f6a549363955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -52,9 +52,9 @@ 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.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.util.settings.SecureSettings;
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 5efe05f6db46..84e6df23e740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -60,9 +60,9 @@ import com.android.internal.util.IntPair;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
new file mode 100644
index 000000000000..eebcbe63d004
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
@@ -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 com.android.systemui.usb
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.hardware.usb.IUsbSerialReader
+import android.hardware.usb.UsbAccessory
+import android.hardware.usb.UsbManager
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import com.android.systemui.SysuiTestCase
+import 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.runner.RunWith
+import java.lang.Exception
+
+/**
+ * UsbPermissionActivityTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UsbPermissionActivityTest : SysuiTestCase() {
+
+ class UsbPermissionActivityTestable : UsbPermissionActivity()
+
+ @Rule
+ @JvmField
+ var activityRule = ActivityTestRule<UsbPermissionActivityTestable>(
+ UsbPermissionActivityTestable::class.java, false, false)
+
+ private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java)
+ .apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ putExtra(UsbManager.EXTRA_PACKAGE, "com.android.systemui")
+ putExtra(Intent.EXTRA_INTENT, PendingIntent.getBroadcast(
+ mContext,
+ 334,
+ Intent("NO_ACTION"),
+ PendingIntent.FLAG_MUTABLE))
+ putExtra(UsbManager.EXTRA_ACCESSORY, UsbAccessory(
+ "manufacturer",
+ "model",
+ "description",
+ "version",
+ "uri",
+ object : IUsbSerialReader.Stub() {
+ override fun getSerial(packageName: String): String {
+ return "serial"
+ }
+ }))
+ }
+
+ @Before
+ fun setUp() {
+ activityRule.launchActivity(activityIntent)
+ }
+
+ @After
+ fun tearDown() {
+ activityRule.finishActivity()
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun testHideNonSystemOverlay() {
+ assertThat(activityRule.activity.window.attributes.privateFlags and
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+ .isEqualTo(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
new file mode 100644
index 000000000000..78fc6803ea7e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util.concurrency;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MessageRouterImplTest extends SysuiTestCase {
+ private static final int MESSAGE_A = 0;
+ private static final int MESSAGE_B = 1;
+ private static final int MESSAGE_C = 2;
+
+ private static final String METADATA_A = "A";
+ private static final String METADATA_B = "B";
+ private static final String METADATA_C = "C";
+ private static final Foobar METADATA_FOO = new Foobar();
+
+ FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+ @Mock
+ MessageRouter.SimpleMessageListener mNoMdListener;
+ @Mock
+ MessageRouter.DataMessageListener<String> mStringListener;
+ @Mock
+ MessageRouter.DataMessageListener<Foobar> mFoobarListener;
+ private MessageRouterImpl mMR;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mMR = new MessageRouterImpl(mFakeExecutor);
+ }
+
+ @Test
+ public void testSingleMessage_NoMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testSingleMessage_WithMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ }
+
+ @Test
+ public void testMessages_WithMixedMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+ mMR.subscribeTo(Foobar.class, mFoobarListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ reset(mStringListener);
+ reset(mFoobarListener);
+
+ mMR.sendMessage(METADATA_FOO);
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener, never()).onMessage(any(Foobar.class));
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+ }
+
+ @Test
+ public void testMessages_WithAndWithoutMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ reset(mNoMdListener);
+ reset(mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+ verify(mStringListener).onMessage(METADATA_A);
+ }
+
+ @Test
+ public void testRepeatedMessage() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ InOrder ordered = inOrder(mStringListener);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ ordered.verify(mStringListener).onMessage(METADATA_B);
+ }
+
+ @Test
+ public void testCancelMessage() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.cancelMessages(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+
+ InOrder ordered = inOrder(mNoMdListener);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_C);
+ }
+
+ @Test
+ public void testSendMessage_NoSubscriber() {
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mMR.sendMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testUnsubscribe_SpecificMessage_NoMetadata() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+ InOrder ordered = inOrder(mNoMdListener);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_A);
+ ordered.verify(mNoMdListener).onMessage(MESSAGE_B);
+
+ reset(mNoMdListener);
+ mMR.unsubscribeFrom(MESSAGE_A, mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+ verify(mNoMdListener).onMessage(MESSAGE_B);
+ }
+
+ @Test
+ public void testUnsubscribe_SpecificMessage_WithMetadata() {
+ mMR.subscribeTo(String.class, mStringListener);
+ mMR.subscribeTo(Foobar.class, mFoobarListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_FOO);
+
+ mFakeExecutor.runNextReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ mFakeExecutor.runNextReady();
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+
+ reset(mStringListener);
+ reset(mFoobarListener);
+ mMR.unsubscribeFrom(String.class, mStringListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_FOO);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(METADATA_A);
+ verify(mFoobarListener).onMessage(METADATA_FOO);
+ }
+
+ @Test
+ public void testUnsubscribe_AllMessages_NoMetadata() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_B, mNoMdListener);
+ mMR.subscribeTo(MESSAGE_C, mNoMdListener);
+
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ verify(mNoMdListener).onMessage(MESSAGE_B);
+ verify(mNoMdListener).onMessage(MESSAGE_C);
+
+ reset(mNoMdListener);
+
+ mMR.unsubscribeFrom(mNoMdListener);
+ mMR.sendMessage(MESSAGE_A);
+ mMR.sendMessage(MESSAGE_B);
+ mMR.sendMessage(MESSAGE_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(MESSAGE_A);
+ verify(mNoMdListener, never()).onMessage(MESSAGE_B);
+ verify(mNoMdListener, never()).onMessage(MESSAGE_C);
+ }
+
+ @Test
+ public void testUnsubscribe_AllMessages_WithMetadata() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ mMR.sendMessage(METADATA_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_A);
+ verify(mStringListener).onMessage(METADATA_B);
+ verify(mStringListener).onMessage(METADATA_C);
+
+ reset(mStringListener);
+
+ mMR.unsubscribeFrom(mStringListener);
+ mMR.sendMessage(METADATA_A);
+ mMR.sendMessage(METADATA_B);
+ mMR.sendMessage(METADATA_C);
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(METADATA_A);
+ verify(mStringListener, never()).onMessage(METADATA_B);
+ verify(mStringListener, never()).onMessage(METADATA_C);
+ }
+
+ @Test
+ public void testSingleDelayedMessage_NoMetaData() {
+ mMR.subscribeTo(MESSAGE_A, mNoMdListener);
+
+ mMR.sendMessageDelayed(MESSAGE_A, 100);
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener, never()).onMessage(anyInt());
+
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ verify(mNoMdListener).onMessage(MESSAGE_A);
+ }
+
+ @Test
+ public void testSingleDelayedMessage_WithMetaData() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_C, 1000);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.advanceClockToNext();
+ mFakeExecutor.runAllReady();
+ verify(mStringListener).onMessage(METADATA_C);
+ }
+
+ @Test
+ public void testMultipleDelayedMessages() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_A, 100);
+ mMR.sendMessageDelayed(METADATA_B, 1000);
+ mMR.sendMessageDelayed(METADATA_C, 500);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ InOrder ordered = inOrder(mStringListener);
+ ordered.verify(mStringListener).onMessage(METADATA_A);
+ ordered.verify(mStringListener).onMessage(METADATA_C);
+ ordered.verify(mStringListener).onMessage(METADATA_B);
+ }
+
+ @Test
+ public void testCancelDelayedMessages() {
+ mMR.subscribeTo(String.class, mStringListener);
+
+ mMR.sendMessageDelayed(METADATA_A, 100);
+ mMR.sendMessageDelayed(METADATA_B, 1000);
+ mMR.sendMessageDelayed(METADATA_C, 500);
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mFakeExecutor.runAllReady();
+ verify(mStringListener, never()).onMessage(anyString());
+
+ mMR.cancelMessages(String.class);
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ verify(mStringListener, never()).onMessage(anyString());
+ }
+
+ private static class Foobar {}
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
index bcc20c2d1b37..724d14e20374 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/leak/GarbageMonitorTest.java
@@ -17,124 +17,94 @@
package com.android.systemui.util.leak;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.MessageRouterImpl;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
public class GarbageMonitorTest extends SysuiTestCase {
- private LeakReporter mLeakReporter;
- private TrackedGarbage mTrackedGarbage;
- private TestableGarbageMonitor mGarbageMonitor;
+ @Mock private LeakReporter mLeakReporter;
+ @Mock private TrackedGarbage mTrackedGarbage;
+ private GarbageMonitor mGarbageMonitor;
+ private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
@Before
public void setup() {
- mTrackedGarbage = mock(TrackedGarbage.class);
- mLeakReporter = mock(LeakReporter.class);
+ MockitoAnnotations.initMocks(this);
mGarbageMonitor =
- new TestableGarbageMonitor(
+ new GarbageMonitor(
mContext,
- TestableLooper.get(this).getLooper(),
+ mFakeExecutor,
+ new MessageRouterImpl(mFakeExecutor),
new LeakDetector(null, mTrackedGarbage, null),
mLeakReporter);
}
@Test
- public void testCallbacks_getScheduled() {
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- }
-
- @Test
- public void testNoGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
-
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
-
- verify(mLeakReporter, never()).dumpLeak(anyInt());
- }
-
- @Test
public void testALittleGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(4);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE);
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
- mGarbageMonitor.runCallbacksOnce();
+ mGarbageMonitor.reinspectGarbageAfterGc();
verify(mLeakReporter, never()).dumpLeak(anyInt());
}
@Test
public void testTransientGarbage_doesntDump() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
+ // Start the leak monitor. Nothing gets reported immediately.
mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runInspectCallback();
+ mFakeExecutor.runAllReady();
+ verify(mLeakReporter, never()).dumpLeak(anyInt());
+ // Garbage gets reset to 0 before the leak reporte actually gets called.
when(mTrackedGarbage.countOldGarbage()).thenReturn(0);
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
- mGarbageMonitor.runReinspectCallback();
-
+ // Therefore nothing gets dumped.
verify(mLeakReporter, never()).dumpLeak(anyInt());
}
@Test
public void testLotsOfPersistentGarbage_dumps() {
- when(mTrackedGarbage.countOldGarbage()).thenReturn(100);
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
- mGarbageMonitor.startLeakMonitor();
- mGarbageMonitor.runCallbacksOnce();
+ mGarbageMonitor.reinspectGarbageAfterGc();
- verify(mLeakReporter).dumpLeak(anyInt());
+ verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
}
- private static class TestableGarbageMonitor extends GarbageMonitor {
- public TestableGarbageMonitor(
- Context context,
- Looper looper,
- LeakDetector leakDetector,
- LeakReporter leakReporter) {
- super(context, looper, leakDetector, leakReporter);
- }
-
- void runInspectCallback() {
- startLeakMonitor();
- }
-
- void runReinspectCallback() {
- reinspectGarbageAfterGc();
- }
-
- void runCallbacksOnce() {
- // Note that TestableLooper doesn't currently support delayed messages so we need to run
- // callbacks explicitly.
- runInspectCallback();
- runReinspectCallback();
- }
+ @Test
+ public void testLotsOfPersistentGarbage_dumpsAfterAtime() {
+ when(mTrackedGarbage.countOldGarbage()).thenReturn(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
+
+ // Start the leak monitor. Nothing gets reported immediately.
+ mGarbageMonitor.startLeakMonitor();
+ mFakeExecutor.runAllReady();
+ verify(mLeakReporter, never()).dumpLeak(anyInt());
+
+ mFakeExecutor.advanceClockToLast();
+ mFakeExecutor.runAllReady();
+
+ verify(mLeakReporter).dumpLeak(GarbageMonitor.GARBAGE_ALLOWANCE + 1);
}
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
index a34c5986f36c..0e9d96c61e54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.util.sensors;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -338,30 +340,25 @@ public class ProximitySensorDualTest extends SysuiTestCase {
@Test
public void testSecondaryCancelsSecondary() {
TestableListener listener = new TestableListener();
- ThresholdSensor.Listener cancelingListener = new ThresholdSensor.Listener() {
- @Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) {
- mProximitySensor.pause();
- }
- };
+ ThresholdSensor.Listener cancelingListener = event -> mProximitySensor.pause();
mProximitySensor.register(listener);
mProximitySensor.register(cancelingListener);
- assertNull(listener.mLastEvent);
- assertEquals(0, listener.mCallCount);
+ assertThat(listener.mLastEvent).isNull();
+ assertThat(listener.mCallCount).isEqualTo(0);
mThresholdSensorPrimary.triggerEvent(true, 0);
- assertNull(listener.mLastEvent);
- assertEquals(0, listener.mCallCount);
+ assertThat(listener.mLastEvent).isNull();
+ assertThat(listener.mCallCount).isEqualTo(0);
mThresholdSensorSecondary.triggerEvent(true, 0);
- assertTrue(listener.mLastEvent.getBelow());
- assertEquals(1, listener.mCallCount);
+ assertThat(listener.mLastEvent.getBelow()).isTrue();
+ assertThat(listener.mCallCount).isEqualTo(1);
// The proximity sensor should now be canceled. Advancing the clock should do nothing.
- assertEquals(0, mFakeExecutor.numPending());
+ assertThat(mFakeExecutor.numPending()).isEqualTo(0);
mThresholdSensorSecondary.triggerEvent(false, 1);
- assertTrue(listener.mLastEvent.getBelow());
- assertEquals(1, listener.mCallCount);
+ assertThat(listener.mLastEvent.getBelow()).isTrue();
+ assertThat(listener.mCallCount).isEqualTo(1);
mProximitySensor.unregister(listener);
}
@@ -372,33 +369,66 @@ public class ProximitySensorDualTest extends SysuiTestCase {
TestableListener listener = new TestableListener();
- // WE immediately register the secondary sensor.
+ // We immediately register the secondary sensor.
mProximitySensor.register(listener);
- assertFalse(mThresholdSensorPrimary.isPaused());
- assertFalse(mThresholdSensorSecondary.isPaused());
- assertNull(listener.mLastEvent);
- assertEquals(0, listener.mCallCount);
+ assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+ assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+ assertThat(listener.mLastEvent).isNull();
+ assertThat(listener.mCallCount).isEqualTo(0);
mThresholdSensorPrimary.triggerEvent(true, 0);
- assertNull(listener.mLastEvent);
- assertEquals(0, listener.mCallCount);
+ assertThat(listener.mLastEvent).isNull();
+ assertThat(listener.mCallCount).isEqualTo(0);
mThresholdSensorSecondary.triggerEvent(true, 0);
- assertTrue(listener.mLastEvent.getBelow());
- assertEquals(1, listener.mCallCount);
+ assertThat(listener.mLastEvent.getBelow()).isTrue();
+ assertThat(listener.mCallCount).isEqualTo(1);
// The secondary sensor should now remain resumed indefinitely.
- assertFalse(mThresholdSensorSecondary.isPaused());
+ assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
mThresholdSensorSecondary.triggerEvent(false, 1);
- assertFalse(listener.mLastEvent.getBelow());
- assertEquals(2, listener.mCallCount);
+ assertThat(listener.mLastEvent.getBelow()).isFalse();
+ assertThat(listener.mCallCount).isEqualTo(2);
// The secondary is still running, and not polling with the executor.
- assertFalse(mThresholdSensorSecondary.isPaused());
- assertEquals(0, mFakeExecutor.numPending());
+ assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+ assertThat(mFakeExecutor.numPending()).isEqualTo(0);
mProximitySensor.unregister(listener);
}
+ @Test
+ public void testSecondaryPausesPrimary() {
+ TestableListener listener = new TestableListener();
+
+ mProximitySensor.register(listener);
+
+ assertThat(mThresholdSensorPrimary.isPaused()).isFalse();
+ assertThat(mThresholdSensorSecondary.isPaused()).isTrue();
+
+ mProximitySensor.setSecondarySafe(true);
+
+ assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+ assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+ }
+
+ @Test
+ public void testSecondaryResumesPrimary() {
+ mProximitySensor.setSecondarySafe(true);
+
+ TestableListener listener = new TestableListener();
+ mProximitySensor.register(listener);
+
+ assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+ assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+
+ mProximitySensor.setSecondarySafe(false);
+
+ assertThat(mThresholdSensorPrimary.isPaused()).isFalse();
+ assertThat(mThresholdSensorSecondary.isPaused()).isTrue();
+
+
+ }
+
private static class TestableListener implements ThresholdSensor.Listener {
ThresholdSensor.ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index 69764227040b..7bb26748a9d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -31,6 +31,7 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
private final Map<SettingsKey, String> mValues = new HashMap<>();
private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
new HashMap<>();
+ private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
@@ -55,9 +56,15 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
@Override
public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
ContentObserver settingsObserver, int userHandle) {
- SettingsKey key = new SettingsKey(userHandle, uri.toString());
- mContentObservers.putIfAbsent(key, new ArrayList<>());
- List<ContentObserver> observers = mContentObservers.get(key);
+ List<ContentObserver> observers;
+ if (userHandle == UserHandle.USER_ALL) {
+ mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
+ observers = mContentObserversAllUsers.get(uri.toString());
+ } else {
+ SettingsKey key = new SettingsKey(userHandle, uri.toString());
+ mContentObservers.putIfAbsent(key, new ArrayList<>());
+ observers = mContentObservers.get(key);
+ }
observers.add(settingsObserver);
}
@@ -67,6 +74,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
List<ContentObserver> observers = mContentObservers.get(key);
observers.remove(settingsObserver);
}
+ for (String key : mContentObserversAllUsers.keySet()) {
+ List<ContentObserver> observers = mContentObserversAllUsers.get(key);
+ observers.remove(settingsObserver);
+ }
}
@Override
@@ -114,6 +125,10 @@ public class FakeSettings implements SecureSettings, GlobalSettings, SystemSetti
for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
observer.dispatchChange(false, List.of(uri), userHandle);
}
+ for (ContentObserver observer :
+ mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
+ observer.dispatchChange(false, List.of(uri), userHandle);
+ }
return true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index 0d560f237a07..34cae58d30e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.database.ContentObserver;
+import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -89,6 +90,16 @@ public class FakeSettingsTest extends SysuiTestCase {
}
@Test
+ public void testRegisterContentObserverAllUsers() {
+ mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+ }
+
+ @Test
public void testUnregisterContentObserver() {
mFakeSettings.registerContentObserver("cat", mContentObserver);
mFakeSettings.unregisterContentObserver(mContentObserver);
@@ -98,4 +109,16 @@ public class FakeSettingsTest extends SysuiTestCase {
verify(mContentObserver, never()).dispatchChange(
anyBoolean(), any(Collection.class), anyInt());
}
+
+ @Test
+ public void testUnregisterContentObserverAllUsers() {
+ mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
+ mFakeSettings.unregisterContentObserver(mContentObserver);
+
+ mFakeSettings.putString("cat", "hat");
+
+ verify(mContentObserver, never()).dispatchChange(
+ anyBoolean(), any(Collection.class), anyInt());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 6d1e6ce9ea33..8e1c0f7301e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -22,7 +22,7 @@ import com.android.systemui.shared.plugins.PluginManager;
public class FakePluginManager implements PluginManager {
- private final BaseLeakChecker<PluginListener> mLeakChecker;
+ private final BaseLeakChecker<PluginListener<?>> mLeakChecker;
public FakePluginManager(LeakCheck test) {
mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
@@ -30,7 +30,7 @@ public class FakePluginManager implements PluginManager {
@Override
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
- Class cls, boolean allowMultiple) {
+ Class<?> cls, boolean allowMultiple) {
mLeakChecker.addCallback(listener);
}
@@ -62,17 +62,7 @@ public class FakePluginManager implements PluginManager {
}
@Override
- public String[] getWhitelistedPlugins() {
+ public String[] getPrivilegedPlugins() {
return new String[0];
}
-
- @Override
- public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
- return null;
- }
-
- @Override
- public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
- return null;
- }
}
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 f243077afc93..9f755f7be2c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -24,6 +24,7 @@ import static android.service.notification.NotificationListenerService.REASON_CA
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.bubbles.Bubbles.DISMISS_NOTIF_CANCEL;
import static com.google.common.truth.Truth.assertThat;
@@ -75,11 +76,11 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
import com.android.systemui.statusbar.RankingBuilder;
@@ -120,6 +121,7 @@ import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.google.common.collect.ImmutableList;
@@ -331,7 +333,8 @@ public class BubblesTest extends SysuiTestCase {
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
@@ -439,7 +442,7 @@ public class BubblesTest extends SysuiTestCase {
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
mBubbleController.removeBubble(
- mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
+ mRow.getKey(), DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getSbn()), any(), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -1144,6 +1147,28 @@ public class BubblesTest extends SysuiTestCase {
// Verify these are in the overflow
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
+
+ // Would have loaded bubbles twice because of user switch
+ verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
+ }
+
+ /**
+ * Verifies we only load the overflow data once.
+ */
+ @Test
+ public void testOverflowLoadedOnce() {
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbles().isEmpty()).isFalse();
+
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+ mBubbleController.removeBubble(mBubbleEntry.getKey(), DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(mBubbleEntry2.getKey(), DISMISS_NOTIF_CANCEL);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
}
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 e4c78009f491..a3bbb26c6b23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.wmshell;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -47,6 +48,7 @@ import android.content.pm.LauncherApps;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
@@ -62,10 +64,10 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -100,6 +102,7 @@ import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -171,6 +174,10 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
private ExpandableNotificationRow mNonBubbleNotifRow;
private BubbleEntry mBubbleEntry;
private BubbleEntry mBubbleEntry2;
+
+ private BubbleEntry mBubbleEntryUser11;
+ private BubbleEntry mBubbleEntry2User11;
+
@Mock
private Bubbles.BubbleExpandListener mBubbleExpandListener;
@Mock
@@ -240,6 +247,13 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
+ UserHandle handle = mock(UserHandle.class);
+ when(handle.getIdentifier()).thenReturn(11);
+ mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry(
+ mNotificationTestHelper.createBubble(handle));
+ mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
+ mNotificationTestHelper.createBubble(handle));
+
mZenModeConfig.suppressedVisualEffects = 0;
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
@@ -275,7 +289,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
mPositioner,
mock(DisplayController.class),
syncExecutor,
- mock(Handler.class));
+ mock(Handler.class),
+ mock(SyncTransactionQueue.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
@@ -906,6 +921,65 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
groupSummary.getEntry().getSbn().getGroupKey()));
}
+
+ /**
+ * Verifies that when the user changes, the bubbles in the overflow list is cleared. Doesn't
+ * test the loading from the repository which would be a nice thing to add.
+ */
+ @Test
+ public void testOnUserChanged_overflowState() {
+ int firstUserId = mBubbleEntry.getStatusBarNotification().getUser().getIdentifier();
+ int secondUserId = mBubbleEntryUser11.getStatusBarNotification().getUser().getIdentifier();
+
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+ assertTrue(mBubbleController.hasBubbles());
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+
+ // Verify these are in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey())).isNotNull();
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2.getKey())).isNotNull();
+
+ // Switch users
+ mBubbleController.onUserChanged(secondUserId);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ // Give this user some bubbles
+ mBubbleController.updateBubble(mBubbleEntryUser11);
+ mBubbleController.updateBubble(mBubbleEntry2User11);
+ assertTrue(mBubbleController.hasBubbles());
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+
+ // Verify these are in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntryUser11.getKey())).isNotNull();
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
+
+ // Would have loaded bubbles twice because of user switch
+ verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
+ }
+
+ /**
+ * Verifies we only load the overflow data once.
+ */
+ @Test
+ public void testOverflowLoadedOnce() {
+ when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey()))
+ .thenReturn(mRow);
+ when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey()))
+ .thenReturn(mRow2);
+
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleData.dismissAll(Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.getOverflowBubbles()).isNotEmpty();
+
+ mEntryListener.onEntryRemoved(mRow, REASON_APP_CANCEL);
+ mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
+
+ verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
+ }
+
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index cd5aa9a3f9dc..7b77cb0b5b30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -32,6 +32,7 @@ import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
/**
@@ -54,11 +55,12 @@ public class TestableBubbleController extends BubbleController {
BubblePositioner positioner,
DisplayController displayController,
ShellExecutor shellMainExecutor,
- Handler shellMainHandler) {
+ Handler shellMainHandler,
+ SyncTransactionQueue syncQueue) {
super(context, data, Runnable::run, floatingContentCoordinator, dataRepository,
statusBarService, windowManager, windowManagerShellWrapper, launcherApps,
bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, displayController,
- shellMainExecutor, shellMainHandler);
+ shellMainExecutor, shellMainHandler, syncQueue);
setInflateSynchronously(true);
initialize();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 5691660b4882..8480702c57c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -41,6 +41,7 @@ import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.splitscreen.SplitScreen;
import org.junit.Before;
import org.junit.Test;
@@ -69,6 +70,7 @@ public class WMShellTest extends SysuiTestCase {
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock LegacySplitScreen mLegacySplitScreen;
+ @Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
@@ -81,7 +83,7 @@ public class WMShellTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
- Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
@@ -96,8 +98,15 @@ public class WMShellTest extends SysuiTestCase {
}
@Test
+ public void initLegacySplitScreen_registersCallbacks() {
+ mWMShell.initLegacySplitScreen(mLegacySplitScreen);
+
+ verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
+ }
+
+ @Test
public void initSplitScreen_registersCallbacks() {
- mWMShell.initSplitScreen(mLegacySplitScreen);
+ mWMShell.initSplitScreen(mSplitScreen);
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
}
diff --git a/packages/VpnDialogs/res/values-af/strings.xml b/packages/VpnDialogs/res/values-af/strings.xml
index ac82b0e0009a..88ccbd9e98dc 100644
--- a/packages/VpnDialogs/res/values-af/strings.xml
+++ b/packages/VpnDialogs/res/values-af/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingversoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit sal toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn boaan jou skerm as VPN aktief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil \'n VPN-verbinding opstel wat dit toelaat om netwerkverkeer te monitor. Aanvaar dit net as jy die bron vertrou. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verskyn op jou skerm wanneer VPN aktief is."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is gekoppel"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Tydsduur:"</string>
diff --git a/packages/VpnDialogs/res/values-am/strings.xml b/packages/VpnDialogs/res/values-am/strings.xml
index ad9773b248a4..9fc5ff43d946 100644
--- a/packages/VpnDialogs/res/values-am/strings.xml
+++ b/packages/VpnDialogs/res/values-am/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"የግንኙነት ጥያቄ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ መከታተል የሚያስችል የVPN ግንኑነት ማዋቀር ይፈልጋል። ምንጩን የሚያምኑት ብቻ ከሆኑ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ገቢር ሲሆን በማያ ገጽዎ ላይኛው ክፍል ላይ ይታያል።"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> የአውታረ መረብ ትራፊክን ለመቆጣጠር የሚያስችል የVPN ግንኙነትን ማዋቀር ይፈልጋል። ምንጩን የሚያምኑ ከሆነ ብቻ ይቀበሉ። &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ማያ ገጹ ላይ VPN ገቢር ሲሆን ይታያል።"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ተያይዟል"</string>
<string name="session" msgid="6470628549473641030">"ክፍለ ጊዜ፡"</string>
<string name="duration" msgid="3584782459928719435">"ጊዜ"</string>
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 808cde906d2f..33be6a3e458a 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
<string name="warning" msgid="809658604548412033">"‏يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة افتراضية خاصة (VPN) يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر على شاشتك عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN متصلة"</string>
<string name="session" msgid="6470628549473641030">"الجلسة"</string>
<string name="duration" msgid="3584782459928719435">"المدة:"</string>
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
index 45d8458f4d45..4669a6941cf4 100644
--- a/packages/VpnDialogs/res/values-as/strings.xml
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দৃশ্যমান হয়৷"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>এ এটা ভিপিএন সংযোগ ছেট আপ কৰিব বিচাৰে, যিটোৱে ইয়াক নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ দিয়ে। আপুনি উৎসটোক বিশ্বাস কৰিলেহে গ্ৰহণ কৰক। ভিপিএনটো সক্ৰিয় হৈ থকাৰ সময়ত আপোনাৰ স্ক্ৰীনত&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; প্ৰদৰ্শিত হয়।"</string>
<string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
<string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
diff --git a/packages/VpnDialogs/res/values-az/strings.xml b/packages/VpnDialogs/res/values-az/strings.xml
index 2bdf23ee2aa0..d8788350bb8c 100644
--- a/packages/VpnDialogs/res/values-az/strings.xml
+++ b/packages/VpnDialogs/res/values-az/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı Sorğusu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN bağlantı yaratmaq istəyir ki, bu da şəbəkə trafikini izləyə bilər. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. VPN aktiv olan zaman &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ekranın yuxarısında görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> şəbəkə trafikini izləməyə imkan verən VPN bağlantısı yaratmaq istəyir. Yalnız mənbəyə güvəndiyiniz halda qəbul edin. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN aktiv olan zaman ekranda görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN qoşuludur"</string>
<string name="session" msgid="6470628549473641030">"Sessiya:"</string>
<string name="duration" msgid="3584782459928719435">"Müddət:"</string>
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
index f40e40670bf3..a1075d22df84 100644
--- a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja joj omogućava da prati mrežni saobraćaj. Prihvatite ovo samo ako imate poverenja u izvor. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se prikazuje na ekranu kada je VPN aktivan."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 0903c8ece36b..fc3f8787b5d7 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з\'явіцца значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
<string name="session" msgid="6470628549473641030">"Сессія"</string>
<string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-bg/strings.xml b/packages/VpnDialogs/res/values-bg/strings.xml
index 9ac853d2016f..6345f1dd7911 100644
--- a/packages/VpnDialogs/res/values-bg/strings.xml
+++ b/packages/VpnDialogs/res/values-bg/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Заявка за свързване"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с виртуална частна мрежа (VPN), за да може да наблюдава мрежовия трафик. Приемете само ако източникът е надежден. Иконата &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва в долната част на екрана при активирана VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> иска да настрои връзка с VPN, за да може да наблюдава трафика в мрежата. Приемете само ако източникът е надежден. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се показва на екрана при активирана VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е свързана"</string>
<string name="session" msgid="6470628549473641030">"Сесия:"</string>
<string name="duration" msgid="3584782459928719435">"Продължителност:"</string>
diff --git a/packages/VpnDialogs/res/values-bn/strings.xml b/packages/VpnDialogs/res/values-bn/strings.xml
index 5e11fd9934b6..352b786bc009 100644
--- a/packages/VpnDialogs/res/values-bn/strings.xml
+++ b/packages/VpnDialogs/res/values-bn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"সংযোগের অনুরোধ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট-আপ করতে চাচ্ছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি যদি উৎসটিকে বিশ্বাস করেন, তাহলেই কেবল এতে সম্মতি দিন। VPN সক্রিয় থাকলে আপনার স্ক্রীনের উপরে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দেখা যাবে।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> এমন একটি VPN সংযোগ সেট আপ করতে চাইছে যেটি দিয়ে এটি নেটওয়ার্ক ট্রাফিক নিরীক্ষণ করতে পারবে। আপনি সোর্সটি বিশ্বাস করলে একমাত্র তখনই অ্যাক্সেপ্ট করুন। PN অ্যাক্টিভ থাকলে &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; আপনার স্ক্রিনে দেখা যায়।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN সংযুক্ত হয়েছে"</string>
<string name="session" msgid="6470628549473641030">"অধিবেশন:"</string>
<string name="duration" msgid="3584782459928719435">"সময়কাল:"</string>
diff --git a/packages/VpnDialogs/res/values-bs/strings.xml b/packages/VpnDialogs/res/values-bs/strings.xml
index 56812d59e106..fa5f4ea2b762 100644
--- a/packages/VpnDialogs/res/values-bs/strings.xml
+++ b/packages/VpnDialogs/res/values-bs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi podesiti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako je izvor pouzdan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se pojavi na vrhu ekrana kada je VPN aktivna."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu koja joj omogućava praćenje mrežnog saobraćaja. Prihvatite samo ako vjerujete izvoru. Kada je VPN aktivan, na ekranu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN veza uspostavljena"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-ca/strings.xml b/packages/VpnDialogs/res/values-ca/strings.xml
index 97738c316f4b..cdb754723c28 100644
--- a/packages/VpnDialogs/res/values-ca/strings.xml
+++ b/packages/VpnDialogs/res/values-ca/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Sol·licitud de connexió"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti controlar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; es mostra a la part superior de la pantalla quan la VPN està activada."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vol configurar una connexió VPN que li permeti monitorar el trànsit de xarxa. Accepta la sol·licitud només si prové d\'una font de confiança. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; és la icona que veuràs a la pantalla quan la VPN estigui activa."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN està connectada"</string>
<string name="session" msgid="6470628549473641030">"Sessió:"</string>
<string name="duration" msgid="3584782459928719435">"Durada:"</string>
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 5cc809c7cb02..c06f6ff0cf02 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žádost o připojení"</string>
<string name="warning" msgid="809658604548412033">"Aplikace <xliff:g id="APP">%s</xliff:g> žádá o nastavení připojení VPN, pomocí kterého bude moci sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, v horní části obrazovky se zobrazuje tato ikona."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikace <xliff:g id="APP">%s</xliff:g> chce nastavit připojení VPN, které umožňuje sledovat síťový provoz. Povolte, jen pokud zdroji důvěřujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; – když je síť VPN aktivní, na obrazovce se zobrazuje tato ikona."</string>
<string name="legacy_title" msgid="192936250066580964">"Síť VPN je připojena"</string>
<string name="session" msgid="6470628549473641030">"Relace:"</string>
<string name="duration" msgid="3584782459928719435">"Doba trvání:"</string>
diff --git a/packages/VpnDialogs/res/values-da/strings.xml b/packages/VpnDialogs/res/values-da/strings.xml
index 7641158af3da..a4ddc1963de8 100644
--- a/packages/VpnDialogs/res/values-da/strings.xml
+++ b/packages/VpnDialogs/res/values-da/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Forbindelsesanmodning"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-forbindelse, der giver appen mulighed for at registrere netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på din skærm, når VPN-forbindelsen er aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> anmoder om at konfigurere en VPN-forbindelse, der giver appen tilladelse til at holde øje med netværkstrafik. Du bør kun acceptere dette, hvis du har tillid til appen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på din skærm, når VPN-forbindelsen er aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilsluttet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Varighed:"</string>
diff --git a/packages/VpnDialogs/res/values-de/strings.xml b/packages/VpnDialogs/res/values-de/strings.xml
index 0f1e00980439..f38e3953855a 100644
--- a/packages/VpnDialogs/res/values-de/strings.xml
+++ b/packages/VpnDialogs/res/values-de/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindungsanfrage"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. Wenn VPN aktiv ist, wird oben im Display &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; angezeigt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> möchte eine VPN-Verbindung herstellen, über die der Netzwerkverkehr überwacht werden kann. Lass die Verbindung nur zu, wenn die App vertrauenswürdig ist. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wird auf dem Display angezeigt, wenn VPN aktiv ist."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ist verbunden"</string>
<string name="session" msgid="6470628549473641030">"Sitzung:"</string>
<string name="duration" msgid="3584782459928719435">"Dauer:"</string>
diff --git a/packages/VpnDialogs/res/values-el/strings.xml b/packages/VpnDialogs/res/values-el/strings.xml
index 78bcc43ff609..e3eb4603ea61 100644
--- a/packages/VpnDialogs/res/values-el/strings.xml
+++ b/packages/VpnDialogs/res/values-el/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Αίτημα σύνδεσης"</string>
<string name="warning" msgid="809658604548412033">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στο επάνω μέρος της οθόνης σας όταν είναι ενεργό το VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Η εφαρμογή <xliff:g id="APP">%s</xliff:g> επιθυμεί να ρυθμίσει μια σύνδεση VPN που της επιτρέπει να παρακολουθεί την επισκεψιμότητα του δικτύου. Αποδεχτείτε το αίτημα μόνο εάν εμπιστεύεστε την πηγή. Το εικονίδιο &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; εμφανίζεται στην οθόνη σας όταν είναι ενεργό το VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Το VPN συνδέθηκε"</string>
<string name="session" msgid="6470628549473641030">"Περίοδος σύνδεσης"</string>
<string name="duration" msgid="3584782459928719435">"Διάρκεια:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rAU/strings.xml b/packages/VpnDialogs/res/values-en-rAU/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rAU/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rAU/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rCA/strings.xml b/packages/VpnDialogs/res/values-en-rCA/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rGB/strings.xml b/packages/VpnDialogs/res/values-en-rGB/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rGB/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rGB/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rIN/strings.xml b/packages/VpnDialogs/res/values-en-rIN/strings.xml
index 6ed50a7668ae..cb8b79d61ace 100644
--- a/packages/VpnDialogs/res/values-en-rIN/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rIN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Connection request"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN is connected"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Duration:"</string>
diff --git a/packages/VpnDialogs/res/values-en-rXC/strings.xml b/packages/VpnDialogs/res/values-en-rXC/strings.xml
index 9d010e63518f..f5e2deb071a1 100644
--- a/packages/VpnDialogs/res/values-en-rXC/strings.xml
+++ b/packages/VpnDialogs/res/values-en-rXC/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Connection request‎‏‎‎‏‎"</string>
<string name="warning" msgid="809658604548412033">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears at the top of your screen when VPN is active.‎‏‎‎‏‎"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP">%s</xliff:g>‎‏‎‎‏‏‏‎ wants to set up a VPN connection that allows it to monitor network traffic. Only accept if you trust the source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; appears on your screen when VPN is active.‎‏‎‎‏‎"</string>
<string name="legacy_title" msgid="192936250066580964">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎VPN is connected‎‏‎‎‏‎"</string>
<string name="session" msgid="6470628549473641030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‎Session:‎‏‎‎‏‎"</string>
<string name="duration" msgid="3584782459928719435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‎Duration:‎‏‎‎‏‎"</string>
diff --git a/packages/VpnDialogs/res/values-es-rUS/strings.xml b/packages/VpnDialogs/res/values-es-rUS/strings.xml
index 21cfc042e707..108a24e7930e 100644
--- a/packages/VpnDialogs/res/values-es-rUS/strings.xml
+++ b/packages/VpnDialogs/res/values-es-rUS/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN capaz de controlar el tráfico de la red. Acéptala solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se activa la VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita supervisar el tráfico de red. Solo acéptala si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en tu pantalla cuando se active la VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"La VPN está conectada."</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-es/strings.xml b/packages/VpnDialogs/res/values-es/strings.xml
index 372147f2479a..0eaf3592a510 100644
--- a/packages/VpnDialogs/res/values-es/strings.xml
+++ b/packages/VpnDialogs/res/values-es/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitud de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN para controlar el tráfico de red. Solo debes aceptarla si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece en la parte superior de la pantalla cuando se active la conexión VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> quiere configurar una conexión VPN que le permita monitorizar el tráfico de red. Acéptalo solo si confías en la fuente. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá en la pantalla cuando la VPN esté activa."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-et/strings.xml b/packages/VpnDialogs/res/values-et/strings.xml
index c328cd725396..140c18311607 100644
--- a/packages/VpnDialogs/res/values-et/strings.xml
+++ b/packages/VpnDialogs/res/values-et/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ühendamise taotlus"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraani ülaservas, kui VPN on aktiivne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> tahab seadistada VPN-i ühenduse, mis võimaldab jälgida võrguliiklust. Nõustuge ainult siis, kui usaldate seda allikat. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; kuvatakse ekraanil, kui VPN on aktiivne."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on ühendatud"</string>
<string name="session" msgid="6470628549473641030">"Seansid"</string>
<string name="duration" msgid="3584782459928719435">"Kestus:"</string>
diff --git a/packages/VpnDialogs/res/values-eu/strings.xml b/packages/VpnDialogs/res/values-eu/strings.xml
index a3b7716e91d3..a27a66a86c9d 100644
--- a/packages/VpnDialogs/res/values-eu/strings.xml
+++ b/packages/VpnDialogs/res/values-eu/strings.xml
@@ -17,14 +17,15 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Konektatzeko eskaera"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexioa ezarri nahi du sareko trafikoa kontrolatzeko. Iturburua fidagarria bada bakarrik baimendu. &lt;br /&gt; &lt;br /&gt; VPN bidezko konexioa aktibo dagoenean, &lt;img src=vpn_icon /&gt; agertuko da pantailaren goialdean."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> aplikazioak VPN bidezko konexio bat konfiguratu nahi du sareko trafikoa gainbegiratzeko. Onartu soilik iturburuaz fidatzen bazara. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; agertzen da pantailan, VPNa aktibo dagoenean."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN sarera konektatuta dago"</string>
<string name="session" msgid="6470628549473641030">"Saioa:"</string>
<string name="duration" msgid="3584782459928719435">"Iraupena:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"Bidalita:"</string>
<string name="data_received" msgid="4062776929376067820">"Jasota:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byte / <xliff:g id="NUMBER_1">%2$s</xliff:g> pakete"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPN sarea"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"Ezin da konektatu beti aktibatuta dagoen VPNa"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP_0">%1$s</xliff:g>, baina une honetan ezin da konektatu. <xliff:g id="VPN_APP_1">%1$s</xliff:g> sarera berriro konektatu ahal izan arte, sare publiko bat erabiliko du telefonoak."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"Beti aktibatuta egoteko dago konfiguratuta <xliff:g id="VPN_APP">%1$s</xliff:g>, baina une honetan ezin da konektatu. VPN sarearen konexioa berreskuratu arte, ez duzu izango konexiorik."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
diff --git a/packages/VpnDialogs/res/values-fa/strings.xml b/packages/VpnDialogs/res/values-fa/strings.xml
index 56f847c15827..6fb5a001316e 100644
--- a/packages/VpnDialogs/res/values-fa/strings.xml
+++ b/packages/VpnDialogs/res/values-fa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"درخواست اتصال"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط در صورتی بپذیرید که به منبع آن اطمینان دارید. هنگامی که VPN فعال شد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در بالای صفحه نمایش شما نشان داده می‌شود."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> می‌خواهد یک اتصال VPN راه‌اندازی کند که به آن امکان نظارت بر ترافیک شبکه را می‌دهد. فقط درصورتی‌که به منبع اعتماد دارید قبول کنید. وقتی VPN فعال باشد، &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; در صفحه‌نمایش نشان داده می‌شود."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN متصل است"</string>
<string name="session" msgid="6470628549473641030">"جلسه:"</string>
<string name="duration" msgid="3584782459928719435">"مدت زمان:"</string>
diff --git a/packages/VpnDialogs/res/values-fi/strings.xml b/packages/VpnDialogs/res/values-fi/strings.xml
index 91c918af09c3..8abca06c2739 100644
--- a/packages/VpnDialogs/res/values-fi/strings.xml
+++ b/packages/VpnDialogs/res/values-fi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yhteyspyyntö"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> haluaa tehdä asetukset VPN-yhteydellä, jonka kautta sovellus voi valvoa verkkoliikennettä. Hyväksy vain, jos lähde on luotettava. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy ruudun yläreunassa, kun VPN on käytössä."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> haluaa muodostaa VPN-yhteyden, jonka avulla se voi valvoa verkkoliikennettä. Salli tämä vain, jos luotat lähteeseen. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; näkyy näytölläsi, kun VPN on aktiivinen."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN on yhdistetty"</string>
<string name="session" msgid="6470628549473641030">"Käyttökerta"</string>
<string name="duration" msgid="3584782459928719435">"Kesto:"</string>
diff --git a/packages/VpnDialogs/res/values-fr-rCA/strings.xml b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
index aa86c7ca8a7f..876111c26cdf 100644
--- a/packages/VpnDialogs/res/values-fr-rCA/strings.xml
+++ b/packages/VpnDialogs/res/values-fr-rCA/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt;&lt;br /&gt;&lt;img src=vpn_icon/&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> veut configurer une connexion RPV qui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche dans le haut de votre écran lorsqu\'une connexion RPV est active."</string>
<string name="legacy_title" msgid="192936250066580964">"RPV connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-fr/strings.xml b/packages/VpnDialogs/res/values-fr/strings.xml
index 71801197ddf2..27ebfb01f098 100644
--- a/packages/VpnDialogs/res/values-fr/strings.xml
+++ b/packages/VpnDialogs/res/values-fr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Demande de connexion"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche en haut de votre écran lorsqu\'une connexion VPN est active."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> souhaite configurer une connexion VPN qui lui permet de surveiller le trafic réseau. N\'acceptez que si vous faites confiance à la source. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; s\'affiche à l\'écran lorsqu\'un VPN est actif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connecté"</string>
<string name="session" msgid="6470628549473641030">"Session :"</string>
<string name="duration" msgid="3584782459928719435">"Durée :"</string>
diff --git a/packages/VpnDialogs/res/values-gl/strings.xml b/packages/VpnDialogs/res/values-gl/strings.xml
index 8a66d081a71b..cd8ee8d89474 100644
--- a/packages/VpnDialogs/res/values-gl/strings.xml
+++ b/packages/VpnDialogs/res/values-gl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitude de conexión"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permite controlar o tráfico da rede. Acepta soamente se confías na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior da pantalla cando se activa a VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A aplicación <xliff:g id="APP">%s</xliff:g> quere configurar unha conexión VPN que lle permita supervisar o tráfico de rede. Acepta só se confías nela. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na pantalla cando a VPN está activa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está conectada"</string>
<string name="session" msgid="6470628549473641030">"Sesión:"</string>
<string name="duration" msgid="3584782459928719435">"Duración:"</string>
diff --git a/packages/VpnDialogs/res/values-gu/strings.xml b/packages/VpnDialogs/res/values-gu/strings.xml
index 961711c57c3d..5ffdcb1d8079 100644
--- a/packages/VpnDialogs/res/values-gu/strings.xml
+++ b/packages/VpnDialogs/res/values-gu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"કનેક્શન વિનંતી"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN કનેક્શન સેટ કરવા માગે છે જે તેને નેટવર્ક ટ્રાફિક મૉનિટર કરવાની મંજૂરી આપે છે. જો તમને સ્રોત પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીનની ટોચ પર ત્યારે દેખાય છે જ્યારે VPN સક્રિય હોય છે."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> એક એવું VPN કનેક્શન સેટ કરવા માગે છે કે જે તેને નેટવર્ક ટ્રાફિકનું નિરીક્ષણ કરવાની મંજૂરી આપતું હોય. જો તમને સૉર્સ પર વિશ્વાસ હોય તો જ સ્વીકારો. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; તમારી સ્ક્રીન પર ત્યારે દેખાય છે, જ્યારે VPN સક્રિય હોય છે."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN કનેક્ટ કરેલું છે"</string>
<string name="session" msgid="6470628549473641030">"સત્ર:"</string>
<string name="duration" msgid="3584782459928719435">"અવધિ:"</string>
@@ -28,7 +29,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>ને હંમેશાં જોડાયેલ રહેવા માટે સેટ કરેલ છે, પરંતુ તે હાલમાં કનેક્ટ કરી શકાતું નથી. તમારો ફોન જ્યાં સુધી <xliff:g id="VPN_APP_1">%1$s</xliff:g> સાથે ફરીથી કનેક્ટ ન થાય ત્યાં સુધી તે સાર્વજનિક નેટવર્કનો ઉપયોગ કરશે."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>ને હંમેશાં જોડાયેલ રહેવા માટે સેટ કરેલ છે, પરંતુ તે હાલમાં કનેક્ટ કરી શકાતું નથી. VPN ફરીથી કનેક્ટ ન થઈ શકે ત્યાં સુધી તમારી પાસે કોઈ કનેક્શન હશે નહીં."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN સેટિંગ્સ બદલો"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN સેટિંગ બદલો"</string>
<string name="configure" msgid="4905518375574791375">"ગોઠવો"</string>
<string name="disconnect" msgid="971412338304200056">"ડિસ્કનેક્ટ કરો"</string>
<string name="open_app" msgid="3717639178595958667">"ઍપ ખોલો"</string>
diff --git a/packages/VpnDialogs/res/values-hi/strings.xml b/packages/VpnDialogs/res/values-hi/strings.xml
index eed0858787d9..c9c65d5ac593 100644
--- a/packages/VpnDialogs/res/values-hi/strings.xml
+++ b/packages/VpnDialogs/res/values-hi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्शन अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> वीपीएन कनेक्‍शन सेट अप करना चाहता है, जिससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. इसकी मंज़ूरी तभी दें जब आपको इस पर भरोसा हो. वीपीएन चालू होने पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; आपकी स्क्रीन के सबसे ऊपर दिखाई देता है."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> को वीपीएन कनेक्शन सेट अप करने की अनुमति चाहिए. इससे वह नेटवर्क ट्रैफ़िक पर नज़र रख पाएगा. अनुमति तब दें, जब आपको ऐप्लिकेशन पर भरोसा हो. वीपीएन चालू होने पर, आपकी स्क्रीन पर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिखेगा."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट है"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
diff --git a/packages/VpnDialogs/res/values-hr/strings.xml b/packages/VpnDialogs/res/values-hr/strings.xml
index aa9e436f56e7..576d99761571 100644
--- a/packages/VpnDialogs/res/values-hr/strings.xml
+++ b/packages/VpnDialogs/res/values-hr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahtjev za povezivanje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kada je VPN aktivan, pri vrhu zaslona prikazuje se &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi postaviti VPN vezu pomoću koje će moći nadzirati mrežni promet. Prihvatite samo ako smatrate izvor pouzdanim. Kad je VPN aktivan, na zaslonu se prikazuje ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN je spojen"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-hu/strings.xml b/packages/VpnDialogs/res/values-hu/strings.xml
index 703aa792f3c3..69b999fee1e1 100644
--- a/packages/VpnDialogs/res/values-hu/strings.xml
+++ b/packages/VpnDialogs/res/values-hu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kapcsolódási kérés"</string>
<string name="warning" msgid="809658604548412033">"A(z) <xliff:g id="APP">%s</xliff:g> VPN kapcsolatot akar beállítani, amelynek segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor a VPN aktív, &lt;img src=vpn_icon /&gt; ikon jelenik meg a képernyő tetején."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A(z) <xliff:g id="APP">%s</xliff:g> alkalmazás VPN-kapcsolatot szeretne beállítani, amely segítségével figyelheti a hálózati forgalmat. Csak akkor fogadja el, ha megbízik a forrásban. &lt;br /&gt; &lt;br /&gt; Amikor aktív a VPN, a következő ikon látható a képernyőn: &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN csatlakoztatva van"</string>
<string name="session" msgid="6470628549473641030">"Munkamenet:"</string>
<string name="duration" msgid="3584782459928719435">"Időtartam:"</string>
diff --git a/packages/VpnDialogs/res/values-hy/strings.xml b/packages/VpnDialogs/res/values-hy/strings.xml
index c296c8547283..d2a6d421592c 100644
--- a/packages/VpnDialogs/res/values-hy/strings.xml
+++ b/packages/VpnDialogs/res/values-hy/strings.xml
@@ -17,7 +17,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Միացման հայց"</string>
- <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին: Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+ <string name="warning" msgid="809658604548412033">"«<xliff:g id="APP">%s</xliff:g>» հավելվածը ցանկանում է VPN կապ հաստատել՝ ցանցային երթևեկը հսկելու համար: Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվ լինի, ձեր էկրանի վերին հատվածում կհայտնվի &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերը:"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> հավելվածն ուզում է միանալ VPN-ի ցանցին՝ թրաֆիկին հետևելու համար։ Թույլատրեք, միայն եթե վստահում եք աղբյուրին։ Երբ VPN-ն ակտիվացված լինի, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; պատկերակը կհայտնվի ձեր էկրանին։"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ը կապակցված է"</string>
<string name="session" msgid="6470628549473641030">"Աշխատաշրջան`"</string>
<string name="duration" msgid="3584782459928719435">"Տևողությունը՝"</string>
@@ -25,7 +26,7 @@
<string name="data_received" msgid="4062776929376067820">"Ստացվել է՝"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> բայթ / <xliff:g id="NUMBER_1">%2$s</xliff:g> փաթեթ"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Չի հաջողվում միանալ միշտ միացված VPN-ին"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին:"</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Ձեր հեռախոսը կօգտագործի հանրային ցանցը, մինչև նորից կարողանա միանալ <xliff:g id="VPN_APP_1">%1$s</xliff:g>-ին։"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ն այնպես է կարգավորված, որ միշտ միացած մնա, սակայն ներկայումս կապակցման խնդիր կա: Մինչև VPN-ը նորից չմիանա, դուք կապ չեք ունենա:"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Փոխել VPN-ի կարգավորումները"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 18ef372a8cda..059008f3674e 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan sambungan VPN yang memungkinkannya memantau traffic jaringan. Terima hanya jika Anda memercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di bagian atas layar Anda saat VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyiapkan koneksi VPN yang memungkinkannya memantau traffic jaringan. Hanya terima jika Anda memercayai sumbernya. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul di layar bila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN tersambung"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Durasi:"</string>
diff --git a/packages/VpnDialogs/res/values-is/strings.xml b/packages/VpnDialogs/res/values-is/strings.xml
index 70fb40fc467c..a75371d77951 100644
--- a/packages/VpnDialogs/res/values-is/strings.xml
+++ b/packages/VpnDialogs/res/values-is/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Beiðni um tengingu"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til þess að geta fylgst með netumferð. Samþykktu þetta aðeins ef þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist efst á skjánum þegar VPN er virkt."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill setja upp VPN-tengingu til að fylgjast með netumferð. Ekki samþykkja þú treystir upprunanum. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; birtist á skjánum hjá þér þegar VPN er virkt."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tengt"</string>
<string name="session" msgid="6470628549473641030">"Lota:"</string>
<string name="duration" msgid="3584782459928719435">"Tímalengd:"</string>
diff --git a/packages/VpnDialogs/res/values-it/strings.xml b/packages/VpnDialogs/res/values-it/strings.xml
index 2602493faf00..c443c510198e 100644
--- a/packages/VpnDialogs/res/values-it/strings.xml
+++ b/packages/VpnDialogs/res/values-it/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Richiesta di connessione"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vuole impostare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, nella parte superiore dello schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vuole configurare una connessione VPN che le consenta di monitorare il traffico di rete. Accetta soltanto se ritieni la fonte attendibile. Quando la connessione VPN è attiva, sullo schermo viene visualizzata l\'icona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN connessa"</string>
<string name="session" msgid="6470628549473641030">"Sessione:"</string>
<string name="duration" msgid="3584782459928719435">"Durata:"</string>
diff --git a/packages/VpnDialogs/res/values-iw/strings.xml b/packages/VpnDialogs/res/values-iw/strings.xml
index ebabd4e71aef..81903d2b2442 100644
--- a/packages/VpnDialogs/res/values-iw/strings.xml
+++ b/packages/VpnDialogs/res/values-iw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"בקשת חיבור"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> רוצה להגדיר חיבור VPN שיאפשר לו לפקח על תעבורת הרשת. אשר את הבקשה רק אם אתה נותן אמון במקור. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע בחלק העליון של המסך כאשר VPN פעיל."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏האפליקציה <xliff:g id="APP">%s</xliff:g> מבקשת להגדיר חיבור VPN שבאמצעותו היא תנהל מעקב אחר התנועה ברשת. יש לאשר את הבקשה רק אם המקור נראה לך אמין. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; מופיע על המסך כאשר חיבור ה-VPN פעיל."</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN מחובר"</string>
<string name="session" msgid="6470628549473641030">"הפעלה"</string>
<string name="duration" msgid="3584782459928719435">"משך:"</string>
diff --git a/packages/VpnDialogs/res/values-ja/strings.xml b/packages/VpnDialogs/res/values-ja/strings.xml
index 8480692e9dd3..e03e9d38087b 100644
--- a/packages/VpnDialogs/res/values-ja/strings.xml
+++ b/packages/VpnDialogs/res/values-ja/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"接続リクエスト"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> がネットワーク トラフィックを監視するため VPN 接続をセットアップしようとしています。信頼できるソースである場合にのみ許可してください。&lt;br /&gt; &lt;br /&gt; VPN がアクティブになると画面の上部に &lt;img src=vpn_icon /&gt; が表示されます。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> は、ネットワーク トラフィックを監視できるよう、VPN 接続を設定するよう求めています。ソースを信頼できる場合のみ、許可してください。VPN が有効になると、画面に &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; が表示されます。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN接続済み"</string>
<string name="session" msgid="6470628549473641030">"セッション:"</string>
<string name="duration" msgid="3584782459928719435">"期間:"</string>
diff --git a/packages/VpnDialogs/res/values-ka/strings.xml b/packages/VpnDialogs/res/values-ka/strings.xml
index e5a07532c32e..9c4388e0e30e 100644
--- a/packages/VpnDialogs/res/values-ka/strings.xml
+++ b/packages/VpnDialogs/res/values-ka/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"კავშირის მოთხოვნა"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. მიიღოთ მხოლოდ ისეთ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენი ეკრანის სიის თავში გამოჩნდება, როდესაც VPN აქტიურია."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>-ს სურს დააყენოს VPN კავშირი, რაც ქსელის ტრაფიკის მონიტორინგის საშუალებას იძლევა. დათანხმდით მხოლოდ იმ შემთხვევაში, თუ წყაროს ენდობით. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; თქვენს ეკრანზე გამოჩნდება, როდესაც VPN აქტიურია."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN დაკავშირებულია"</string>
<string name="session" msgid="6470628549473641030">"სესია:"</string>
<string name="duration" msgid="3584782459928719435">"ხანგრძლივობა:"</string>
diff --git a/packages/VpnDialogs/res/values-kk/strings.xml b/packages/VpnDialogs/res/values-kk/strings.xml
index 79f79c34e1b4..9a499d346ef7 100644
--- a/packages/VpnDialogs/res/values-kk/strings.xml
+++ b/packages/VpnDialogs/res/values-kk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Байланысты сұрау"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> VPN байланысын орнатқысы келеді, бұл оған желілік трафикті бақылауға мүмкіндік береді. Көзге сенсеңіз ғана қабылдаңыз. VPN белсенді болғанда экранның жоғарғы жағында &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; көрсетіледі."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> қолданбасы VPN байланысын орнатқысы келеді, бұл оған желі трафигін бақылауға мүмкіндік береді. Сұрауды қабылдамас бұрын, дереккөздің сенімді екеніне көз жеткізіңіз. VPN белсенді болған кезде, экранда &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; белгішесі пайда болады."</string>
<string name="legacy_title" msgid="192936250066580964">"ВЖЖ қосылған"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Ұзақтығы:"</string>
diff --git a/packages/VpnDialogs/res/values-km/strings.xml b/packages/VpnDialogs/res/values-km/strings.xml
index 06f34dbf2733..0ed2e84b80fa 100644
--- a/packages/VpnDialogs/res/values-km/strings.xml
+++ b/packages/VpnDialogs/res/values-km/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"សំណើ​សុំ​ការ​តភ្ជាប់"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ចង់​បង្កើត​ការ​តភ្ជាប់ VPN ដែល​អនុញ្ញាត​ឲ្យ​វា​ត្រួតពិនិត្យ​ចរាចរ​បណ្ដាញ។ ព្រម​ទទួល ប្រសិនបើ​អ្នក​ទុកចិត្ត​លើ​ប្រភព​តែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; នឹង​លេចឡើង​នៅ​ផ្នែក​ខាងលើ​នៃ​អេក្រង់​របស់​អ្នក ពេល VPN សកម្ម។"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ចង់រៀបចំការតភ្ជាប់ VPN ដែលអនុញ្ញាតឱ្យវាត្រួតពិនិត្យ​ចរាចរណ៍បណ្តាញ។ យល់ព្រម ប្រសិនបើអ្នកជឿទុកចិត្តលើប្រភពនេះតែប៉ុណ្ណោះ។ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; បង្ហាញនៅលើអេក្រង់របស់អ្នក នៅពេល VPN កំពុងដំណើរការ។"</string>
<string name="legacy_title" msgid="192936250066580964">"បា​ន​ភ្ជាប់ VPN"</string>
<string name="session" msgid="6470628549473641030">"សម័យ៖"</string>
<string name="duration" msgid="3584782459928719435">"ថិរវេលា៖"</string>
diff --git a/packages/VpnDialogs/res/values-kn/strings.xml b/packages/VpnDialogs/res/values-kn/strings.xml
index 040cd6c5aeda..6308f1844bfd 100644
--- a/packages/VpnDialogs/res/values-kn/strings.xml
+++ b/packages/VpnDialogs/res/values-kn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ಸಂಪರ್ಕ ವಿನಂತಿ"</string>
<string name="warning" msgid="809658604548412033">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನೀವು ಮೂಲವನ್ನು ನಂಬಿದರೆ ಮಾತ್ರ ಸಮ್ಮತಿಸಿ. VPN ಸಕ್ರಿಯವಾಗಿರುವಾಗ ನಿಮ್ಮ ಪರದೆಯ ಮೇಲ್ಭಾಗದಲ್ಲಿ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ಗೋರಿಸುತ್ತದೆ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಅನ್ನು ಮೇಲ್ವಿಚಾರಣೆ ಮಾಡಲು ಅನುಮತಿಸುವಂತಹ VPN ಸಂಪರ್ಕವನ್ನು ಹೊಂದಿಸಲು <xliff:g id="APP">%s</xliff:g> ಬಯಸುತ್ತದೆ. ನಿಮಗೆ ಮೂಲದ ಮೇಲೆ ನಂಬಿಕೆ ಇದ್ದರೆ ಮಾತ್ರ ಸ್ವೀಕರಿಸಿ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ಸಕ್ರಿಯವಾದ ನಂತರ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುತ್ತದೆ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="session" msgid="6470628549473641030">"ಸೆಷನ್:"</string>
<string name="duration" msgid="3584782459928719435">"ಅವಧಿ:"</string>
diff --git a/packages/VpnDialogs/res/values-ko/strings.xml b/packages/VpnDialogs/res/values-ko/strings.xml
index 6ad497680ae7..6e179bb9bcb0 100644
--- a/packages/VpnDialogs/res/values-ko/strings.xml
+++ b/packages/VpnDialogs/res/values-ko/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"연결 요청"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링하도록 허용하는 VPN 연결을 설정하려고 합니다. 출처를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성화되면 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;이 화면 위에 표시됩니다."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>에서 네트워크 트래픽을 모니터링할 수 있도록 VPN 연결을 설정하려고 합니다. 소스를 신뢰할 수 있는 경우에만 수락하세요. VPN이 활성 상태일 때는 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; 아이콘이 화면에 표시됩니다."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN이 연결되었습니다."</string>
<string name="session" msgid="6470628549473641030">"세션:"</string>
<string name="duration" msgid="3584782459928719435">"기간:"</string>
diff --git a/packages/VpnDialogs/res/values-ky/strings.xml b/packages/VpnDialogs/res/values-ky/strings.xml
index 23c9be8819a8..31f9e2da11c7 100644
--- a/packages/VpnDialogs/res/values-ky/strings.xml
+++ b/packages/VpnDialogs/res/values-ky/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Туташуу сурамы"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> тармактык трафикти көзөмөлдөөгө уруксат берген VPN туташуусун орноткусу келет. Аны булакка ишенсеңиз гана кабыл алыңыз. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN иштеп турганда экраныңыздын жогору жагынан көрүнөт."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> тармак трафигин көзөмөлдөөгө уруксат берген VPN байланышын орноткусу келет. Булакка ишенсеңиз гана кабыл алыңыз. VPN иштеп жатканда, экраныңызда &lt;br /&gt; &lt;br /&gt; &amp;It;img src=vpn_icon /&gt; көрүнөт."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN байланышта"</string>
<string name="session" msgid="6470628549473641030">"Сессия:"</string>
<string name="duration" msgid="3584782459928719435">"Узактыгы:"</string>
diff --git a/packages/VpnDialogs/res/values-lo/strings.xml b/packages/VpnDialogs/res/values-lo/strings.xml
index c591308480c1..cec69f0fe9c0 100644
--- a/packages/VpnDialogs/res/values-lo/strings.xml
+++ b/packages/VpnDialogs/res/values-lo/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ການ​ຮ້ອງ​ຂໍ​ການ​ເຊື່ອມ​ຕໍ່"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງ​ການ​ຕັ້​ງ​ຄ່າ​ການ​ເຊື່ອມ​ຕໍ່ VPN ທີ່​ອະ​ນຸ​ຍາດ​ໃຫ້​ຕິດ​ຕາມ​ທຣາບ​ຟິກ​ເຄືອ​ຂ່າຍ​ໄດ້. ​ທ່ານ​​ຄວນ​​ຍິນຍອມ​ສະ​ເພາະ​ໃນ​ກໍ​ລະ​ນີ​ທີ່​ທ່ານ​ເຊື່ອ​ຖື​ແຫລ່ງ​ຂໍ້​ມູນ​ເທົ່າ​ນັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະ​ປາ​ກົດ​ຢູ່​ດ້ານ​ເທິງ​ຂອງ​ໜ້າ​ຈໍ​ເມື່ອ​ມີ​ການ​ເປີດ​ໃຊ້ VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ຕ້ອງການຕັ້ງຄ່າການເຊື່ອມຕໍ່ VPN ທີ່ອະນຸຍາດໃຫ້ມັນກວດກາການ​ຈາ​ລະ​ຈອນ​ເຄືອ​ຂ່າ​ຍໄດ້. ໃຫ້ຍອມຮັບສະເພາະໃນກໍລະນີທີ່ທ່ານເຊື່ອຖືແຫຼ່ງທີ່ມາເທົ່ານັ້ນ. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ຈະປາກົດຢູ່ໜ້າຈໍຂອງທ່ານເມື່ອເປີດໃຊ້ VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
<string name="session" msgid="6470628549473641030">"ເຊສຊັນ:"</string>
<string name="duration" msgid="3584782459928719435">"ໄລຍະເວລາ:"</string>
diff --git a/packages/VpnDialogs/res/values-lt/strings.xml b/packages/VpnDialogs/res/values-lt/strings.xml
index 8846310730ce..97abd0d66eb3 100644
--- a/packages/VpnDialogs/res/values-lt/strings.xml
+++ b/packages/VpnDialogs/res/values-lt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ryšio užklausa"</string>
<string name="warning" msgid="809658604548412033">"„<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; rodoma ekrano viršuje, kai VPN aktyvus."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Programa „<xliff:g id="APP">%s</xliff:g>“ nori nustatyti VPN ryšį, kad galėtų stebėti tinklo srautą. Sutikite, tik jei pasitikite šaltiniu. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; piktograma rodoma ekrane, kai VPN aktyvus."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN prijungtas"</string>
<string name="session" msgid="6470628549473641030">"Sesija"</string>
<string name="duration" msgid="3584782459928719435">"Trukmė:"</string>
diff --git a/packages/VpnDialogs/res/values-lv/strings.xml b/packages/VpnDialogs/res/values-lv/strings.xml
index 07625b6173c6..6341fbdf0158 100644
--- a/packages/VpnDialogs/res/values-lv/strings.xml
+++ b/packages/VpnDialogs/res/values-lv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Savienojuma pieprasījums"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; tiek rādīta ekrāna augšdaļā, kad darbojas VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Lietotne <xliff:g id="APP">%s</xliff:g> vēlas izveidot VPN savienojumu, kas ļaus pārraudzīt tīkla datplūsmu. Piekrītiet tikai tad, ja uzticaties avotam. &lt;br /&gt; &lt;br /&gt; Ekrānā tiek rādīta ikona &lt;img src=vpn_icon /&gt;, kad darbojas VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Ir izveidots savienojums ar VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesija:"</string>
<string name="duration" msgid="3584782459928719435">"Ilgums:"</string>
diff --git a/packages/VpnDialogs/res/values-mk/strings.xml b/packages/VpnDialogs/res/values-mk/strings.xml
index b5a64f213066..689d028fd724 100644
--- a/packages/VpnDialogs/res/values-mk/strings.xml
+++ b/packages/VpnDialogs/res/values-mk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Барање за поврзување"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со ВПН коешто му дозволува да го набљудува сообраќајот на мрежата. Прифатете само доколку му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се појавува на врвот на екранот кога ВПН е активна."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> сака да постави поврзување со VPN што ќе дозволи да го набљудува сообраќајот на мрежата. Прифатете само ако му верувате на изворот. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ќе се појави на екранот кога ќе се активира VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN е поврзана"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Времетраење:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Променете ги поставките за VPN"</string>
<string name="configure" msgid="4905518375574791375">"Конфигурирај"</string>
- <string name="disconnect" msgid="971412338304200056">"Исклучи"</string>
+ <string name="disconnect" msgid="971412338304200056">"Прекини врска"</string>
<string name="open_app" msgid="3717639178595958667">"Отвори ја апликацијата"</string>
<string name="dismiss" msgid="6192859333764711227">"Отфрли"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-ml/strings.xml b/packages/VpnDialogs/res/values-ml/strings.xml
index 680d0ef539b7..8284a78c26f8 100644
--- a/packages/VpnDialogs/res/values-ml/strings.xml
+++ b/packages/VpnDialogs/res/values-ml/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"കണക്ഷൻ അഭ്യർത്ഥന"</string>
<string name="warning" msgid="809658604548412033">"നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ <xliff:g id="APP">%s</xliff:g> സജ്ജീകരിക്കേണ്ടതുണ്ട്. ഉറവിടം പരിചിതമാണെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്ക്രീനിന്റെ മുകളിൽ ദൃശ്യമാകുന്നു."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"നെറ്റ്‌വർക്ക് ട്രാഫിക് നിരീക്ഷിക്കാൻ അനുവദിക്കുന്ന ഒരു VPN കണക്ഷൻ സജ്ജീകരിക്കാൻ <xliff:g id="APP">%s</xliff:g> താൽപ്പര്യപ്പെടുന്നു. നിങ്ങൾ ഉറവിടം വിശ്വസിക്കുന്നുണ്ടെങ്കിൽ മാത്രം അംഗീകരിക്കുക. VPN സജീവമാകുമ്പോൾ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകും."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN കണക്‌റ്റുചെയ്‌തു"</string>
<string name="session" msgid="6470628549473641030">"സെഷൻ:"</string>
<string name="duration" msgid="3584782459928719435">"സമയദൈര്‍ഘ്യം:"</string>
diff --git a/packages/VpnDialogs/res/values-mn/strings.xml b/packages/VpnDialogs/res/values-mn/strings.xml
index 9aa104aff5ab..1dd4c15c43bb 100644
--- a/packages/VpnDialogs/res/values-mn/strings.xml
+++ b/packages/VpnDialogs/res/values-mn/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Холболтын хүсэлт"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> нь сүлжээний трафикыг хянах боломж бүхий VPN холболт үүсгэхийг хүсэж байна. Та зөвхөн эх үүсвэрт итгэж байгаа бол зөвшөөрнө үү. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; таны дэлгэц дээр VPN идэвхтэй үед гарч ирнэ."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> нь түүнд сүлжээний ачааллыг хянах боломжийг олгодог VPN холболт тохируулахыг хүсэж байна. Зөвхөн та эх сурвалжид итгэдэг тохиолдолд зөвшөөрнө үү. VPN идэвхтэй үед таны дэлгэц дээр &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; харагдана."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN холбогдов"</string>
<string name="session" msgid="6470628549473641030">"Сешн:"</string>
<string name="duration" msgid="3584782459928719435">"Үргэлжлэх хугацаа:"</string>
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index 41d74290815d..22fb502129a5 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -18,18 +18,19 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"कनेक्‍शन विनंती"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> नेटवर्क रहदारीचे परीक्षण करण्‍यासाठी त्यास अनुमती देणारे VPN कनेक्‍शन सेट करू इच्‍छितो. तुम्हाला स्रोत विश्वसनीय वाटत असेल तरच स्वीकार करा. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN सक्रिय असताना आपल्‍या स्क्रीनच्या शीर्षावर दिसते."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ला नेटवर्क ट्रॅफिकवर लक्ष ठेवण्याची अनुमती देणारे VPN कनेक्‍शन सेट करायचे आहे. तुमचा स्रोतावर विश्वास असेल तरच स्वीकारा. VPN अ‍ॅक्टिव्ह असल्यास, तुमच्या स्क्रीनवर &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; दिसते."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN कनेक्‍ट केले"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"कालावधी:"</string>
<string name="data_transmitted" msgid="7988167672982199061">"प्रेषित:"</string>
<string name="data_received" msgid="4062776929376067820">"प्राप्त झाले:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> बाइट / <xliff:g id="NUMBER_1">%2$s</xliff:g> पॅकेट"</string>
- <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम चालू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
+ <string name="always_on_disconnected_title" msgid="1906740176262776166">"कायम सुरू असलेल्या VPN शी कनेक्ट करू शकत नाही"</string>
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. <xliff:g id="VPN_APP_1">%1$s</xliff:g> शी पुन्हा कनेक्ट होईपर्यंत तुमचा फोन सार्वजनिक नेटवर्क वापरेल."</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"‍डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अ‍ॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/VpnDialogs/res/values-ms/strings.xml b/packages/VpnDialogs/res/values-ms/strings.xml
index b489f2edabc0..c9961d2654e2 100644
--- a/packages/VpnDialogs/res/values-ms/strings.xml
+++ b/packages/VpnDialogs/res/values-ms/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Permintaan sambungan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl memantau trafik rangkaian. Terima hanya jika anda mempercayai sumber. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; terpapar pada bahagian atas skrin anda apabila VPN aktif."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ingin menyediakan sambungan VPN yang membenarkan apl tersebut memantau trafik rangkaian. Hanya terima jika anda mempercayai sumber tersebut. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; muncul pada skrin anda apabila VPN aktif."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN telah disambungkan"</string>
<string name="session" msgid="6470628549473641030">"Sesi:"</string>
<string name="duration" msgid="3584782459928719435">"Tempoh:"</string>
diff --git a/packages/VpnDialogs/res/values-my/strings.xml b/packages/VpnDialogs/res/values-my/strings.xml
index 9d60ff42a7cd..36348c8b5c8c 100644
--- a/packages/VpnDialogs/res/values-my/strings.xml
+++ b/packages/VpnDialogs/res/values-my/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ချိတ်ဆက်ရန် တောင်းဆိုချက်"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက် လုပ်ငန်းကို စောင့်ကြည့်ခွင့် ပြုမည့် VPN ချိတ်ဆက်မှုကို ထူထောင်လိုသည်။ ရင်းမြစ်ကို သင်က ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; မှာ VPN အလုပ်လုပ်နေလျှင် သင်၏ မျက်နှာပြင် ထိပ်မှာ ပေါ်လာမည်။"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> က ကွန်ရက်ဒေတာ စီးဆင်းမှုကို စောင့်ကြည့်ရန် ခွင့်ပြုသည့် VPN ချိတ်ဆက်မှုကို စနစ်ထည့်သွင်းလိုသည်။ ဤရင်းမြစ်ကို သင်ယုံကြည်မှသာ လက်ခံပါ။ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; သည် VPN ဖွင့်ထားသောအခါ သင့်ဖန်သားပြင်တွင် ပေါ်ပါသည်။"</string>
<string name="legacy_title" msgid="192936250066580964">"VPNနှင့်ချိတ်ဆက်ထားသည်"</string>
<string name="session" msgid="6470628549473641030">"သတ်မှတ်ပေးထားသည့်အချိန်:"</string>
<string name="duration" msgid="3584782459928719435">"အချိန်ကာလ-"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ဆက်တင်များ ပြောင်းရန်"</string>
<string name="configure" msgid="4905518375574791375">"ပုံပေါ်စေသည်"</string>
- <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်ခြင်းရပ်ရန်"</string>
+ <string name="disconnect" msgid="971412338304200056">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="open_app" msgid="3717639178595958667">"အက်ပ်ကို ဖွင့်ရန်"</string>
<string name="dismiss" msgid="6192859333764711227">"ပယ်ရန်"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nb/strings.xml b/packages/VpnDialogs/res/values-nb/strings.xml
index be572d4408f8..14c84d702712 100644
--- a/packages/VpnDialogs/res/values-nb/strings.xml
+++ b/packages/VpnDialogs/res/values-nb/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Tilkoblingsforespørsel"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ønsker å bruke en VPN-tilkobling som tillater at appen overvåker nettverkstrafikken. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises øverst på skjermen din når VPN er aktivert."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vil konfigurere en VPN-tilkobling som lar appen overvåke nettverkstrafikk. Du bør bare godta dette hvis du stoler på kilden. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; vises på skjermen når VPN er aktivert."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN er tilkoblet"</string>
<string name="session" msgid="6470628549473641030">"Økt:"</string>
<string name="duration" msgid="3584782459928719435">"Varighet:"</string>
diff --git a/packages/VpnDialogs/res/values-ne/strings.xml b/packages/VpnDialogs/res/values-ne/strings.xml
index b716c35cfad4..2a5648d147c1 100644
--- a/packages/VpnDialogs/res/values-ne/strings.xml
+++ b/packages/VpnDialogs/res/values-ne/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"जडान अनुरोध"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ले नेटवर्क यातायात अनुगमन गर्न अनुमति दिने VPN जडान स्थापना गर्न चाहन्छ। तपाईँले स्रोत भरोसा छ भने मात्र स्वीकार गर्नुहोस्। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; जब VPN सक्रिय हुन्छ आफ्नो स्क्रिनको माथि देखा पर्छन्।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ले कुनै VPN कनेक्सन सेटअप गर्न चाहन्छ। यसको सहायताले यो एप नेटवर्क ट्राफिकको निगरानी राख्न सक्छ। तपाईं यो एपमाथि विश्वास गर्नुहुन्छ भने मात्र स्वीकार गर्नुहोस्। VPN सक्रिय हुँदा तपाईंको स्क्रिनमा &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; देखा पर्छ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN जोडिएको छ"</string>
<string name="session" msgid="6470628549473641030">"सत्र:"</string>
<string name="duration" msgid="3584782459928719435">"अवधि:"</string>
@@ -30,7 +31,7 @@
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सम्बन्धी सेटिङहरू परिवर्तन गर्नुहोस्"</string>
<string name="configure" msgid="4905518375574791375">"कन्फिगर गर्नुहोस्"</string>
- <string name="disconnect" msgid="971412338304200056">"विच्छेदन गर्नुहोस्"</string>
- <string name="open_app" msgid="3717639178595958667">"अनुप्रयोग खोल्नुहोस्"</string>
+ <string name="disconnect" msgid="971412338304200056">"डिस्कनेक्ट गर्नुहोस्"</string>
+ <string name="open_app" msgid="3717639178595958667">"एप खोल्नुहोस्"</string>
<string name="dismiss" msgid="6192859333764711227">"खारेज गर्नुहोस्"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-nl/strings.xml b/packages/VpnDialogs/res/values-nl/strings.xml
index 8073b09e203c..33f8a89a1562 100644
--- a/packages/VpnDialogs/res/values-nl/strings.xml
+++ b/packages/VpnDialogs/res/values-nl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Verbindingsverzoek"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding opzetten om netwerkverkeer te controleren. Accepteer het verzoek alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; wordt boven aan je scherm weergegeven wanneer VPN actief is."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> wil een VPN-verbinding instellen waarmee de app het netwerkverkeer kan bijhouden. Accepteer dit alleen als je de bron vertrouwt. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; verschijnt op je scherm als het VPN actief is."</string>
<string name="legacy_title" msgid="192936250066580964">"Verbinding met VPN"</string>
<string name="session" msgid="6470628549473641030">"Sessie:"</string>
<string name="duration" msgid="3584782459928719435">"Duur:"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index f1122ebd4386..0604b47cdb50 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ସଂଯୋଗ ଅନୁରୋଧ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହି ନେଟ୍‌ୱର୍କର ଟ୍ରାଫିକକୁ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦିଏ। ଆପଣ ସୋର୍ସ ଉପରେ ବିଶ୍ୱାସ କରିବା ବଦଳରେ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବାବେଳେ ଏହା ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‍ର ଉପରେ ଦେଖାଯାଏ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ଏକ VPN ସଂଯୋଗ ସେଟ୍ ଅପ୍ କରିବାକୁ ଚାହେଁ, ଯାହା ଏହାକୁ ନେଟୱାର୍କ ଟ୍ରାଫିକ ମନିଟର୍ କରିବାକୁ ଅନୁମତି ଦେଇଥାଏ। ଯଦି ଆପଣ ସୋର୍ସରେ ବିଶ୍ୱାସ କରୁଛନ୍ତି, ତେବେ ହିଁ କେବଳ ସ୍ୱୀକାର କରନ୍ତୁ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN ସକ୍ରିୟ ଥିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ ଉପରେ ଦେଖାଯାଏ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
<string name="session" msgid="6470628549473641030">"ସେସନ୍‍:"</string>
<string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
@@ -28,7 +29,7 @@
<string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇ ରହିବା ପାଇଁ ସେଟଅପ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ଫୋନ୍‍, <xliff:g id="VPN_APP_1">%1$s</xliff:g> ସହ କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଏକ ପବ୍ଲିକ୍‍ ନେଟ୍‌ୱର୍କ ବ୍ୟବହାର କରିବ।"</string>
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> ସବୁ ସମୟରେ କନେକ୍ଟ ହୋଇରହିବାକୁ ସେଟଅପ୍‍ କରାଯାଇଛି, କିନ୍ତୁ ଏହା ବର୍ତ୍ତମାନ କନେକ୍ଟ କରିପାରୁ ନାହିଁ। VPN ପୁଣି କନେକ୍ଟ ନହେବା ପର୍ଯ୍ୟନ୍ତ ଆପଣଙ୍କର କୌଣସି କନେକ୍ସନ୍‌ ରହିବନାହିଁ।"</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଙ୍ଗ ବଦଳାନ୍ତୁ"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ୍ ବଦଳାନ୍ତୁ"</string>
<string name="configure" msgid="4905518375574791375">"କନଫିଗର୍‍ କରନ୍ତୁ"</string>
<string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="open_app" msgid="3717639178595958667">"ଆପ୍‌ ଖୋଲନ୍ତୁ"</string>
diff --git a/packages/VpnDialogs/res/values-pa/strings.xml b/packages/VpnDialogs/res/values-pa/strings.xml
index 1815f4fb0d25..d2eba0f52005 100644
--- a/packages/VpnDialogs/res/values-pa/strings.xml
+++ b/packages/VpnDialogs/res/values-pa/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ਕਨੈਕਸ਼ਨ ਬੇਨਤੀ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ਇੱਕ VPN ਕਨੈਕਸ਼ਨ ਸੈਟ ਅਪ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟ੍ਰੈਫਿਕ ਦਾ ਨਿਰੀਖਣ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਕੇਵਲ ਤਾਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇਕਰ ਤੁਸੀਂ ਸਰੋਤ ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ। &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਦੇ ਟੌਪ ਤੇ ਪ੍ਰਗਟ ਹੁੰਦਾ ਹੈ ਜਦੋਂ VPN ਸਕਿਰਿਆ ਹੁੰਦਾ ਹੈ।"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ਕਿਸੇ ਅਜਿਹੇ VPN ਕਨੈਕਸ਼ਨ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ ਜੋ ਇਸਨੂੰ ਨੈੱਟਵਰਕ ਟਰੈਫ਼ਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿੰਦਾ ਹੈ। ਸਿਰਫ਼ ਉਦੋਂ ਹੀ ਸਵੀਕਾਰ ਕਰੋ ਜੇ ਤੁਹਾਨੂੰ ਸਰੋਤ \'ਤੇ ਭਰੋਸਾ ਹੈ। VPN ਦੇ ਕਿਰਿਆਸ਼ੀਲ ਹੋਣ \'ਤੇ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦਾ ਹੈ।"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
<string name="session" msgid="6470628549473641030">"ਸੈਸ਼ਨ:"</string>
<string name="duration" msgid="3584782459928719435">"ਮਿਆਦ:"</string>
diff --git a/packages/VpnDialogs/res/values-pl/strings.xml b/packages/VpnDialogs/res/values-pl/strings.xml
index d5201d7fbdf5..82161d389368 100644
--- a/packages/VpnDialogs/res/values-pl/strings.xml
+++ b/packages/VpnDialogs/res/values-pl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Żądanie połączenia"</string>
<string name="warning" msgid="809658604548412033">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu sieciowego. Zaakceptuj, tylko jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br /&gt;Gdy sieć VPN jest aktywna, u góry ekranu pojawia się &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacja <xliff:g id="APP">%s</xliff:g> chce utworzyć połączenie VPN, które pozwoli jej na monitorowanie ruchu w sieci. Zaakceptuj, jeśli masz zaufanie do źródła. &lt;br /&gt; &lt;br Gdy sieć VPN jest aktywna, na ekranie pojawia się ikona /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Połączono z VPN"</string>
<string name="session" msgid="6470628549473641030">"Sesja:"</string>
<string name="duration" msgid="3584782459928719435">"Czas trwania:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rBR/strings.xml b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
index 75c140617cf5..0d6dd0b136e9 100644
--- a/packages/VpnDialogs/res/values-pt-rBR/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rBR/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-pt-rPT/strings.xml b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
index 01beddbab4e4..a310104555a2 100644
--- a/packages/VpnDialogs/res/values-pt-rPT/strings.xml
+++ b/packages/VpnDialogs/res/values-pt-rPT/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Pedido de ligação"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na fonte. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece na parte superior do seu ecrã quando a VPN está ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"A app <xliff:g id="APP">%s</xliff:g> pretende configurar uma ligação VPN que lhe permita monitorizar o tráfego de rede. Aceite apenas se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparece no ecrã quando a VPN está ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"A VPN está ligada"</string>
<string name="session" msgid="6470628549473641030">"Sessão"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
@@ -25,12 +26,12 @@
<string name="data_received" msgid="4062776929376067820">"Recebidos:"</string>
<string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bytes / <xliff:g id="NUMBER_1">%2$s</xliff:g> pacotes"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Não é possível estabelecer ligação à VPN sempre ativada"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"A aplicação <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à aplicação <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
- <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A aplicação <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"A app <xliff:g id="VPN_APP_0">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. O seu telemóvel irá utilizar uma rede pública até conseguir restabelecer ligação à app <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
+ <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"A app <xliff:g id="VPN_APP">%1$s</xliff:g> está configurada para se manter sempre ligada, mas, neste momento, não é possível estabelecer ligação. Não terá ligação até que a VPN a consiga restabelecer."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Alterar as definições da VPN"</string>
<string name="configure" msgid="4905518375574791375">"Configurar"</string>
<string name="disconnect" msgid="971412338304200056">"Desligar"</string>
- <string name="open_app" msgid="3717639178595958667">"Abrir aplicação"</string>
+ <string name="open_app" msgid="3717639178595958667">"Abrir app"</string>
<string name="dismiss" msgid="6192859333764711227">"Ignorar"</string>
</resources>
diff --git a/packages/VpnDialogs/res/values-pt/strings.xml b/packages/VpnDialogs/res/values-pt/strings.xml
index 75c140617cf5..0d6dd0b136e9 100644
--- a/packages/VpnDialogs/res/values-pt/strings.xml
+++ b/packages/VpnDialogs/res/values-pt/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitação de conexão"</string>
<string name="warning" msgid="809658604548412033">"O <xliff:g id="APP">%s</xliff:g> quer configurar uma conexão VPN que permite monitorar o tráfego da rede. Aceite somente se confiar na origem. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; é exibido na parte superior da tela quando a rede VPN estiver ativa."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"O app <xliff:g id="APP">%s</xliff:g> quer definir uma conexão VPN para monitorar o tráfego da rede. Aceite apenas se você confiar na fonte. O ícone &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; aparecerá na tela quando a VPN estiver ativa."</string>
<string name="legacy_title" msgid="192936250066580964">"O VPN está conectado"</string>
<string name="session" msgid="6470628549473641030">"Sessão:"</string>
<string name="duration" msgid="3584782459928719435">"Duração:"</string>
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 4e60df2eca8e..5bda87e5c257 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Solicitare de conexiune"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> dorește să configureze o conexiune VPN care să îi permită să monitorizeze traficul în rețea. Acceptați numai dacă aveți încredere în sursă. Atunci când conexiunea VPN este activă, &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; se afișează în partea de sus a ecranului."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> solicită permisiunea de a configura o conexiune VPN care să îi permită să monitorizeze traficul de rețea. Acceptați numai dacă aveți încredere în sursă. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; va apărea pe ecran atunci când conexiunea VPN este activă."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN este conectat"</string>
<string name="session" msgid="6470628549473641030">"Sesiune:"</string>
<string name="duration" msgid="3584782459928719435">"Durată:"</string>
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index f8fcfb83aa9a..ce099562f854 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
<string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. Когда подключение к сети VPN активно, на экране появляется значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-si/strings.xml b/packages/VpnDialogs/res/values-si/strings.xml
index bb97a5d86c5f..a836baef545c 100644
--- a/packages/VpnDialogs/res/values-si/strings.xml
+++ b/packages/VpnDialogs/res/values-si/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"සම්බන්ධතා ඉල්ලීම"</string>
<string name="warning" msgid="809658604548412033">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> අවශ්‍යය වේ. ප්‍රභවය ඔබ විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. VPN සක්‍රිය විට &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"ජාල තදබදය නිරීක්ෂණය කිරීමට ඉඩ දෙන VPN සම්බන්ධතාවක් සැකසීමට <xliff:g id="APP">%s</xliff:g> හට අවශ්‍ය වේ. ඔබ මූලාශ්‍රය විශ්වාස කරන්නේ නම් පමණක් පිළිගන්න. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN සක්‍රිය විට ඔබගේ තිරයෙහි දිස් වේ."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN සම්බන්ධිතයි"</string>
<string name="session" msgid="6470628549473641030">"සැසිය:"</string>
<string name="duration" msgid="3584782459928719435">"කාල සීමාව:"</string>
diff --git a/packages/VpnDialogs/res/values-sk/strings.xml b/packages/VpnDialogs/res/values-sk/strings.xml
index a08117adfac1..766c1393b524 100644
--- a/packages/VpnDialogs/res/values-sk/strings.xml
+++ b/packages/VpnDialogs/res/values-sk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Žiadosť o pripojenie"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> žiada o nastavenie pripojenia VPN, pomocou ktorého bude môcť sledovať sieťové prenosy. Povoľte iba v prípade, že zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa zobrazuje v hornej časti obrazovky, keď je pripojenie VPN aktívne."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikácia <xliff:g id="APP">%s</xliff:g> chce nastaviť pripojenie k sieti VPN, ktoré jej umožňuje sledovať sieťovú premávku. Povoľte to iba v prípade, ak zdroju dôverujete. &lt;br /&gt; &lt;br /&gt; Keď je sieť VPN aktívna, na obrazovke sa zobrazí ikona &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Sieť VPN je pripojená"</string>
<string name="session" msgid="6470628549473641030">"Relácia"</string>
<string name="duration" msgid="3584782459928719435">"Trvanie:"</string>
diff --git a/packages/VpnDialogs/res/values-sl/strings.xml b/packages/VpnDialogs/res/values-sl/strings.xml
index d5014fa34394..361a5fa2f1fa 100644
--- a/packages/VpnDialogs/res/values-sl/strings.xml
+++ b/packages/VpnDialogs/res/values-sl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Zahteva za povezavo"</string>
<string name="warning" msgid="809658604548412033">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, se na vrhu zaslona prikaže ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Aplikacija <xliff:g id="APP">%s</xliff:g> želi nastaviti povezavo VPN, ki ji omogoča nadzor omrežnega prometa. To sprejmite samo, če zaupate viru. Ko je povezava VPN aktivna, je na zaslonu prikazana ikona &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
<string name="session" msgid="6470628549473641030">"Seja:"</string>
<string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
diff --git a/packages/VpnDialogs/res/values-sq/strings.xml b/packages/VpnDialogs/res/values-sq/strings.xml
index 4a96e7b92212..0b4ce4df9514 100644
--- a/packages/VpnDialogs/res/values-sq/strings.xml
+++ b/packages/VpnDialogs/res/values-sq/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kërkesë për lidhje"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN-je që e lejon të monitorojë trafikun e rrjetit. Prano vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në krye të ekranit kur VPN-ja është aktive."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> kërkon të vendosë një lidhje VPN që i lejon të monitorojë trafikun e rrjetit. Pranoje vetëm nëse i beson burimit. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; shfaqet në ekranin tënd kur është aktive VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-ja është e lidhur"</string>
<string name="session" msgid="6470628549473641030">"Sesioni:"</string>
<string name="duration" msgid="3584782459928719435">"Kohëzgjatja:"</string>
diff --git a/packages/VpnDialogs/res/values-sr/strings.xml b/packages/VpnDialogs/res/values-sr/strings.xml
index 8ce8060e333d..01bd4df700a7 100644
--- a/packages/VpnDialogs/res/values-sr/strings.xml
+++ b/packages/VpnDialogs/res/values-sr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Захтев за повезивање"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која омогућава праћење саобраћаја на мрежи. Прихватите само ако верујете извору. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује у врху екрана када је VPN активан."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Апликација <xliff:g id="APP">%s</xliff:g> жели да подеси VPN везу која јој омогућава да прати мрежни саобраћај. Прихватите ово само ако имате поверења у извор. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; се приказује на екрану када је VPN активан."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN је повезан"</string>
<string name="session" msgid="6470628549473641030">"Сесија:"</string>
<string name="duration" msgid="3584782459928719435">"Трајање:"</string>
diff --git a/packages/VpnDialogs/res/values-sv/strings.xml b/packages/VpnDialogs/res/values-sv/strings.xml
index 16b6a31d7d1a..60ed75250856 100644
--- a/packages/VpnDialogs/res/values-sv/strings.xml
+++ b/packages/VpnDialogs/res/values-sv/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Anslutningsförfrågan"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> vill starta en VPN-anslutning som tillåter att appen övervakar nätverkstrafiken. Godkänn endast detta om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas längst upp på skärmen när VPN-anslutningen är aktiv."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> vill skapa en VPN-anslutning så att den kan övervaka nätverkstrafik. Godkänn bara om du litar på källan. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; visas på skärmen när VPN-anslutningen är aktiv."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN är anslutet"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Längd:"</string>
diff --git a/packages/VpnDialogs/res/values-sw/strings.xml b/packages/VpnDialogs/res/values-sw/strings.xml
index ea2688438b7a..c4f46628f8bc 100644
--- a/packages/VpnDialogs/res/values-sw/strings.xml
+++ b/packages/VpnDialogs/res/values-sw/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ombi la muunganisho"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> inataka kusanidi muunganisho wa VPN utakaoiruhusu kufuatilia shughuli kwenye mtandao. Kubali ikiwa tu unakiamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana sehemu ya juu ya skrini yako VPN inapofanya kazi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> inagependa kuweka mipangilio ya muunganisho wa VPN inayoiruhusu kufuatilia trafiki ya mtandao. Kubali tu iwapo unaamini chanzo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; huonekana kwenye skrini yako VPN inapotumika."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN imeunganishwa"</string>
<string name="session" msgid="6470628549473641030">"Kipindi:"</string>
<string name="duration" msgid="3584782459928719435">"Muda:"</string>
diff --git a/packages/VpnDialogs/res/values-ta/strings.xml b/packages/VpnDialogs/res/values-ta/strings.xml
index 3b4cc571d860..1385bdc401c3 100644
--- a/packages/VpnDialogs/res/values-ta/strings.xml
+++ b/packages/VpnDialogs/res/values-ta/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"இணைப்புக் கோரிக்கை"</string>
<string name="warning" msgid="809658604548412033">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க வசதியாக VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> கோருகிறது. நம்பகமான மூலத்தை மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயக்கத்தில் உள்ளபோது திரையின் மேல் பகுதியில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"நெட்வொர்க் டிராஃபிக்கைக் கண்காணிக்க அனுமதிக்கும் VPN இணைப்பை அமைக்க <xliff:g id="APP">%s</xliff:g> விரும்புகிறது. நம்பகமான VPN ஆப்ஸாக இருந்தால் மட்டுமே ஏற்கவும். &lt;br /&gt; &lt;br /&gt; VPN இயங்கும்போது உங்கள் திரையில் &lt;img src=vpn_icon /&gt; தோன்றும்."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN இணைக்கப்பட்டது"</string>
<string name="session" msgid="6470628549473641030">"அமர்வு:"</string>
<string name="duration" msgid="3584782459928719435">"காலஅளவு:"</string>
diff --git a/packages/VpnDialogs/res/values-te/strings.xml b/packages/VpnDialogs/res/values-te/strings.xml
index 864c926bc615..8f8ff0778d06 100644
--- a/packages/VpnDialogs/res/values-te/strings.xml
+++ b/packages/VpnDialogs/res/values-te/strings.xml
@@ -16,8 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="prompt" msgid="3183836924226407828">"కనెక్షన్ అభ్యర్థన"</string>
+ <string name="prompt" msgid="3183836924226407828">"కనెక్షన్ రిక్వెస్ట్‌"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించగలగడానికి VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు మూలాన్ని విశ్వసిస్తే మాత్రమే ఆమోదించండి. VPN సక్రియంగా ఉన్నప్పుడు మీ స్క్రీన్ ఎగువన &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; కనిపిస్తుంది."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"నెట్‌వర్క్ ట్రాఫిక్‌ను పర్యవేక్షించగలగడానికి, <xliff:g id="APP">%s</xliff:g> VPN కనెక్షన్‌ను సెటప్ చేయాలనుకుంటోంది. మీరు సోర్స్‌ను విశ్వసిస్తే మాత్రమే ఆమోదించండి. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; VPN యాక్టివ్‌గా ఉన్నప్పుడు మీ స్క్రీన్ పై కనిపిస్తుంది."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN కనెక్ట్ చేయబడింది"</string>
<string name="session" msgid="6470628549473641030">"సెషన్:"</string>
<string name="duration" msgid="3584782459928719435">"వ్యవధి:"</string>
diff --git a/packages/VpnDialogs/res/values-th/strings.xml b/packages/VpnDialogs/res/values-th/strings.xml
index 333ff5fefacc..2e174cd7b296 100644
--- a/packages/VpnDialogs/res/values-th/strings.xml
+++ b/packages/VpnDialogs/res/values-th/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"ขอการเชื่อมต่อ"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ต้องการสร้างการเชื่อมต่อ VPN เพื่อให้แอปสามารถตรวจสอบการเข้าใช้งานเครือข่าย โปรดยอมรับหากคุณเชื่อถือแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏที่ด้านบนหน้าจอเมื่อมีการใช้งาน VPN อยู่"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ต้องการตั้งค่าการเชื่อมต่อ VPN เพื่อให้ตรวจสอบการจราจรของข้อมูลในเครือข่ายได้ ยอมรับต่อเมื่อคุณไว้วางใจแหล่งที่มานี้เท่านั้น &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; จะปรากฏบนหน้าจอเมื่อใช้งาน VPN อยู่"</string>
<string name="legacy_title" msgid="192936250066580964">"เชื่อมต่อ VPN แล้ว"</string>
<string name="session" msgid="6470628549473641030">"เซสชัน"</string>
<string name="duration" msgid="3584782459928719435">"ระยะเวลา:"</string>
diff --git a/packages/VpnDialogs/res/values-tl/strings.xml b/packages/VpnDialogs/res/values-tl/strings.xml
index 9c01c32d0d0d..ea69fba45f1e 100644
--- a/packages/VpnDialogs/res/values-tl/strings.xml
+++ b/packages/VpnDialogs/res/values-tl/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Kahilingan sa koneksyon"</string>
<string name="warning" msgid="809658604548412033">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko ng network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa itaas ng iyong screen kapag aktibo ang VPN."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"Gusto ng <xliff:g id="APP">%s</xliff:g> na mag-set up ng koneksyon sa VPN na nagbibigay-daan ditong masubaybayan ang trapiko sa network. Tanggapin lang kung pinagkakatiwalaan mo ang pinagmulan. Lalabas ang &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sa iyong screen kapag aktibo ang VPN."</string>
<string name="legacy_title" msgid="192936250066580964">"Nakakonekta ang VPN"</string>
<string name="session" msgid="6470628549473641030">"Session:"</string>
<string name="duration" msgid="3584782459928719435">"Tagal:"</string>
diff --git a/packages/VpnDialogs/res/values-tr/strings.xml b/packages/VpnDialogs/res/values-tr/strings.xml
index 8665a47e6633..7ffa4bc1e4e9 100644
--- a/packages/VpnDialogs/res/values-tr/strings.xml
+++ b/packages/VpnDialogs/res/values-tr/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Bağlantı isteği"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ağ trafiğini izlemesine olanak veren bir VPN bağlantısı oluşturmak istiyor. Sadece, ilgili kaynağa güveniyorsanız kabul edin. &lt;br /&gt; &lt;br /&gt; VPN aktif olduğunda ekranınızın üst tarafında &lt;img src=vpn_icon /&gt; simgesi görünür."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g>, ağ trafiğini izlemesine izin veren bir VPN bağlantısı oluşturmak istiyor. Yalnızca kaynağa güveniyorsanız kabul edin. VPN etkin olduğunda ekranınızda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; görünür."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN bağlı"</string>
<string name="session" msgid="6470628549473641030">"Oturum:"</string>
<string name="duration" msgid="3584782459928719435">"Süre:"</string>
diff --git a/packages/VpnDialogs/res/values-uk/strings.xml b/packages/VpnDialogs/res/values-uk/strings.xml
index 8f91abf990b3..6411d7cf9d76 100644
--- a/packages/VpnDialogs/res/values-uk/strings.xml
+++ b/packages/VpnDialogs/res/values-uk/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запит на під’єднання"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Дозволяйте, якщо довіряєте джерелу. Коли мережа VPN активна, угорі екрана відображається значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> хоче під’єднатися до мережі VPN, щоб контролювати мережевий трафік. Надавайте дозвіл, лише якщо довіряєте джерелу. Коли мережа VPN активна, на екрані з’являється значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
<string name="legacy_title" msgid="192936250066580964">"Мережу VPN під’єднано"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Тривалість:"</string>
diff --git a/packages/VpnDialogs/res/values-ur/strings.xml b/packages/VpnDialogs/res/values-ur/strings.xml
index db0c2971a64c..3a23e940d9e9 100644
--- a/packages/VpnDialogs/res/values-ur/strings.xml
+++ b/packages/VpnDialogs/res/values-ur/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"کنکشن کی درخواست"</string>
<string name="warning" msgid="809658604548412033">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن ترتیب دینا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہے۔ اگر آپ کو ماخذ پر بھروسہ ہے تبھی قبول کریں۔ &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; آپ کی اسکرین کے اوپر اس وقت ظاہر ہوتا ہے جب VPN فعال ہوتا ہے۔"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"‏<xliff:g id="APP">%s</xliff:g> ایک ایسا VPN کنکشن سیٹ اپ کرنا چاہتی ہے جو اسے نیٹ ورک ٹریفک کو مانیٹر کرنے کی اجازت دیتا ہو۔ آپ کو ماخذ پر اعتماد ہونے پر ہی قبول کریں۔ ‎&lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ‏VPN کے فعال ہونے پر آپ کی اسکرین پر ظاہر ہوتا ہے۔"</string>
<string name="legacy_title" msgid="192936250066580964">"‏VPN مربوط ہے"</string>
<string name="session" msgid="6470628549473641030">"سیشن:"</string>
<string name="duration" msgid="3584782459928719435">"دورانیہ:"</string>
diff --git a/packages/VpnDialogs/res/values-uz/strings.xml b/packages/VpnDialogs/res/values-uz/strings.xml
index 5a348a0610d3..a3256e7bb927 100644
--- a/packages/VpnDialogs/res/values-uz/strings.xml
+++ b/packages/VpnDialogs/res/values-uz/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Ulanish uchun so‘rov"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ilovasi trafikni kuzatish uchun VPN tarmog‘iga ulanmoqchi. Agar ilovaga ishonsangiz, so‘rovga rozi bo‘ling.&lt;br /&gt; &lt;br /&gt;VPN faol bo‘lsa, ekranning yuqori qismida &lt;img src=vpn_icon /&gt; belgisi paydo bo‘ladi."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> ilovasi tarmoqdagi trafikni kuzatish uchun VPN aloqasini sozlamoqchi. Agar unga ishonsangiz, ruxsat bering. VPN aloqa faolligida ekranda &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; chiqadi."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN ulangan"</string>
<string name="session" msgid="6470628549473641030">"Seans:"</string>
<string name="duration" msgid="3584782459928719435">"Davomiyligi:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9aeee013..184d08d7d665 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. &lt;br /&gt; &lt;br /&gt; Biểu tượng &lt;img src=vpn_icon /&gt; xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Bạn chỉ nên chấp nhận nếu tin tưởng nguồn đó. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; sẽ xuất hiện trên màn hình khi VPN đang hoạt động."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
<string name="session" msgid="6470628549473641030">"Phiên"</string>
<string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rCN/strings.xml b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
index 7e528bdfb04a..a7262bedce96 100644
--- a/packages/VpnDialogs/res/values-zh-rCN/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rCN/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"网络连接请求"</string>
<string name="warning" msgid="809658604548412033">"“<xliff:g id="APP">%s</xliff:g>”想要设置一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请勿接受此请求。&lt;br /&gt; &lt;br /&gt;启用 VPN 后,屏幕顶部会出现一个 &lt;img src=vpn_icon /&gt; 图标。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"“<xliff:g id="APP">%s</xliff:g>”想要建立一个 VPN 连接,以便监控网络流量。除非您信任该来源,否则请不要接受。VPN 处于启用状态时,屏幕上会显示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"已连接VPN"</string>
<string name="session" msgid="6470628549473641030">"会话:"</string>
<string name="duration" msgid="3584782459928719435">"时长:"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rHK/strings.xml b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
index 49605b08cdee..e4e643234143 100644
--- a/packages/VpnDialogs/res/values-zh-rHK/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rHK/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線以監控網絡流量。除非您信任要求來源,否則請勿隨意接受要求。VPN 啟用時,畫面會顯示 &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"時段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間︰"</string>
diff --git a/packages/VpnDialogs/res/values-zh-rTW/strings.xml b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
index edd8e61d5555..f54ca4a7a576 100644
--- a/packages/VpnDialogs/res/values-zh-rTW/strings.xml
+++ b/packages/VpnDialogs/res/values-zh-rTW/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"連線要求"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> 要求設定 VPN 連線,允許此要求即開放該來源監控網路流量。除非你信任該來源,否則請勿任意接受要求。&lt;br /&gt; &lt;br /&gt;VPN 啟用時,畫面頂端會顯示 &lt;img src=vpn_icon /&gt;。"</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"「<xliff:g id="APP">%s</xliff:g>」要求設定 VPN 連線,以便監控網路流量。除非你信任該來源,否則請勿接受要求。&lt;br /&gt; &lt;br /&gt; VPN 啟用時,畫面上會顯示 &lt;img src=vpn_icon /&gt;。"</string>
<string name="legacy_title" msgid="192936250066580964">"VPN 已連線"</string>
<string name="session" msgid="6470628549473641030">"工作階段:"</string>
<string name="duration" msgid="3584782459928719435">"持續時間:"</string>
diff --git a/packages/VpnDialogs/res/values-zu/strings.xml b/packages/VpnDialogs/res/values-zu/strings.xml
index 4ab1225e6fc6..c224b13b06da 100644
--- a/packages/VpnDialogs/res/values-zu/strings.xml
+++ b/packages/VpnDialogs/res/values-zu/strings.xml
@@ -18,6 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Isicelo soxhumo"</string>
<string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumo lwe-VPN eyivumela ukwengamela ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ibonakala phezu kwesikrini sakho uma i-VPN isebenza."</string>
+ <string name="warning" product="tv" msgid="5188957997628124947">"I-<xliff:g id="APP">%s</xliff:g> ifuna ukusetha uxhumano lwe-VPN oluyivumela ukuthi igade ithrafikhi yenethiwekhi. Yamukela kuphela uma wethemba umthombo. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; ivela kusikrini sakho lapho i-VPN isebenza."</string>
<string name="legacy_title" msgid="192936250066580964">"I-VPN ixhunyiwe"</string>
<string name="session" msgid="6470628549473641030">"Iseshini:"</string>
<string name="duration" msgid="3584782459928719435">"Ubude besikhathi:"</string>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 000000000000..adc308600ebb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Lewer programme onder uitsnede-area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 000000000000..648e1d4cf383
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ከተቆረጠው አከባቢ በታች የመተግበሪያዎች ምስልን ስራ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 000000000000..2d3b506d8cad
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"عرض التطبيقات أسفل منطقة الصورة المقطوعة للشاشة"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 000000000000..db2b15a142c3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"এপ্‌সমূহ কাটআউট অঞ্চলৰ তলত দেখুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 000000000000..a6b7c4346e7e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Tətbiqləri kəsilmə sahəsinin aşağısında göstərin"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..f80fa8d14054
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Prikazuj aplikacije ispod oblasti izreza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 000000000000..0e5c8bcb55a2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Візуалізацыя праграм ніжэй месца выраза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 000000000000..e97bb57068ca
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Изобразяване на приложенията под областта на прореза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 000000000000..d13c777fe468
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"কাটআউট এরিয়ার নিচে অ্যাপ রেন্ডার করুন"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 000000000000..9c9f43779b66
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderovanje aplikacija ispod izrezanog područja"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 000000000000..e0a577e5290f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderitza les aplicacions per sota de l\'àrea de retallada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 000000000000..0f64473c7260
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykreslovat aplikace pod oblastí výseče"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 000000000000..d0cc43e8025f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gengiv apps under skærmhakkets område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 000000000000..a7759ea6175a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps unterhalb des Aussparungs-Bereichs darstellen"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 000000000000..b71679a1912f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Απόδοση εφαρμογών κάτω από την περιοχή εγκοπής"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..8c85cbdebb92
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render apps below cutout area"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..8b72d9f77c49
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‎‎‏‎Render apps below cutout area‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..359cdd0eab52
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps debajo del área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 000000000000..47f525ec1d28
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicaciones por debajo de la zona de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 000000000000..0cc5a25868c2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Väljalõikeala all olevate rakenduste renderdamine"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 000000000000..15d7d6045361
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Errendatu mozketa-eremutik kanpo geratzen diren aplikazioak"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 000000000000..0865f7559ef9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"پرداز زدن برنامه‌ها در زیر ناحیه بریده‌شده"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 000000000000..1a6bf7a16839
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderöi sovellukset lovialueen alle"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..ea0a27b069da
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Rendre les applications sous la zone de découpe"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 000000000000..6d91a9d603f4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Afficher les applis sous la zone d\'encoche"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 000000000000..382497b1caf0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar aplicacións que aparezan na zona recortada"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 000000000000..d578d9286d4c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ઍપને કટઆઉટ ક્ષેત્રની નીચે રેન્ડર કરો"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 000000000000..e1f09f249bda
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ऐप्लिकेशन को कटआउट एरिया के नीचे दिखाएं"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 000000000000..db734e8254e5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderiraj aplikacije ispod područja ureza"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 000000000000..264095b6f4e2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Alkalmazások megjelenítése a kivágási terület alatt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 000000000000..72e67ec1c424
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Արտապատկերել հավելվածները էկրանի կտրված հատվածի ներքևում"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 000000000000..c49bf0ca939a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Render aplikasi di bawah area potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 000000000000..0b90991b0b1e
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Birta forrit fyrir neðan útklippta svæðið"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 000000000000..2a0f026b39a6
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visualizza le app sotto l\'area di ritaglio"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 000000000000..cc7a0a486c54
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"עיבוד האפליקציות שמתחת לאזור המגרעת"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 000000000000..9e99482e5e2a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"カットアウト領域の下でアプリをレンダリング"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 000000000000..5464a5699449
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"აპების ასახვა ჭრილის ქვემოთ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 000000000000..6a2623f8696f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Экран ойығының астындағы қолданбаларды көрсету"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 000000000000..4b4d169cc738
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"បំប្លែងកម្មវិធីខាងក្រោមផ្នែកឆក"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 000000000000..7a929d13d83f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ಕಟೌಟ್ ಪ್ರದೇಶದ ಕೆಳಗಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ರೆಂಡರ್ ಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 000000000000..4b9e64020eee
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"컷아웃 영역 아래에 앱 렌더링"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 000000000000..1ac6a8bb9c1f
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Колдонмолорду кесилген аймактын ылдый жагында көрсөтүү"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 000000000000..4c38580169af
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ສະແດງພາບແອັບຢູ່ທາງລຸ່ມພື້ນທີ່ຮອຍເສັ້ນ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 000000000000..c43736d006dd
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Pateikti programas po išpjovos sritimi"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 000000000000..f95abb69abf5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Atveidot lietotnes zem izgriezuma apgabala"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 000000000000..ff236be46b14
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Прикажувај апликации под отсечената област"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 000000000000..ef728ab64ab5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"കട്ടൗട്ട് ഭാഗത്തിന് താഴെ ആപ്പുകൾ റെൻഡർ ചെയ്യുക"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 000000000000..23dbe0c822f0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Аппуудыг тасалж авсан хэсгийн доор буулгах"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 000000000000..42f09cb1a1f0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट क्षेत्राच्या खाली असलेली ॲप्स रेंडर करा"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 000000000000..e348630e0447
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Serahkan apl di bawah kawasan potongan"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 000000000000..90cb0a5f56a4
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ဖြတ်ထုတ်ထားသော နေရာအောက်ရှိ အက်ပ်များ ပြသရန်"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 000000000000..b8b4e7526ab2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Gjengi apper under utklippsområdet"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 000000000000..bd213bb64a0d
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"कटआउट गरिएको क्षेत्रभन्दा तल पर्ने एपहरू रेन्डर गर्नुहोस्"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 000000000000..68f5c0701beb
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Apps renderen onder display-cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 000000000000..162a29e8968c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ଆପଗୁଡ଼ିକୁ କଟଆଉଟ୍ ଏରିଆ ନିମ୍ନରେ ରେଣ୍ଡର୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 000000000000..908393b1abb0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"ਕੱਟਆਊਟ ਖੇਤਰ ਹੇਠ ਐਪਾਂ ਨੂੰ ਰੈਂਡਰ ਕਰੋ"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 000000000000..c027d5266928
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderuj aplikacje pod obszarem wycięcia"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..d09ed97121fa
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..d38ce43204d2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de recorte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 000000000000..d09ed97121fa
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Renderizar apps abaixo da área de corte"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 000000000000..6e5947c0d753
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redați aplicațiile sub zona de decupaj"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 000000000000..c7f54bbff6a7
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Отображать приложения под вырезом"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 000000000000..4a14a360e857
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"කටවුට් ප්‍රදේශයට පහළින් යෙදුම් විදහන්න"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 000000000000..98b82e636392
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Vykresľovať aplikácie pod oblasťou výrezu"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 000000000000..dcf0c842cd36
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Upodobitev aplikacij pod predelom zareze zaslona"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 000000000000..d7b0676970b8
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Paraqiti aplikacionet poshtë zonës së prerjes"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 000000000000..c2b611e70da9
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Приказуј апликације испод области изреза"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 000000000000..3007ffb99e10
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Visa appar under skärmutskärningens område"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 000000000000..b605554b46f3
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Usionyeshe programu chini ya eneo lenye pengo"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 000000000000..c4d06fb68564
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"கட் அவுட் பகுதிக்குள்ளாக ஆப்ஸை ரெண்டர் செய்"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 000000000000..08fa4ae7669b
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"కట్అవుట్ ఏరియా కింద యాప్‌లను రెండర్ చేయండి"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 000000000000..9a302507411a
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"แสดงผลแอปใต้บริเวณรอยบาก"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 000000000000..a3d4a3afe376
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"I-render ang mga app sa ibaba ng lugar ng cutout"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 000000000000..12e0f3019814
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Uygulamaları kesme alanının altında oluşturun"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 000000000000..08b1521a12a0
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Відображати додатки під областю вирізу екрана"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 000000000000..711b5389b26c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"کٹ آؤٹ ایریا کے نیچے رینڈر ایپس"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 000000000000..7f6f2b45fbd2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Ekran kesimi quyidagi ilovalarni renderlash"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 000000000000..a7d54fbae9f5
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Hiển thị các ứng dụng bên dưới khu vực có vết cắt"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..f596520fad30
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在刘海区域下方呈现应用"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..ddb1df77b00c
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在凹口區域下方輸出應用程式"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..7aad79c11bf2
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"在螢幕凹口底下顯示應用程式畫面"</string>
+</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 000000000000..d861c5e60708
--- /dev/null
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Nikezela ngama-app angaphansi kwendawo yokukhipha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
new file mode 100644
index 000000000000..b021da75878d
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Versteek"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
new file mode 100644
index 000000000000..0ca6135776b5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ደብቅ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
new file mode 100644
index 000000000000..7e41cb1935b9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"إخفاء"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
new file mode 100644
index 000000000000..d2399fffc8c3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকুৱাওক"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
new file mode 100644
index 000000000000..420c5133cbd1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizlədin"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
new file mode 100644
index 000000000000..ce75c4584dac
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Схаваць"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
new file mode 100644
index 000000000000..7c3170c73355
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скриване"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
new file mode 100644
index 000000000000..1e627256d943
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"লুকান"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
new file mode 100644
index 000000000000..6ae5ffd32bf8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Amaga"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
new file mode 100644
index 000000000000..068bb9463c33
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrýt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
new file mode 100644
index 000000000000..6ecb7674d65b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
new file mode 100644
index 000000000000..44278e800c5a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ausblenden"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
new file mode 100644
index 000000000000..96b1b86dd4bc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Απόκρυψη"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
new file mode 100644
index 000000000000..1ab9b2d49ee1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Hide"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
new file mode 100644
index 000000000000..a20e59446fcb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎Hide‎‏‎‎‏‎"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
new file mode 100644
index 000000000000..4e5428dc59b8
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Peida"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
new file mode 100644
index 000000000000..f33bb5005cb3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ezkutatu"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
new file mode 100644
index 000000000000..8de756085a84
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"پنهان کردن"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
new file mode 100644
index 000000000000..c42e52d1aaaf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Piilota"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
new file mode 100644
index 000000000000..31fa56766ebc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
new file mode 100644
index 000000000000..31fa56766ebc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Masquer"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
new file mode 100644
index 000000000000..7e4b33a8749c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"છુપાવો"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
new file mode 100644
index 000000000000..1513f2a8ffe5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"छिपाएं"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
new file mode 100644
index 000000000000..082e5864065f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sakrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
new file mode 100644
index 000000000000..2b9717fca1cf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Elrejtés"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
new file mode 100644
index 000000000000..3407bb499645
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Թաքցնել"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
new file mode 100644
index 000000000000..6c017b390aa2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
new file mode 100644
index 000000000000..229c113bbdb3
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fela"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
new file mode 100644
index 000000000000..07444aa372f6
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Nascondi"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
new file mode 100644
index 000000000000..221a129317f9
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"הסתרה"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
new file mode 100644
index 000000000000..3bf17fb81abe
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"非表示"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
new file mode 100644
index 000000000000..160052841a7c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"დამალვა"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
new file mode 100644
index 000000000000..23d02b81c62e
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жасыру"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
new file mode 100644
index 000000000000..624b81fa6dcc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"លាក់"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
new file mode 100644
index 000000000000..9cd6da7fb314
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ಮರೆಮಾಡಿ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
new file mode 100644
index 000000000000..efb656860724
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"숨기기"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
new file mode 100644
index 000000000000..419132513fad
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Жашыруу"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
new file mode 100644
index 000000000000..8850dfb2d244
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ເຊື່ອງ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
new file mode 100644
index 000000000000..6364b96096e0
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Slėpti"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
new file mode 100644
index 000000000000..61f2ad3d51fb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Paslēpt"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
new file mode 100644
index 000000000000..505c2059f5ac
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сокриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
new file mode 100644
index 000000000000..1c30dec6bc83
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"മറയ്ക്കുക"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
new file mode 100644
index 000000000000..7e2719b719fe
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Нуух"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
new file mode 100644
index 000000000000..46f0ab889226
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लपवा"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
new file mode 100644
index 000000000000..6c017b390aa2
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Sembunyikan"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
new file mode 100644
index 000000000000..84be3f9a0986
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ဝှက်ရန်"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
new file mode 100644
index 000000000000..6ecb7674d65b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skjul"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
new file mode 100644
index 000000000000..ff920b290dbb
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"लुकाइयोस्"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
new file mode 100644
index 000000000000..00900f8c6f6f
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Verbergen"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
new file mode 100644
index 000000000000..fcfd7252a8bf
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ଲୁଚାନ୍ତୁ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
new file mode 100644
index 000000000000..9f37e0bce721
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ਲੁਕਾਓ"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
new file mode 100644
index 000000000000..3d65546bf794
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ukryj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
new file mode 100644
index 000000000000..351c1cdf3084
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ocultar"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
new file mode 100644
index 000000000000..e6281fd09b1a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascundeți"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
new file mode 100644
index 000000000000..901839684a9b
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Скрыть"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
new file mode 100644
index 000000000000..b06a52cfc90c
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"සඟවන්න"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
new file mode 100644
index 000000000000..965d95b0f389
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skryť"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
new file mode 100644
index 000000000000..e8adb9810303
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Skrij"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
new file mode 100644
index 000000000000..85fb95a1f283
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fshih"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
new file mode 100644
index 000000000000..26afbf962495
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сакриј"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
new file mode 100644
index 000000000000..193c1798fc87
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Dölj"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
new file mode 100644
index 000000000000..58e35e2eadfd
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ficha"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
new file mode 100644
index 000000000000..b743c660f183
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"மறை"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
new file mode 100644
index 000000000000..de04152a4e20
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"దాచండి"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
new file mode 100644
index 000000000000..1c8bd9d01818
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"ซ่อน"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
new file mode 100644
index 000000000000..cc45f6335271
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Itago"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
new file mode 100644
index 000000000000..b20f1f7d3120
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Gizle"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
new file mode 100644
index 000000000000..938b0e222af1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Сховати"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
new file mode 100644
index 000000000000..0f081705f6f1
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"چھپائیں"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
new file mode 100644
index 000000000000..5d22045d9cdc
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Berkitish"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
new file mode 100644
index 000000000000..fc83d257fdba
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ẩn"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
new file mode 100644
index 000000000000..acdfd1c2e4a5
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隐藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
new file mode 100644
index 000000000000..abb8e81fbca4
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000000..abb8e81fbca4
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"隱藏"</string>
+</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
new file mode 100644
index 000000000000..11842d91a53a
--- /dev/null
+++ b/packages/overlays/NoCutoutOverlay/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Fihla"</string>
+</resources>
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBlend.java b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
index a1c79ef938c4..3109cd88bd7d 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBlend.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBlend.java
@@ -104,7 +104,7 @@ public class ScriptIntrinsicBlend extends ScriptIntrinsic {
* @param opt LaunchOptions for clipping
*/
public void forEachSrc(Allocation ain, Allocation aout, Script.LaunchOptions opt) {
- blend(1, ain, aout, null);
+ blend(1, ain, aout, opt);
}
/**
@@ -641,4 +641,3 @@ public class ScriptIntrinsicBlend extends ScriptIntrinsic {
}
*/
}
-
diff --git a/services/Android.bp b/services/Android.bp
index c83a697a71c1..82d9b3e51c2d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -21,6 +21,9 @@ java_defaults {
// "-XepPatchLocation:/tmp/refaster/",
],
},
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ },
}
filegroup {
@@ -65,7 +68,6 @@ filegroup {
":services.texttospeech-sources",
":services.usage-sources",
":services.usb-sources",
- ":services.uwb-sources",
":services.voiceinteraction-sources",
":services.wifi-sources",
],
@@ -119,7 +121,6 @@ java_library {
"services.texttospeech",
"services.usage",
"services.usb",
- "services.uwb",
"services.voiceinteraction",
"services.wifi",
"service-blobstore",
@@ -130,6 +131,7 @@ java_library {
libs: [
"android.hidl.manager-V1.0-java",
"framework-tethering.stubs.module_lib",
+ "service-art.stubs.system_server",
],
// Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/OWNERS b/services/OWNERS
index 3b972e922e95..b7128a32fcee 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1,6 +1,6 @@
per-file Android.bp = file:platform/build/soong:/OWNERS
# art-team@ manages the system server profile
-per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
per-file java/com/android/server/* = toddke@google.com,patb@google.com
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e9c9899023b1..b88366b5da8c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -21,8 +21,13 @@ import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILIT
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK;
+import static android.view.accessibility.AccessibilityInteractionClient.IGNORE_CALL_STACK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -31,6 +36,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.annotation.NonNull;
@@ -103,10 +109,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
FingerprintGestureDispatcher.FingerprintGestureClient {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "AbstractAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_SVC_CONN = LOG_TAG + ".IAccessibilityServiceConnection";
+ private static final String TRACE_SVC_CLIENT = LOG_TAG + ".IAccessibilityServiceClient";
+ private static final String TRACE_WM = "WindowManagerInternal";
private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
protected static final String TAKE_SCREENSHOT = "takeScreenshot";
@@ -134,6 +139,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
protected final AccessibilitySecurityPolicy mSecurityPolicy;
protected final AccessibilityTrace mTrace;
+ // The attribution tag set by the service that is bound to this instance
+ protected String mAttributionTag;
+
// The service that's bound to this instance. Whenever this value is non-null, this
// object is registered as a death recipient
IBinder mService;
@@ -298,9 +306,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
return false;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onKeyEvent",
- keyEvent + ", " + sequenceNumber);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onKeyEvent", keyEvent + ", " + sequenceNumber);
}
mServiceInterface.onKeyEvent(keyEvent, sequenceNumber);
} catch (RemoteException e) {
@@ -365,17 +372,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setOnKeyEventResult(boolean handled, int sequence) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setOnKeyEventResult",
- "handled=" + handled + ";sequence=" + sequence);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setOnKeyEventResult", "handled=" + handled + ";sequence=" + sequence);
}
mSystemSupport.getKeyEventDispatcher().setOnKeyEventResult(this, handled, sequence);
}
@Override
public AccessibilityServiceInfo getServiceInfo() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getServiceInfo");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getServiceInfo", "");
}
synchronized (mLock) {
return mAccessibilityServiceInfo;
@@ -393,8 +399,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setServiceInfo", "info=" + info);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setServiceInfo", "info=" + info);
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -416,13 +422,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
+ @Override
+ public void setAttributionTag(String attributionTag) {
+ mAttributionTag = attributionTag;
+ }
+
+ String getAttributionTag() {
+ return mAttributionTag;
+ }
+
protected abstract boolean hasRightsToCurrentUserLocked();
@Nullable
@Override
public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindows");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindows", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -458,8 +473,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindow", "windowId=" + windowId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindow", "windowId=" + windowId);
}
synchronized (mLock) {
int displayId = Display.INVALID_DISPLAY;
@@ -496,8 +511,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String viewIdResName, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByViewId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByViewId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";viewIdResName=" + viewIdResName + ";interactionId="
+ interactionId + ";callback=" + callback + ";interrogatingTid="
@@ -539,6 +554,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByViewId",
+ accessibilityNodeId + ";" + viewIdResName + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByViewId(accessibilityNodeId,
viewIdResName, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -564,8 +585,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfosByText",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfosByText",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
+ accessibilityNodeId + ";text=" + text + ";interactionId=" + interactionId
+ ";callback=" + callback + ";interrogatingTid=" + interrogatingTid);
@@ -606,6 +627,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfosByText",
+ accessibilityNodeId + ";" + text + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findAccessibilityNodeInfosByText(accessibilityNodeId,
text, partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -631,13 +658,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int accessibilityWindowId, long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid, Bundle arguments) throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".findAccessibilityNodeInfoByAccessibilityId",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findAccessibilityNodeInfoByAccessibilityId",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
- + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
- + ";arguments=" + arguments);
+ + accessibilityNodeId + ";interactionId=" + interactionId + ";callback="
+ + callback + ";flags=" + flags + ";interrogatingTid=" + interrogatingTid
+ + ";arguments=" + arguments);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -675,6 +701,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findAccessibilityNodeInfoByAccessibilityId",
+ accessibilityNodeId + ";" + partialInteractiveRegion + ";" + interactionId + ";"
+ + callback + ";" + (mFetchFlags | flags) + ";" + interrogatingPid + ";"
+ + interrogatingTid + ";" + spec + ";" + arguments);
+ }
try {
connection.getRemote().findAccessibilityNodeInfoByAccessibilityId(
accessibilityNodeId, partialInteractiveRegion, interactionId, callback,
@@ -700,12 +732,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".findFocus",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("findFocus",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";focusType=" + focusType + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -743,6 +775,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("findFocus",
+ accessibilityNodeId + ";" + focusType + ";" + partialInteractiveRegion + ";"
+ + interactionId + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid
+ + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().findFocus(accessibilityNodeId, focusType,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -768,12 +806,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".focusSearch",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("focusSearch",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";direction=" + direction + ";interactionId="
- + interactionId + ";callback=" + callback + ";interrogatingTid="
- + interrogatingTid);
+ + accessibilityNodeId + ";direction=" + direction + ";interactionId="
+ + interactionId + ";callback=" + callback + ";interrogatingTid="
+ + interrogatingTid);
}
final int resolvedWindowId;
RemoteAccessibilityConnection connection;
@@ -810,6 +848,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
callback = replaceCallbackIfNeeded(callback, resolvedWindowId, interactionId,
interrogatingPid, interrogatingTid);
final long identityToken = Binder.clearCallingIdentity();
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("focusSearch",
+ accessibilityNodeId + ";" + direction + ";" + partialInteractiveRegion
+ + ";" + interactionId + ";" + callback + ";" + mFetchFlags + ";"
+ + interrogatingPid + ";" + interrogatingTid + ";" + spec);
+ }
try {
connection.getRemote().focusSearch(accessibilityNodeId, direction,
partialInteractiveRegion, interactionId, callback, mFetchFlags,
@@ -832,17 +876,17 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".sendGesture",
- "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn(
+ "sendGesture", "sequence=" + sequence + ";gestureSteps=" + gestureSteps);
}
}
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".dispatchGesture", "sequence="
- + sequence + ";gestureSteps=" + gestureSteps + ";displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("dispatchGesture", "sequence=" + sequence + ";gestureSteps="
+ + gestureSteps + ";displayId=" + displayId);
}
}
@@ -851,12 +895,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performAccessibilityAction",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performAccessibilityAction",
"accessibilityWindowId=" + accessibilityWindowId + ";accessibilityNodeId="
- + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
- + ";interactionId=" + interactionId + ";callback=" + callback
- + ";interrogatingTid=" + interrogatingTid);
+ + accessibilityNodeId + ";action=" + action + ";arguments=" + arguments
+ + ";interactionId=" + interactionId + ";callback=" + callback
+ + ";interrogatingTid=" + interrogatingTid);
}
final int resolvedWindowId;
synchronized (mLock) {
@@ -879,9 +923,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean performGlobalAction(int action) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".performGlobalAction",
- "action=" + action);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("performGlobalAction", "action=" + action);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -893,8 +936,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public @NonNull List<AccessibilityNodeInfo.AccessibilityAction> getSystemActions() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSystemActions");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSystemActions", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -906,9 +949,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean isFingerprintGestureDetectionAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CONNECTION + ".isFingerprintGestureDetectionAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isFingerprintGestureDetectionAvailable", "");
}
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
return false;
@@ -923,9 +965,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationScale(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationScale",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationScale", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -942,9 +983,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public Region getMagnificationRegion(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationRegion",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationRegion", "displayId=" + displayId);
}
synchronized (mLock) {
final Region region = Region.obtain();
@@ -970,9 +1010,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterX(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterX",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterX", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -996,9 +1035,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public float getMagnificationCenterY(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getMagnificationCenterY",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getMagnificationCenterY", "displayId=" + displayId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1032,9 +1070,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean resetMagnification(int displayId, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".resetMagnification",
- "displayId=" + displayId + ";animate=" + animate);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("resetMagnification", "displayId=" + displayId + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1058,10 +1095,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
float centerY, boolean animate) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationScaleAndCenter",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationScaleAndCenter",
"displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
- + ";centerY=" + centerY + ";animate=" + animate);
+ + ";centerY=" + centerY + ";animate=" + animate);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -1087,8 +1124,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setMagnificationCallbackEnabled",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setMagnificationCallbackEnabled",
"displayId=" + displayId + ";enabled=" + enabled);
}
mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
@@ -1100,18 +1137,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setSoftKeyboardCallbackEnabled(boolean enabled) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardCallbackEnabled",
- "enabled=" + enabled);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardCallbackEnabled", "enabled=" + enabled);
}
mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
}
@Override
public void takeScreenshot(int displayId, RemoteCallback callback) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".takeScreenshot",
- "displayId=" + displayId + ";callback=" + callback);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("takeScreenshot", "displayId=" + displayId + ";callback=" + callback);
}
final long currentTimestamp = SystemClock.uptimeMillis();
if (mRequestTakeScreenshotTimestampMs != 0
@@ -1237,6 +1272,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final long identity = Binder.clearCallingIdentity();
try {
final IBinder overlayWindowToken = new Binder();
+ if (wmTracingEnabled()) {
+ logTraceWM("addWindowToken",
+ overlayWindowToken + ";TYPE_ACCESSIBILITY_OVERLAY;" + displayId + ";null");
+ }
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
displayId, null /* options */);
synchronized (mLock) {
@@ -1263,6 +1302,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
public void onDisplayRemoved(int displayId) {
final long identity = Binder.clearCallingIdentity();
+ if (wmTracingEnabled()) {
+ logTraceWM(
+ "addWindowToken", mOverlayWindowTokens.get(displayId) + ";true;" + displayId);
+ }
try {
mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true,
displayId);
@@ -1282,9 +1325,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public IBinder getOverlayWindowToken(int displayId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getOverlayWindowToken",
- "displayId=" + displayId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getOverlayWindowToken", "displayId=" + displayId);
}
synchronized (mLock) {
return mOverlayWindowTokens.get(displayId);
@@ -1299,9 +1341,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
*/
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getWindowIdForLeashToken",
- "token=" + token);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getWindowIdForLeashToken", "token=" + token);
}
synchronized (mLock) {
return mA11yWindowManager.getWindowIdLocked(token);
@@ -1314,8 +1355,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
// Clear the proxy in the other process so this
// IAccessibilityServiceConnection can be garbage collected.
if (mServiceInterface != null) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", "null, " + mId + ", null");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init", "null, " + mId + ", null");
}
mServiceInterface.init(null, mId, null);
}
@@ -1465,9 +1506,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityEvent",
- event + ";" + serviceWantsEvent);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityEvent", event + ";" + serviceWantsEvent);
}
listener.onAccessibilityEvent(event, serviceWantsEvent);
if (DEBUG) {
@@ -1522,9 +1562,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onMagnificationChanged", displayId
- + ", " + region + ", " + scale + ", " + centerX + ", " + centerY);
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onMagnificationChanged", displayId + ", " + region + ", "
+ + scale + ", " + centerX + ", " + centerY);
}
listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
} catch (RemoteException re) {
@@ -1541,9 +1581,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSoftKeyboardShowModeChanged",
- String.valueOf(showState));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSoftKeyboardShowModeChanged", String.valueOf(showState));
}
listener.onSoftKeyboardShowModeChanged(showState);
} catch (RemoteException re) {
@@ -1557,9 +1596,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonClicked",
- String.valueOf(displayId));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonClicked", String.valueOf(displayId));
}
listener.onAccessibilityButtonClicked(displayId);
} catch (RemoteException re) {
@@ -1579,9 +1617,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(
- TRACE_A11Y_SERVICE_CLIENT + ".onAccessibilityButtonAvailabilityChanged",
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onAccessibilityButtonAvailabilityChanged",
String.valueOf(available));
}
listener.onAccessibilityButtonAvailabilityChanged(available);
@@ -1597,9 +1634,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onGesture",
- gestureInfo.toString());
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onGesture", gestureInfo.toString());
}
listener.onGesture(gestureInfo);
} catch (RemoteException re) {
@@ -1613,8 +1649,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onSystemActionsChanged");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onSystemActionsChanged", "");
}
listener.onSystemActionsChanged();
} catch (RemoteException re) {
@@ -1628,8 +1664,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".clearAccessibilityCache");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("clearAccessibilityCache", "");
}
listener.clearAccessibilityCache();
} catch (RemoteException re) {
@@ -1668,6 +1704,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* @param displayId The logical display id.
*/
private void ensureWindowsAvailableTimedLocked(int displayId) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ return;
+ }
+
if (mA11yWindowManager.getWindowListLocked(displayId) != null) {
return;
}
@@ -1747,6 +1787,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
LocalServices.getService(ActivityTaskManagerInternal.class)
.setFocusedActivity(activityToken);
}
+ if (intConnTracingEnabled()) {
+ logTraceIntConn("performAccessibilityAction",
+ accessibilityNodeId + ";" + action + ";" + arguments + ";" + interactionId
+ + ";" + callback + ";" + mFetchFlags + ";" + interrogatingPid + ";"
+ + interrogatingTid);
+ }
connection.getRemote().performAccessibilityAction(accessibilityNodeId, action,
arguments, interactionId, callback, fetchFlags, interrogatingPid,
interrogatingTid);
@@ -1957,8 +2003,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setGestureDetectionPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setGestureDetectionPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region);
@@ -1966,8 +2012,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setTouchExplorationPassthroughRegion",
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setTouchExplorationPassthroughRegion",
"displayId=" + displayId + ";region=" + region);
}
mSystemSupport.setTouchExplorationPassthroughRegion(displayId, region);
@@ -1975,20 +2021,56 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
@Override
public void setFocusAppearance(int strokeWidth, int color) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setFocusAppearance",
- "strokeWidth=" + strokeWidth + ";color=" + color);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setFocusAppearance", "strokeWidth=" + strokeWidth + ";color=" + color);
}
}
@Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, Bundle callingStack) {
- if (mTrace.isA11yTracingEnabled()) {
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, Bundle callingStack) {
+ if (mTrace.isA11yTracingEnabledForTypes(loggingTypes)) {
ArrayList<StackTraceElement> list =
(ArrayList<StackTraceElement>) callingStack.getSerializable(CALL_STACK);
- mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid,
- list.toArray(new StackTraceElement[list.size()]));
+ HashSet<String> ignoreList =
+ (HashSet<String>) callingStack.getSerializable(IGNORE_CALL_STACK);
+ mTrace.logTrace(timestamp, where, loggingTypes, callingParams, processId, threadId,
+ callingUid, list.toArray(new StackTraceElement[list.size()]), ignoreList);
}
}
+
+ protected boolean svcClientTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
+ }
+
+ protected void logTraceSvcClient(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CLIENT + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, params);
+ }
+
+ protected boolean svcConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CONNECTION);
+ }
+
+ protected void logTraceSvcConn(String methodName, String params) {
+ mTrace.logTrace(TRACE_SVC_CONN + "." + methodName,
+ FLAGS_ACCESSIBILITY_SERVICE_CONNECTION, params);
+ }
+
+ protected boolean intConnTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ protected void logTraceIntConn(String methodName, String params) {
+ mTrace.logTrace(LOG_TAG + "." + methodName,
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION, params);
+ }
+
+ protected boolean wmTracingEnabled() {
+ return mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ protected void logTraceWM(String methodName, String params) {
+ mTrace.logTrace(TRACE_WM + "." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7403af7605bc..7d2b71f7852b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -19,7 +19,9 @@ package com.android.server.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.MainThread;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
@@ -43,7 +45,10 @@ import com.android.server.accessibility.magnification.WindowMagnificationGesture
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.StringJoiner;
/**
* This class is an input filter for implementing accessibility features such
@@ -171,9 +176,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private int mEnabledFeatures;
- private EventStreamState mMouseStreamState;
+ private final SparseArray<EventStreamState> mMouseStreamStates = new SparseArray<>(0);
- private EventStreamState mTouchScreenStreamState;
+ private final SparseArray<EventStreamState> mTouchScreenStreamStates = new SparseArray<>(0);
private EventStreamState mKeyboardStreamState;
@@ -211,10 +216,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
super.onUninstalled();
}
- void onDisplayChanged() {
+ void onDisplayAdded(@NonNull Display display) {
if (mInstalled) {
- disableFeatures();
- enableFeatures();
+ resetStreamStateForDisplay(display.getDisplayId());
+ enableFeaturesForDisplay(display);
+ }
+ }
+
+ void onDisplayRemoved(int displayId) {
+ if (mInstalled) {
+ disableFeaturesForDisplay(displayId);
+ resetStreamStateForDisplay(displayId);
}
}
@@ -224,7 +236,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
Slog.d(TAG, "Received event: " + event + ", policyFlags=0x"
+ Integer.toHexString(policyFlags));
}
-
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(TAG + ".onInputEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mEventHandler.size() == 0) {
if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
@@ -237,16 +254,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
return;
}
- int eventSource = event.getSource();
+ final int eventSource = event.getSource();
+ final int displayId = event.getDisplayId();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
state.reset();
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
super.onInputEvent(event, policyFlags);
return;
}
if (state.updateInputSource(event.getSource())) {
- clearEventsForAllEventHandlers(eventSource);
+ clearEventStreamHandler(displayId, eventSource);
}
if (!state.inputSourceValid()) {
@@ -275,35 +293,39 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
*/
private EventStreamState getEventStreamState(InputEvent event) {
if (event instanceof MotionEvent) {
- if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
- if (mTouchScreenStreamState == null) {
- mTouchScreenStreamState = new TouchScreenEventStreamState();
- }
- return mTouchScreenStreamState;
- }
- if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
- if (mMouseStreamState == null) {
- mMouseStreamState = new MouseEventStreamState();
- }
- return mMouseStreamState;
- }
+ final int displayId = event.getDisplayId();
+
+ if (event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
+ EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState == null) {
+ touchScreenStreamState = new TouchScreenEventStreamState();
+ mTouchScreenStreamStates.put(displayId, touchScreenStreamState);
+ }
+ return touchScreenStreamState;
+ }
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+ EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState == null) {
+ mouseStreamState = new MouseEventStreamState();
+ mMouseStreamStates.put(displayId, mouseStreamState);
+ }
+ return mouseStreamState;
+ }
} else if (event instanceof KeyEvent) {
- if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
- if (mKeyboardStreamState == null) {
- mKeyboardStreamState = new KeyboardEventStreamState();
- }
- return mKeyboardStreamState;
- }
+ if (event.isFromSource(InputDevice.SOURCE_KEYBOARD)) {
+ if (mKeyboardStreamState == null) {
+ mKeyboardStreamState = new KeyboardEventStreamState();
+ }
+ return mKeyboardStreamState;
+ }
}
return null;
}
- private void clearEventsForAllEventHandlers(int eventSource) {
- for (int i = 0; i < mEventHandler.size(); i++) {
- final EventStreamTransformation eventHandler = mEventHandler.valueAt(i);
- if (eventHandler != null) {
- eventHandler.clearEvents(eventSource);
- }
+ private void clearEventStreamHandler(int displayId, int eventSource) {
+ final EventStreamTransformation eventHandler = mEventHandler.get(displayId);
+ if (eventHandler != null) {
+ eventHandler.clearEvents(eventSource);
}
}
@@ -419,55 +441,69 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private void enableFeatures() {
if (DEBUG) Slog.i(TAG, "enableFeatures()");
- resetStreamState();
+ resetAllStreamState();
final ArrayList<Display> displaysList = mAms.getValidDisplayList();
- if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
- mAutoclickController = new AutoclickController(mContext, mUserId);
- addFirstEventHandlerForAllDisplays(displaysList, mAutoclickController);
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ enableFeaturesForDisplay(displaysList.get(i));
}
+ enableDisplayIndependentFeatures();
+ }
- for (int i = displaysList.size() - 1; i >= 0; i--) {
- final int displayId = displaysList.get(i).getDisplayId();
- final Context displayContext = mContext.createDisplayContext(displaysList.get(i));
+ private void enableFeaturesForDisplay(Display display) {
+ if (DEBUG) {
+ Slog.i(TAG, "enableFeaturesForDisplay() : display Id = " + display.getDisplayId());
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
- TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
- if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
- explorer.setServiceHandlesDoubleTap(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
- explorer.setMultiFingerGesturesEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
- explorer.setTwoFingerPassthroughEnabled(true);
- }
- if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
- explorer.setSendMotionEventsEnabled(true);
- }
- addFirstEventHandler(displayId, explorer);
- mTouchExplorer.put(displayId, explorer);
- }
+ final Context displayContext = mContext.createDisplayContext(display);
+ final int displayId = display.getDisplayId();
- if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
- || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
- || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
- final MagnificationGestureHandler magnificationGestureHandler =
- createMagnificationGestureHandler(displayId,
- displayContext);
- addFirstEventHandler(displayId, magnificationGestureHandler);
- mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
+ if (mAutoclickController == null) {
+ mAutoclickController = new AutoclickController(
+ mContext, mUserId, mAms.getTraceManager());
}
+ addFirstEventHandler(displayId, mAutoclickController);
+ }
- if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
- MotionEventInjector injector = new MotionEventInjector(
- mContext.getMainLooper());
- addFirstEventHandler(displayId, injector);
- mMotionEventInjectors.put(displayId, injector);
+ if ((mEnabledFeatures & FLAG_FEATURE_TOUCH_EXPLORATION) != 0) {
+ TouchExplorer explorer = new TouchExplorer(displayContext, mAms);
+ if ((mEnabledFeatures & FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0) {
+ explorer.setServiceHandlesDoubleTap(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) {
+ explorer.setMultiFingerGesturesEnabled(true);
+ }
+ if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) {
+ explorer.setTwoFingerPassthroughEnabled(true);
}
+ if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) {
+ explorer.setSendMotionEventsEnabled(true);
+ }
+ addFirstEventHandler(displayId, explorer);
+ mTouchExplorer.put(displayId, explorer);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
+ || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+ || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
+ final MagnificationGestureHandler magnificationGestureHandler =
+ createMagnificationGestureHandler(displayId,
+ displayContext);
+ addFirstEventHandler(displayId, magnificationGestureHandler);
+ mMagnificationGestureHandler.put(displayId, magnificationGestureHandler);
+ }
+
+ if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+ MotionEventInjector injector = new MotionEventInjector(
+ mContext.getMainLooper(), mAms.getTraceManager());
+ addFirstEventHandler(displayId, injector);
+ mMotionEventInjectors.put(displayId, injector);
}
+ }
+ private void enableDisplayIndependentFeatures() {
if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
mAms.setMotionEventInjectors(mMotionEventInjectors);
}
@@ -500,55 +536,57 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mEventHandler.put(displayId, eventHandler);
}
- /**
- * Adds an event handler to the event handler chain for all displays. The handler is added at
- * the beginning of the chain.
- *
- * @param displayList The list of displays
- * @param handler The handler to be added to the event handlers list.
- */
- private void addFirstEventHandlerForAllDisplays(ArrayList<Display> displayList,
- EventStreamTransformation handler) {
- for (int i = 0; i < displayList.size(); i++) {
- final int displayId = displayList.get(i).getDisplayId();
- addFirstEventHandler(displayId, handler);
+ private void disableFeatures() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ disableFeaturesForDisplay(displaysList.get(i).getDisplayId());
}
+ mAms.setMotionEventInjectors(null);
+ disableDisplayIndependentFeatures();
+
+ resetAllStreamState();
}
- private void disableFeatures() {
- for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) {
- final MotionEventInjector injector = mMotionEventInjectors.valueAt(i);
- if (injector != null) {
- injector.onDestroy();
- }
+ private void disableFeaturesForDisplay(int displayId) {
+ if (DEBUG) {
+ Slog.i(TAG, "disableFeaturesForDisplay() : display Id = " + displayId);
}
- mAms.setMotionEventInjectors(null);
- mMotionEventInjectors.clear();
+
+ final MotionEventInjector injector = mMotionEventInjectors.get(displayId);
+ if (injector != null) {
+ injector.onDestroy();
+ mMotionEventInjectors.remove(displayId);
+ }
+
+ final TouchExplorer explorer = mTouchExplorer.get(displayId);
+ if (explorer != null) {
+ explorer.onDestroy();
+ mTouchExplorer.remove(displayId);
+ }
+
+ final MagnificationGestureHandler handler = mMagnificationGestureHandler.get(displayId);
+ if (handler != null) {
+ handler.onDestroy();
+ mMagnificationGestureHandler.remove(displayId);
+ }
+
+ final EventStreamTransformation eventStreamTransformation = mEventHandler.get(displayId);
+ if (eventStreamTransformation != null) {
+ mEventHandler.remove(displayId);
+ }
+ }
+
+ private void disableDisplayIndependentFeatures() {
if (mAutoclickController != null) {
mAutoclickController.onDestroy();
mAutoclickController = null;
}
- for (int i = mTouchExplorer.size() - 1; i >= 0; i--) {
- final TouchExplorer explorer = mTouchExplorer.valueAt(i);
- if (explorer != null) {
- explorer.onDestroy();
- }
- }
- mTouchExplorer.clear();
- for (int i = mMagnificationGestureHandler.size() - 1; i >= 0; i--) {
- final MagnificationGestureHandler handler = mMagnificationGestureHandler.valueAt(i);
- if (handler != null) {
- handler.onDestroy();
- }
- }
- mMagnificationGestureHandler.clear();
+
if (mKeyboardInterceptor != null) {
mKeyboardInterceptor.onDestroy();
mKeyboardInterceptor = null;
}
-
- mEventHandler.clear();
- resetStreamState();
}
private MagnificationGestureHandler createMagnificationGestureHandler(
@@ -563,32 +601,46 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
final Context uiContext = displayContext.createWindowContext(
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new WindowMagnificationGestureHandler(uiContext,
- mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getWindowMagnificationMgr(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
displayId);
} else {
final Context uiContext = displayContext.createWindowContext(
TYPE_MAGNIFICATION_OVERLAY, null /* options */);
magnificationGestureHandler = new FullScreenMagnificationGestureHandler(uiContext,
- mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(),
- detectControlGestures, triggerable,
+ mAms.getFullScreenMagnificationController(), mAms.getTraceManager(),
+ mAms.getMagnificationController(), detectControlGestures, triggerable,
new WindowMagnificationPromptController(displayContext, mUserId), displayId);
}
return magnificationGestureHandler;
}
- void resetStreamState() {
- if (mTouchScreenStreamState != null) {
- mTouchScreenStreamState.reset();
- }
- if (mMouseStreamState != null) {
- mMouseStreamState.reset();
+ void resetAllStreamState() {
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+
+ for (int i = displaysList.size() - 1; i >= 0; i--) {
+ resetStreamStateForDisplay(displaysList.get(i).getDisplayId());
}
+
if (mKeyboardStreamState != null) {
mKeyboardStreamState.reset();
}
}
+ void resetStreamStateForDisplay(int displayId) {
+ final EventStreamState touchScreenStreamState = mTouchScreenStreamStates.get(displayId);
+ if (touchScreenStreamState != null) {
+ touchScreenStreamState.reset();
+ mTouchScreenStreamStates.remove(displayId);
+ }
+
+ final EventStreamState mouseStreamState = mMouseStreamStates.get(displayId);
+ if (mouseStreamState != null) {
+ mouseStreamState.reset();
+ mMouseStreamStates.remove(displayId);
+ }
+ }
+
@Override
public void onDestroy() {
/* ignore */
@@ -839,4 +891,45 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
mTouchExplorer.get(displayId).setTouchExplorationPassthroughRegion(region);
}
}
+
+ /**
+ * Dumps all {@link AccessibilityInputFilter}s here.
+ */
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+ if (mEventHandler == null) {
+ return;
+ }
+ pw.append("A11yInputFilter Info : ");
+ pw.println();
+
+ final ArrayList<Display> displaysList = mAms.getValidDisplayList();
+ for (int i = 0; i < displaysList.size(); i++) {
+ final int displayId = displaysList.get(i).getDisplayId();
+ EventStreamTransformation next = mEventHandler.get(displayId);
+ if (next != null) {
+ pw.append("Enabled features of Display [");
+ pw.append(Integer.toString(displayId));
+ pw.append("] = ");
+
+ final StringJoiner joiner = new StringJoiner(",", "[", "]");
+
+ while (next != null) {
+ if (next instanceof MagnificationGestureHandler) {
+ joiner.add("MagnificationGesture");
+ } else if (next instanceof KeyboardInterceptor) {
+ joiner.add("KeyboardInterceptor");
+ } else if (next instanceof TouchExplorer) {
+ joiner.add("TouchExplorer");
+ } else if (next instanceof AutoclickController) {
+ joiner.add("AutoclickController");
+ } else if (next instanceof MotionEventInjector) {
+ joiner.add("MotionEventInjector");
+ }
+ next = next.getNext();
+ }
+ pw.append(joiner.toString());
+ }
+ pw.println();
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f63198866b08..fd05c231fa49 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,6 +16,15 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_FINGERPRINT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_PACKAGE_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -289,8 +298,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = packageManager;
@@ -311,8 +320,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
- mTraceManager = new AccessibilityTraceManager(
- mWindowManagerService.getAccessibilityController(), this);
+ mTraceManager = AccessibilityTraceManager.getInstance(
+ mWindowManagerService.getAccessibilityController(), this, mLock);
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -324,7 +333,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
- mWindowManagerService, this, mSecurityPolicy, this);
+ mWindowManagerService, this, mSecurityPolicy, this, mTraceManager);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
@@ -339,26 +348,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public int getCurrentUserIdLocked() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCurrentUserIdLocked");
- }
return mCurrentUserId;
}
@Override
public boolean isAccessibilityButtonShown() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".isAccessibilityButtonShown");
- }
return mIsAccessibilityButtonShown;
}
@Override
public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(
- LOG_TAG + ".onServiceInfoChangedLocked", "userState=" + userState);
- }
mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
userState.mBoundServices);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -424,8 +423,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER);
}
synchronized (mLock) {
@@ -452,8 +452,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// mBindingServices in binderDied() during updating. Remove services from this
// package from mBindingServices, and then update the user state to re-bind new
// versions of them.
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
synchronized (mLock) {
@@ -485,8 +486,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"packageName=" + packageName + ";uid=" + uid);
}
@@ -529,8 +531,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
"intent=" + intent + ";packages=" + packages + ";uid=" + uid
+ ";doit=" + doit);
}
@@ -580,8 +583,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".BR.onReceive",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) {
+ mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER,
"context=" + context + ";intent=" + intent);
}
@@ -668,8 +671,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".addClient",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".addClient", FLAGS_ACCESSIBILITY_MANAGER,
"callback=" + callback + ";userId=" + userId);
}
@@ -739,11 +742,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent", FLAGS_ACCESSIBILITY_MANAGER,
"event=" + event + ";userId=" + userId);
}
boolean dispatchEvent = false;
+ int resolvedUserId;
synchronized (mLock) {
if (event.getWindowId() ==
@@ -759,8 +763,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ resolvedUserId = mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// Make sure the reported package is one the caller has access to.
event.setPackageName(mSecurityPolicy.resolveValidReportedPackageLocked(
@@ -792,17 +795,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
int displayId = Display.INVALID_DISPLAY;
synchronized (mLock) {
final int windowId = event.getWindowId();
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- && windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
+ if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowIdLocked(
- mCurrentUserId, windowId);
+ resolvedUserId, windowId);
+ event.setDisplayId(displayId);
}
- if (displayId != Display.INVALID_DISPLAY
+
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ && displayId != Display.INVALID_DISPLAY
&& mA11yWindowManager.isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
}
if (shouldComputeWindows) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.computeWindowsForAccessibility",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "display=" + displayId);
+ }
final WindowManagerInternal wm = LocalServices.getService(
WindowManagerInternal.class);
wm.computeWindowsForAccessibility(displayId);
@@ -835,9 +844,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void registerSystemAction(RemoteAction action, int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
- "action=" + action + ";actionId=" + actionId);
+ FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().registerSystemAction(actionId, action);
@@ -850,8 +859,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void unregisterSystemAction(int actionId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction", "actionId=" + actionId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction",
+ FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId);
}
mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
getSystemActionPerformer().unregisterSystemAction(actionId);
@@ -867,9 +877,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getInstalledAccessibilityServiceList",
- "userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
final int resolvedUserId;
@@ -903,8 +913,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getEnabledAccessibilityServiceList",
+ FLAGS_ACCESSIBILITY_MANAGER,
"feedbackType=" + feedbackType + ";userId=" + userId);
}
@@ -936,8 +947,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void interrupt(int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".interrupt", "userId=" + userId);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".interrupt",
+ FLAGS_ACCESSIBILITY_MANAGER, "userId=" + userId);
}
List<IAccessibilityServiceClient> interfacesToInterrupt;
@@ -966,8 +978,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
try {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt");
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".IAccessibilityServiceClient.onInterrupt",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT);
}
interfacesToInterrupt.get(i).onInterrupt();
} catch (RemoteException re) {
@@ -981,8 +995,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".addAccessibilityInteractionConnection",
+ FLAGS_ACCESSIBILITY_MANAGER,
"windowToken=" + windowToken + "leashToken=" + leashToken + ";connection="
+ connection + "; packageName=" + packageName + ";userId=" + userId);
}
@@ -993,9 +1008,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".removeAccessibilityInteractionConnection",
- "window=" + window);
+ FLAGS_ACCESSIBILITY_MANAGER, "window=" + window);
}
mA11yWindowManager.removeAccessibilityInteractionConnection(window);
}
@@ -1003,9 +1018,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setPictureInPictureActionReplacingConnection(
IAccessibilityInteractionConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".setPictureInPictureActionReplacingConnection",
- "connection=" + connection);
+ FLAGS_ACCESSIBILITY_MANAGER, "connection=" + connection);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.MODIFY_ACCESSIBILITY_DATA,
SET_PIP_ACTION_REPLACEMENT);
@@ -1017,10 +1032,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService", "owner=" + owner
- + ";serviceClient=" + serviceClient + ";accessibilityServiceInfo="
- + accessibilityServiceInfo + ";flags=" + flags);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".registerUiTestAutomationService",
+ FLAGS_ACCESSIBILITY_MANAGER,
+ "owner=" + owner + ";serviceClient=" + serviceClient
+ + ";accessibilityServiceInfo=" + accessibilityServiceInfo + ";flags=" + flags);
}
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
@@ -1037,9 +1053,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".unregisterUiTestAutomationService",
- "serviceClient=" + serviceClient);
+ FLAGS_ACCESSIBILITY_MANAGER, "serviceClient=" + serviceClient);
}
synchronized (mLock) {
mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
@@ -1049,15 +1065,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void temporaryEnableAccessibilityStateUntilKeyguardRemoved(
ComponentName service, boolean touchExplorationEnabled) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(
LOG_TAG + ".temporaryEnableAccessibilityStateUntilKeyguardRemoved",
+ FLAGS_ACCESSIBILITY_MANAGER,
"service=" + service + ";touchExplorationEnabled=" + touchExplorationEnabled);
}
mSecurityPolicy.enforceCallingPermission(
Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.isKeyguardLocked",
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
if (!mWindowManagerService.isKeyguardLocked()) {
return;
}
@@ -1083,9 +1104,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public IBinder getWindowToken(int windowId, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getWindowToken",
- "windowId=" + windowId + ";userId=" + userId);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowId=" + windowId + ";userId=" + userId);
}
mSecurityPolicy.enforceCallingPermission(
@@ -1127,8 +1148,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
+ FLAGS_ACCESSIBILITY_MANAGER,
"displayId=" + displayId + ";targetName=" + targetName);
}
@@ -1157,9 +1179,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
- "shown=" + shown);
+ FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown);
}
mSecurityPolicy.enforceCallingOrSelfPermission(
@@ -1190,10 +1212,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void onSystemActionsChanged() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onSystemActionsChanged");
- }
-
synchronized (mLock) {
AccessibilityUserState state = getCurrentUserStateLocked();
notifySystemActionsChangedLocked(state);
@@ -1256,11 +1274,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getMotionEventInjectorForDisplayLocked",
- "displayId=" + displayId);
- }
-
final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS;
MotionEventInjector motionEventInjector = null;
while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1309,7 +1322,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
public boolean accessibilityFocusOnlyInActiveWindow() {
synchronized (mLock) {
- return mA11yWindowManager.isTrackingWindowsLocked();
+ return mA11yWindowManager.accessibilityFocusOnlyInActiveWindowLocked();
}
}
@@ -1323,6 +1336,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
token = getWindowToken(windowId, mCurrentUserId);
}
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace("WindowManagerInternal.getWindowFrame",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "token=" + token + ";outBounds=" + outBounds);
+ }
mWindowManagerService.getWindowFrame(token, outBounds);
if (!outBounds.isEmpty()) {
return true;
@@ -1471,7 +1488,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private int getClientStateLocked(AccessibilityUserState userState) {
return userState.getClientStateLocked(
mUiAutomationManager.isUiAutomationRunningLocked(),
- mTraceManager.isA11yTracingEnabled());
+ mTraceManager.getTraceStateForAccessibilityManagerClientState());
}
private InteractionBridge getInteractionBridge() {
@@ -1681,6 +1698,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private void updateRelevantEventsLocked(AccessibilityUserState userState) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateRelevantEventsLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
@@ -1830,12 +1851,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void persistComponentNamesToSettingLocked(String settingName,
Set<ComponentName> componentNames, int userId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".persistComponentNamesToSettingLocked",
- "settingName=" + settingName + ";componentNames=" + componentNames + ";userId="
- + userId);
- }
-
persistColonDelimitedSetToSettingLocked(settingName, userId, componentNames,
componentName -> componentName.flattenToShortString());
}
@@ -1960,7 +1975,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
final int clientState = getClientStateLocked(userState);
if (userState.getLastSentClientStateLocked() != clientState
&& (mGlobalClients.getRegisteredCallbackCount() > 0
@@ -1983,6 +1998,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void sendStateToClients(int clientState,
RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".sendStateToClients",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "clientState=" + clientState);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.setState(clientState)));
}
@@ -2003,6 +2022,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void notifyClientsOfServicesStateChange(
RemoteCallbackList<IAccessibilityManagerClient> clients, long uiTimeout) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".notifyClientsOfServicesStateChange",
+ FLAGS_ACCESSIBILITY_MANAGER_CLIENT, "uiTimeout=" + uiTimeout);
+ }
clients.broadcast(ignoreRemoteException(
client -> client.notifyServicesStateChanged(uiTimeout)));
}
@@ -2082,6 +2105,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
if (setInputFilter) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL
+ | FLAGS_INPUT_FILTER)) {
+ mTraceManager.logTrace("WindowManagerInternal.setInputFilter",
+ FLAGS_WINDOW_MANAGER_INTERNAL | FLAGS_INPUT_FILTER,
+ "inputFilter=" + inputFilter);
+ }
mWindowManagerService.setInputFilter(inputFilter);
}
}
@@ -2805,26 +2834,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@GuardedBy("mLock")
@Override
public MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecLocked",
- "windowId=" + windowId);
- }
-
IBinder windowToken = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(
mCurrentUserId, windowId);
if (windowToken != null) {
- return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
- windowToken);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mTraceManager.logTrace(LOG_TAG + ".getCompatibleMagnificationSpecForWindow",
+ FLAGS_WINDOW_MANAGER_INTERNAL, "windowToken=" + windowToken);
+ }
+
+ return mWindowManagerService.getCompatibleMagnificationSpecForWindow(windowToken);
}
return null;
}
@Override
public KeyEventDispatcher getKeyEventDispatcher() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getKeyEventDispatcher");
- }
-
if (mKeyEventDispatcher == null) {
mKeyEventDispatcher = new KeyEventDispatcher(
mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock,
@@ -2837,13 +2861,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent,
int flags) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getPendingIntentActivity",
- "context=" + context + ";requestCode=" + requestCode + ";intent=" + intent
- + ";flags=" + flags);
- }
-
-
return PendingIntent.getActivity(context, requestCode, intent, flags);
}
@@ -2858,9 +2875,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void performAccessibilityShortcut(String targetName) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".performAccessibilityShortcut",
- "targetName=" + targetName);
+ FLAGS_ACCESSIBILITY_MANAGER, "targetName=" + targetName);
}
if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)
@@ -3048,9 +3065,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityShortcutTargets",
- "shortcutType=" + shortcutType);
+ FLAGS_ACCESSIBILITY_MANAGER, "shortcutType=" + shortcutType);
}
if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY)
@@ -3122,11 +3139,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void sendAccessibilityEventForCurrentUserLocked(AccessibilityEvent event) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEventForCurrentUserLocked",
- "event=" + event);
- }
-
sendAccessibilityEventLocked(event, mCurrentUserId);
}
@@ -3148,8 +3160,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public boolean sendFingerprintGesture(int gestureKeyCode) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT)) {
mTraceManager.logTrace(LOG_TAG + ".sendFingerprintGesture",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_FINGERPRINT,
"gestureKeyCode=" + gestureKeyCode);
}
@@ -3174,9 +3188,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public int getAccessibilityWindowId(@Nullable IBinder windowToken) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".getAccessibilityWindowId",
- "windowToken=" + windowToken);
+ FLAGS_ACCESSIBILITY_MANAGER, "windowToken=" + windowToken);
}
synchronized (mLock) {
@@ -3196,8 +3210,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public long getRecommendedTimeoutMillis() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getRecommendedTimeoutMillis");
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(
+ LOG_TAG + ".getRecommendedTimeoutMillis", FLAGS_ACCESSIBILITY_MANAGER);
}
synchronized(mLock) {
@@ -3214,8 +3229,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setWindowMagnificationConnection(
IWindowMagnificationConnection connection) throws RemoteException {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
mTraceManager.logTrace(LOG_TAG + ".setWindowMagnificationConnection",
+ FLAGS_ACCESSIBILITY_MANAGER | FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
"connection=" + connection);
}
@@ -3249,9 +3266,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
- if (mTraceManager.isA11yTracingEnabled()) {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
mTraceManager.logTrace(LOG_TAG + ".associateEmbeddedHierarchy",
- "host=" + host + ";embedded=" + embedded);
+ FLAGS_ACCESSIBILITY_MANAGER, "host=" + host + ";embedded=" + embedded);
}
synchronized (mLock) {
@@ -3261,8 +3278,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy", "token=" + token);
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".disassociateEmbeddedHierarchy",
+ FLAGS_ACCESSIBILITY_MANAGER, "token=" + token);
}
synchronized (mLock) {
@@ -3274,7 +3292,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Gets the stroke width of the focus rectangle.
* @return The stroke width.
*/
+ @Override
public int getFocusStrokeWidth() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusStrokeWidth", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3286,7 +3308,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Gets the color of the focus rectangle.
* @return The color.
*/
+ @Override
public int getFocusColor() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getFocusColor", FLAGS_ACCESSIBILITY_MANAGER);
+ }
synchronized (mLock) {
final AccessibilityUserState userState = getCurrentUserStateLocked();
@@ -3314,6 +3340,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.dump(fd, pw, args);
+ }
pw.println("Global client list info:{");
mGlobalClients.dump(pw, " Client list ");
pw.println(" Registered clients:{");
@@ -3350,9 +3379,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".getFullScreenMagnificationController");
- }
synchronized (mLock) {
return mMagnificationController.getFullScreenMagnificationController();
}
@@ -3360,11 +3386,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".onClientChangeLocked",
- "serviceInfoChanged=" + serviceInfoChanged);
- }
-
AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
@@ -3569,7 +3590,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
synchronized (mLock) {
mDisplaysList.add(display);
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3591,7 +3612,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
if (mInputFilter != null) {
- mInputFilter.onDisplayChanged();
+ mInputFilter.onDisplayRemoved(displayId);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
@@ -3891,11 +3912,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setGestureDetectionPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setGestureDetectionPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setGestureDetectionPassthroughRegionInternal,
@@ -3906,11 +3922,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
@Override
public void setTouchExplorationPassthroughRegion(int displayId, Region region) {
- if (mTraceManager.isA11yTracingEnabled()) {
- mTraceManager.logTrace(LOG_TAG + ".setTouchExplorationPassthroughRegion",
- "displayId=" + displayId + ";region=" + region);
- }
-
mMainHandler.sendMessage(
obtainMessage(
AccessibilityManagerService::setTouchExplorationPassthroughRegionInternal,
@@ -3939,7 +3950,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (userState.mUserId != mCurrentUserId) {
return;
}
-
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateFocusAppearanceDataLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
client.mCallback.setFocusAppearance(userState.getFocusStrokeWidthLocked(),
@@ -3949,7 +3963,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
- AccessibilityTraceManager getTraceManager() {
+ public AccessibilityTraceManager getTraceManager() {
return mTraceManager;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index dc2628f0bc0b..0ab0c89ec9ae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -444,8 +444,10 @@ public class AccessibilitySecurityPolicy {
private boolean isValidPackageForUid(String packageName, int uid) {
final long token = Binder.clearCallingIdentity();
try {
+ // Since we treat calls from a profile as if made by its parent, using
+ // MATCH_ANY_USER to query the uid of the given package name.
return uid == mPackageManager.getPackageUidAsUser(
- packageName, UserHandle.getUserId(uid));
+ packageName, PackageManager.MATCH_ANY_USER, UserHandle.getUserId(uid));
} catch (PackageManager.NameNotFoundException e) {
return false;
} finally {
@@ -534,7 +536,8 @@ public class AccessibilitySecurityPolicy {
int servicePackageUid = serviceInfo.applicationInfo.uid;
if (mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- servicePackageUid, serviceInfo.packageName) != AppOpsManager.MODE_ALLOWED) {
+ servicePackageUid, serviceInfo.packageName, null, null)
+ != AppOpsManager.MODE_ALLOWED) {
Slog.w(LOG_TAG, "Skipping accessibility service " + new ComponentName(
serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ ": disallowed by AppOps");
@@ -559,17 +562,21 @@ public class AccessibilitySecurityPolicy {
return true;
}
- final int uid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int servicePackageUid = resolveInfo.serviceInfo.applicationInfo.uid;
+ final int callingPid = Binder.getCallingPid();
final long identityToken = Binder.clearCallingIdentity();
+ final String attributionTag = service.getAttributionTag();
try {
// For the caller is system, just block the data to a11y services.
- if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+ if (OWN_PROCESS_ID == callingPid) {
return mAppOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, attributionTag, null)
+ == AppOpsManager.MODE_ALLOWED;
}
return mAppOpsManager.noteOp(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- uid, packageName) == AppOpsManager.MODE_ALLOWED;
+ servicePackageUid, packageName, attributionTag, null)
+ == AppOpsManager.MODE_ALLOWED;
} finally {
Binder.restoreCallingIdentity(identityToken);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 7d75b738d818..467cab5fec04 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -53,10 +54,7 @@ import java.util.Set;
*/
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CONNECTION =
- LOG_TAG + ".IAccessibilityServiceConnection";
- private static final String TRACE_A11Y_SERVICE_CLIENT =
- LOG_TAG + ".IAccessibilityServiceClient";
+
/*
Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
lists of bound and binding services. These are freed on user changes, but just in case it
@@ -137,8 +135,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public void disableSelf() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".disableSelf");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("disableSelf", "");
}
synchronized (mLock) {
AccessibilityUserState userState = mUserStateWeakReference.get();
@@ -218,9 +216,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
return;
}
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".init", this + ", " + mId + ", "
- + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("init",
+ this + "," + mId + "," + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
} catch (RemoteException re) {
@@ -264,9 +262,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean setSoftKeyboardShowMode(int showMode) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".setSoftKeyboardShowMode",
- "showMode=" + showMode);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("setSoftKeyboardShowMode", "showMode=" + showMode);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -280,8 +277,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public int getSoftKeyboardShowMode() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".getSoftKeyboardShowMode");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("getSoftKeyboardShowMode", "");
}
final AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
@@ -289,9 +286,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean switchToInputMethod(String imeId) {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".switchToInputMethod",
- "imeId=" + imeId);
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("switchToInputMethod", "imeId=" + imeId);
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -311,8 +307,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
@Override
public boolean isAccessibilityButtonAvailable() {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CONNECTION + ".isAccessibilityButtonAvailable");
+ if (svcConnTracingEnabled()) {
+ logTraceSvcConn("isAccessibilityButtonAvailable", "");
}
synchronized (mLock) {
if (!hasRightsToCurrentUserLocked()) {
@@ -373,9 +369,9 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT
- + ".onFingerprintCapturingGesturesChanged", String.valueOf(active));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient(
+ "onFingerprintCapturingGesturesChanged", String.valueOf(active));
}
mServiceInterface.onFingerprintCapturingGesturesChanged(active);
} catch (RemoteException e) {
@@ -394,9 +390,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
}
if (serviceInterface != null) {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onFingerprintGesture",
- String.valueOf(gesture));
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onFingerprintGesture", String.valueOf(gesture));
}
mServiceInterface.onFingerprintGesture(gesture);
} catch (RemoteException e) {
@@ -410,15 +405,17 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
+ if (wmTracingEnabled()) {
+ logTraceWM("isTouchOrFaketouchDevice", "");
+ }
if (motionEventInjector != null
&& mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
try {
- if (mTrace.isA11yTracingEnabled()) {
- mTrace.logTrace(TRACE_A11Y_SERVICE_CLIENT + ".onPerformGestureResult",
- sequence + ", false");
+ if (svcClientTracingEnabled()) {
+ logTraceSvcClient("onPerformGestureResult", sequence + ", false");
}
mServiceInterface.onPerformGestureResult(sequence, false);
} catch (RemoteException re) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index 6396960281b7..8cf5547b05ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -60,7 +60,7 @@ final class AccessibilityShellCommand extends ShellCommand {
}
case "start-trace":
case "stop-trace":
- return mService.getTraceManager().onShellCommand(cmd);
+ return mService.getTraceManager().onShellCommand(cmd, this);
}
return -1;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
deleted file mode 100644
index 03914138cd42..000000000000
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java
+++ /dev/null
@@ -1,66 +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.server.accessibility;
-
-/**
- * Interface to log accessibility trace.
- */
-public interface AccessibilityTrace {
- /**
- * Whether the trace is enabled.
- */
- boolean isA11yTracingEnabled();
-
- /**
- * Start tracing.
- */
- void startTrace();
-
- /**
- * Stop tracing.
- */
- void stopTrace();
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- */
- void logTrace(String where);
-
- /**
- * Log one trace entry.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the method to be logged.
- */
- void logTrace(String where, String callingParams);
-
- /**
- * Log one trace entry. Accessibility services using AccessibilityInteractionClient to
- * make screen content related requests use this API to log entry when receive callback.
- * @param timestamp The timestamp when a callback is received.
- * @param where A string to identify this log entry, which can be used to filter/search
- * through the tracing file.
- * @param callingParams The parameters for the callback.
- * @param processId The process id of the calling component.
- * @param threadId The threadId of the calling component.
- * @param callingUid The calling uid of the callback.
- * @param callStack The call stack of the callback.
- */
- void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack);
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
index 6105e8a6724b..51e01ea58a35 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTraceManager.java
@@ -15,72 +15,197 @@
*/
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_ALL;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_LOGGING_NONE;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+
+import android.accessibilityservice.AccessibilityTrace;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.os.Binder;
+import android.os.ShellCommand;
import com.android.server.wm.WindowManagerInternal;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Manager of accessibility trace.
*/
-class AccessibilityTraceManager implements AccessibilityTrace {
+public class AccessibilityTraceManager implements AccessibilityTrace {
private final WindowManagerInternal.AccessibilityControllerInternal mA11yController;
private final AccessibilityManagerService mService;
+ private final Object mA11yMSLock;
+
+ private long mEnabledLoggingFlags;
+
+ private static AccessibilityTraceManager sInstance = null;
+
+ @MainThread
+ static AccessibilityTraceManager getInstance(
+ @NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTraceManager(a11yController, service, lock);
+ }
+ return sInstance;
+ }
- AccessibilityTraceManager(
+ private AccessibilityTraceManager(
@NonNull WindowManagerInternal.AccessibilityControllerInternal a11yController,
- @NonNull AccessibilityManagerService service) {
+ @NonNull AccessibilityManagerService service,
+ @NonNull Object lock) {
mA11yController = a11yController;
mService = service;
+ mA11yMSLock = lock;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
}
@Override
public boolean isA11yTracingEnabled() {
- return mA11yController.isAccessibilityTracingEnabled();
+ synchronized (mA11yMSLock) {
+ return mEnabledLoggingFlags != FLAGS_LOGGING_NONE;
+ }
+ }
+
+ @Override
+ public boolean isA11yTracingEnabledForTypes(long typeIdFlags) {
+ synchronized (mA11yMSLock) {
+ return ((typeIdFlags & mEnabledLoggingFlags) != FLAGS_LOGGING_NONE);
+ }
+ }
+
+ @Override
+ public int getTraceStateForAccessibilityManagerClientState() {
+ int state = 0x0;
+ synchronized (mA11yMSLock) {
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CONNECTION_CB_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_INTERACTION_CLIENT)) {
+ state |= STATE_FLAG_TRACE_A11Y_INTERACTION_CLIENT_ENABLED;
+ }
+ if (isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE)) {
+ state |= STATE_FLAG_TRACE_A11Y_SERVICE_ENABLED;
+ }
+ }
+ return state;
}
@Override
- public void startTrace() {
- if (!mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.startTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
+ public void startTrace(long loggingTypes) {
+ if (loggingTypes == FLAGS_LOGGING_NONE) {
+ // Ignore start none request
+ return;
+ }
+
+ synchronized (mA11yMSLock) {
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = loggingTypes;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
}
+
+ mA11yController.startTrace(loggingTypes);
}
@Override
public void stopTrace() {
- if (mA11yController.isAccessibilityTracingEnabled()) {
+ boolean stop = false;
+ synchronized (mA11yMSLock) {
+ stop = isA11yTracingEnabled();
+
+ long oldEnabled = mEnabledLoggingFlags;
+ mEnabledLoggingFlags = FLAGS_LOGGING_NONE;
+
+ if (needToNotifyClients(oldEnabled)) {
+ mService.scheduleUpdateClientsIfNeededLocked(mService.getCurrentUserState());
+ }
+ }
+ if (stop) {
mA11yController.stopTrace();
- mService.scheduleUpdateClientsIfNeeded(mService.getCurrentUserState());
}
}
@Override
- public void logTrace(String where) {
- logTrace(where, "");
+ public void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
}
@Override
- public void logTrace(String where, String callingParams) {
- mA11yController.logTrace(where, callingParams, "".getBytes(),
- Binder.getCallingUid(), Thread.currentThread().getStackTrace());
+ public void logTrace(String where, long loggingTypes, String callingParams) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(),
+ Binder.getCallingUid(), Thread.currentThread().getStackTrace(),
+ new HashSet<String>(Arrays.asList("logTrace")));
+ }
}
@Override
- public void logTrace(long timestamp, String where, String callingParams, int processId,
- long threadId, int callingUid, StackTraceElement[] callStack) {
- if (mA11yController.isAccessibilityTracingEnabled()) {
- mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack,
- timestamp, processId, threadId);
+ public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
+ int processId, long threadId, int callingUid, StackTraceElement[] callStack,
+ Set<String> ignoreElementList) {
+ if (isA11yTracingEnabledForTypes(loggingTypes)) {
+ mA11yController.logTrace(where, loggingTypes, callingParams, "".getBytes(), callingUid,
+ callStack, timestamp, processId, threadId,
+ ((ignoreElementList == null) ? new HashSet<String>() : ignoreElementList));
}
}
- int onShellCommand(String cmd) {
+ private boolean needToNotifyClients(long otherTypesEnabled) {
+ return (mEnabledLoggingFlags & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES)
+ != (otherTypesEnabled & FLAGS_ACCESSIBILITY_MANAGER_CLIENT_STATES);
+ }
+
+ int onShellCommand(String cmd, ShellCommand shell) {
switch (cmd) {
case "start-trace": {
- startTrace();
+ String opt = shell.getNextOption();
+ if (opt == null) {
+ startTrace(FLAGS_LOGGING_ALL);
+ return 0;
+ }
+ List<String> types = new ArrayList<String>();
+ while (opt != null) {
+ switch (opt) {
+ case "-t": {
+ String type = shell.getNextArg();
+ while (type != null) {
+ types.add(type);
+ type = shell.getNextArg();
+ }
+ break;
+ }
+ default: {
+ shell.getErrPrintWriter().println(
+ "Error: option not recognized " + opt);
+ stopTrace();
+ return -1;
+ }
+ }
+ opt = shell.getNextOption();
+ }
+ long enabledTypes = AccessibilityTrace.getLoggingFlagsFromNames(types);
+ startTrace(enabledTypes);
return 0;
}
case "stop-trace": {
@@ -92,8 +217,29 @@ class AccessibilityTraceManager implements AccessibilityTrace {
}
void onHelp(PrintWriter pw) {
- pw.println(" start-trace");
- pw.println(" Start the debug tracing.");
+ pw.println(" start-trace [-t LOGGING_TYPE [LOGGING_TYPE...]]");
+ pw.println(" Start the debug tracing. If no option is present, full trace will be");
+ pw.println(" generated. Options are:");
+ pw.println(" -t: Only generate tracing for the logging type(s) specified here.");
+ pw.println(" LOGGING_TYPE can be any one of below:");
+ pw.println(" IAccessibilityServiceConnection");
+ pw.println(" IAccessibilityServiceClient");
+ pw.println(" IAccessibilityManager");
+ pw.println(" IAccessibilityManagerClient");
+ pw.println(" IAccessibilityInteractionConnection");
+ pw.println(" IAccessibilityInteractionConnectionCallback");
+ pw.println(" IRemoteMagnificationAnimationCallback");
+ pw.println(" IWindowMagnificationConnection");
+ pw.println(" IWindowMagnificationConnectionCallback");
+ pw.println(" WindowManagerInternal");
+ pw.println(" WindowsForAccessibilityCallback");
+ pw.println(" MagnificationCallbacks");
+ pw.println(" InputFilter");
+ pw.println(" Gesture");
+ pw.println(" AccessibilityService");
+ pw.println(" PMBroadcastReceiver");
+ pw.println(" UserBroadcastReceiver");
+ pw.println(" FingerprintGesture");
pw.println(" stop-trace");
pw.println(" Stop the debug tracing.");
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0fde0de59c07..c70bf73fc7f5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -392,7 +392,7 @@ class AccessibilityUserState {
return mBoundServices;
}
- int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) {
+ int getClientStateLocked(boolean isUiAutomationRunning, int traceClientState) {
int clientState = 0;
final boolean a11yEnabled = isUiAutomationRunning
|| isHandlingAccessibilityEventsLocked();
@@ -408,9 +408,9 @@ class AccessibilityUserState {
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
}
- if (isTracingEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED;
- }
+
+ clientState |= traceClientState;
+
return clientState;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index ff794691d2b4..09485d179e60 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
@@ -69,6 +71,7 @@ public class AccessibilityWindowManager {
private final AccessibilityEventSender mAccessibilityEventSender;
private final AccessibilitySecurityPolicy mSecurityPolicy;
private final AccessibilityUserManager mAccessibilityUserManager;
+ private final AccessibilityTraceManager mTraceManager;
// Connections and window tokens for cross-user windows
private final SparseArray<RemoteAccessibilityConnection>
@@ -140,26 +143,20 @@ public class AccessibilityWindowManager {
/**
* Starts tracking windows changes from window manager by registering callback.
- *
- * @return true if callback registers successful.
*/
- boolean startTrackingWindowsLocked() {
- boolean result = true;
-
+ void startTrackingWindowsLocked() {
if (!mTrackingWindows) {
// Turns on the flag before setup the callback.
// In some cases, onWindowsForAccessibilityChanged will be called immediately in
// setWindowsForAccessibilityCallback. We'll lost windows if flag is false.
mTrackingWindows = true;
- result = mWindowManagerInternal.setWindowsForAccessibilityCallback(
- mDisplayId, this);
- if (!result) {
- mTrackingWindows = false;
- Slog.w(LOG_TAG, "set windowsObserver callbacks fail, displayId:"
- + mDisplayId);
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=" + this);
}
+ mWindowManagerInternal.setWindowsForAccessibilityCallback(
+ mDisplayId, this);
}
- return result;
}
/**
@@ -167,6 +164,10 @@ public class AccessibilityWindowManager {
*/
void stopTrackingWindowsLocked() {
if (mTrackingWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("setWindowsForAccessibilityCallback",
+ "displayId=" + mDisplayId + ";callback=null");
+ }
mWindowManagerInternal.setWindowsForAccessibilityCallback(
mDisplayId, null);
mTrackingWindows = false;
@@ -474,6 +475,9 @@ public class AccessibilityWindowManager {
if (oldWindow.displayId != newWindow.displayId) {
return true;
}
+ if (oldWindow.taskId != newWindow.taskId) {
+ return true;
+ }
return false;
}
@@ -674,6 +678,7 @@ public class AccessibilityWindowManager {
reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
reportedWindow.setPictureInPicture(window.inPictureInPicture);
reportedWindow.setDisplayId(window.displayId);
+ reportedWindow.setTaskId(window.taskId);
final int parentId = findWindowIdLocked(userId, window.parentToken);
if (parentId >= 0) {
@@ -844,19 +849,21 @@ public class AccessibilityWindowManager {
}
/**
- * Constructor for AccessibilityManagerService.
+ * Constructor for AccessibilityWindowManager.
*/
public AccessibilityWindowManager(@NonNull Object lock, @NonNull Handler handler,
@NonNull WindowManagerInternal windowManagerInternal,
@NonNull AccessibilityEventSender accessibilityEventSender,
@NonNull AccessibilitySecurityPolicy securityPolicy,
- @NonNull AccessibilityUserManager accessibilityUserManager) {
+ @NonNull AccessibilityUserManager accessibilityUserManager,
+ @NonNull AccessibilityTraceManager traceManager) {
mLock = lock;
mHandler = handler;
mWindowManagerInternal = windowManagerInternal;
mAccessibilityEventSender = accessibilityEventSender;
mSecurityPolicy = securityPolicy;
mAccessibilityUserManager = accessibilityUserManager;
+ mTraceManager = traceManager;
}
/**
@@ -873,9 +880,8 @@ public class AccessibilityWindowManager {
if (observer.isTrackingWindowsLocked()) {
return;
}
- if (observer.startTrackingWindowsLocked()) {
- mDisplayWindowsObservers.put(displayId, observer);
- }
+ observer.startTrackingWindowsLocked();
+ mDisplayWindowsObservers.put(displayId, observer);
}
}
@@ -957,6 +963,9 @@ public class AccessibilityWindowManager {
final int windowId;
boolean shouldComputeWindows = false;
final IBinder token = window.asBinder();
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + token);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
@@ -1003,9 +1012,15 @@ public class AccessibilityWindowManager {
registerIdLocked(leashToken, windowId);
}
if (shouldComputeWindows) {
+ if (traceWMEnabled()) {
+ logTraceWM("computeWindowsForAccessibility", "displayId=" + displayId);
+ }
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
}
-
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata",
+ "token=" + token + ";windowId=" + windowId);
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
return windowId;
}
@@ -1139,6 +1154,10 @@ public class AccessibilityWindowManager {
mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
if (binder != null) {
+ if (traceWMEnabled()) {
+ logTraceWM("setAccessibilityIdToSurfaceMetadata", "token=" + binder
+ + ";windowId=AccessibilityWindowInfo.UNDEFINED_WINDOW_ID");
+ }
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
}
@@ -1169,6 +1188,9 @@ public class AccessibilityWindowManager {
* @return The userId
*/
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
+ if (traceWMEnabled()) {
+ logTraceWM("getWindowOwnerUserId", "token=" + windowToken);
+ }
return mWindowManagerInternal.getWindowOwnerUserId(windowToken);
}
@@ -1361,20 +1383,9 @@ public class AccessibilityWindowManager {
// the touched window are delivered is fine.
final int oldActiveWindow = mActiveWindowId;
setActiveWindowLocked(mTopFocusedWindowId);
-
- // If there is no service that can operate with interactive windows
- // then we keep the old behavior where a window loses accessibility
- // focus if it is no longer active. This still changes the behavior
- // for services that do not operate with interactive windows and run
- // at the same time as the one(s) which does. In practice however,
- // there is only one service that uses accessibility focus and it
- // is typically the one that operates with interactive windows, So,
- // this is fine. Note that to allow a service to work across windows
- // we have to allow accessibility focus stay in any of them. Sigh...
- final boolean accessibilityFocusOnlyInActiveWindow = !isTrackingWindowsLocked();
if (oldActiveWindow != mActiveWindowId
&& mAccessibilityFocusedWindowId == oldActiveWindow
- && accessibilityFocusOnlyInActiveWindow) {
+ && accessibilityFocusOnlyInActiveWindowLocked()) {
clearAccessibilityFocusLocked(oldActiveWindow);
}
}
@@ -1547,6 +1558,10 @@ public class AccessibilityWindowManager {
for (int i = 0; i < connectionList.size(); i++) {
final RemoteAccessibilityConnection connection = connectionList.get(i);
if (connection != null) {
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
+
try {
connection.getRemote().notifyOutsideTouch();
} catch (RemoteException re) {
@@ -1567,6 +1582,9 @@ public class AccessibilityWindowManager {
*/
public int getDisplayIdByUserIdAndWindowIdLocked(int userId, int windowId) {
final IBinder windowToken = getWindowTokenForUserAndWindowIdLocked(userId, windowId);
+ if (traceWMEnabled()) {
+ logTraceWM("getDisplayIdForWindow", "token=" + windowToken);
+ }
final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken);
return displayId;
}
@@ -1588,6 +1606,15 @@ public class AccessibilityWindowManager {
return displayList;
}
+ // If there is no service that can operate with interactive windows
+ // then a window loses accessibility focus if it is no longer active.
+ // This inspection happens when the user interaction is ended.
+ // Note that to allow a service to work across windows,
+ // we have to allow accessibility focus stay in any of them.
+ boolean accessibilityFocusOnlyInActiveWindowLocked() {
+ return !isTrackingWindowsLocked();
+ }
+
/**
* Gets current input focused window token from window manager, and returns its windowId.
*
@@ -1595,6 +1622,9 @@ public class AccessibilityWindowManager {
* @return The input focused windowId, or -1 if not found
*/
private int findFocusedWindowId(int userId) {
+ if (traceWMEnabled()) {
+ logTraceWM("getFocusedWindowToken", "");
+ }
final IBinder token = mWindowManagerInternal.getFocusedWindowToken();
synchronized (mLock) {
return findWindowIdLocked(userId, token);
@@ -1644,6 +1674,9 @@ public class AccessibilityWindowManager {
return;
}
}
+ if (traceIntConnEnabled()) {
+ logTraceIntConn("notifyOutsideTouch");
+ }
try {
connection.getRemote().clearAccessibilityFocus();
} catch (RemoteException re) {
@@ -1666,6 +1699,25 @@ public class AccessibilityWindowManager {
return null;
}
+ private boolean traceWMEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTraceWM(String methodName, String params) {
+ mTraceManager.logTrace("WindowManagerInternal." + methodName,
+ FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
+ private boolean traceIntConnEnabled() {
+ return mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
+ private void logTraceIntConn(String methodName) {
+ mTraceManager.logTrace(
+ LOG_TAG + "." + methodName, FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION);
+ }
+
/**
* Associate the token of the embedded view hierarchy to the host view hierarchy.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index f5b0eb1eb5b6..95f3560dbbb8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
@@ -56,6 +57,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
+ private final AccessibilityTraceManager mTrace;
private final Context mContext;
private final int mUserId;
@@ -63,13 +65,18 @@ public class AutoclickController extends BaseEventStreamTransformation {
private ClickScheduler mClickScheduler;
private ClickDelayObserver mClickDelayObserver;
- public AutoclickController(Context context, int userId) {
+ public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
+ mTrace = trace;
mContext = context;
mUserId = userId;
}
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (mClickScheduler == null) {
Handler handler = new Handler(mContext.getMainLooper());
@@ -89,6 +96,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
+ mTrace.logTrace(LOG_TAG + ".onKeyEvent", AccessibilityTrace.FLAGS_INPUT_FILTER,
+ "event=" + event + ";policyFlags=" + policyFlags);
+ }
if (mClickScheduler != null) {
if (KeyEvent.isModifierKey(event.getKeyCode())) {
mClickScheduler.updateMetaState(event.getMetaState());
diff --git a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
index bc379c204d44..b8250c028f62 100644
--- a/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/KeyboardInterceptor.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
+
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -64,6 +66,10 @@ public class KeyboardInterceptor extends BaseEventStreamTransformation implement
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(FLAGS_INPUT_FILTER)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
+ FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
+ }
/*
* Certain keys have system-level behavior that affects accessibility services.
* Let that behavior settle before handling the keys
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 2673cd1ac3b8..5cbd1a208ce1 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -16,6 +16,7 @@
package com.android.server.accessibility;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.GestureStep;
import android.accessibilityservice.GestureDescription.TouchPoint;
@@ -68,6 +69,7 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
private final Handler mHandler;
private final SparseArray<Boolean> mOpenGesturesInProgress = new SparseArray<>();
+ private final AccessibilityTraceManager mTrace;
private IAccessibilityServiceClient mServiceInterfaceForCurrentGesture;
private IntArray mSequencesInProgress = new IntArray(5);
private boolean mIsDestroyed = false;
@@ -80,15 +82,17 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
/**
* @param looper A looper on the main thread to use for dispatching new events
*/
- public MotionEventInjector(Looper looper) {
+ public MotionEventInjector(Looper looper, AccessibilityTraceManager trace) {
mHandler = new Handler(looper, this);
+ mTrace = trace;
}
/**
* @param handler A handler to post messages. Exposes internal state for testing only.
*/
- public MotionEventInjector(Handler handler) {
+ public MotionEventInjector(Handler handler, AccessibilityTraceManager trace) {
mHandler = handler;
+ mTrace = trace;
}
/**
@@ -112,6 +116,12 @@ public class MotionEventInjector extends BaseEventStreamTransformation implement
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace(LOG_TAG + ".onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
// MotionEventInjector would cancel any injected gesture when any MotionEvent arrives.
// For user using an external device to control the pointer movement, it's almost
// impossible to perform the gestures. Any slightly unintended movement results in the
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 9547280018e4..7ee0690265f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.annotation.Nullable;
import android.app.UiAutomation;
@@ -126,7 +127,6 @@ class UiAutomationManager {
mUiAutomationServiceOwner = owner;
mUiAutomationServiceInfo = accessibilityServiceInfo;
mUiAutomationService.mServiceInterface = serviceClient;
- mUiAutomationService.onAdded();
try {
mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
0);
@@ -136,6 +136,8 @@ class UiAutomationManager {
return;
}
+ mUiAutomationService.onAdded();
+
mUiAutomationService.connectServiceUnknownThread();
}
}
@@ -269,6 +271,14 @@ class UiAutomationManager {
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
+ AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT,
+ "serviceConnection=" + this + ";connectionId=" + mId
+ + "windowToken="
+ + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
+ }
serviceInterface.init(this, mId,
mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index d7bc04091181..74f0bcb4245c 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.gestures;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_GESTURE;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_INPUT_FILTER;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
@@ -82,6 +84,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
implements GestureManifold.Listener {
static final boolean DEBUG = false;
+ private static final long LOGGING_FLAGS = FLAGS_GESTURE | FLAGS_INPUT_FILTER;
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -254,6 +257,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onMotionEvent", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
super.onMotionEvent(event, rawEvent, policyFlags);
return;
@@ -308,6 +315,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onAccessibilityEvent",
+ LOGGING_FLAGS, "event=" + event);
+ }
final int eventType = event.getEventType();
if (eventType == TYPE_VIEW_HOVER_EXIT) {
@@ -346,6 +357,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTapAndHold", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) {
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
if (isSendMotionEventsEnabled()) {
@@ -362,6 +377,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onDoubleTap(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onDoubleTap", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
mAms.onTouchInteractionEnd();
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -394,6 +413,9 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureStarted() {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureStarted", LOGGING_FLAGS);
+ }
// We have to perform gesture detection, so
// clear the current state and try to detect.
mSendHoverEnterAndMoveDelayed.cancel();
@@ -407,6 +429,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCompleted",
+ LOGGING_FLAGS, "event=" + gestureEvent);
+ }
endGestureDetection(true);
mSendTouchInteractionEndDelayed.cancel();
dispatchGesture(gestureEvent);
@@ -415,6 +441,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public boolean onGestureCancelled(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mAms.getTraceManager().isA11yTracingEnabledForTypes(LOGGING_FLAGS)) {
+ mAms.getTraceManager().logTrace(LOG_TAG + ".onGestureCancelled", LOGGING_FLAGS,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (mState.isGestureDetecting()) {
endGestureDetection(event.getActionMasked() == ACTION_UP);
return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 6dabe76c7dc9..6ff082673a15 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -98,7 +98,6 @@ public class TouchState {
mLastReceivedEvent.recycle();
mLastReceivedEvent = null;
}
- mLastTouchedWindowId = -1;
mReceivedPointerTracker.clear();
mInjectedPointersDown = 0;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1f66bfdb20c1..718da9e390ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
+
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -46,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.wm.WindowManagerInternal;
import java.util.Locale;
@@ -135,6 +138,10 @@ public class FullScreenMagnificationController {
*/
@GuardedBy("mLock")
boolean register() {
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=" + this);
+ }
mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, this);
if (!mRegistered) {
@@ -142,6 +149,10 @@ public class FullScreenMagnificationController {
return false;
}
mSpecAnimationBridge.setEnabled(true);
+ if (traceEnabled()) {
+ logTrace("getMagnificationRegion",
+ "displayID=" + mDisplayId + ";region=" + mMagnificationRegion);
+ }
// Obtain initial state.
mControllerCtx.getWindowManager().getMagnificationRegion(
mDisplayId, mMagnificationRegion);
@@ -162,6 +173,10 @@ public class FullScreenMagnificationController {
void unregister(boolean delete) {
if (mRegistered) {
mSpecAnimationBridge.setEnabled(false);
+ if (traceEnabled()) {
+ logTrace("setMagnificationCallbacks",
+ "displayID=" + mDisplayId + ";callback=null");
+ }
mControllerCtx.getWindowManager().setMagnificationCallbacks(
mDisplayId, null);
mMagnificationRegion.setEmpty();
@@ -268,7 +283,7 @@ public class FullScreenMagnificationController {
}
@Override
- public void onRotationChanged(int rotation) {
+ public void onDisplaySizeChanged() {
// Treat as context change and reset
final Message m = PooledLambda.obtainMessage(
FullScreenMagnificationController::resetIfNeeded,
@@ -431,6 +446,10 @@ public class FullScreenMagnificationController {
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
mForceShowMagnifiableBounds = show;
+ if (traceEnabled()) {
+ logTrace("setForceShowMagnifiableBounds",
+ "displayID=" + mDisplayId + ";show=" + show);
+ }
mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
mDisplayId, show);
}
@@ -1255,6 +1274,16 @@ public class FullScreenMagnificationController {
}
}
+ private boolean traceEnabled() {
+ return mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL);
+ }
+
+ private void logTrace(String methodName, String params) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal." + methodName, FLAGS_WINDOW_MANAGER_INTERNAL, params);
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -1320,6 +1349,13 @@ public class FullScreenMagnificationController {
mEnabled = enabled;
if (!mEnabled) {
mSentMagnificationSpec.clear();
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1367,6 +1403,13 @@ public class FullScreenMagnificationController {
}
mSentMagnificationSpec.setTo(spec);
+ if (mControllerCtx.getTraceManager().isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MANAGER_INTERNAL)) {
+ mControllerCtx.getTraceManager().logTrace(
+ "WindowManagerInternal.setMagnificationSpec",
+ FLAGS_WINDOW_MANAGER_INTERNAL,
+ "displayID=" + mDisplayId + ";spec=" + mSentMagnificationSpec);
+ }
mControllerCtx.getWindowManager().setMagnificationSpec(
mDisplayId, mSentMagnificationSpec);
}
@@ -1455,6 +1498,7 @@ public class FullScreenMagnificationController {
public static class ControllerContext {
private final Context mContext;
private final AccessibilityManagerService mAms;
+ private final AccessibilityTraceManager mTrace;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
private final Long mAnimationDuration;
@@ -1469,6 +1513,7 @@ public class FullScreenMagnificationController {
long animationDuration) {
mContext = context;
mAms = ams;
+ mTrace = ams.getTraceManager();
mWindowManager = windowManager;
mHandler = handler;
mAnimationDuration = animationDuration;
@@ -1491,6 +1536,14 @@ public class FullScreenMagnificationController {
}
/**
+ * @return AccessibilityTraceManager
+ */
+ @NonNull
+ public AccessibilityTraceManager getTraceManager() {
+ return mTrace;
+ }
+
+ /**
* @return WindowManagerInternal
*/
@NonNull
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index f7d1b9a311ba..c3d8d4c2c96a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -61,6 +61,7 @@ import android.view.ViewConfiguration;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.gestures.GestureUtils;
/**
@@ -142,12 +143,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap,
boolean detectShortcutTrigger,
@NonNull WindowMagnificationPromptController promptController,
int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Log.i(mLogTag,
"FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index f9aecd739d33..19601b4eff5a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -189,10 +189,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb
if (animationCallback.mCurrentMode == targetMode) {
animationCallback.restoreToCurrentMagnificationMode();
return;
+ } else {
+ Slog.w(TAG, "discard duplicate request");
+ return;
}
- Slog.w(TAG, "request during transition, abandon current:"
- + animationCallback.mTargetMode);
- animationCallback.setExpiredAndRemoveFromListLocked();
}
if (magnificationCenter == null) {
@@ -411,8 +411,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
synchronized (mLock) {
if (mWindowMagnificationMgr == null) {
mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
- mAms.getCurrentUserIdLocked(),
- this);
+ mAms.getCurrentUserIdLocked(), this, mAms.getTraceManager());
}
return mWindowMagnificationMgr;
}
@@ -465,7 +464,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private final TransitionCallBack mTransitionCallBack;
private boolean mExpired = false;
private final int mDisplayId;
+ // The mode the in-progress animation is going to.
private final int mTargetMode;
+ // The mode the in-progress animation is going from.
private final int mCurrentMode;
private final float mCurrentScale;
private final PointF mCurrentCenter = new PointF();
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index bbe40b6faf74..19b339645557 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -20,11 +20,13 @@ import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_UP;
+import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
import android.util.Log;
import android.util.Slog;
import android.view.MotionEvent;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.BaseEventStreamTransformation;
import java.util.ArrayDeque;
@@ -99,14 +101,17 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
void onTripleTapped(int displayId, int mode);
}
+ private final AccessibilityTraceManager mTrace;
protected final Callback mCallback;
protected MagnificationGestureHandler(int displayId, boolean detectTripleTap,
boolean detectShortcutTrigger,
+ AccessibilityTraceManager trace,
@NonNull Callback callback) {
mDisplayId = displayId;
mDetectTripleTap = detectTripleTap;
mDetectShortcutTrigger = detectShortcutTrigger;
+ mTrace = trace;
mCallback = callback;
mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null;
@@ -118,6 +123,12 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
if (DEBUG_ALL) {
Slog.i(mLogTag, "onMotionEvent(" + event + ")");
}
+ if (mTrace.isA11yTracingEnabledForTypes(
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE)) {
+ mTrace.logTrace("MagnificationGestureHandler.onMotionEvent",
+ AccessibilityTrace.FLAGS_INPUT_FILTER | AccessibilityTrace.FLAGS_GESTURE,
+ "event=" + event + ";rawEvent=" + rawEvent + ";policyFlags=" + policyFlags);
+ }
if (DEBUG_EVENT_STREAM) {
storeEventInto(mDebugInputEventHistory, event);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
index 993027d1ca3c..527742536480 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
import static android.os.IBinder.DeathRecipient;
import android.annotation.NonNull;
@@ -27,6 +30,8 @@ import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
/**
* A wrapper of {@link IWindowMagnificationConnection}.
*/
@@ -36,9 +41,12 @@ class WindowMagnificationConnectionWrapper {
private static final String TAG = "WindowMagnificationConnectionWrapper";
private final @NonNull IWindowMagnificationConnection mConnection;
+ private final @NonNull AccessibilityTraceManager mTrace;
- WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+ WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection,
+ @NonNull AccessibilityTraceManager trace) {
mConnection = connection;
+ mTrace = trace;
}
//Should not use this instance anymore after calling it.
@@ -52,9 +60,15 @@ class WindowMagnificationConnectionWrapper {
boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".enableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale + ";centerX=" + centerX
+ + ";centerY=" + centerY + ";callback=" + callback);
+ }
try {
mConnection.enableWindowMagnification(displayId, scale, centerX, centerY,
- transformToRemoteCallback(callback));
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling enableWindowMagnification()", e);
@@ -65,6 +79,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean setScale(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".setScale", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
try {
mConnection.setScale(displayId, scale);
} catch (RemoteException e) {
@@ -78,8 +96,14 @@ class WindowMagnificationConnectionWrapper {
boolean disableWindowMagnification(int displayId,
@Nullable MagnificationAnimationCallback callback) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".disableWindowMagnification",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";callback=" + callback);
+ }
try {
- mConnection.disableWindowMagnification(displayId, transformToRemoteCallback(callback));
+ mConnection.disableWindowMagnification(displayId,
+ transformToRemoteCallback(callback, mTrace));
} catch (RemoteException e) {
if (DBG) {
Slog.e(TAG, "Error calling disableWindowMagnification()", e);
@@ -90,6 +114,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".moveWindowMagnifier", FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";offsetX=" + offsetX + ";offsetY=" + offsetY);
+ }
try {
mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
} catch (RemoteException e) {
@@ -102,6 +130,11 @@ class WindowMagnificationConnectionWrapper {
}
boolean showMagnificationButton(int displayId, int magnificationMode) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".showMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
try {
mConnection.showMagnificationButton(displayId, magnificationMode);
} catch (RemoteException e) {
@@ -114,6 +147,10 @@ class WindowMagnificationConnectionWrapper {
}
boolean removeMagnificationButton(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".removeMagnificationButton",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "displayId=" + displayId);
+ }
try {
mConnection.removeMagnificationButton(displayId);
} catch (RemoteException e) {
@@ -126,6 +163,14 @@ class WindowMagnificationConnectionWrapper {
}
boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + ".setConnectionCallback",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION
+ | FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "callback=" + connectionCallback);
+ }
try {
mConnection.setConnectionCallback(connectionCallback);
} catch (RemoteException e) {
@@ -139,25 +184,38 @@ class WindowMagnificationConnectionWrapper {
private static @Nullable
IRemoteMagnificationAnimationCallback transformToRemoteCallback(
- MagnificationAnimationCallback callback) {
+ MagnificationAnimationCallback callback, AccessibilityTraceManager trace) {
if (callback == null) {
return null;
}
- return new RemoteAnimationCallback(callback);
+ return new RemoteAnimationCallback(callback, trace);
}
private static class RemoteAnimationCallback extends
IRemoteMagnificationAnimationCallback.Stub {
-
private final MagnificationAnimationCallback mCallback;
+ private final AccessibilityTraceManager mTrace;
- RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback) {
+ RemoteAnimationCallback(@NonNull MagnificationAnimationCallback callback,
+ @NonNull AccessibilityTraceManager trace) {
mCallback = callback;
+ mTrace = trace;
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.constructor",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "callback=" + callback);
+ }
}
@Override
public void onResult(boolean success) throws RemoteException {
mCallback.onResult(success);
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK)) {
+ mTrace.logTrace("RemoteAnimationCallback.onResult",
+ FLAGS_REMOTE_MAGNIFICATION_ANIMATION_CALLBACK, "success=" + success);
+ }
+
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 4fb9a03b8ac1..b26d36493a3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -34,6 +34,7 @@ import android.view.Display;
import android.view.MotionEvent;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.gestures.MultiTap;
import com.android.server.accessibility.gestures.MultiTapAndHold;
@@ -89,9 +90,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
public WindowMagnificationGestureHandler(@UiContext Context context,
WindowMagnificationManager windowMagnificationMgr,
+ AccessibilityTraceManager trace,
Callback callback,
boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
Slog.i(mLogTag,
"WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 938cb73dac79..7a111d80b42e 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -16,6 +16,9 @@
package com.android.server.accessibility.magnification;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
@@ -39,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
/**
@@ -111,11 +115,14 @@ public class WindowMagnificationManager implements
}
private final Callback mCallback;
+ private final AccessibilityTraceManager mTrace;
- public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback) {
+ public WindowMagnificationManager(Context context, int userId, @NonNull Callback callback,
+ AccessibilityTraceManager trace) {
mContext = context;
mUserId = userId;
mCallback = callback;
+ mTrace = trace;
}
/**
@@ -135,7 +142,7 @@ public class WindowMagnificationManager implements
mConnectionWrapper = null;
}
if (connection != null) {
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace);
}
if (mConnectionWrapper != null) {
@@ -197,7 +204,10 @@ public class WindowMagnificationManager implements
}
}
}
-
+ if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) {
+ mTrace.logTrace(TAG + ".requestWindowMagnificationConnection",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect);
+ }
final long identity = Binder.clearCallingIdentity();
try {
final StatusBarManagerInternal service = LocalServices.getService(
@@ -511,6 +521,12 @@ public class WindowMagnificationManager implements
@Override
public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";bounds=" + bounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -527,11 +543,23 @@ public class WindowMagnificationManager implements
@Override
public void onChangeMagnificationMode(int displayId, int magnificationMode)
throws RemoteException {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";mode=" + magnificationMode);
+ }
//TODO: Uses this method to change the magnification mode on non-default display.
}
@Override
public void onSourceBoundsChanged(int displayId, Rect sourceBounds) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";source=" + sourceBounds);
+ }
synchronized (mLock) {
WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
if (magnifier == null) {
@@ -543,11 +571,23 @@ public class WindowMagnificationManager implements
@Override
public void onPerformScaleAction(int displayId, float scale) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId + ";scale=" + scale);
+ }
mCallback.onPerformScaleAction(displayId, scale);
}
@Override
public void onAccessibilityActionPerformed(int displayId) {
+ if (mTrace.isA11yTracingEnabledForTypes(
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) {
+ mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed",
+ FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK,
+ "displayId=" + displayId);
+ }
mCallback.onAccessibilityActionPerformed(displayId);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index de5f47dd5dbb..012c5b17c066 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -618,7 +618,7 @@ public final class AutofillManagerService
+ " for " + durationMs + "ms");
enforceCallingPermissionForManagement();
- Preconditions.checkNotNull(serviceName);
+ Objects.requireNonNull(serviceName);
if (durationMs > MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS) {
throw new IllegalArgumentException("Max duration is "
+ MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS + " (called with " + durationMs + ")");
@@ -929,7 +929,7 @@ public final class AutofillManagerService
void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
long expiration) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -939,7 +939,7 @@ public final class AutofillManagerService
void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
long expiration) {
- Preconditions.checkNotNull(componentName);
+ Objects.requireNonNull(componentName);
synchronized (mLock) {
AutofillDisabledInfo info =
getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
@@ -949,7 +949,7 @@ public final class AutofillManagerService
boolean isAutofillDisabledLocked(@UserIdInt int userId,
@NonNull ComponentName componentName) {
- Preconditions.checkNotNull(componentName);
+ Objects.requireNonNull(componentName);
final boolean disabled;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -959,7 +959,7 @@ public final class AutofillManagerService
}
long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
final Long expiration;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -971,7 +971,7 @@ public final class AutofillManagerService
@Nullable
ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
@NonNull String packageName) {
- Preconditions.checkNotNull(packageName);
+ Objects.requireNonNull(packageName);
final ArrayMap<String, Long> disabledList;
synchronized (mLock) {
final AutofillDisabledInfo info = mCache.get(userId);
@@ -1593,8 +1593,8 @@ public final class AutofillManagerService
@NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
- activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
- appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+ activityToken = Objects.requireNonNull(activityToken, "activityToken");
+ appCallback = Objects.requireNonNull(appCallback, "appCallback");
boolean restored = false;
synchronized (mLock) {
@@ -1693,7 +1693,7 @@ public final class AutofillManagerService
@Override
public void onPendingSaveUi(int operation, IBinder token) {
- Preconditions.checkNotNull(token, "token");
+ Objects.requireNonNull(token, "token");
Preconditions.checkArgument(operation == AutofillManager.PENDING_UI_OPERATION_CANCEL
|| operation == AutofillManager.PENDING_UI_OPERATION_RESTORE,
"invalid operation: %d", operation);
diff --git a/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
new file mode 100644
index 000000000000..715697d82cad
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ClientSuggestionsSession.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.autofill;
+
+import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IFillCallback;
+import android.service.autofill.SaveInfo;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Maintains a client suggestions session with the
+ * {@link android.view.autofill.AutofillRequestCallback} through the {@link IAutoFillManagerClient}.
+ *
+ */
+final class ClientSuggestionsSession {
+
+ private static final String TAG = "ClientSuggestionsSession";
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 15 * DateUtils.SECOND_IN_MILLIS;
+
+ private final int mSessionId;
+ private final IAutoFillManagerClient mClient;
+ private final Handler mHandler;
+ private final ComponentName mComponentName;
+
+ private final RemoteFillService.FillServiceCallbacks mCallbacks;
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private AndroidFuture<FillResponse> mPendingFillRequest;
+ @GuardedBy("mLock")
+ private int mPendingFillRequestId = INVALID_REQUEST_ID;
+
+ ClientSuggestionsSession(int sessionId, IAutoFillManagerClient client, Handler handler,
+ ComponentName componentName, RemoteFillService.FillServiceCallbacks callbacks) {
+ mSessionId = sessionId;
+ mClient = client;
+ mHandler = handler;
+ mComponentName = componentName;
+ mCallbacks = callbacks;
+ }
+
+ void onFillRequest(int requestId, InlineSuggestionsRequest inlineRequest, int flags) {
+ final AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+ final AtomicReference<AndroidFuture<FillResponse>> futureRef = new AtomicReference<>();
+ final AndroidFuture<FillResponse> fillRequest = new AndroidFuture<>();
+
+ mHandler.post(() -> {
+ if (sVerbose) {
+ Slog.v(TAG, "calling onFillRequest() for id=" + requestId);
+ }
+
+ try {
+ mClient.requestFillFromClient(requestId, inlineRequest,
+ new FillCallbackImpl(fillRequest, futureRef, cancellationSink));
+ } catch (RemoteException e) {
+ fillRequest.completeExceptionally(e);
+ }
+ });
+
+ fillRequest.orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+ futureRef.set(fillRequest);
+
+ synchronized (mLock) {
+ mPendingFillRequest = fillRequest;
+ mPendingFillRequestId = requestId;
+ }
+
+ fillRequest.whenComplete((res, err) -> mHandler.post(() -> {
+ synchronized (mLock) {
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ }
+ if (err == null) {
+ processAutofillId(res);
+ mCallbacks.onFillRequestSuccess(requestId, res,
+ mComponentName.getPackageName(), flags);
+ } else {
+ Slog.e(TAG, "Error calling on client fill request", err);
+ if (err instanceof TimeoutException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ mCallbacks.onFillRequestTimeout(requestId);
+ } else if (err instanceof CancellationException) {
+ dispatchCancellationSignal(cancellationSink.get());
+ } else {
+ mCallbacks.onFillRequestFailure(requestId, err.getMessage());
+ }
+ }
+ }));
+ }
+
+ /**
+ * Gets the application info for the component.
+ */
+ @Nullable
+ static ApplicationInfo getAppInfo(ComponentName comp, @UserIdInt int userId) {
+ try {
+ ApplicationInfo si = AppGlobals.getPackageManager().getApplicationInfo(
+ comp.getPackageName(),
+ PackageManager.GET_META_DATA,
+ userId);
+ if (si != null) {
+ return si;
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * Gets the user-visible name of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static CharSequence getAppLabelLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadSafeLabel(
+ context.getPackageManager(), 0 /* do not ellipsize */,
+ TextUtils.SAFE_STRING_FLAG_FIRST_LINE | TextUtils.SAFE_STRING_FLAG_TRIM);
+ }
+
+ /**
+ * Gets the user-visible icon of the application.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ static Drawable getAppIconLocked(Context context, ApplicationInfo appInfo) {
+ return appInfo == null ? null : appInfo.loadIcon(context.getPackageManager());
+ }
+
+ int cancelCurrentRequest() {
+ synchronized (mLock) {
+ return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+ ? mPendingFillRequestId
+ : INVALID_REQUEST_ID;
+ }
+ }
+
+ /**
+ * The {@link AutofillId} which the client gets from its view is not contain the session id,
+ * but Autofill framework is using the {@link AutofillId} with a session id. So before using
+ * those ids in the Autofill framework, applies the current session id.
+ *
+ * @param res which response need to apply for a session id
+ */
+ private void processAutofillId(FillResponse res) {
+ if (res == null) {
+ return;
+ }
+
+ final List<Dataset> datasets = res.getDatasets();
+ if (datasets != null && !datasets.isEmpty()) {
+ for (int i = 0; i < datasets.size(); i++) {
+ final Dataset dataset = datasets.get(i);
+ if (dataset != null) {
+ applySessionId(dataset.getFieldIds());
+ }
+ }
+ }
+
+ final SaveInfo saveInfo = res.getSaveInfo();
+ if (saveInfo != null) {
+ applySessionId(saveInfo.getOptionalIds());
+ applySessionId(saveInfo.getRequiredIds());
+ applySessionId(saveInfo.getSanitizerValues());
+ applySessionId(saveInfo.getTriggerId());
+ }
+ }
+
+ private void applySessionId(List<AutofillId> ids) {
+ if (ids == null || ids.isEmpty()) {
+ return;
+ }
+
+ for (int i = 0; i < ids.size(); i++) {
+ applySessionId(ids.get(i));
+ }
+ }
+
+ private void applySessionId(AutofillId[][] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId[] ids) {
+ if (ids == null) {
+ return;
+ }
+ for (int i = 0; i < ids.length; i++) {
+ applySessionId(ids[i]);
+ }
+ }
+
+ private void applySessionId(AutofillId id) {
+ if (id == null) {
+ return;
+ }
+ id.setSessionId(mSessionId);
+ }
+
+ private void dispatchCancellationSignal(@Nullable ICancellationSignal signal) {
+ if (signal == null) {
+ return;
+ }
+ try {
+ signal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error requesting a cancellation", e);
+ }
+ }
+
+ private class FillCallbackImpl extends IFillCallback.Stub {
+ final AndroidFuture<FillResponse> mFillRequest;
+ final AtomicReference<AndroidFuture<FillResponse>> mFutureRef;
+ final AtomicReference<ICancellationSignal> mCancellationSink;
+
+ FillCallbackImpl(AndroidFuture<FillResponse> fillRequest,
+ AtomicReference<AndroidFuture<FillResponse>> futureRef,
+ AtomicReference<ICancellationSignal> cancellationSink) {
+ mFillRequest = fillRequest;
+ mFutureRef = futureRef;
+ mCancellationSink = cancellationSink;
+ }
+
+ @Override
+ public void onCancellable(ICancellationSignal cancellation) {
+ AndroidFuture<FillResponse> future = mFutureRef.get();
+ if (future != null && future.isCancelled()) {
+ dispatchCancellationSignal(cancellation);
+ } else {
+ mCancellationSink.set(cancellation);
+ }
+ }
+
+ @Override
+ public void onSuccess(FillResponse response) {
+ mFillRequest.complete(response);
+ }
+
+ @Override
+ public void onFailure(int requestId, CharSequence message) {
+ String errorMessage = message == null ? "" : String.valueOf(message);
+ mFillRequest.completeExceptionally(
+ new RuntimeException(errorMessage));
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index df269d71f915..a4bf52a3ed1b 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@ import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.FLAG_ENABLED_CLIENT_SUGGESTIONS;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
@@ -53,6 +54,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -347,6 +349,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
+ @Nullable
+ private ClientSuggestionsSession mClientSuggestionsSession;
+
private final AccessibilityManager mAccessibilityManager;
void onSwitchInputMethodLocked() {
@@ -419,6 +424,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** Whether the current {@link FillResponse} is expired. */
@GuardedBy("mLock")
private boolean mExpiredResponse;
+
+ /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */
+ @GuardedBy("mLock")
+ private boolean mClientSuggestionsEnabled;
}
/**
@@ -445,13 +454,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mWaitForInlineRequest = inlineSuggestionsRequest != null;
mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
- maybeRequestFillLocked();
+ mWaitForInlineRequest = inlineSuggestionsRequest != null;
+ maybeRequestFillFromServiceLocked();
viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
} : null;
}
- void maybeRequestFillLocked() {
+ void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) {
+ mPendingFillRequest = null;
+ mWaitForInlineRequest = inlineRequest != null;
+ mPendingInlineSuggestionsRequest = inlineRequest;
+ }
+
+ void maybeRequestFillFromServiceLocked() {
if (mPendingFillRequest == null) {
return;
}
@@ -463,7 +479,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// If a11y touch exploration is enabled, then we do not send an inline fill request
// to the regular af service, because dropdown UI is easier to use.
- if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+ if (mPendingInlineSuggestionsRequest.isServiceSupported()
+ && !mAccessibilityManager.isTouchExplorationEnabled()) {
mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
mPendingFillRequest.getFillContexts(),
mPendingFillRequest.getClientState(),
@@ -575,7 +592,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/*inlineSuggestionsRequest=*/null);
mPendingFillRequest = request;
- maybeRequestFillLocked();
+ maybeRequestFillFromServiceLocked();
}
if (mActivityToken != null) {
@@ -737,30 +754,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Cancels the last request sent to the {@link #mRemoteFillService}.
+ * Cancels the last request sent to the {@link #mRemoteFillService} or the
+ * {@link #mClientSuggestionsSession}.
*/
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
- if (mRemoteFillService == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ if (mRemoteFillService == null && mClientSuggestionsSession == null) {
+ wtf(null, "cancelCurrentRequestLocked() called without a remote service or a "
+ + "client suggestions session. mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
return;
}
- final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // Remove the FillContext as there will never be a response for the service
- if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
- final int numContexts = mContexts.size();
+ if (mRemoteFillService != null) {
+ final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
- // It is most likely the last context, hence search backwards
- for (int i = numContexts - 1; i >= 0; i--) {
- if (mContexts.get(i).getRequestId() == canceledRequest) {
- if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
- mContexts.remove(i);
- break;
+ // Remove the FillContext as there will never be a response for the service
+ if (canceledRequest != INVALID_REQUEST_ID && mContexts != null) {
+ final int numContexts = mContexts.size();
+
+ // It is most likely the last context, hence search backwards
+ for (int i = numContexts - 1; i >= 0; i--) {
+ if (mContexts.get(i).getRequestId() == canceledRequest) {
+ if (sDebug) Slog.d(TAG, "cancelCurrentRequest(): id = " + canceledRequest);
+ mContexts.remove(i);
+ break;
+ }
}
}
}
+
+ if (mClientSuggestionsSession != null) {
+ mClientSuggestionsSession.cancelCurrentRequest();
+ }
}
private boolean isViewFocusedLocked(int flags) {
@@ -825,17 +851,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// structure is taken. This causes only one fill request per burst of focus changes.
cancelCurrentRequestLocked();
- // Only ask IME to create inline suggestions request if Autofill provider supports it and
- // the render service is available except the autofill is triggered manually and the view
- // is also not focused.
+ // Only ask IME to create inline suggestions request when
+ // 1. Autofill provider supports it or client enabled client suggestions.
+ // 2. The render service is available.
+ // 3. The view is focused. (The view may not be focused if the autofill is triggered
+ // manually.)
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if (mSessionFlags.mInlineSupportedByService
+ if ((mSessionFlags.mInlineSupportedByService || mSessionFlags.mClientSuggestionsEnabled)
&& remoteRenderService != null
&& isViewFocusedLocked(flags)) {
- Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
- mAssistReceiver.newAutofillRequestLocked(viewState,
- /* isInlineRequest= */ true);
+ final Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer;
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final int finalRequestId = requestId;
+ inlineSuggestionsRequestConsumer = (inlineSuggestionsRequest) -> {
+ // Using client suggestions
+ synchronized (mLock) {
+ onClientFillRequestLocked(finalRequestId, inlineSuggestionsRequest);
+ }
+ viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
+ };
+ } else {
+ inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(
+ viewState, /* isInlineRequest= */ true);
+ }
if (inlineSuggestionsRequestConsumer != null) {
final AutofillId focusedId = mCurrentViewId;
final int requestIdCopy = requestId;
@@ -851,11 +890,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
);
viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
}
+ } else if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Request client suggestions for the dropdown mode
+ onClientFillRequestLocked(requestId, null);
} else {
mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false);
}
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ // Using client suggestions, unnecessary request AssistStructure
+ return;
+ }
+
// Now request the assist structure data.
+ requestAssistStructureLocked(requestId, flags);
+ }
+
+ @GuardedBy("mLock")
+ private void requestAssistStructureLocked(int requestId, int flags) {
try {
final Bundle receiverExtras = new Bundle();
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
@@ -905,10 +957,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mComponentName = componentName;
mCompatMode = compatMode;
mSessionState = STATE_ACTIVE;
+
synchronized (mLock) {
mSessionFlags = new SessionFlags();
mSessionFlags.mAugmentedAutofillOnly = forAugmentedAutofillOnly;
mSessionFlags.mInlineSupportedByService = mService.isInlineSuggestionsEnabledLocked();
+ mSessionFlags.mClientSuggestionsEnabled =
+ (mFlags & FLAG_ENABLED_CLIENT_SUGGESTIONS) != 0;
setClientLocked(client);
}
@@ -1020,12 +1075,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestLog != null) {
requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
}
- processNullResponseLocked(requestId, requestFlags);
+ processNullResponseOrFallbackLocked(requestId, requestFlags);
return;
}
fieldClassificationIds = response.getFieldClassificationIds();
- if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) {
+ if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null
+ && !mService.isFieldClassificationEnabledLocked()) {
Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
processNullResponseLocked(requestId, requestFlags);
return;
@@ -1104,6 +1160,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void processNullResponseOrFallbackLocked(int requestId, int flags) {
+ if (!mSessionFlags.mClientSuggestionsEnabled) {
+ processNullResponseLocked(requestId, flags);
+ return;
+ }
+
+ // fallback to the default platform password manager
+ mSessionFlags.mClientSuggestionsEnabled = false;
+
+ final InlineSuggestionsRequest inlineRequest =
+ (mLastInlineSuggestionsRequest != null
+ && mLastInlineSuggestionsRequest.first == requestId)
+ ? mLastInlineSuggestionsRequest.second : null;
+ mAssistReceiver.newAutofillRequestLocked(inlineRequest);
+ requestAssistStructureLocked(requestId,
+ flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS);
+ return;
+ }
+
// FillServiceCallbacks
@Override
public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
@@ -3052,13 +3128,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
filterText = value.getTextValue().toString();
}
- final CharSequence serviceLabel;
- final Drawable serviceIcon;
+ final CharSequence targetLabel;
+ final Drawable targetIcon;
synchronized (mLock) {
- serviceLabel = mService.getServiceLabelLocked();
- serviceIcon = mService.getServiceIconLocked();
+ if (mSessionFlags.mClientSuggestionsEnabled) {
+ final ApplicationInfo appInfo = ClientSuggestionsSession.getAppInfo(mComponentName,
+ mService.getUserId());
+ targetLabel = ClientSuggestionsSession.getAppLabelLocked(
+ mService.getMaster().getContext(), appInfo);
+ targetIcon = ClientSuggestionsSession.getAppIconLocked(
+ mService.getMaster().getContext(), appInfo);
+ } else {
+ targetLabel = mService.getServiceLabelLocked();
+ targetIcon = mService.getServiceIconLocked();
+ }
}
- if (serviceLabel == null || serviceIcon == null) {
+ if (targetLabel == null || targetIcon == null) {
wtf(null, "onFillReady(): no service label or icon");
return;
}
@@ -3078,7 +3163,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- serviceLabel, serviceIcon, this, id, mCompatMode);
+ targetLabel, targetIcon, this, id, mCompatMode);
mService.logDatasetShown(id, mClientState);
@@ -3125,6 +3210,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
+ final InlineSuggestionsRequest request = inlineSuggestionsRequest.get();
+ if (mSessionFlags.mClientSuggestionsEnabled && !request.isClientSupported()
+ || !mSessionFlags.mClientSuggestionsEnabled && !request.isServiceSupported()) {
+ if (sDebug) {
+ Slog.d(TAG, "Inline suggestions not supported for "
+ + (mSessionFlags.mClientSuggestionsEnabled ? "client" : "service")
+ + ". Falling back to dropdown.");
+ }
+ return false;
+ }
+
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
if (remoteRenderService == null) {
@@ -3133,7 +3229,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
+ new InlineFillUi.InlineFillUiInfo(request, focusedId,
filterText, remoteRenderService, userId, id);
InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
new InlineFillUi.InlineSuggestionUiCallback() {
@@ -3735,6 +3831,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ @GuardedBy("mLock")
+ private void onClientFillRequestLocked(int requestId,
+ InlineSuggestionsRequest inlineSuggestionsRequest) {
+ if (mClientSuggestionsSession == null) {
+ mClientSuggestionsSession = new ClientSuggestionsSession(id, mClient, mHandler,
+ mComponentName, this);
+ }
+
+ if (mContexts == null) {
+ mContexts = new ArrayList<>(1);
+ }
+
+ if (inlineSuggestionsRequest != null && !inlineSuggestionsRequest.isClientSupported()) {
+ inlineSuggestionsRequest = null;
+ }
+
+ mClientSuggestionsSession.onFillRequest(requestId, inlineSuggestionsRequest, mFlags);
+ }
+
/**
* The result of checking whether to show the save dialog, when session can be saved.
*
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 9ee0159e903a..1a5d91c8cca5 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4369,7 +4369,7 @@ public class UserBackupManagerService {
return OperationType.BACKUP;
}
- long oldCallingId = Binder.clearCallingIdentity();
+ final long oldCallingId = Binder.clearCallingIdentity();
try {
IBackupTransport transport = transportClient.connectOrThrow(
/* caller */ "BMS.getOperationTypeFromTransport");
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 30de4b416410..28a40ebea174 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -138,6 +138,8 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -146,6 +148,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@@ -183,6 +186,11 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private static final String XML_ATTR_TIME_APPROVED = "time_approved";
private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
+ private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ static {
+ sDateFormat.setTimeZone(TimeZone.getDefault());
+ }
+
private final CompanionDeviceManagerImpl mImpl;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private PowerWhitelistManager mPowerWhitelistManager;
@@ -642,7 +650,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
association.getDeviceMacAddress(),
association.getPackageName(),
association.getDeviceProfile(),
- active, /* notifyOnDeviceNearby */
+ active /* notifyOnDeviceNearby */,
association.getTimeApprovedMs());
} else {
return association;
@@ -722,12 +730,40 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
synchronized (mLock) {
for (UserInfo user : getAllUsers()) {
forEach(mCachedAssociations.get(user.id), a -> {
- fout.append(" ")
- .append("u").append("" + a.getUserId()).append(": ")
- .append(a.getPackageName()).append(" - ")
- .append(a.getDeviceMacAddress()).append('\n');
+ fout.append(" ").append(a.toString()).append('\n');
});
}
+
+ }
+ fout.append("Currently Connected Devices:").append('\n');
+ for (int i = 0, size = mCurrentlyConnectedDevices.size(); i < size; i++) {
+ fout.append(" ").append(mCurrentlyConnectedDevices.get(i)).append('\n');
+ }
+
+ fout.append("Devices Last Nearby:").append('\n');
+ for (int i = 0, size = mDevicesLastNearby.size(); i < size; i++) {
+ String device = mDevicesLastNearby.keyAt(i);
+ Date time = mDevicesLastNearby.valueAt(i);
+ fout.append(" ").append(device).append(" -> ")
+ .append(sDateFormat.format(time)).append('\n');
+ }
+
+ fout.append("Discovery Service State:").append('\n');
+ for (int i = 0, size = mServiceConnectors.size(); i < size; i++) {
+ int userId = mServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mServiceConnectors.valueAt(i)))
+ .append('\n');
+ }
+
+ fout.append("Device Listener Services State:").append('\n');
+ for (int i = 0, size = mDeviceListenerServiceConnectors.size(); i < size; i++) {
+ int userId = mDeviceListenerServiceConnectors.keyAt(i);
+ fout.append(" ")
+ .append("u").append(Integer.toString(userId)).append(": ")
+ .append(Objects.toString(mDeviceListenerServiceConnectors.valueAt(i)))
+ .append('\n');
}
}
}
@@ -778,7 +814,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
+ " for " + association
+ " - profile still present in " + otherAssociationWithDeviceProfile);
} else {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mRoleManager.removeRoleHolderAsUser(
association.getDeviceProfile(),
@@ -906,7 +942,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
.getStringArray(com.android.internal.R.array.config_companionDeviceCerts);
Signature[] signatures = mPackageManagerInternal
- .getPackage(packageName).getSigningDetails().signatures;
+ .getPackage(packageName).getSigningDetails().getSignatures();
String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures);
Set<String> sameOemPackageCerts =
@@ -1044,7 +1080,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private List<UserInfo> getAllUsers() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getUsers();
} finally {
@@ -1060,7 +1096,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
private Set<Association> getAllAssociations() {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
ArraySet<Association> result = new ArraySet<>();
for (UserInfo user : mUserManager.getAliveUsers()) {
@@ -1072,6 +1108,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
}
+
private Set<Association> getAllAssociations(
int userId, @Nullable String packageFilter, @Nullable String addressFilter) {
return CollectionUtils.filter(
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 55b982b40611..689890f3ce77 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -92,6 +92,7 @@ java_library_static {
name: "services.core.unboosted",
defaults: ["platform_service_defaults"],
srcs: [
+ ":android.hardware.biometrics.face-V1-java-source",
":statslog-art-java-gen",
":services.core-sources",
":services.core.protologsrc",
@@ -107,6 +108,7 @@ java_library_static {
":display-device-config",
":display-layout-config",
":device-state-config",
+ ":guiconstants_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -115,6 +117,7 @@ java_library_static {
libs: [
"services.net",
+ "android.hardware.common-V2-java",
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V1-java",
"android.hardware.power-V1-java",
@@ -144,7 +147,6 @@ java_library_static {
"android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.1-java",
"android.hardware.weaver-V1.0-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",
@@ -152,7 +154,7 @@ java_library_static {
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hardware.rebootescrow-V1-java",
- "android.hardware.soundtrigger-V2.3-java",
+ "android.hardware.soundtrigger-V2.4-java",
"android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 95186ee444e4..e390ae28a092 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.SigningDetails.CertCapabilities;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
@@ -38,6 +39,7 @@ import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
+import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -69,7 +71,6 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
PACKAGE_BROWSER,
PACKAGE_SYSTEM_TEXT_CLASSIFIER,
PACKAGE_PERMISSION_CONTROLLER,
- PACKAGE_DOCUMENTER,
PACKAGE_CONFIGURATOR,
PACKAGE_INCIDENT_REPORT_APPROVER,
PACKAGE_APP_PREDICTOR,
@@ -352,6 +353,12 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
/**
+ * Retrieve all receivers that can handle a broadcast of the given intent.
+ */
+ public abstract List<ResolveInfo> queryIntentReceivers(Intent intent,
+ String resolvedType, int flags, int filterCallingUid, int userId);
+
+ /**
* Retrieve all services that can be performed for the given intent.
* @see PackageManager#queryIntentServices(Intent, int)
*/
@@ -560,11 +567,6 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
int flags, int userId, int callingUid);
- /**
- * Resolves a content provider intent.
- */
- public abstract ProviderInfo resolveContentProvider(String name, int flags, int userId);
-
/**
* Resolves a content provider intent.
*/
@@ -710,6 +712,29 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
public abstract boolean filterAppAccess(
@NonNull String packageName, int callingUid, int userId);
+ /**
+ * Returns whether or not access to the application which belongs to the given UID should be
+ * filtered. If the UID is part of a shared user ID, return {@code true} if all applications
+ * belong to the shared user ID should be filtered.
+ *
+ * @see #filterAppAccess(AndroidPackage, int, int)
+ */
+ public abstract boolean filterAppAccess(int uid, int callingUid);
+
+ /**
+ * Fetches all app Ids that a given application is currently visible to the provided user.
+ *
+ * <p>
+ * <strong>Note: </strong>This only includes UIDs >= {@link Process#FIRST_APPLICATION_UID}
+ * as all other UIDs can already see all applications.
+ * </p>
+ *
+ * If the app is visible to all UIDs, null is returned. If the app is not visible to any
+ * applications, the int array will be empty.
+ */
+ @Nullable
+ public abstract int[] getVisibilityAllowList(@NonNull String packageName, int userId);
+
/** Returns whether the given package was signed by the platform */
public abstract boolean isPlatformSigned(String pkg);
@@ -736,7 +761,7 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
* signing history for {@code serverUid} and with the {@code capability} specified.
*/
public abstract boolean hasSignatureCapability(int serverUid, int clientUid,
- @PackageParser.SigningDetails.CertCapabilities int capability);
+ @CertCapabilities int capability);
/**
* Get appIds of all available apps which specified android:sharedUserId in the manifest.
@@ -821,7 +846,7 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
/**
* Perform the given action for each installed package for a user.
- * Note that packages lock will be held while performin the actions.
+ * Note that packages lock will be held while performing the actions.
*/
public abstract void forEachInstalledPackage(
@NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index b7c61a0d25cf..0d3b06448948 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -614,8 +614,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
// waive WRITE_SECURE_SETTINGS permission check
final long callingIdentity = Binder.clearCallingIdentity();
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
- Binder.restoreCallingIdentity(callingIdentity);
+ try {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.BLUETOOTH_ON, value);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
}
/**
@@ -2479,7 +2483,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
try {
foregroundUser = ActivityManager.getCurrentUser();
valid = (callingUser == foregroundUser) || parentUser == foregroundUser
- || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid;
+ || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid
+ || callingAppId == Process.SHELL_UID;
if (DBG && !valid) {
Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
+ callingUser + " parentUser=" + parentUser + " foregroundUser="
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 047aae772f44..aae1cc024ede 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -147,6 +147,47 @@ public abstract class IntentResolver<F, R extends Object> {
return true;
}
+ /**
+ * Returns whether an intent matches the IntentFilter with a pre-resolved type.
+ */
+ public static boolean intentMatchesFilter(
+ IntentFilter filter, Intent intent, String resolvedType) {
+ final boolean debug = localLOGV
+ || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+ final Printer logPrinter = debug
+ ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) : null;
+
+ if (debug) {
+ Slog.v(TAG, "Intent: " + intent);
+ Slog.v(TAG, "Matching against filter: " + filter);
+ filter.dump(logPrinter, " ");
+ }
+
+ final int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(),
+ intent.getData(), intent.getCategories(), TAG);
+
+ if (match >= 0) {
+ if (debug) {
+ Slog.v(TAG, "Filter matched! match=0x" + Integer.toHexString(match));
+ }
+ return true;
+ } else {
+ if (debug) {
+ final String reason;
+ switch (match) {
+ case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
+ case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
+ case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
+ case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
+ default: reason = "unknown reason"; break;
+ }
+ Slog.v(TAG, "Filter did not match: " + reason);
+ }
+ return false;
+ }
+ }
+
private ArrayList<F> collectFilters(F[] array, IntentFilter matching) {
ArrayList<F> res = null;
if (array != null) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 91b2440f71fe..3a0357398e3b 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -392,7 +392,7 @@ public final class SensorPrivacyService extends SystemService {
return;
}
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
onSensorUseStarted(uid, packageName, sensor);
} finally {
@@ -744,7 +744,7 @@ public final class SensorPrivacyService extends SystemService {
}
if (!enable) {
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
// Remove any notifications prompting the user to disable sensory privacy
NotificationManager notificationManager =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 69c29269b7a9..351050135812 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -455,12 +455,6 @@ class StorageManagerService extends IStorageManager.Stub
"(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
- /** Automotive device unlockes users before system boot complete and this requires special
- * handling as vold reset can lead into race conditions. When this is set, all users unlocked
- * in {@code UserManager} level are unlocked after vold reset.
- */
- private final boolean mIsAutomotive;
-
private VolumeInfo findVolumeByIdOrThrow(String id) {
synchronized (mLock) {
final VolumeInfo vol = mVolumes.get(id);
@@ -1038,7 +1032,7 @@ class StorageManagerService extends IStorageManager.Stub
final ProviderInfo provider = mPmInternal.resolveContentProvider(
MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- user.id);
+ user.id, Process.SYSTEM_UID);
if (provider != null) {
final IActivityManager am = ActivityManager.getService();
try {
@@ -1133,9 +1127,7 @@ class StorageManagerService extends IStorageManager.Stub
mVold.onUserStarted(userId);
mStoraged.onUserStarted(userId);
}
- if (mIsAutomotive) {
- restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
- }
+ restoreSystemUnlockedUsers(userManager, users, systemUnlockedUsers);
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
mStorageManagerInternal.onReset(mVold);
} catch (Exception e) {
@@ -1219,13 +1211,11 @@ class StorageManagerService extends IStorageManager.Stub
// Record user as started so newly mounted volumes kick off events
// correctly, then synthesize events for any already-mounted volumes.
synchronized (mLock) {
- if (mIsAutomotive) {
- for (int unlockedUser : mSystemUnlockedUsers) {
- if (unlockedUser == userId) {
- // This can happen as restoreAllUnlockedUsers can double post the message.
- Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
- return;
- }
+ for (int unlockedUser : mSystemUnlockedUsers) {
+ if (unlockedUser == userId) {
+ // This can happen as restoreAllUnlockedUsers can double post the message.
+ Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId);
+ return;
}
}
for (int i = 0; i < mVolumes.size(); i++) {
@@ -1551,17 +1541,19 @@ class StorageManagerService extends IStorageManager.Stub
}
if (vol.type == VolumeInfo.TYPE_EMULATED) {
+ if (!mStorageSessionController.supportsExternalStorage(vol.mountUserId)) {
+ Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ + Integer.toString(vol.mountUserId)
+ + " does not support external storage.");
+ return;
+ }
+
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);
- if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
- && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
- Slog.v(TAG, "Found primary storage at " + vol);
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
- vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
- mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
-
- } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
+ if ((Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
+ && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id))
+ || Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) {
Slog.v(TAG, "Found primary storage at " + vol);
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
@@ -1922,9 +1914,6 @@ class StorageManagerService extends IStorageManager.Stub
if (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(this);
}
-
- mIsAutomotive = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
}
private void start() {
@@ -2032,7 +2021,7 @@ class StorageManagerService extends IStorageManager.Stub
return mPmInternal.resolveContentProvider(
authority, PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(UserHandle.USER_SYSTEM));
+ UserHandle.getUserId(UserHandle.USER_SYSTEM), Process.SYSTEM_UID);
}
private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
@@ -3397,8 +3386,12 @@ class StorageManagerService extends IStorageManager.Stub
mInstaller.tryMountDataMirror(volumeUuid);
}
}
- } catch (Exception e) {
+ } catch (RemoteException | Installer.InstallerException e) {
Slog.wtf(TAG, e);
+ // Make sure to re-throw this exception; we must not ignore failure
+ // to prepare the user storage as it could indicate that encryption
+ // wasn't successfully set up.
+ throw new RuntimeException(e);
}
}
@@ -3461,48 +3454,6 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- @Override
- public void notifyAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public void notifyAppIoResumed(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- enforceExternalStorageService();
-
- mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
- }
-
- @Override
- public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
- @StorageManager.AppIoBlockedReason int reason) {
- return isAppIoBlocked(uid);
- }
-
-
- private boolean isAppIoBlocked(int uid) {
- return mStorageSessionController.isAppIoBlocked(uid);
- }
-
- /**
- * Enforces that the caller is the {@link ExternalStorageService}
- *
- * @throws SecurityException if the caller doesn't have the
- * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
- * {@link ExternalStorageService}
- */
- private void enforceExternalStorageService() {
- enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
- int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
- if (callingAppId != mMediaStoreAuthorityAppId) {
- throw new SecurityException("Only the ExternalStorageService is permitted");
- }
- }
-
/**
* Returns PendingIntent which can be used by Apps with MANAGE_EXTERNAL_STORAGE permission
* to launch the manageSpaceActivity of the App specified by packageName.
@@ -3517,7 +3468,7 @@ class StorageManagerService extends IStorageManager.Stub
// We want to call the manageSpaceActivity as a SystemService and clear identity
// of the calling App
int originalUid = Binder.getCallingUidOrThrow();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
ApplicationInfo appInfo = mIPackageManager.getApplicationInfo(packageName, 0,
@@ -3550,6 +3501,46 @@ class StorageManagerService extends IStorageManager.Stub
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void notifyAppIoBlocked(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoBlocked(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public void notifyAppIoResumed(String volumeUuid, int uid, int tid, int reason) {
+ enforceExternalStorageService();
+
+ mStorageSessionController.notifyAppIoResumed(volumeUuid, uid, tid, reason);
+ }
+
+ @Override
+ public boolean isAppIoBlocked(String volumeUuid, int uid, int tid,
+ @StorageManager.AppIoBlockedReason int reason) {
+ return isAppIoBlocked(uid);
+ }
+
+
+ private boolean isAppIoBlocked(int uid) {
+ return mStorageSessionController.isAppIoBlocked(uid);
+ }
+
+ /**
+ * Enforces that the caller is the {@link ExternalStorageService}
+ *
+ * @throws SecurityException if the caller doesn't have the
+ * {@link android.Manifest.permission.WRITE_MEDIA_STORAGE} permission or is not the
+ * {@link ExternalStorageService}
+ */
+ private void enforceExternalStorageService() {
+ enforcePermission(android.Manifest.permission.WRITE_MEDIA_STORAGE);
+ int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
+ if (callingAppId != mMediaStoreAuthorityAppId) {
+ throw new SecurityException("Only the ExternalStorageService is permitted");
+ }
+ }
/** Not thread safe */
class AppFuseMountScope extends AppFuseBridge.MountScope {
@@ -4599,7 +4590,6 @@ class StorageManagerService extends IStorageManager.Stub
pw.println();
pw.println("Local unlocked users: " + mLocalUnlockedUsers);
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
- pw.println("isAutomotive:" + mIsAutomotive);
}
synchronized (mObbMounts) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 85eadf5a5137..10071309bc0d 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1168,8 +1168,8 @@ final class UiModeManagerService extends SystemService {
private boolean doesPackageHaveCallingUid(@NonNull String packageName) {
try {
- return getContext().getPackageManager().getPackageUid(packageName, 0)
- == mInjector.getCallingUid();
+ return getContext().getPackageManager().getPackageUidAsUser(packageName,
+ UserHandle.getCallingUserId()) == mInjector.getCallingUid();
} catch (PackageManager.NameNotFoundException e) {
return false;
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a6a8cf018eef..76b55812db0e 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -60,13 +60,14 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
import android.os.Bundle;
@@ -1833,6 +1834,11 @@ public class AccountManagerService
+ ", skipping since the account already exists");
return false;
}
+ if (accounts.accountsDb.findAllDeAccounts().size() > 100) {
+ Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
+ + ", skipping since more than 50 accounts on device exist");
+ return false;
+ }
long accountId = accounts.accountsDb.insertCeAccount(account, password);
if (accountId < 0) {
Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
@@ -4871,9 +4877,7 @@ public class AccountManagerService
int targetUid = targetActivityInfo.applicationInfo.uid;
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
if (!isExportedSystemActivity(targetActivityInfo)
- && !pmi.hasSignatureCapability(
- targetUid, authUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ && !pmi.hasSignatureCapability(targetUid, authUid, CertCapabilities.AUTH)) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -5245,7 +5249,7 @@ public class AccountManagerService
logStatement.bindLong(6, userDebugDbInsertionPoint);
try {
logStatement.execute();
- } catch (IllegalStateException e) {
+ } catch (IllegalStateException | SQLiteFullException e) {
// Guard against crash, DB can already be closed
// since this statement is executed on a handler thread
Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
@@ -5626,8 +5630,7 @@ public class AccountManagerService
return SIGNATURE_CHECK_UID_MATCH;
}
if (pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
return SIGNATURE_CHECK_MATCH;
}
}
@@ -5668,8 +5671,7 @@ public class AccountManagerService
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
serviceInfos) {
if (isOtherwisePermitted || pmi.hasSignatureCapability(
- serviceInfo.uid, callingUid,
- PackageParser.SigningDetails.CertCapabilities.AUTH)) {
+ serviceInfo.uid, callingUid, CertCapabilities.AUTH)) {
managedAccountTypes.add(serviceInfo.type.type);
}
}
diff --git a/services/core/java/com/android/server/accounts/OWNERS b/services/core/java/com/android/server/accounts/OWNERS
index 8dcc04a27af6..df1b4f432cd6 100644
--- a/services/core/java/com/android/server/accounts/OWNERS
+++ b/services/core/java/com/android/server/accounts/OWNERS
@@ -1,9 +1 @@
-carlosvaldivia@google.com
-dementyev@google.com
-sandrakwan@google.com
-hackbod@google.com
-svetoslavganov@google.com
-fkupolov@google.com
-yamasani@google.com
-omakoto@google.com
-
+include /core/java/android/accounts/OWNERS
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 1f35b88c8cbd..f591b26f1770 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1361,7 +1361,10 @@ public class AdbDebuggingManager {
if (args.length > 1) {
hostname = args[1];
}
- PairDevice device = new PairDevice(fingerprints, hostname, false);
+ PairDevice device = new PairDevice();
+ device.name = fingerprints;
+ device.guid = hostname;
+ device.connected = false;
intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
// Add the key into the keystore
@@ -1843,8 +1846,11 @@ public class AdbDebuggingManager {
if (args.length > 1) {
hostname = args[1];
}
- pairedDevices.put(keyEntry.getKey(), new PairDevice(
- hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+ PairDevice pairDevice = new PairDevice();
+ pairDevice.name = hostname;
+ pairDevice.guid = fingerprints;
+ pairDevice.connected = mWifiConnectedKeys.contains(keyEntry.getKey());
+ pairedDevices.put(keyEntry.getKey(), pairDevice);
}
return pairedDevices;
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 29bb5428dd84..7a4d2ce50cd3 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -27,6 +27,8 @@ import android.database.ContentObserver;
import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
import android.debug.AdbTransportType;
+import android.debug.FingerprintAndPairDevice;
+import android.debug.IAdbCallback;
import android.debug.IAdbManager;
import android.debug.IAdbTransport;
import android.debug.PairDevice;
@@ -35,6 +37,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -87,6 +90,7 @@ public class AdbService extends IAdbManager.Stub {
private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+ private final RemoteCallbackList<IAdbCallback> mCallbacks = new RemoteCallbackList<>();
/**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
*/
@@ -348,12 +352,21 @@ public class AdbService extends IAdbManager.Stub {
}
@Override
- public Map<String, PairDevice> getPairedDevices() {
+ public FingerprintAndPairDevice[] getPairedDevices() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
- if (mDebuggingManager != null) {
- return mDebuggingManager.getPairedDevices();
- }
- return null;
+ if (mDebuggingManager == null) {
+ return null;
+ }
+ Map<String, PairDevice> map = mDebuggingManager.getPairedDevices();
+ FingerprintAndPairDevice[] ret = new FingerprintAndPairDevice[map.size()];
+ int i = 0;
+ for (Map.Entry<String, PairDevice> entry : map.entrySet()) {
+ ret[i] = new FingerprintAndPairDevice();
+ ret[i].keyFingerprint = entry.getKey();
+ ret[i].device = entry.getValue();
+ i++;
+ }
+ return ret;
}
@Override
@@ -401,6 +414,21 @@ public class AdbService extends IAdbManager.Stub {
return mConnectionPort.get();
}
+ @Override
+ public void registerCallback(IAdbCallback callback) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Registering callback " + callback);
+ }
+ mCallbacks.register(callback);
+ }
+
+ @Override
+ public void unregisterCallback(IAdbCallback callback) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Unregistering callback " + callback);
+ }
+ mCallbacks.unregister(callback);
+ }
/**
* This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
* do this.
@@ -507,6 +535,23 @@ public class AdbService extends IAdbManager.Stub {
if (mDebuggingManager != null) {
mDebuggingManager.setAdbEnabled(enable, transportType);
}
+
+ if (DEBUG) {
+ Slog.d(TAG, "Broadcasting enable = " + enable + ", type = " + transportType);
+ }
+ mCallbacks.broadcast((callback) -> {
+ if (DEBUG) {
+ Slog.d(TAG, "Sending enable = " + enable + ", type = " + transportType
+ + " to " + callback);
+ }
+ try {
+ callback.onDebuggingChanged(enable, transportType);
+ } catch (RemoteException ex) {
+ if (DEBUG) {
+ Slog.d(TAG, "Unable to send onDebuggingChanged:", ex);
+ }
+ }
+ });
}
@Override
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a2fec2753340..3ba1cb2188f1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -153,7 +153,6 @@ import android.webkit.WebViewZygote;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -165,6 +164,7 @@ import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.LowMemDetector.MemFactor;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -174,6 +174,7 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -328,14 +329,25 @@ public final class ActiveServices {
};
/**
- * Watch for apps being put into forced app standby, so we can step their fg
+ * Reference to the AppStateTracker service. No lock is needed as we'll assign with the same
+ * instance to it always.
+ */
+ AppStateTracker mAppStateTracker;
+
+ /**
+ * Watch for apps being put into background restricted, so we can step their fg
* services down.
*/
- class ForcedStandbyListener implements AppStateTracker.ServiceStateListener {
+ class BackgroundRestrictedListener implements AppStateTracker.BackgroundRestrictedAppListener {
@Override
- public void stopForegroundServicesForUidPackage(final int uid, final String packageName) {
+ public void updateBackgroundRestrictedForUidPackage(int uid, String packageName,
+ boolean restricted) {
synchronized (mAm) {
- stopAllForegroundServicesLocked(uid, packageName);
+ if (!isForegroundServiceAllowedInBackgroundRestricted(uid, packageName)) {
+ stopAllForegroundServicesLocked(uid, packageName);
+ }
+ mAm.mProcessList.updateBackgroundRestrictedForUidPackageLocked(
+ uid, packageName, restricted);
}
}
}
@@ -520,12 +532,18 @@ public final class ActiveServices {
}
void systemServicesReady() {
- AppStateTracker ast = LocalServices.getService(AppStateTracker.class);
- ast.addServiceStateListener(new ForcedStandbyListener());
+ getAppStateTracker().addBackgroundRestrictedAppListener(new BackgroundRestrictedListener());
mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class);
setAllowListWhileInUsePermissionInFgs();
}
+ private AppStateTracker getAppStateTracker() {
+ if (mAppStateTracker == null) {
+ mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+ }
+ return mAppStateTracker;
+ }
+
private void setAllowListWhileInUsePermissionInFgs() {
final String attentionServicePackageName =
mAm.mContext.getPackageManager().getAttentionServicePackageName();
@@ -604,9 +622,22 @@ public final class ActiveServices {
}
private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
- final int mode = mAm.getAppOpsManager().checkOpNoThrow(
- AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);
- return (mode != AppOpsManager.MODE_ALLOWED);
+ final AppStateTracker appStateTracker = getAppStateTracker();
+ if (appStateTracker != null) {
+ return appStateTracker.isAppBackgroundRestricted(uid, packageName);
+ }
+ return false;
+ }
+
+ void updateAppRestrictedAnyInBackgroundLocked(final int uid, final String packageName) {
+ final boolean restricted = appRestrictedAnyInBackground(uid, packageName);
+ final UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(uid);
+ if (uidRec != null) {
+ final ProcessRecord app = uidRec.getProcessInPackage(packageName);
+ if (app != null) {
+ app.mState.setBackgroundRestricted(restricted);
+ }
+ }
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
@@ -785,8 +816,19 @@ public final class ActiveServices {
return null;
}
- return startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
+ // If what the client try to start/connect was an alias, then we need to return the
+ // alias component name to the client, not the "target" component name, which is
+ // what realResult contains.
+ final ComponentName realResult =
+ startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg,
allowBackgroundActivityStarts, backgroundActivityStartsToken);
+ if (res.aliasComponent != null
+ && !realResult.getPackageName().startsWith("!")
+ && !realResult.getPackageName().startsWith("?")) {
+ return res.aliasComponent;
+ } else {
+ return realResult;
+ }
}
private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service,
@@ -1448,7 +1490,8 @@ public final class ActiveServices {
if (!aa.mAppOnTop) {
// Transitioning a fg-service host app out of top: if it's bg restricted,
// it loses the fg service state now.
- if (!appRestrictedAnyInBackground(aa.mUid, aa.mPackageName)) {
+ if (isForegroundServiceAllowedInBackgroundRestricted(
+ aa.mUid, aa.mPackageName)) {
if (active == null) {
active = new ArrayList<>();
}
@@ -1666,8 +1709,38 @@ public final class ActiveServices {
}
}
- private boolean appIsTopLocked(int uid) {
- return mAm.getUidStateLocked(uid) <= PROCESS_STATE_TOP;
+ /**
+ * Check if the given app is allowed to have FGS running even if it's background restricted.
+ *
+ * <p>
+ * Currently it needs to be in Top/Bound Top/FGS state. An uid could be in the FGS state if:
+ * a) Bound by another process in the FGS state;
+ * b) There is an active FGS running (ServiceRecord.isForeground is true);
+ * c) The startForegroundService() has been called but the startForeground() hasn't - in this
+ * case, it must have passed the background FGS start check so we're safe here.
+ * </p>
+ */
+ private boolean isForegroundServiceAllowedInBackgroundRestricted(ProcessRecord app) {
+ final ProcessStateRecord state = app.mState;
+ if (!state.isBackgroundRestricted()
+ || state.getSetProcState() <= ActivityManager.PROCESS_STATE_BOUND_TOP) {
+ return true;
+ }
+ if (state.getSetProcState() == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ && state.isSetBoundByNonBgRestrictedApp()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the given uid/pkg is allowed to have FGS running even if it's background restricted.
+ */
+ private boolean isForegroundServiceAllowedInBackgroundRestricted(int uid, String packageName) {
+ final UidRecord uidRec = mAm.mProcessList.getUidRecordLOSP(uid);
+ ProcessRecord app = null;
+ return uidRec != null && ((app = uidRec.getProcessInPackage(packageName)) != null)
+ && isForegroundServiceAllowedInBackgroundRestricted(app);
}
/**
@@ -1761,8 +1834,7 @@ public final class ActiveServices {
// Apps that are TOP or effectively similar may call startForeground() on
// their services even if they are restricted from doing that while in bg.
if (!ignoreForeground
- && !appIsTopLocked(r.appInfo.uid)
- && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
+ && !isForegroundServiceAllowedInBackgroundRestricted(r.app)) {
Slog.w(TAG,
"Service.startForeground() not allowed due to bg restriction: service "
+ r.shortInstanceName);
@@ -2777,7 +2849,7 @@ public final class ActiveServices {
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
- callerApp.uid, callerApp.processName, callingPackage);
+ callerApp.uid, callerApp.processName, callingPackage, res.aliasComponent);
IBinder binder = connection.asBinder();
s.addConnection(binder, c);
@@ -2854,8 +2926,13 @@ public final class ActiveServices {
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
+
+ // If what the client try to start/connect was an alias, then we need to
+ // pass the alias component name instead to the client.
+ final ComponentName clientSideComponentName =
+ res.aliasComponent != null ? res.aliasComponent : s.name;
try {
- c.conn.connected(s.name, b.intent.binder, false);
+ c.conn.connected(clientSideComponentName, b.intent.binder, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortInstanceName
+ " to connection " + c.conn.asBinder()
@@ -2926,8 +3003,12 @@ public final class ActiveServices {
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
+ // If what the client try to start/connect was an alias, then we need to
+ // pass the alias component name instead to the client.
+ final ComponentName clientSideComponentName =
+ c.aliasComponent != null ? c.aliasComponent : r.name;
try {
- c.conn.connected(r.name, service, false);
+ c.conn.connected(clientSideComponentName, service, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.shortInstanceName
+ " to connection " + c.conn.asBinder()
@@ -3079,9 +3160,22 @@ public final class ActiveServices {
final ServiceRecord record;
final String permission;
- ServiceLookupResult(ServiceRecord _record, String _permission) {
+ /**
+ * Set only when we looked up to this service via an alias. Otherwise, it's null.
+ */
+ @Nullable
+ final ComponentName aliasComponent;
+
+ ServiceLookupResult(ServiceRecord _record, ComponentName _aliasComponent) {
record = _record;
+ permission = null;
+ aliasComponent = _aliasComponent;
+ }
+
+ ServiceLookupResult(String _permission) {
+ record = null;
permission = _permission;
+ aliasComponent = null;
}
}
@@ -3113,10 +3207,19 @@ public final class ActiveServices {
/* name= */ "service", callingPackage);
ServiceMap smap = getServiceMapLocked(userId);
+
+ // See if the intent refers to an alias. If so, update the intent with the target component
+ // name. `resolution` will contain the alias component name, which we need to return
+ // to the client.
+ final ComponentAliasResolver.Resolution<ComponentName> resolution =
+ mAm.mComponentAliasResolver.resolveService(service, resolvedType,
+ /* match flags */ 0, userId, callingUid);
+
final ComponentName comp;
if (instanceName == null) {
comp = service.getComponent();
} else {
+ // This is for isolated services
final ComponentName realComp = service.getComponent();
if (realComp == null) {
throw new IllegalArgumentException("Can't use custom instance name '" + instanceName
@@ -3125,6 +3228,7 @@ public final class ActiveServices {
comp = new ComponentName(realComp.getPackageName(),
realComp.getClassName() + ":" + instanceName);
}
+
if (comp != null) {
r = smap.mServicesByInstanceName.get(comp);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by component: " + r);
@@ -3182,7 +3286,7 @@ public final class ActiveServices {
String msg = "association not allowed between packages "
+ callingPackage + " and " + name.getPackageName();
Slog.w(TAG, "Service lookup failed: " + msg);
- return new ServiceLookupResult(null, msg);
+ return new ServiceLookupResult(msg);
}
// Store the defining packageName and uid, as they might be changed in
@@ -3300,11 +3404,11 @@ public final class ActiveServices {
String msg = "association not allowed between packages "
+ callingPackage + " and " + r.packageName;
Slog.w(TAG, "Service lookup failed: " + msg);
- return new ServiceLookupResult(null, msg);
+ return new ServiceLookupResult(msg);
}
if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
resolvedType, r.appInfo)) {
- return new ServiceLookupResult(null, "blocked by firewall");
+ return new ServiceLookupResult("blocked by firewall");
}
if (mAm.checkComponentPermission(r.permission,
callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
@@ -3313,14 +3417,14 @@ public final class ActiveServices {
+ " from pid=" + callingPid
+ ", uid=" + callingUid
+ " that is not exported from uid " + r.appInfo.uid);
- return new ServiceLookupResult(null, "not exported from uid "
+ return new ServiceLookupResult("not exported from uid "
+ r.appInfo.uid);
}
Slog.w(TAG, "Permission Denial: Accessing service " + r.shortInstanceName
+ " from pid=" + callingPid
+ ", uid=" + callingUid
+ " requires " + r.permission);
- return new ServiceLookupResult(null, r.permission);
+ return new ServiceLookupResult(r.permission);
} else if (Manifest.permission.BIND_HOTWORD_DETECTION_SERVICE.equals(r.permission)
&& callingUid != Process.SYSTEM_UID) {
// Hotword detection must run in its own sandbox, and we don't even trust
@@ -3331,7 +3435,7 @@ public final class ActiveServices {
+ ", uid=" + callingUid
+ " requiring permission " + r.permission
+ " can only be bound to from the system.");
- return new ServiceLookupResult(null, "can only be bound to "
+ return new ServiceLookupResult("can only be bound to "
+ "by the system.");
} else if (r.permission != null && callingPackage != null) {
final int opCode = AppOpsManager.permissionToOpCode(r.permission);
@@ -3344,7 +3448,7 @@ public final class ActiveServices {
return null;
}
}
- return new ServiceLookupResult(r, null);
+ return new ServiceLookupResult(r, resolution.getAlias());
}
return null;
}
@@ -3487,6 +3591,8 @@ public final class ActiveServices {
final long now = SystemClock.uptimeMillis();
final String reason;
+ final int oldPosInRestarting = mRestartingServices.indexOf(r);
+ boolean inRestarting = oldPosInRestarting != -1;
if ((r.serviceInfo.applicationInfo.flags
&ApplicationInfo.FLAG_PERSISTENT) == 0) {
long minDuration = mAm.mConstants.SERVICE_RESTART_DURATION;
@@ -3554,58 +3660,89 @@ public final class ActiveServices {
}
if (isServiceRestartBackoffEnabledLocked(r.packageName)) {
- r.nextRestartTime = now + r.restartDelay;
-
- // Make sure that we don't end up restarting a bunch of services
- // all at the same time.
- boolean repeat;
- final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
- do {
- repeat = false;
- for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
- final ServiceRecord r2 = mRestartingServices.get(i);
- if (r2 != r
- && r.nextRestartTime >= (r2.nextRestartTime - restartTimeBetween)
- && r.nextRestartTime < (r2.nextRestartTime + restartTimeBetween)) {
- r.nextRestartTime = r2.nextRestartTime + restartTimeBetween;
- r.restartDelay = r.nextRestartTime - now;
- repeat = true;
- break;
+ r.nextRestartTime = r.mEarliestRestartTime = now + r.restartDelay;
+
+ if (inRestarting) {
+ // Take it out of the list temporarily for easier maintenance of the list.
+ mRestartingServices.remove(oldPosInRestarting);
+ inRestarting = false;
+ }
+ if (mRestartingServices.isEmpty()) {
+ // Apply the extra delay even if it's the only one in the list.
+ final long extraDelay = getExtraRestartTimeInBetweenLocked();
+ r.nextRestartTime = Math.max(now + extraDelay, r.nextRestartTime);
+ r.restartDelay = r.nextRestartTime - now;
+ } else {
+ // Make sure that we don't end up restarting a bunch of services
+ // all at the same time.
+ boolean repeat;
+ final long restartTimeBetween = getExtraRestartTimeInBetweenLocked()
+ + mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
+ do {
+ repeat = false;
+ final long nextRestartTime = r.nextRestartTime;
+ // mRestartingServices is sorted by nextRestartTime.
+ for (int i = mRestartingServices.size() - 1; i >= 0; i--) {
+ final ServiceRecord r2 = mRestartingServices.get(i);
+ final long nextRestartTime2 = r2.nextRestartTime;
+ if (nextRestartTime >= (nextRestartTime2 - restartTimeBetween)
+ && nextRestartTime < (nextRestartTime2 + restartTimeBetween)) {
+ r.nextRestartTime = nextRestartTime2 + restartTimeBetween;
+ r.restartDelay = r.nextRestartTime - now;
+ repeat = true;
+ break;
+ } else if (nextRestartTime >= nextRestartTime2 + restartTimeBetween) {
+ // This spot fulfills our needs, bail out.
+ break;
+ }
}
- }
- } while (repeat);
+ } while (repeat);
+ }
} else {
// It's been forced to ignore the restart backoff, fix the delay here.
r.restartDelay = mAm.mConstants.SERVICE_RESTART_DURATION;
r.nextRestartTime = now + r.restartDelay;
}
-
} else {
// Persistent processes are immediately restarted, so there is no
// reason to hold of on restarting their services.
r.totalRestartCount++;
r.restartCount = 0;
r.restartDelay = 0;
+ r.mEarliestRestartTime = 0;
r.nextRestartTime = now;
reason = "persistent";
}
- if (!mRestartingServices.contains(r)) {
- r.createdFromFg = false;
- mRestartingServices.add(r);
- synchronized (mAm.mProcessStats.mLock) {
- r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
+ r.mRestartSchedulingTime = now;
+ if (!inRestarting) {
+ if (oldPosInRestarting == -1) {
+ r.createdFromFg = false;
+ synchronized (mAm.mProcessStats.mLock) {
+ r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
+ }
+ }
+ boolean added = false;
+ for (int i = 0, size = mRestartingServices.size(); i < size; i++) {
+ final ServiceRecord r2 = mRestartingServices.get(i);
+ if (r2.nextRestartTime > r.nextRestartTime) {
+ mRestartingServices.add(i, r);
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ mRestartingServices.add(r);
}
}
cancelForegroundNotificationLocked(r);
- performScheduleRestartLocked(r, "Scheduling", reason, SystemClock.uptimeMillis());
+ performScheduleRestartLocked(r, "Scheduling", reason, now);
return true;
}
- @VisibleForTesting
@GuardedBy("mAm")
void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling,
@NonNull String reason, @UptimeMillisLong long now) {
@@ -3618,6 +3755,161 @@ public final class ActiveServices {
r.userId, r.shortInstanceName, r.restartDelay);
}
+ /**
+ * Reschedule service restarts based on the given memory pressure.
+ *
+ * @param prevMemFactor The previous memory factor.
+ * @param curMemFactor The current memory factor.
+ * @param reason The human-readable text about why we're doing rescheduling.
+ * @param now The uptimeMillis
+ */
+ @GuardedBy("mAm")
+ void rescheduleServiceRestartOnMemoryPressureIfNeededLocked(@MemFactor int prevMemFactor,
+ @MemFactor int curMemFactor, @NonNull String reason, @UptimeMillisLong long now) {
+ final boolean enabled = mAm.mConstants.mEnableExtraServiceRestartDelayOnMemPressure;
+ if (!enabled) {
+ return;
+ }
+ performRescheduleServiceRestartOnMemoryPressureLocked(
+ mAm.mConstants.mExtraServiceRestartDelayOnMemPressure[prevMemFactor],
+ mAm.mConstants.mExtraServiceRestartDelayOnMemPressure[curMemFactor], reason, now);
+ }
+
+ /**
+ * Reschedule service restarts based on if the extra delays are enabled or not.
+ *
+ * @param prevEnable The previous state of whether or not it's enabled.
+ * @param curEnabled The current state of whether or not it's enabled.
+ * @param now The uptimeMillis
+ */
+ @GuardedBy("mAm")
+ void rescheduleServiceRestartOnMemoryPressureIfNeededLocked(boolean prevEnabled,
+ boolean curEnabled, @UptimeMillisLong long now) {
+ if (prevEnabled == curEnabled) {
+ return;
+ }
+ final @MemFactor int memFactor = mAm.mAppProfiler.getLastMemoryLevelLocked();
+ final long delay = mAm.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ performRescheduleServiceRestartOnMemoryPressureLocked(prevEnabled ? delay : 0,
+ curEnabled ? delay : 0, "config", now);
+ }
+
+ /**
+ * Rescan the list of pending restarts, reschedule them if needed.
+ *
+ * @param extraRestartTimeBetween The extra interval between restarts.
+ * @param minRestartTimeBetween The minimal interval between restarts.
+ * @param reason The human-readable text about why we're doing rescheduling.
+ * @param now The uptimeMillis
+ */
+ @GuardedBy("mAm")
+ void rescheduleServiceRestartIfPossibleLocked(long extraRestartTimeBetween,
+ long minRestartTimeBetween, @NonNull String reason, @UptimeMillisLong long now) {
+ final long restartTimeBetween = extraRestartTimeBetween + minRestartTimeBetween;
+ final long spanForInsertOne = restartTimeBetween * 2; // Min space to insert a restart.
+
+ long lastRestartTime = now;
+ int lastRestartTimePos = -1; // The list index where the "lastRestartTime" comes from.
+ for (int i = 0, size = mRestartingServices.size(); i < size; i++) {
+ final ServiceRecord r = mRestartingServices.get(i);
+ if ((r.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
+ || !isServiceRestartBackoffEnabledLocked(r.packageName)) {
+ lastRestartTime = r.nextRestartTime;
+ lastRestartTimePos = i;
+ continue;
+ }
+ if (lastRestartTime + restartTimeBetween <= r.mEarliestRestartTime) {
+ // Bounded by the earliest restart time, honor it; but we also need to
+ // check if the interval between the earlist and its prior one is enough or not.
+ r.nextRestartTime = Math.max(now, Math.max(r.mEarliestRestartTime, i > 0
+ ? mRestartingServices.get(i - 1).nextRestartTime + restartTimeBetween
+ : 0));
+ } else {
+ if (lastRestartTime <= now) {
+ // It hasn't moved, this is the first one (besides persistent process),
+ // we don't need to insert the minRestartTimeBetween for it, but need
+ // the extraRestartTimeBetween still.
+ r.nextRestartTime = Math.max(now, Math.max(r.mEarliestRestartTime,
+ r.mRestartSchedulingTime + extraRestartTimeBetween));
+ } else {
+ r.nextRestartTime = Math.max(now, lastRestartTime + restartTimeBetween);
+ }
+ if (i > lastRestartTimePos + 1) {
+ // Move the current service record ahead in the list.
+ mRestartingServices.remove(i);
+ mRestartingServices.add(lastRestartTimePos + 1, r);
+ }
+ }
+ // Find the next available slot to insert one if there is any
+ for (int j = lastRestartTimePos + 1; j <= i; j++) {
+ final ServiceRecord r2 = mRestartingServices.get(j);
+ final long timeInBetween = r2.nextRestartTime - (j == 0 ? lastRestartTime
+ : mRestartingServices.get(j - 1).nextRestartTime);
+ if (timeInBetween >= spanForInsertOne) {
+ break;
+ }
+ lastRestartTime = r2.nextRestartTime;
+ lastRestartTimePos = j;
+ }
+ r.restartDelay = r.nextRestartTime - now;
+ performScheduleRestartLocked(r, "Rescheduling", reason, now);
+ }
+ }
+
+ @GuardedBy("mAm")
+ void performRescheduleServiceRestartOnMemoryPressureLocked(long oldExtraDelay,
+ long newExtraDelay, @NonNull String reason, @UptimeMillisLong long now) {
+ final long delta = newExtraDelay - oldExtraDelay;
+ if (delta == 0) {
+ return;
+ }
+ if (delta > 0) {
+ final long restartTimeBetween = mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN
+ + newExtraDelay;
+ long lastRestartTime = now;
+ // Make the delay in between longer.
+ for (int i = 0, size = mRestartingServices.size(); i < size; i++) {
+ final ServiceRecord r = mRestartingServices.get(i);
+ if ((r.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) != 0
+ || !isServiceRestartBackoffEnabledLocked(r.packageName)) {
+ lastRestartTime = r.nextRestartTime;
+ continue;
+ }
+ boolean reschedule = false;
+ if (lastRestartTime <= now) {
+ // It hasn't moved, this is the first one (besides persistent process),
+ // we don't need to insert the minRestartTimeBetween for it, but need
+ // the newExtraDelay still.
+ final long oldVal = r.nextRestartTime;
+ r.nextRestartTime = Math.max(now, Math.max(r.mEarliestRestartTime,
+ r.mRestartSchedulingTime + newExtraDelay));
+ reschedule = r.nextRestartTime != oldVal;
+ } else if (r.nextRestartTime - lastRestartTime < restartTimeBetween) {
+ r.nextRestartTime = Math.max(lastRestartTime + restartTimeBetween, now);
+ reschedule = true;
+ }
+ r.restartDelay = r.nextRestartTime - now;
+ lastRestartTime = r.nextRestartTime;
+ if (reschedule) {
+ performScheduleRestartLocked(r, "Rescheduling", reason, now);
+ }
+ }
+ } else if (delta < 0) {
+ // Make the delay in between shorter, we'd do a rescan and reschedule.
+ rescheduleServiceRestartIfPossibleLocked(newExtraDelay,
+ mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN, reason, now);
+ }
+ }
+
+ @GuardedBy("mAm")
+ long getExtraRestartTimeInBetweenLocked() {
+ if (!mAm.mConstants.mEnableExtraServiceRestartDelayOnMemPressure) {
+ return 0;
+ }
+ final @MemFactor int memFactor = mAm.mAppProfiler.getLastMemoryLevelLocked();
+ return mAm.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ }
+
final void performServiceRestartLocked(ServiceRecord r) {
if (!mRestartingServices.contains(r)) {
return;
@@ -3706,6 +3998,9 @@ public final class ActiveServices {
performScheduleRestartLocked(r, "Rescheduling", reason, now);
}
}
+ // mRestartingServices is sorted by nextRestartTime.
+ Collections.sort(mRestartingServices,
+ (a, b) -> (int) (a.nextRestartTime - b.nextRestartTime));
}
} else {
removeServiceRestartBackoffEnabledLocked(packageName);
@@ -4153,6 +4448,8 @@ public final class ActiveServices {
// being brought down. Mark it as dead.
cr.serviceDead = true;
cr.stopAssociation();
+ final ComponentName clientSideComponentName =
+ cr.aliasComponent != null ? cr.aliasComponent : r.name;
try {
cr.conn.connected(r.name, null, true);
} catch (Exception e) {
@@ -4640,6 +4937,11 @@ public final class ActiveServices {
boolean attachApplicationLocked(ProcessRecord proc, String processName)
throws RemoteException {
boolean didSomething = false;
+
+ // Update the app background restriction of the caller
+ proc.mState.setBackgroundRestricted(appRestrictedAnyInBackground(
+ proc.uid, proc.info.packageName));
+
// Collect any services that are waiting for this process to come up.
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
@@ -4683,6 +4985,7 @@ public final class ActiveServices {
// run at this point just because their restart time hasn't come up.
if (mRestartingServices.size() > 0) {
ServiceRecord sr;
+ boolean didImmediateRestart = false;
for (int i=0; i<mRestartingServices.size(); i++) {
sr = mRestartingServices.get(i);
if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
@@ -4691,6 +4994,18 @@ public final class ActiveServices {
}
mAm.mHandler.removeCallbacks(sr.restarter);
mAm.mHandler.post(sr.restarter);
+ didImmediateRestart = true;
+ }
+ if (didImmediateRestart) {
+ // Since we kicked off all its pending restarts, there could be some open slots
+ // in the pending restarts list, schedule a check on it. We are posting to the same
+ // handler, so by the time of the check, those immediate restarts should be done.
+ mAm.mHandler.post(() ->
+ rescheduleServiceRestartIfPossibleLocked(
+ getExtraRestartTimeInBetweenLocked(),
+ mAm.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN,
+ "other", SystemClock.uptimeMillis())
+ );
}
}
return didSomething;
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index eeb41a3df969..ac537faa31fb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -21,6 +21,7 @@ import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -31,6 +32,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerExemptionManager;
+import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
@@ -40,6 +42,8 @@ import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
@@ -52,7 +56,8 @@ final class ActivityManagerConstants extends ContentObserver {
private static final String TAG = "ActivityManagerConstants";
// Key names stored in the settings value.
- private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
+ static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
+
private static final String KEY_FGSERVICE_MIN_SHOWN_TIME
= "fgservice_min_shown_time";
private static final String KEY_FGSERVICE_MIN_REPORT_TIME
@@ -111,9 +116,17 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate";
static final String KEY_FGS_START_DENIED_LOG_SAMPLE_RATE = "fgs_start_denied_log_sample_rate";
static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out";
+ static final String KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE =
+ "extra_delay_svc_restart_mem_pressure";
+ static final String KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE =
+ "enable_extra_delay_svc_restart_mem_pressure";
+ static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE = "kill_bg_restricted_cached_idle";
+ static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME =
+ "kill_bg_restricted_cached_idle_settle_time";
+ static final String KEY_ENABLE_COMPONENT_ALIAS = "enable_experimental_component_alias";
+ static final String KEY_COMPONENT_ALIAS_OVERRIDES = "component_alias_overrides";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
- private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
@@ -156,6 +169,11 @@ final class ActivityManagerConstants extends ContentObserver {
private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25%
private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100%
+
+ static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000;
+ static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000;
+ static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true;
+
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -163,6 +181,27 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR = 1;
private static final boolean DEFAULT_FGS_ALLOW_OPT_OUT = false;
+ /**
+ * The extra delays we're putting to service restarts, based on current memory pressure.
+ */
+ private static final long DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_NORMAL_MEM = 0; // ms
+ private static final long DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MODERATE_MEM = 10000; // ms
+ private static final long DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_LOW_MEM = 20000; // ms
+ private static final long DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_CRITICAL_MEM = 30000; // ms
+ private static final long[] DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = {
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_NORMAL_MEM,
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MODERATE_MEM,
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_LOW_MEM,
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_CRITICAL_MEM,
+ };
+
+ /**
+ * Whether or not to enable the extra delays to service restarts on memory pressure.
+ */
+ private static final boolean DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = true;
+ private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false;
+ private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = "";
+
// Flag stored in the DeviceConfig API.
/**
* Maximum number of cached processes.
@@ -514,11 +553,46 @@ final class ActivityManagerConstants extends ContentObserver {
volatile float mFgsStartDeniedLogSampleRate = DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE;
/**
+ * Whether or not to kill apps in background restricted mode and it's cached, its UID state is
+ * idle.
+ */
+ volatile boolean mKillBgRestrictedAndCachedIdle = DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE;
+
+ /**
+ * The amount of time we allow an app in background restricted mode to settle after it goes
+ * into the cached &amp; UID idle, before we decide to kill it.
+ */
+ volatile long mKillBgRestrictedAndCachedIdleSettleTimeMs =
+ DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS;
+
+ /**
* Whether to allow "opt-out" from the foreground service restrictions.
* (https://developer.android.com/about/versions/12/foreground-services)
*/
volatile boolean mFgsAllowOptOut = DEFAULT_FGS_ALLOW_OPT_OUT;
+ /*
+ * The extra delays we're putting to service restarts, based on current memory pressure.
+ */
+ @GuardedBy("mService")
+ long[] mExtraServiceRestartDelayOnMemPressure =
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
+
+ /**
+ * Whether or not to enable the extra delays to service restarts on memory pressure.
+ */
+ @GuardedBy("mService")
+ boolean mEnableExtraServiceRestartDelayOnMemPressure =
+ DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
+ /** Whether to enable "component alias" experimental feature. */
+ volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS;
+
+ /**
+ * Defines component aliases. Format
+ * ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )*
+ */
+ volatile String mComponentAliasOverrides = DEFAULT_COMPONENT_ALIAS_OVERRIDES;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -735,9 +809,25 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_FGS_START_DENIED_LOG_SAMPLE_RATE:
updateFgsStartDeniedLogSamplePercent();
break;
+ case KEY_KILL_BG_RESTRICTED_CACHED_IDLE:
+ updateKillBgRestrictedCachedIdle();
+ break;
+ case KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME:
+ updateKillBgRestrictedCachedIdleSettleTime();
+ break;
case KEY_FGS_ALLOW_OPT_OUT:
updateFgsAllowOptOut();
break;
+ case KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE:
+ updateExtraServiceRestartDelayOnMemPressure();
+ break;
+ case KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE:
+ updateEnableExtraServiceRestartDelayOnMemPressure();
+ break;
+ case KEY_ENABLE_COMPONENT_ALIAS:
+ case KEY_COMPONENT_ALIAS_OVERRIDES:
+ updateComponentAliases();
+ break;
default:
break;
}
@@ -1095,6 +1185,28 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE);
}
+ private void updateKillBgRestrictedCachedIdle() {
+ mKillBgRestrictedAndCachedIdle = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_KILL_BG_RESTRICTED_CACHED_IDLE,
+ DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE);
+ }
+
+ private void updateKillBgRestrictedCachedIdleSettleTime() {
+ final long currentSettleTime = mKillBgRestrictedAndCachedIdleSettleTimeMs;
+ mKillBgRestrictedAndCachedIdleSettleTimeMs = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME,
+ DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS);
+ if (mKillBgRestrictedAndCachedIdleSettleTimeMs != currentSettleTime) {
+ mService.mHandler.removeMessages(
+ ActivityManagerService.IDLE_UIDS_MSG);
+ mService.mHandler.sendEmptyMessageDelayed(
+ ActivityManagerService.IDLE_UIDS_MSG,
+ mKillBgRestrictedAndCachedIdleSettleTimeMs);
+ }
+ }
+
private void updateFgsAllowOptOut() {
mFgsAllowOptOut = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1102,6 +1214,63 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_FGS_ALLOW_OPT_OUT);
}
+ private void updateExtraServiceRestartDelayOnMemPressure() {
+ synchronized (mService) {
+ final int memFactor = mService.mAppProfiler.getLastMemoryLevelLocked();
+ final long[] prevDelays = mExtraServiceRestartDelayOnMemPressure;
+ mExtraServiceRestartDelayOnMemPressure = parseLongArray(
+ KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE);
+ mService.mServices.performRescheduleServiceRestartOnMemoryPressureLocked(
+ mExtraServiceRestartDelayOnMemPressure[memFactor],
+ prevDelays[memFactor], "config", SystemClock.uptimeMillis());
+ }
+ }
+
+ private void updateEnableExtraServiceRestartDelayOnMemPressure() {
+ synchronized (mService) {
+ final boolean prevEnabled = mEnableExtraServiceRestartDelayOnMemPressure;
+ mEnableExtraServiceRestartDelayOnMemPressure = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
+ DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE);
+ mService.mServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ prevEnabled, mEnableExtraServiceRestartDelayOnMemPressure,
+ SystemClock.uptimeMillis());
+ }
+ }
+
+ private long[] parseLongArray(@NonNull String key, @NonNull long[] def) {
+ final String val = DeviceConfig.getString(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ key, null);
+ if (!TextUtils.isEmpty(val)) {
+ final String[] ss = val.split(",");
+ if (ss.length == def.length) {
+ final long[] tmp = new long[ss.length];
+ try {
+ for (int i = 0; i < ss.length; i++) {
+ tmp[i] = Long.parseLong(ss[i]);
+ }
+ return tmp;
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+ return def;
+ }
+
+ private void updateComponentAliases() {
+ mEnableComponentAlias = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_ENABLE_COMPONENT_ALIAS,
+ DEFAULT_ENABLE_COMPONENT_ALIAS);
+ mComponentAliasOverrides = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_COMPONENT_ALIAS_OVERRIDES,
+ DEFAULT_COMPONENT_ALIAS_OVERRIDES);
+ mService.mComponentAliasResolver.update(mEnableComponentAlias, mComponentAliasOverrides);
+ }
+
private void updateImperceptibleKillExemptions() {
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -1331,6 +1500,10 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
pw.print("="); pw.println(mFgsAllowOptOut);
+ pw.print(" "); pw.print(KEY_ENABLE_COMPONENT_ALIAS);
+ pw.print("="); pw.println(mEnableComponentAlias);
+ pw.print(" "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES);
+ pw.print("="); pw.println(mComponentAliasOverrides);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 953e6e24236f..b202c237da47 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -258,6 +258,7 @@ import android.os.IDeviceIdentifiersPolicyService;
import android.os.IPermissionController;
import android.os.IProcessInfoService;
import android.os.IProgressListener;
+import android.os.InputConstants;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -778,6 +779,9 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
private ArrayMap<String, PackageAssociationInfo> mAllowedAssociations;
+ @GuardedBy("this")
+ final ComponentAliasResolver mComponentAliasResolver;
+
/**
* Tracks association information for a particular package along with debuggability.
* <p> Associations for a package A are allowed to package B if B is part of the
@@ -2219,6 +2223,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUseFifoUiScheduling = false;
mEnableOffloadQueue = false;
mFgBroadcastQueue = mBgBroadcastQueue = mOffloadBroadcastQueue = null;
+ mComponentAliasResolver = new ComponentAliasResolver(this);
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2345,6 +2350,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mInternal = new LocalService();
mPendingStartActivityUids = new PendingStartActivityUids(mContext);
mTraceErrorLogger = new TraceErrorLogger();
+ mComponentAliasResolver = new ComponentAliasResolver(this);
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -3291,13 +3297,19 @@ public class ActivityManagerService extends IActivityManager.Stub
final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
final long now = System.currentTimeMillis();
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
}
}
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
@@ -3350,7 +3362,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// control of all writes to the file in question.
// We must complete all stack dumps within 20 seconds.
- long remainingTime = 20 * 1000;
+ long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
// As applications are usually interested with the ANR stack traces, but we can't share with
// them the stack traces other than their own stacks. So after the very first PID is
@@ -3453,30 +3465,39 @@ public class ActivityManagerService extends IActivityManager.Stub
final long callingId = Binder.clearCallingIdentity();
try {
IPackageManager pm = AppGlobals.getPackageManager();
+ boolean permitted = true;
// Instant packages are not protected
if (getPackageManagerInternal().isPackageDataProtected(
resolvedUserId, packageName)) {
- throw new SecurityException(
- "Cannot clear data for a protected package: " + packageName);
+ if (ActivityManager.checkUidPermission(android.Manifest.permission.MANAGE_USERS,
+ uid) == PERMISSION_GRANTED) {
+ // The caller has the MANAGE_USERS permission, tell them what's going on.
+ throw new SecurityException(
+ "Cannot clear data for a protected package: " + packageName);
+ } else {
+ permitted = false; // fall through and throw the SecurityException below.
+ }
}
ApplicationInfo applicationInfo = null;
- try {
- applicationInfo = pm.getApplicationInfo(packageName,
- MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
- } catch (RemoteException e) {
- /* ignore */
+ if (permitted) {
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName,
+ MATCH_UNINSTALLED_PACKAGES, resolvedUserId);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ permitted = (applicationInfo != null && applicationInfo.uid == uid) // own uid data
+ || (checkComponentPermission(permission.CLEAR_APP_USER_DATA,
+ pid, uid, -1, true) == PackageManager.PERMISSION_GRANTED);
}
- appInfo = applicationInfo;
-
- final boolean clearingOwnUidData = appInfo != null && appInfo.uid == uid;
- if (!clearingOwnUidData && checkComponentPermission(permission.CLEAR_APP_USER_DATA,
- pid, uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+ if (!permitted) {
throw new SecurityException("PID " + pid + " does not have permission "
+ android.Manifest.permission.CLEAR_APP_USER_DATA + " to clear data"
+ " of package " + packageName);
}
+ appInfo = applicationInfo;
final boolean hasInstantMetadata = getPackageManagerInternal()
.hasInstantApplicationMetadata(packageName, resolvedUserId);
@@ -3522,15 +3543,17 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1);
intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
+ final int[] visibilityAllowList =
+ mPackageManagerInt.getVisibilityAllowList(packageName, resolvedUserId);
if (isInstantApp) {
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
- false, false, resolvedUserId, false, null);
+ false, false, resolvedUserId, false, null, visibilityAllowList);
} else {
broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
null, null, 0, null, null, null, null, false, false, resolvedUserId,
- false, null);
+ false, null, visibilityAllowList);
}
if (observer != null) {
@@ -4111,11 +4134,8 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
if (appId < 0) {
- try {
- appId = UserHandle.getAppId(AppGlobals.getPackageManager()
- .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
- } catch (RemoteException e) {
- }
+ appId = UserHandle.getAppId(getPackageManagerInternal().getPackageUid(packageName,
+ MATCH_DEBUG_TRIAGED_MISSING | MATCH_ANY_USER, UserHandle.USER_SYSTEM));
}
mProcessList.killAppZygotesLocked(packageName, appId, userId, true /* force */);
@@ -4132,11 +4152,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (appId < 0 && packageName != null) {
- try {
- appId = UserHandle.getAppId(AppGlobals.getPackageManager()
- .getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, 0));
- } catch (RemoteException e) {
- }
+ appId = UserHandle.getAppId(getPackageManagerInternal().getPackageUid(packageName,
+ MATCH_DEBUG_TRIAGED_MISSING | MATCH_ANY_USER, UserHandle.USER_SYSTEM));
}
boolean didSomething;
@@ -4772,6 +4789,10 @@ public class ActivityManagerService extends IActivityManager.Stub
showConsoleNotificationIfActive();
t.traceEnd();
+
+ // Load the component aliases.
+ mComponentAliasResolver.update(
+ mConstants.mEnableComponentAlias, mConstants.mComponentAliasOverrides);
}
private void showConsoleNotificationIfActive() {
@@ -5685,13 +5706,18 @@ public class ActivityManagerService extends IActivityManager.Stub
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
+ if (uid != ROOT_UID) { // bypass the root
+ if (mPackageManagerInt.filterAppAccess(uid, Binder.getCallingUid())) {
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags)
? 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 modeFlags, int userId, IBinder callerToken) {
final int size = uris.size();
int[] res = new int[size];
// Default value DENIED.
@@ -5699,9 +5725,9 @@ public class ActivityManagerService extends IActivityManager.Stub
for (int i = 0; i < size; i++) {
final Uri uri = uris.get(i);
- final int userId = ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
+ final int userIdFromUri = ContentProvider.getUserIdFromUri(uri, userId);
res[i] = checkUriPermission(ContentProvider.getUriWithoutUserId(uri), pid, uid,
- modeFlags, userId, callerToken);
+ modeFlags, userIdFromUri, callerToken);
}
return res;
}
@@ -8333,11 +8359,13 @@ public class ActivityManagerService extends IActivityManager.Stub
if (lines > 0) {
sb.append("\n");
- // Merge several logcat streams, and take the last N lines
InputStreamReader input = null;
try {
java.lang.Process logcat = new ProcessBuilder(
- "/system/bin/timeout", "-k", "15s", "10s",
+ // Time out after 10s, but kill logcat with SEGV
+ // so we can investigate why it didn't finish.
+ "/system/bin/timeout", "-s", "SEGV", "10s",
+ // Merge several logcat streams, and take the last N lines.
"/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
"-b", "main", "-b", "crash", "-t", String.valueOf(lines))
.redirectErrorStream(true).start();
@@ -8380,9 +8408,13 @@ public class ActivityManagerService extends IActivityManager.Stub
// assume our apps are happy - lazy create the list
final List<ActivityManager.ProcessErrorStateInfo>[] errList = new List[1];
+ final int callingUid = Binder.getCallingUid();
final boolean allUsers = ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL,
- Binder.getCallingUid()) == PackageManager.PERMISSION_GRANTED;
- int userId = UserHandle.getUserId(Binder.getCallingUid());
+ callingUid) == PackageManager.PERMISSION_GRANTED;
+ int userId = UserHandle.getUserId(callingUid);
+
+ final boolean hasDumpPermission = ActivityManager.checkUidPermission(
+ android.Manifest.permission.DUMP, callingUid) == PackageManager.PERMISSION_GRANTED;
synchronized (mProcLock) {
// iterate across all processes
@@ -8390,6 +8422,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!allUsers && app.userId != userId) {
return;
}
+ if (!hasDumpPermission && app.info.uid != callingUid) {
+ return;
+ }
final ProcessErrorStateRecord errState = app.mErrorState;
final boolean crashing = errState.isCrashing();
final boolean notResponding = errState.isNotResponding();
@@ -8765,6 +8800,12 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println("-------------------------------------------------------------------------------");
}
dumpUsers(pw);
+
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ mComponentAliasResolver.dump(pw);
}
}
@@ -9081,6 +9122,8 @@ public class ActivityManagerService extends IActivityManager.Stub
opti++;
}
mProcessList.mAppExitInfoTracker.dumpHistoryProcessExitInfo(pw, dumpPackage);
+ } else if ("component-alias".equals(cmd)) {
+ mComponentAliasResolver.dump(pw);
} else {
// Dumping a single activity?
if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll,
@@ -9457,6 +9500,15 @@ public class ActivityManagerService extends IActivityManager.Stub
TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow);
pw.println();
});
+
+ if (!mProcessList.mAppsInBackgroundRestricted.isEmpty()) {
+ pw.println(" Processes that are in background restricted:");
+ for (int i = 0, size = mProcessList.mAppsInBackgroundRestricted.size();
+ i < size; i++) {
+ pw.println(String.format("%s #%2d: %s", " ", i,
+ mProcessList.mAppsInBackgroundRestricted.valueAt(i).toString()));
+ }
+ }
}
if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
|| mOrigWaitForDebugger) {
@@ -12076,6 +12128,31 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.w(TAG, "Unable to bind backup agent for " + packageName);
return false;
}
+ if (app.backupAgentName != null) {
+ final ComponentName backupAgentName = new ComponentName(
+ app.packageName, app.backupAgentName);
+ int enableState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ try {
+ enableState = pm.getComponentEnabledSetting(backupAgentName, instantiatedUserId);
+ } catch (RemoteException e) {
+ // can't happen; package manager is process-local
+ }
+ switch (enableState) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ Slog.w(TAG, "Unable to bind backup agent for " + backupAgentName
+ + ", the backup agent component is disabled.");
+ return false;
+
+ case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+ case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+ default:
+ // Since there's no way to declare a backup agent disabled in the manifest,
+ // assume the case COMPONENT_ENABLED_STATE_DEFAULT to be enabled.
+ break;
+ }
+ }
int oldBackupUid;
int newBackupUid;
@@ -12578,76 +12655,72 @@ public class ActivityManagerService extends IActivityManager.Stub
int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
List<ResolveInfo> receivers = null;
- try {
- HashSet<ComponentName> singleUserReceivers = null;
- boolean scannedFirstReceivers = false;
- for (int user : users) {
- // Skip users that have Shell restrictions
- if (callingUid == SHELL_UID
- && mUserController.hasUserRestriction(
- UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
- continue;
- }
- List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
- .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
- if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
- // If this is not the system user, we need to check for
- // any receivers that should be filtered out.
- for (int i=0; i<newReceivers.size(); i++) {
- ResolveInfo ri = newReceivers.get(i);
- if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
- newReceivers.remove(i);
- i--;
- }
+ HashSet<ComponentName> singleUserReceivers = null;
+ boolean scannedFirstReceivers = false;
+ for (int user : users) {
+ // Skip users that have Shell restrictions
+ if (callingUid == SHELL_UID
+ && mUserController.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
+ continue;
+ }
+ List<ResolveInfo> newReceivers = mPackageManagerInt
+ .queryIntentReceivers(intent, resolvedType, pmFlags, callingUid, user);
+ if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
+ // If this is not the system user, we need to check for
+ // any receivers that should be filtered out.
+ for (int i = 0; i < newReceivers.size(); i++) {
+ ResolveInfo ri = newReceivers.get(i);
+ if ((ri.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
+ newReceivers.remove(i);
+ i--;
}
}
- if (newReceivers != null && newReceivers.size() == 0) {
- newReceivers = null;
- }
- if (receivers == null) {
- receivers = newReceivers;
- } else if (newReceivers != null) {
- // We need to concatenate the additional receivers
- // found with what we have do far. This would be easy,
- // but we also need to de-dup any receivers that are
- // singleUser.
- if (!scannedFirstReceivers) {
- // Collect any single user receivers we had already retrieved.
- scannedFirstReceivers = true;
- for (int i=0; i<receivers.size(); i++) {
- ResolveInfo ri = receivers.get(i);
- if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
- ComponentName cn = new ComponentName(
- ri.activityInfo.packageName, ri.activityInfo.name);
- if (singleUserReceivers == null) {
- singleUserReceivers = new HashSet<ComponentName>();
- }
- singleUserReceivers.add(cn);
- }
- }
- }
- // Add the new results to the existing results, tracking
- // and de-dupping single user receivers.
- for (int i=0; i<newReceivers.size(); i++) {
- ResolveInfo ri = newReceivers.get(i);
+ }
+ if (newReceivers != null && newReceivers.size() == 0) {
+ newReceivers = null;
+ }
+ if (receivers == null) {
+ receivers = newReceivers;
+ } else if (newReceivers != null) {
+ // We need to concatenate the additional receivers
+ // found with what we have do far. This would be easy,
+ // but we also need to de-dup any receivers that are
+ // singleUser.
+ if (!scannedFirstReceivers) {
+ // Collect any single user receivers we had already retrieved.
+ scannedFirstReceivers = true;
+ for (int i = 0; i < receivers.size(); i++) {
+ ResolveInfo ri = receivers.get(i);
if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
ComponentName cn = new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name);
if (singleUserReceivers == null) {
singleUserReceivers = new HashSet<ComponentName>();
}
- if (!singleUserReceivers.contains(cn)) {
- singleUserReceivers.add(cn);
- receivers.add(ri);
- }
- } else {
+ singleUserReceivers.add(cn);
+ }
+ }
+ }
+ // Add the new results to the existing results, tracking
+ // and de-dupping single user receivers.
+ for (int i = 0; i < newReceivers.size(); i++) {
+ ResolveInfo ri = newReceivers.get(i);
+ if ((ri.activityInfo.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ ComponentName cn = new ComponentName(
+ ri.activityInfo.packageName, ri.activityInfo.name);
+ if (singleUserReceivers == null) {
+ singleUserReceivers = new HashSet<ComponentName>();
+ }
+ if (!singleUserReceivers.contains(cn)) {
+ singleUserReceivers.add(cn);
receivers.add(ri);
}
+ } else {
+ receivers.add(ri);
}
}
}
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
}
if (receivers != null && broadcastAllowList != null) {
for (int i = receivers.size() - 1; i >= 0; i--) {
@@ -13281,8 +13354,7 @@ public class ActivityManagerService extends IActivityManager.Stub
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- == 0) {
+ if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
receivers = collectReceiverComponents(
intent, resolvedType, callingUid, users, broadcastAllowList);
}
@@ -13600,7 +13672,8 @@ public class ActivityManagerService extends IActivityManager.Stub
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
int userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken) {
+ @Nullable IBinder backgroundActivityStartsToken,
+ @Nullable int[] broadcastAllowList) {
synchronized(this) {
intent = verifyBroadcastLocked(intent);
@@ -13612,8 +13685,7 @@ public class ActivityManagerService extends IActivityManager.Stub
resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
realCallingPid, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken,
- null /* broadcastAllowList */);
+ backgroundActivityStartsToken, broadcastAllowList);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -14383,6 +14455,10 @@ public class ActivityManagerService extends IActivityManager.Stub
final int capability = uidRec != null ? uidRec.getSetCapability() : 0;
final boolean ephemeral = uidRec != null ? uidRec.isEphemeral() : isEphemeralLocked(uid);
+ if (uidRec != null && uidRec.isIdle() && (change & UidRecord.CHANGE_IDLE) != 0) {
+ mProcessList.killAppIfBgRestrictedAndCachedIdleLocked(uidRec);
+ }
+
if (uidRec != null && !uidRec.isIdle() && (change & UidRecord.CHANGE_GONE) != 0) {
// If this uid is going away, and we haven't yet reported it is gone,
// then do so now.
@@ -15282,6 +15358,12 @@ public class ActivityManagerService extends IActivityManager.Stub
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
+
+ @Override
+ public Pair<String, String> getAppProfileStatsForDebugging(long time, int lines) {
+ return mAppProfiler.getAppProfileStatsForDebugging(time, lines);
+ }
+
@Override
public String checkContentProviderAccess(String authority, int userId) {
return mCpHelper.checkContentProviderAccess(authority, userId);
@@ -15798,13 +15880,14 @@ public class ActivityManagerService extends IActivityManager.Stub
IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
int userId, boolean allowBackgroundActivityStarts,
- @Nullable IBinder backgroundActivityStartsToken) {
+ @Nullable IBinder backgroundActivityStartsToken,
+ @Nullable int[] broadcastAllowList) {
synchronized (ActivityManagerService.this) {
return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
resultCode, resultData, resultExtras, requiredPermission, bOptions,
serialized, sticky, userId, allowBackgroundActivityStarts,
- backgroundActivityStartsToken);
+ backgroundActivityStartsToken, broadcastAllowList);
}
}
@@ -15935,8 +16018,22 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void inputDispatchingResumed(int pid) {
- // TODO (b/171218828)
- return;
+ final ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc != null) {
+ mAppErrors.handleDismissAnrDialogs(proc);
+ }
+ }
+
+ @Override
+ public void rescheduleAnrDialog(Object data) {
+ Message msg = Message.obtain();
+ msg.what = SHOW_NOT_RESPONDING_UI_MSG;
+ msg.obj = (AppNotRespondingDialog.Data) data;
+
+ mUiHandler.sendMessageDelayed(msg, InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
}
@Override
@@ -16349,6 +16446,15 @@ public class ActivityManagerService extends IActivityManager.Stub
return mProcessList.getIsolatedProcessesLocked(uid);
}
}
+
+ /** @see ActivityManagerService#sendIntentSender */
+ @Override
+ public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
+ Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
+ intent, resolvedType, finishedReceiver, requiredPermission, options);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index bcb42bb38495..0dfdfe94b6ec 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -1058,6 +1058,7 @@ class AppErrors {
}
synchronized (mProcLock) {
final ProcessErrorStateRecord errState = proc.mErrorState;
+ errState.setAnrData(data);
if (!proc.isPersistent()) {
packageList = proc.getPackageListWithVersionCode();
}
@@ -1109,6 +1110,24 @@ class AppErrors {
}
}
+ void handleDismissAnrDialogs(ProcessRecord proc) {
+ synchronized (mProcLock) {
+ final ProcessErrorStateRecord errState = proc.mErrorState;
+
+ // Cancel any rescheduled ANR dialogs
+ mService.mUiHandler.removeMessages(
+ ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG, errState.getAnrData());
+
+ // Dismiss any ANR dialogs currently visible
+ if (errState.getDialogController().hasAnrDialogs()) {
+ errState.setNotResponding(false);
+ errState.setNotRespondingReport(null);
+ errState.getDialogController().clearAnrDialogs();
+ }
+ proc.mErrorState.setAnrData(null);
+ }
+ }
+
/**
* Information about a process that is currently marked as bad.
*/
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index b233a2ccc6e3..878ef31f143d 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -48,12 +48,14 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
private final ActivityManagerService mService;
private final ProcessRecord mProc;
+ private final Data mData;
public AppNotRespondingDialog(ActivityManagerService service, Context context, Data data) {
super(context);
mService = service;
mProc = data.proc;
+ mData = data;
Resources res = context.getResources();
setCancelable(false);
@@ -165,6 +167,8 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli
errState.getDialogController().clearAnrDialogs();
}
mService.mServices.scheduleServiceTimeoutLocked(app);
+ // If the app remains unresponsive, show the dialog again after a delay.
+ mService.mInternal.rescheduleAnrDialog(mData);
}
break;
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 36c0de919279..f2fb3714b03a 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -393,6 +393,7 @@ public class AppProfiler {
static final int COLLECT_PSS_BG_MSG = 1;
static final int DEFER_PSS_MSG = 2;
static final int STOP_DEFERRING_PSS_MSG = 3;
+ static final int MEMORY_PRESSURE_CHANGED = 4;
BgHandler(Looper looper) {
super(looper);
}
@@ -409,6 +410,11 @@ public class AppProfiler {
case STOP_DEFERRING_PSS_MSG:
stopDeferPss();
break;
+ case MEMORY_PRESSURE_CHANGED:
+ synchronized (mService) {
+ handleMemoryPressureChangedLocked(msg.arg1, msg.arg2);
+ }
+ break;
}
}
}
@@ -912,12 +918,18 @@ public class AppProfiler {
}
@GuardedBy("mService")
- int getLastMemoryLevelLocked() {
+ @MemFactor int getLastMemoryLevelLocked() {
+ if (mMemFactorOverride != ADJ_MEM_FACTOR_NOTHING) {
+ return mMemFactorOverride;
+ }
return mLastMemoryLevel;
}
@GuardedBy("mService")
boolean isLastMemoryLevelNormal() {
+ if (mMemFactorOverride != ADJ_MEM_FACTOR_NOTHING) {
+ return mMemFactorOverride <= ADJ_MEM_FACTOR_NORMAL;
+ }
return mLastMemoryLevel <= ADJ_MEM_FACTOR_NORMAL;
}
@@ -989,6 +1001,8 @@ public class AppProfiler {
if (memFactor != mLastMemoryLevel) {
EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
+ mBgHandler.obtainMessage(BgHandler.MEMORY_PRESSURE_CHANGED, mLastMemoryLevel, memFactor)
+ .sendToTarget();
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();
@@ -1636,6 +1650,13 @@ public class AppProfiler {
}
}
+ @GuardedBy("mService")
+ private void handleMemoryPressureChangedLocked(@MemFactor int oldMemFactor,
+ @MemFactor int newMemFactor) {
+ mService.mServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ oldMemFactor, newMemFactor, "mem-pressure-event", SystemClock.uptimeMillis());
+ }
+
@GuardedBy("mProfilerLock")
private void stopProfilerLPf(ProcessRecord proc, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
@@ -2318,6 +2339,27 @@ public class AppProfiler {
}
}
+ Pair<String, String> getAppProfileStatsForDebugging(long time, int linesOfStats) {
+ String cpuLoad = null;
+ String stats = null;
+ synchronized (mProcessCpuTracker) {
+ updateCpuStatsNow();
+ cpuLoad = mProcessCpuTracker.printCurrentLoad();
+ stats = mProcessCpuTracker.printCurrentState(time);
+ }
+ // Only return linesOfStats lines of Cpu stats.
+ int toIndex = 0;
+ for (int i = 0; i <= linesOfStats; i++) {
+ int nextIndex = stats.indexOf('\n', toIndex);
+ if (nextIndex == -1) {
+ toIndex = stats.length();
+ break;
+ }
+ toIndex = nextIndex + 1;
+ }
+ return new Pair(cpuLoad, stats.substring(0, toIndex));
+ }
+
@GuardedBy("mProfilerLock")
void writeProcessesToGcToProto(ProtoOutputStream proto, long fieldId, String dumpPackage) {
if (mProcessesToGc.size() > 0) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 0c633cacda92..449f02ea5224 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -79,6 +79,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
// There is some accuracy error in wifi reports so allow some slop in the results.
private static final long MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS = 750;
+ // Delay for clearing out battery stats for UIDs corresponding to a removed user
+ public static final int UID_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS = 10_000;
+
private final ScheduledExecutorService mExecutorService =
Executors.newSingleThreadScheduledExecutor(
(ThreadFactory) r -> {
@@ -346,6 +349,17 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
}
}
+ @Override
+ public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ return mExecutorService.schedule(() -> {
+ synchronized (mStats) {
+ mStats.clearRemovedUserUidsLocked(userId);
+ }
+ }, UID_REMOVAL_AFTER_USER_REMOVAL_DELAY_MILLIS, TimeUnit.MILLISECONDS);
+ }
+ }
+
/**
* Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a
* new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0).
@@ -481,7 +495,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync {
mStats.removeIsolatedUidLocked(uid, SystemClock.elapsedRealtime(),
SystemClock.uptimeMillis());
}
- mStats.clearPendingRemovedUids();
+ mStats.clearPendingRemovedUidsLocked();
}
} catch (Exception e) {
Slog.wtf(TAG, "Error updating external stats: ", e);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ae14ca7b66bd..60530a3ec09a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -492,9 +492,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
powerMgr.registerLowPowerModeObserver(this);
synchronized (mStats) {
- mStats.notePowerSaveModeLocked(
+ mStats.notePowerSaveModeLockedInit(
powerMgr.getLowPowerState(ServiceType.BATTERY_STATS).batterySaverEnabled,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis(), true);
+ SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
}
(new WakeupReasonThread()).start();
}
@@ -537,7 +537,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler.post(() -> {
synchronized (mStats) {
mStats.notePowerSaveModeLocked(result.batterySaverEnabled,
- elapsedRealtime, uptime, false);
+ elapsedRealtime, uptime);
}
});
}
@@ -1984,7 +1984,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
- public void noteResetBleScan() {
+ public void noteBleScanReset() {
enforceCallingPermission();
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/CacheOomRanker.java b/services/core/java/com/android/server/am/CacheOomRanker.java
index 50278fd81bb2..1ead7e3c589f 100644
--- a/services/core/java/com/android/server/am/CacheOomRanker.java
+++ b/services/core/java/com/android/server/am/CacheOomRanker.java
@@ -38,16 +38,25 @@ public class CacheOomRanker {
private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
- @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
+ @VisibleForTesting
+ static final String KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS =
+ "oom_re_ranking_preserve_top_n_apps";
+ @VisibleForTesting
+ static final int DEFAULT_PRESERVE_TOP_N_APPS = 3;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
@VisibleForTesting
static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
- @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
+ @VisibleForTesting
+ static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
new ScoreComparator();
@@ -66,15 +75,21 @@ public class CacheOomRanker {
@GuardedBy("mPhenotypeFlagLock")
private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
+ @GuardedBy("mPhenotypeFlagLock")
+ @VisibleForTesting
+ int mPreserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
// Weight to apply to the LRU ordering.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
+ @VisibleForTesting
+ float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
// Weight to apply to the ordering by number of times the process has been added to the cache.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
+ @VisibleForTesting
+ float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
// Weight to apply to the ordering by RSS used by the processes.
@GuardedBy("mPhenotypeFlagLock")
- @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
+ @VisibleForTesting
+ float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
// Positions to replace in the lru list.
@GuardedBy("mPhenotypeFlagLock")
@@ -93,6 +108,8 @@ public class CacheOomRanker {
updateUseOomReranking();
} else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
updateNumberToReRank();
+ } else if (KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS.equals(name)) {
+ updatePreserveTopNApps();
} else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
updateLruWeight();
} else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
@@ -160,6 +177,19 @@ public class CacheOomRanker {
}
@GuardedBy("mPhenotypeFlagLock")
+ private void updatePreserveTopNApps() {
+ int preserveTopNApps = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS, DEFAULT_PRESERVE_TOP_N_APPS);
+ if (preserveTopNApps < 0) {
+ Slog.w(OomAdjuster.TAG,
+ "Found negative value for preserveTopNApps, setting to default: "
+ + preserveTopNApps);
+ preserveTopNApps = DEFAULT_PRESERVE_TOP_N_APPS;
+ }
+ mPreserveTopNApps = preserveTopNApps;
+ }
+
+ @GuardedBy("mPhenotypeFlagLock")
private void updateLruWeight() {
mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
@@ -183,6 +213,33 @@ public class CacheOomRanker {
*/
@GuardedBy({"mService", "mProcLock"})
void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
+ // The lruList is a list of processes ordered by how recently they were used. The
+ // least-recently-used apps are at the beginning of the list. We keep track of two
+ // indices in the lruList:
+ //
+ // getNumberToReRank=5, preserveTopNApps=3, lruProcessServiceStart=7,
+ // lruList=
+ // 0: app A ^
+ // 1: app B | These apps are re-ranked, as they are the first five apps (see
+ // 2: app C | getNumberToReRank), excluding...
+ // 3: app D v
+ // 4: app E ^
+ // 5: app F | The three most-recently-used apps in the cache (see preserveTopNApps).
+ // 6: app G v
+ // 7: service A ^
+ // 8: service B | Everything beyond lruProcessServiceStart is ignored, as these aren't
+ // 9: service C | apps.
+ // 10: activity A |
+ // ... |
+ //
+ // `numProcessesEvaluated` moves across the apps (indices 0-6) or until we've found enough
+ // apps to re-rank, and made sure none of them are in the top `preserveTopNApps` apps.
+ // Re-ranked apps are copied into `scoredProcessRecords`, where the re-ranking calculation
+ // happens.
+ //
+ // Note that some apps in the `lruList` can be skipped, if they don't pass
+ //`appCanBeReRanked`.
+
float lruWeight;
float usesWeight;
float rssWeight;
@@ -202,52 +259,67 @@ public class CacheOomRanker {
return;
}
+ int numProcessesEvaluated = 0;
// Collect the least recently used processes to re-rank, only rank cached
// processes further down the list than mLruProcessServiceStart.
- int cachedProcessPos = 0;
- for (int i = 0; i < lruProcessServiceStart
- && cachedProcessPos < scoredProcessRecords.length; ++i) {
- ProcessRecord app = lruList.get(i);
+ int numProcessesReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesReRanked < scoredProcessRecords.length) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
// Processes that will be assigned a cached oom adj score.
- if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
- >= ProcessList.UNKNOWN_ADJ) {
- scoredProcessRecords[cachedProcessPos].proc = app;
- scoredProcessRecords[cachedProcessPos].score = 0.0f;
- lruPositions[cachedProcessPos] = i;
- ++cachedProcessPos;
+ if (appCanBeReRanked(process)) {
+ scoredProcessRecords[numProcessesReRanked].proc = process;
+ scoredProcessRecords[numProcessesReRanked].score = 0.0f;
+ lruPositions[numProcessesReRanked] = numProcessesEvaluated;
+ ++numProcessesReRanked;
}
+ ++numProcessesEvaluated;
}
- // TODO maybe ensure a certain number above this in the cache before re-ranking.
- if (cachedProcessPos < scoredProcessRecords.length) {
- // Ignore we don't have enough processes to worry about re-ranking.
- return;
+ // Count how many apps we're not re-ranking (up to mPreserveTopNApps).
+ int numProcessesNotReRanked = 0;
+ while (numProcessesEvaluated < lruProcessServiceStart
+ && numProcessesNotReRanked < mPreserveTopNApps) {
+ ProcessRecord process = lruList.get(numProcessesEvaluated);
+ if (appCanBeReRanked(process)) {
+ numProcessesNotReRanked++;
+ }
+ numProcessesEvaluated++;
+ }
+ // Exclude the top `mPreserveTopNApps` apps from re-ranking.
+ if (numProcessesNotReRanked < mPreserveTopNApps) {
+ numProcessesReRanked -= mPreserveTopNApps - numProcessesNotReRanked;
+ if (numProcessesReRanked < 0) {
+ numProcessesReRanked = 0;
+ }
}
// Add scores for each of the weighted features we want to rank based on.
if (lruWeight > 0.0f) {
// This doesn't use the LRU list ordering as after the first re-ranking
// that will no longer be lru.
- Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ LAST_ACTIVITY_TIME_COMPARATOR);
addToScore(scoredProcessRecords, lruWeight);
}
if (rssWeight > 0.0f) {
synchronized (mService.mAppProfiler.mProfilerLock) {
- Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, LAST_RSS_COMPARATOR);
}
addToScore(scoredProcessRecords, rssWeight);
}
if (usesWeight > 0.0f) {
- Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked, CACHE_USE_COMPARATOR);
addToScore(scoredProcessRecords, usesWeight);
}
// Re-rank by the new combined score.
- Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
+ Arrays.sort(scoredProcessRecords, 0, numProcessesReRanked,
+ SCORED_PROCESS_RECORD_COMPARATOR);
if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
boolean printedHeader = false;
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
if (scoredProcessRecords[i].proc.getPid()
!= lruList.get(lruPositions[i]).getPid()) {
if (!printedHeader) {
@@ -260,12 +332,18 @@ public class CacheOomRanker {
}
}
- for (int i = 0; i < scoredProcessRecords.length; ++i) {
+ for (int i = 0; i < numProcessesReRanked; ++i) {
lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
scoredProcessRecords[i].proc = null;
}
}
+ private static boolean appCanBeReRanked(ProcessRecord process) {
+ return !process.isKilledByAm()
+ && process.getThread() != null
+ && process.mState.getCurAdj() >= ProcessList.UNKNOWN_ADJ;
+ }
+
private static void addToScore(RankedProcessRecord[] scores, float weight) {
for (int i = 1; i < scores.length; ++i) {
scores[i].score += i * weight;
diff --git a/services/core/java/com/android/server/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java
new file mode 100644
index 000000000000..3577f72bfa28
--- /dev/null
+++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.am;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages and handles component aliases, which is an experimental feature.
+ *
+ * For now, this is an experimental feature to evaluate feasibility, so the implementation is
+ * "quick & dirty". For example, to define aliases, we use a regular intent filter and meta-data
+ * in the manifest, instead of adding proper tags/attributes to AndroidManifest.xml.
+ *
+ * Also, for now, aliases can be defined across any packages, but in the final version, there'll
+ * be restrictions:
+ * - We probably should only allow either privileged or preinstalled apps.
+ * - Aliases can only be defined across packages that are atomically installed, and signed with the
+ * same key.
+ */
+public class ComponentAliasResolver {
+ private static final String TAG = "ComponentAliasResolver";
+ private static final boolean DEBUG = true;
+
+ private final Object mLock = new Object();
+ private final ActivityManagerService mAm;
+ private final Context mContext;
+
+ @GuardedBy("mLock")
+ private boolean mEnabled;
+
+ @GuardedBy("mLock")
+ private String mOverrideString;
+
+ @GuardedBy("mLock")
+ private final ArrayMap<ComponentName, ComponentName> mFromTo = new ArrayMap<>();
+
+ private static final String ALIAS_FILTER_ACTION = "android.intent.action.EXPERIMENTAL_IS_ALIAS";
+ private static final String META_DATA_ALIAS_TARGET = "alias_target";
+
+ public ComponentAliasResolver(ActivityManagerService service) {
+ mAm = service;
+ mContext = service.mContext;
+ }
+
+ public boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
+ }
+ }
+
+ /**
+ * When there's any change to packages, we refresh all the aliases.
+ * TODO: In the production version, we should update only the changed package.
+ */
+ final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageModified(String packageName) {
+ refresh();
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ refresh();
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ refresh();
+ }
+ };
+
+ /**
+ * (Re-)loads aliases from <meta-data> and the device config override.
+ */
+ public void update(boolean enabled, String overrides) {
+ synchronized (mLock) {
+ if (enabled == mEnabled && Objects.equals(overrides, mOverrideString)) {
+ return;
+ }
+ if (enabled != mEnabled) {
+ Slog.i(TAG, (enabled ? "Enabling" : "Disabling") + " component aliases...");
+ if (enabled) {
+ mPackageMonitor.register(mAm.mContext, UserHandle.ALL,
+ /* externalStorage= */ false, BackgroundThread.getHandler());
+ } else {
+ mPackageMonitor.unregister();
+ }
+ }
+ mEnabled = enabled;
+ mOverrideString = overrides;
+
+ if (mEnabled) {
+ refreshLocked();
+ } else {
+ mFromTo.clear();
+ }
+ }
+ }
+
+ private void refresh() {
+ synchronized (mLock) {
+ refreshLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void refreshLocked() {
+ if (DEBUG) Slog.d(TAG, "Refreshing aliases...");
+ mFromTo.clear();
+ loadFromMetadataLocked();
+ loadOverridesLocked();
+ }
+
+ /**
+ * Scans all the "alias" components and inserts the from-to pairs to the map.
+ */
+ @GuardedBy("mLock")
+ private void loadFromMetadataLocked() {
+ if (DEBUG) Slog.d(TAG, "Scanning aliases...");
+ Intent i = new Intent(ALIAS_FILTER_ACTION);
+
+ List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser(
+ i,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.GET_META_DATA,
+ UserHandle.USER_SYSTEM);
+
+ for (ResolveInfo ri : services) {
+ final ComponentInfo ci = ri.getComponentInfo();
+ final ComponentName from = ci.getComponentName();
+ final ComponentName to = ComponentName.unflattenFromString(
+ ci.metaData.getString(META_DATA_ALIAS_TARGET));
+ if (!validateComponentName(to)) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "" + from.flattenToShortString() + " -> " + to.flattenToShortString());
+ }
+ mFromTo.put(from, to);
+ }
+
+ // TODO: Scan for other component types as well.
+ }
+
+ /**
+ * Parses an "override" string and inserts the from-to pairs to the map.
+ *
+ * The format is:
+ * ALIAS-COMPONENT-1 ":" TARGET-COMPONENT-1 ( "," ALIAS-COMPONENT-2 ":" TARGET-COMPONENT-2 )*
+ */
+ @GuardedBy("mLock")
+ private void loadOverridesLocked() {
+ if (DEBUG) Slog.d(TAG, "Loading aliases overrides ...");
+ for (String line : mOverrideString.split("\\,+")) {
+ final String[] fields = line.split("\\:+", 2);
+ final ComponentName from = ComponentName.unflattenFromString(fields[0]);
+ if (!validateComponentName(from)) {
+ continue;
+ }
+
+ if (fields.length == 1) {
+ if (DEBUG) Slog.d(TAG, "" + from.flattenToShortString() + " [removed]");
+ mFromTo.remove(from);
+ } else {
+ final ComponentName to = ComponentName.unflattenFromString(fields[1]);
+ if (!validateComponentName(to)) {
+ continue;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG,
+ "" + from.flattenToShortString() + " -> " + to.flattenToShortString());
+ }
+ mFromTo.put(from, to);
+ }
+ }
+ }
+
+ private boolean validateComponentName(ComponentName cn) {
+ if (cn != null) {
+ return true;
+ }
+ Slog.e(TAG, "Invalid component name detected: " + cn);
+ return false;
+ }
+
+ /**
+ * Dump the aliases for dumpsys / bugrports.
+ */
+ public void dump(PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println("ACTIVITY MANAGER COMPONENT-ALIAS (dumpsys activity component-alias)");
+ pw.print(" Enabled: "); pw.println(mEnabled);
+
+ pw.println(" Aliases:");
+ for (int i = 0; i < mFromTo.size(); i++) {
+ ComponentName from = mFromTo.keyAt(i);
+ ComponentName to = mFromTo.valueAt(i);
+ pw.print(" ");
+ pw.print(from.flattenToShortString());
+ pw.print(" -> ");
+ pw.print(to.flattenToShortString());
+ pw.println();
+ }
+ pw.println();
+ }
+ }
+
+ /**
+ * Contains alias resolution information.
+ */
+ public static class Resolution<T> {
+ /** "From" component. Null if component alias is disabled. */
+ @Nullable
+ public final T source;
+
+ /** "To" component. Null if component alias is disabled, or the source isn't an alias. */
+ @Nullable
+ public final T resolved;
+
+ public Resolution(T source, T resolved) {
+ this.source = source;
+ this.resolved = resolved;
+ }
+
+ @Nullable
+ public boolean isAlias() {
+ return this.resolved != null;
+ }
+
+ @Nullable
+ public T getAlias() {
+ return isAlias() ? source : null;
+ }
+
+ @Nullable
+ public T getTarget() {
+ return isAlias() ? resolved : null;
+ }
+ }
+
+ @Nullable
+ public Resolution<ComponentName> resolveService(
+ @NonNull Intent service, @Nullable String resolvedType,
+ int packageFlags, int userId, int callingUid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (!mEnabled) {
+ return new Resolution<>(null, null);
+ }
+
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+
+ ResolveInfo rInfo = pmi.resolveService(service,
+ resolvedType, packageFlags, userId, callingUid);
+ ServiceInfo sInfo = rInfo != null ? rInfo.serviceInfo : null;
+ if (sInfo == null) {
+ return null; // Service not found.
+ }
+ final ComponentName alias =
+ new ComponentName(sInfo.applicationInfo.packageName, sInfo.name);
+ final ComponentName target = mFromTo.get(alias);
+
+ if (target != null) {
+ // It's an alias. Keep the original intent, and rewrite it.
+ service.setOriginalIntent(new Intent(service));
+
+ service.setPackage(null);
+ service.setComponent(target);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Alias resolved: " + alias.flattenToShortString()
+ + " -> " + target.flattenToShortString());
+ }
+ }
+ return new Resolution<>(alias, target);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 916127126117..b434328ef224 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -18,8 +18,10 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import android.annotation.Nullable;
import android.app.IServiceConnection;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -48,6 +50,12 @@ final class ConnectionRecord {
String stringName; // Caching of toString.
boolean serviceDead; // Well is it?
private Object mProcStatsLock; // Internal lock for accessing AssociationState
+ /**
+ * If the connection was made against an alias, then the alias conponent name. Otherwise, null.
+ * We return this component name to the client.
+ */
+ @Nullable
+ final ComponentName aliasComponent;
// Please keep the following two enum list synced.
private static final int[] BIND_ORIG_ENUMS = new int[] {
@@ -102,7 +110,8 @@ final class ConnectionRecord {
ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
IServiceConnection _conn, int _flags,
int _clientLabel, PendingIntent _clientIntent,
- int _clientUid, String _clientProcessName, String _clientPackageName) {
+ int _clientUid, String _clientProcessName, String _clientPackageName,
+ ComponentName _aliasComponent) {
binding = _binding;
activity = _activity;
conn = _conn;
@@ -112,6 +121,7 @@ final class ConnectionRecord {
clientUid = _clientUid;
clientProcessName = _clientProcessName;
clientPackageName = _clientPackageName;
+ aliasComponent = _aliasComponent;
}
public boolean hasFlag(final int flag) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 5c9d38515e49..2e3e635c1157 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -94,8 +94,6 @@ final class CoreSettingsObserver extends ContentObserver {
sGlobalSettingToTypeMap.put(
Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
sGlobalSettingToTypeMap.put(
- Settings.Global.ANGLE_ALLOWLIST, String.class);
- sGlobalSettingToTypeMap.put(
Settings.Global.ANGLE_EGL_FEATURES, String.class);
sGlobalSettingToTypeMap.put(
Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5c19ceb7067a..e94276c53db3 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -256,7 +256,7 @@ public class OomAdjuster {
@GuardedBy("mService")
private boolean mPendingFullOomAdjUpdate = false;
- private final PlatformCompatCache mPlatformCompatCache;
+ final PlatformCompatCache mPlatformCompatCache;
/** Overrideable by a test */
@VisibleForTesting
@@ -309,6 +309,12 @@ public class OomAdjuster {
}
}
+ void onApplicationInfoChanged(ApplicationInfo app) {
+ for (int i = mCaches.size() - 1; i >= 0; i--) {
+ mCaches.valueAt(i).onApplicationInfoChanged(app);
+ }
+ }
+
static class CacheItem implements CompatChange.ChangeListener {
private final PlatformCompat mPlatformCompat;
private final long mChangeId;
@@ -328,22 +334,14 @@ public class OomAdjuster {
final int index = mCache.indexOfKey(app.packageName);
Pair<Boolean, WeakReference<ApplicationInfo>> p;
if (index < 0) {
- p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
- app),
- new WeakReference<>(app));
- mCache.put(app.packageName, p);
- return p.first;
+ return fetchLocked(app, index);
}
p = mCache.valueAt(index);
if (p.second.get() == app) {
return p.first;
}
// Cache is invalid, regenerate it
- p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
- app),
- new WeakReference<>(app));
- mCache.setValueAt(index, p);
- return p.first;
+ return fetchLocked(app, index);
}
}
@@ -353,10 +351,40 @@ public class OomAdjuster {
}
}
+ @GuardedBy("mLock")
+ boolean fetchLocked(ApplicationInfo app, int index) {
+ final Pair<Boolean, WeakReference<ApplicationInfo>> p = new Pair<>(
+ mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId, app),
+ new WeakReference<>(app));
+ if (index >= 0) {
+ mCache.setValueAt(index, p);
+ } else {
+ mCache.put(app.packageName, p);
+ }
+ return p.first;
+ }
+
+ void onApplicationInfoChanged(ApplicationInfo app) {
+ synchronized (mLock) {
+ final int index = mCache.indexOfKey(app.packageName);
+ if (index >= 0) {
+ fetchLocked(app, index);
+ }
+ }
+ }
+
@Override
public void onCompatChange(String packageName) {
synchronized (mLock) {
- mCache.remove(packageName);
+ final int index = mCache.indexOfKey(packageName);
+ if (index >= 0) {
+ final ApplicationInfo app = mCache.valueAt(index).second.get();
+ if (app != null) {
+ fetchLocked(app, index);
+ } else {
+ mCache.removeAt(index);
+ }
+ }
}
}
}
@@ -483,6 +511,7 @@ public class OomAdjuster {
}
app.mState.resetCachedInfo();
+ app.mState.setCurBoundByNonBgRestrictedApp(false);
UidRecord uidRec = app.getUidRecord();
if (uidRec != null) {
if (DEBUG_UID_OBSERVERS) {
@@ -616,6 +645,7 @@ public class OomAdjuster {
state.setContainsCycle(false);
state.setProcStateChanged(false);
state.resetCachedInfo();
+ state.setCurBoundByNonBgRestrictedApp(false);
// Check if this process is in the pending list too, remove from pending list if so.
mPendingProcessSet.remove(app);
boolean success = performUpdateOomAdjLSP(app, cachedAdj, topApp,
@@ -906,6 +936,7 @@ public class OomAdjuster {
state.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
state.setSetCapability(PROCESS_CAPABILITY_NONE);
state.resetCachedInfo();
+ state.setCurBoundByNonBgRestrictedApp(false);
}
}
mProcessesInCycle.clear();
@@ -1533,6 +1564,7 @@ public class OomAdjuster {
state.resetAllowStartFgsState();
if (!cycleReEval) {
// Don't reset this flag when doing cycles re-evaluation.
+ state.setNoKillOnBgRestrictedAndIdle(false);
app.mOptRecord.setShouldNotFreeze(false);
}
@@ -1872,6 +1904,7 @@ public class OomAdjuster {
}
int capabilityFromFGS = 0; // capability from foreground service.
+ boolean boundByNonBgRestricted = state.isCurBoundByNonBgRestrictedApp();
boolean scheduleLikeTopApp = false;
for (int is = psr.numberOfRunningServices() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
@@ -1986,6 +2019,11 @@ public class OomAdjuster {
final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
+ boundByNonBgRestricted |= cstate.isCurBoundByNonBgRestrictedApp()
+ || clientProcState <= PROCESS_STATE_BOUND_TOP
+ || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+ && !cstate.isBackgroundRestricted());
+
if (client.mOptRecord.shouldNotFreeze()) {
// Propagate the shouldNotFreeze flag down the bindings.
app.mOptRecord.setShouldNotFreeze(true);
@@ -2308,6 +2346,12 @@ public class OomAdjuster {
// Propagate the shouldNotFreeze flag down the bindings.
app.mOptRecord.setShouldNotFreeze(true);
}
+
+ boundByNonBgRestricted |= cstate.isCurBoundByNonBgRestrictedApp()
+ || clientProcState <= PROCESS_STATE_BOUND_TOP
+ || (clientProcState == PROCESS_STATE_FOREGROUND_SERVICE
+ && !cstate.isBackgroundRestricted());
+
String adjType = null;
if (adj > clientAdj) {
if (state.hasShownUi() && !state.getCachedIsHomeProcess()
@@ -2485,6 +2529,7 @@ public class OomAdjuster {
state.updateLastInvisibleTime(hasVisibleActivities);
state.setHasForegroundActivities(foregroundActivities);
state.setCompletedAdjSeq(mAdjSeq);
+ state.setCurBoundByNonBgRestrictedApp(boundByNonBgRestricted);
// if curAdj or curProcState improved, then this process was promoted
return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
@@ -2827,6 +2872,19 @@ public class OomAdjuster {
state.setSetCapability(state.getCurCapability());
}
+ final boolean curBoundByNonBgRestrictedApp = state.isCurBoundByNonBgRestrictedApp();
+ if (curBoundByNonBgRestrictedApp != state.isSetBoundByNonBgRestrictedApp()) {
+ state.setSetBoundByNonBgRestrictedApp(curBoundByNonBgRestrictedApp);
+ if (!curBoundByNonBgRestrictedApp && state.isBackgroundRestricted()) {
+ mService.mHandler.post(() -> {
+ synchronized (mService) {
+ mService.mServices.stopAllForegroundServicesLocked(
+ app.uid, app.info.packageName);
+ }
+ });
+ }
+ }
+
if (changes != 0) {
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Changes in " + app + ": " + changes);
@@ -2843,6 +2901,23 @@ public class OomAdjuster {
+ " target=" + state.getAdjTarget() + " capability=" + item.capability);
}
+ if (state.isCached() && !state.shouldNotKillOnBgRestrictedAndIdle()) {
+ // It's eligible to get killed when in UID idle and bg restricted mode,
+ // check if these states are just flipped.
+ if (!state.isSetCached() || state.isSetNoKillOnBgRestrictedAndIdle()) {
+ // Take the timestamp, we'd hold the killing for the background settle time
+ // (for states debouncing to avoid from thrashing).
+ state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed);
+ // Kick off the delayed checkup message if needed.
+ if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+ mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+ mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
+ }
+ }
+ }
+ state.setSetCached(state.isCached());
+ state.setSetNoKillOnBgRestrictedAndIdle(state.shouldNotKillOnBgRestrictedAndIdle());
+
return success;
}
@@ -2991,6 +3066,20 @@ public class OomAdjuster {
if (mLocalPowerManager != null) {
mLocalPowerManager.finishUidChanges();
}
+ // Also check if there are any apps in cached and background restricted mode,
+ // if so, kill it if it's been there long enough, or kick off a msg to check
+ // it later.
+ if (mService.mConstants.mKillBgRestrictedAndCachedIdle) {
+ final ArraySet<ProcessRecord> apps = mProcessList.mAppsInBackgroundRestricted;
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ // Check to see if needs to be killed.
+ final long bgTime = mProcessList.killAppIfBgRestrictedAndCachedIdleLocked(
+ apps.valueAt(i), nowElapsed) - mConstants.BACKGROUND_SETTLE_TIME;
+ if (bgTime > 0 && (nextTime == 0 || nextTime > bgTime)) {
+ nextTime = bgTime;
+ }
+ }
+ }
if (nextTime > 0) {
mService.mHandler.removeMessages(IDLE_UIDS_MSG);
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index dc924c11f867..175da9cab77b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -481,7 +481,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
key.featureId, uid, callingUid, callingPid, finalIntent,
resolvedType, finishedReceiver, code, null, null,
requiredPermission, options, (finishedReceiver != null), false,
- userId, allowedByToken || allowTrampoline, bgStartsToken);
+ userId, allowedByToken || allowTrampoline, bgStartsToken,
+ null /* broadcastAllowList */);
if (sent == ActivityManager.BROADCAST_SUCCESS) {
sendFinish = false;
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 7e79ef5a5e69..8d3e44276620 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -117,6 +117,12 @@ class ProcessErrorStateRecord {
private ComponentName mErrorReportReceiver;
/**
+ * ANR dialog data used to dismiss any visible ANR dialogs if the app becomes responsive.
+ */
+ @CompositeRWLock({"mService", "mProcLock"})
+ private AppNotRespondingDialog.Data mAnrData;
+
+ /**
* Optional local handler to be invoked in the process crash.
*/
@CompositeRWLock({"mService", "mProcLock"})
@@ -209,6 +215,16 @@ class ProcessErrorStateRecord {
return mDialogController;
}
+ @GuardedBy({"mService", "mProcLock"})
+ void setAnrData(AppNotRespondingDialog.Data data) {
+ mAnrData = data;
+ }
+
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ AppNotRespondingDialog.Data getAnrData() {
+ return mAnrData;
+ }
+
ProcessErrorStateRecord(ProcessRecord app) {
mApp = app;
mService = app.mService;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b77270f5963b..07d9cb28d2b3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -42,6 +42,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESSES_CHANGED_UI_MSG;
import static com.android.server.am.ActivityManagerService.DISPATCH_PROCESS_DIED_UI_MSG;
+import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_DELAY_MS;
import static com.android.server.am.ActivityManagerService.KILL_APP_ZYGOTE_MSG;
import static com.android.server.am.ActivityManagerService.PERSISTENT_MASK;
@@ -126,6 +127,7 @@ import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
+import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
@@ -544,6 +546,12 @@ public final class ProcessList {
final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
+ /**
+ * The list of apps in background restricted mode.
+ */
+ @GuardedBy("mService")
+ final ArraySet<ProcessRecord> mAppsInBackgroundRestricted = new ArraySet<>();
+
private PlatformCompat mPlatformCompat = null;
/**
@@ -2370,6 +2378,16 @@ public final class ProcessList {
allowlistedAppDataInfoMap = null;
}
+ AppStateTracker ast = mService.mServices.mAppStateTracker;
+ if (ast != null) {
+ final boolean inBgRestricted = ast.isAppBackgroundRestricted(
+ app.info.uid, app.info.packageName);
+ if (inBgRestricted) {
+ mAppsInBackgroundRestricted.add(app);
+ }
+ app.mState.setBackgroundRestricted(inBgRestricted);
+ }
+
final Process.ProcessStartResult startResult;
boolean regularZygote = false;
if (hostingRecord.usesWebviewZygote()) {
@@ -3104,6 +3122,7 @@ public final class ProcessList {
if (record != null && record.appZygote) {
removeProcessFromAppZygoteLocked(record);
}
+ mAppsInBackgroundRestricted.remove(record);
return old;
}
@@ -4710,6 +4729,8 @@ public final class ProcessList {
if (ai != null) {
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
+ mService.mOomAdjuster.mPlatformCompatCache
+ .onApplicationInfoChanged(ai);
}
app.getThread().scheduleApplicationInfoChanged(ai);
targetProcesses.add(app.getWindowProcessController());
@@ -5017,6 +5038,75 @@ public final class ProcessList {
return true;
}
+ @GuardedBy("mService")
+ void updateBackgroundRestrictedForUidPackageLocked(int uid, String packageName,
+ boolean restricted) {
+ final UidRecord uidRec = getUidRecordLOSP(uid);
+ if (uidRec != null) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ uidRec.forEachProcess(app -> {
+ if (TextUtils.equals(app.info.packageName, packageName)) {
+ app.mState.setBackgroundRestricted(restricted);
+ if (restricted) {
+ mAppsInBackgroundRestricted.add(app);
+ final long future = killAppIfBgRestrictedAndCachedIdleLocked(
+ app, nowElapsed);
+ if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+ mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
+ future - nowElapsed);
+ }
+ } else {
+ mAppsInBackgroundRestricted.remove(app);
+ }
+ if (!app.isKilledByAm()) {
+ mService.enqueueOomAdjTargetLocked(app);
+ }
+ }
+ });
+ /* Will be a no-op if nothing pending */
+ mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+ }
+ }
+
+ /**
+ * Kill the given app if it's in cached idle and background restricted mode.
+ *
+ * @return A future timestamp when the app should be killed at, or a 0 if it shouldn't
+ * be killed or it has been killed.
+ */
+ @GuardedBy("mService")
+ long killAppIfBgRestrictedAndCachedIdleLocked(ProcessRecord app, long nowElapsed) {
+ final UidRecord uidRec = app.getUidRecord();
+ final long lastCanKillTime = app.mState.getLastCanKillOnBgRestrictedAndIdleTime();
+ if (!mService.mConstants.mKillBgRestrictedAndCachedIdle
+ || app.isKilled() || app.getThread() == null || uidRec == null || !uidRec.isIdle()
+ || !app.isCached() || app.mState.shouldNotKillOnBgRestrictedAndIdle()
+ || !app.mState.isBackgroundRestricted() || lastCanKillTime == 0) {
+ return 0;
+ }
+ final long future = lastCanKillTime
+ + mService.mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs;
+ if (future <= nowElapsed) {
+ app.killLocked("cached idle & background restricted",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY,
+ true);
+ return 0;
+ }
+ return future;
+ }
+
+ /**
+ * Called by {@link ActivityManagerService#enqueueUidChangeLocked} only, it doesn't schedule
+ * the standy killing checks because it should have been scheduled before enqueueing UID idle
+ * changed.
+ */
+ @GuardedBy("mService")
+ void killAppIfBgRestrictedAndCachedIdleLocked(UidRecord uidRec) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ uidRec.forEachProcess(app -> killAppIfBgRestrictedAndCachedIdleLocked(app, nowElapsed));
+ }
+
/**
* Called by ActivityManagerService when a process died.
*/
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index dc6bcd8ab9de..46144f586dbd 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -302,6 +302,23 @@ final class ProcessStateRecord {
private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
/**
+ * Whether or not the app is background restricted (OP_RUN_ANY_IN_BACKGROUND is NOT allowed).
+ */
+ @GuardedBy("mService")
+ private boolean mBackgroundRestricted = false;
+
+ /**
+ * Whether or not this process is being bound by a non-background restricted app.
+ */
+ @GuardedBy("mService")
+ private boolean mCurBoundByNonBgRestrictedApp = false;
+
+ /**
+ * Last set state of {@link #mCurBoundByNonBgRestrictedApp}.
+ */
+ private boolean mSetBoundByNonBgRestrictedApp = false;
+
+ /**
* Debugging: primary thing impacting oom_adj.
*/
@GuardedBy("mService")
@@ -357,6 +374,34 @@ final class ProcessStateRecord {
@ElapsedRealtimeLong
private long mLastInvisibleTime;
+ /**
+ * Whether or not this process could be killed when it's in background restricted mode
+ * and cached &amp; idle state.
+ */
+ @GuardedBy("mService")
+ private boolean mNoKillOnBgRestrictedAndIdle;
+
+ /**
+ * Last set value of {@link #mCached}.
+ */
+ @GuardedBy("mService")
+ private boolean mSetCached;
+
+ /**
+ * Last set value of {@link #mNoKillOnBgRestrictedAndIdle}.
+ */
+ @GuardedBy("mService")
+ private boolean mSetNoKillOnBgRestrictedAndIdle;
+
+ /**
+ * The last time when the {@link #mNoKillOnBgRestrictedAndIdle} is false and the
+ * {@link #mCached} is true, and either the former state is flipping from true to false
+ * when latter state is true, or the latter state is flipping from false to true when the
+ * former state is false.
+ */
+ @GuardedBy("mService")
+ private @ElapsedRealtimeLong long mLastCanKillOnBgRestrictedAndIdleTime;
+
// Below are the cached task info for OomAdjuster only
private static final int VALUE_INVALID = -1;
private static final int VALUE_FALSE = 0;
@@ -1113,6 +1158,36 @@ final class ProcessStateRecord {
}
@GuardedBy("mService")
+ boolean isBackgroundRestricted() {
+ return mBackgroundRestricted;
+ }
+
+ @GuardedBy("mService")
+ void setBackgroundRestricted(boolean restricted) {
+ mBackgroundRestricted = restricted;
+ }
+
+ @GuardedBy("mService")
+ boolean isCurBoundByNonBgRestrictedApp() {
+ return mCurBoundByNonBgRestrictedApp;
+ }
+
+ @GuardedBy("mService")
+ void setCurBoundByNonBgRestrictedApp(boolean bound) {
+ mCurBoundByNonBgRestrictedApp = bound;
+ }
+
+ @GuardedBy("mService")
+ boolean isSetBoundByNonBgRestrictedApp() {
+ return mSetBoundByNonBgRestrictedApp;
+ }
+
+ @GuardedBy("mService")
+ void setSetBoundByNonBgRestrictedApp(boolean bound) {
+ mSetBoundByNonBgRestrictedApp = bound;
+ }
+
+ @GuardedBy("mService")
void updateLastInvisibleTime(boolean hasVisibleActivities) {
if (hasVisibleActivities) {
mLastInvisibleTime = Long.MAX_VALUE;
@@ -1127,6 +1202,47 @@ final class ProcessStateRecord {
return mLastInvisibleTime;
}
+ @GuardedBy("mService")
+ void setNoKillOnBgRestrictedAndIdle(boolean shouldNotKill) {
+ mNoKillOnBgRestrictedAndIdle = shouldNotKill;
+ }
+
+ @GuardedBy("mService")
+ boolean shouldNotKillOnBgRestrictedAndIdle() {
+ return mNoKillOnBgRestrictedAndIdle;
+ }
+
+ @GuardedBy("mService")
+ void setSetCached(boolean cached) {
+ mSetCached = cached;
+ }
+
+ @GuardedBy("mService")
+ boolean isSetCached() {
+ return mSetCached;
+ }
+
+ @GuardedBy("mService")
+ void setSetNoKillOnBgRestrictedAndIdle(boolean shouldNotKill) {
+ mSetNoKillOnBgRestrictedAndIdle = shouldNotKill;
+ }
+
+ @GuardedBy("mService")
+ boolean isSetNoKillOnBgRestrictedAndIdle() {
+ return mSetNoKillOnBgRestrictedAndIdle;
+ }
+
+ @GuardedBy("mService")
+ void setLastCanKillOnBgRestrictedAndIdleTime(@ElapsedRealtimeLong long now) {
+ mLastCanKillOnBgRestrictedAndIdleTime = now;
+ }
+
+ @ElapsedRealtimeLong
+ @GuardedBy("mService")
+ long getLastCanKillOnBgRestrictedAndIdleTime() {
+ return mLastCanKillOnBgRestrictedAndIdleTime;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void dump(PrintWriter pw, String prefix, long nowUptime) {
if (mReportedInteraction || mFgInteractionTime != 0) {
@@ -1164,7 +1280,14 @@ final class ProcessStateRecord {
ActivityManager.printCapabilitiesFull(pw, mSetCapability);
pw.println();
pw.print(prefix); pw.print("allowStartFgsState=");
- pw.println(mAllowStartFgsState);
+ pw.print(mAllowStartFgsState);
+ if (mBackgroundRestricted) {
+ pw.print(" backgroundRestricted=");
+ pw.print(mBackgroundRestricted);
+ pw.print(" boundByNonBgRestrictedApp=");
+ pw.print(mSetBoundByNonBgRestrictedApp);
+ }
+ pw.println();
if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
pw.print(" pendingUiClean="); pw.println(mApp.mProfile.hasPendingUiClean());
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 804e442bc8de..97ed0a38fabe 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -196,6 +196,22 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
boolean mKeepWarming; // Whether or not it'll keep critical code path of the host warm
+ /**
+ * The original earliest restart time, which considers the number of crashes, etc.,
+ * but doesn't include the extra delays we put in between to scatter the restarts;
+ * it's the earliest time this auto service restart could happen alone(except those
+ * batch restarts which happens at time of process attach).
+ */
+ long mEarliestRestartTime;
+
+ /**
+ * The original time when the service start is scheduled, it does NOT include the reschedules.
+ *
+ * <p>The {@link #restartDelay} would be updated when its restart is rescheduled, but this field
+ * won't, so it could be used when dumping how long the restart is delayed actually.</p>
+ */
+ long mRestartSchedulingTime;
+
static class StartItem {
final ServiceRecord sr;
final boolean taskRemoved;
@@ -373,10 +389,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
if (destroying || destroyTime != 0) {
ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now);
}
- if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) {
+ if (crashCount != 0 || restartCount != 0 || (nextRestartTime - mRestartSchedulingTime) != 0
+ || nextRestartTime != 0) {
long crashToken = proto.start(ServiceRecordProto.CRASH);
proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount);
- ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, restartDelay, now);
+ ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY,
+ (nextRestartTime - mRestartSchedulingTime), now);
ProtoUtils.toDuration(proto,
ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now);
proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount);
@@ -504,10 +522,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
pw.println();
}
if (crashCount != 0 || restartCount != 0
- || restartDelay != 0 || nextRestartTime != 0) {
+ || (nextRestartTime - mRestartSchedulingTime) != 0 || nextRestartTime != 0) {
pw.print(prefix); pw.print("restartCount="); pw.print(restartCount);
pw.print(" restartDelay=");
- TimeUtils.formatDuration(restartDelay, now, pw);
+ TimeUtils.formatDuration(nextRestartTime - mRestartSchedulingTime, now, pw);
pw.print(" nextRestartTime=");
TimeUtils.formatDuration(nextRestartTime, now, pw);
pw.print(" crashCount="); pw.println(crashCount);
@@ -899,6 +917,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
restartCount = 0;
restartDelay = 0;
restartTime = 0;
+ mEarliestRestartTime = 0;
+ mRestartSchedulingTime = 0;
}
public StartItem findDeliveredStart(int id, boolean taskRemoved, boolean remove) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e022e977e02f..fc02f1913487 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -84,6 +84,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
+ DeviceConfig.NAMESPACE_LMKD_NATIVE,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
@@ -92,6 +93,8 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_STATSD_NATIVE,
DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_SURFACE_FLINGER_NATIVE_BOOT,
+ DeviceConfig.NAMESPACE_SWCODEC_NATIVE,
DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
};
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 4ba59faea9ec..6101e267effc 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -21,6 +21,7 @@ import android.app.ActivityManager;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -282,6 +283,17 @@ public final class UidRecord {
}
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ ProcessRecord getProcessInPackage(String packageName) {
+ for (int i = mProcRecords.size() - 1; i >= 0; i--) {
+ final ProcessRecord app = mProcRecords.valueAt(i);
+ if (app != null && TextUtils.equals(app.info.packageName, packageName)) {
+ return app;
+ }
+ }
+ return null;
+ }
+
@GuardedBy({"mService", "mProcLock"})
void addProcess(ProcessRecord app) {
mProcRecords.add(app);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba3e1fb95e7d..fa7eae3b2231 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -425,6 +425,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
+ @VisibleForTesting
List<Integer> getRunningUsersLU() {
ArrayList<Integer> runningUsers = new ArrayList<>();
for (Integer userId : mUserLru) {
@@ -450,7 +451,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- void stopRunningUsersLU(int maxRunningUsers) {
+ private void stopRunningUsersLU(int maxRunningUsers) {
List<Integer> currentlyRunning = getRunningUsersLU();
Iterator<Integer> iterator = currentlyRunning.iterator();
while (currentlyRunning.size() > maxRunningUsers && iterator.hasNext()) {
@@ -590,7 +591,11 @@ class UserController implements Handler.Callback {
Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
return;
}
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("UM.onBeforeUnlockUser-" + userId);
mInjector.getUserManager().onBeforeUnlockUser(userId);
+ t.traceEnd();
synchronized (mLock) {
// Do not proceed if unexpected state
if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
@@ -612,7 +617,7 @@ class UserController implements Handler.Callback {
* Step from {@link UserState#STATE_RUNNING_UNLOCKING} to
* {@link UserState#STATE_RUNNING_UNLOCKED}.
*/
- void finishUserUnlocked(final UserState uss) {
+ private void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
// Only keep marching forward if user is actually unlocked
@@ -907,7 +912,7 @@ class UserController implements Handler.Callback {
private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
final IStopUserCallback stopUserCallback,
KeyEvictedCallback keyEvictedCallback) {
- if (DEBUG_MU) Slogf.i(TAG, "stopSingleUserLocked userId=" + userId);
+ Slogf.i(TAG, "stopSingleUserLU userId=" + userId);
final UserState uss = mStartedUsers.get(userId);
if (uss == null) { // User is not started
// If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
@@ -1000,7 +1005,7 @@ class UserController implements Handler.Callback {
}
}
- void finishUserStopping(final int userId, final UserState uss,
+ private void finishUserStopping(final int userId, final UserState uss,
final boolean allowDelayedLocking) {
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_STOPPING, userId);
synchronized (mLock) {
@@ -1041,6 +1046,7 @@ class UserController implements Handler.Callback {
Binder.getCallingPid(), userId);
}
+ @VisibleForTesting
void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
final int userId = uss.mHandle.getIdentifier();
if (DEBUG_MU) {
@@ -1264,7 +1270,7 @@ class UserController implements Handler.Callback {
});
}
- void startProfiles() {
+ private void startProfiles() {
int currentUserId = getCurrentUserId();
if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
@@ -1317,6 +1323,7 @@ class UserController implements Handler.Callback {
return startUserNoChecks(userId, /* foreground= */ false, /* unlockListener= */ null);
}
+ @VisibleForTesting
boolean startUser(final @UserIdInt int userId, final boolean foreground) {
return startUser(userId, foreground, null);
}
@@ -1683,7 +1690,11 @@ class UserController implements Handler.Callback {
return false;
}
- if (!finishUserUnlocking(uss)) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("finishUserUnlocking-" + userId);
+ final boolean finishUserUnlockingResult = finishUserUnlocking(uss);
+ t.traceEnd();
+ if (!finishUserUnlockingResult) {
notifyFinished(userId, listener);
return false;
}
@@ -1775,6 +1786,7 @@ class UserController implements Handler.Callback {
}
/** Called on handler thread */
+ @VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int userId) {
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1848,7 +1860,11 @@ class UserController implements Handler.Callback {
}
}
+ @VisibleForTesting
void dispatchUserSwitch(final UserState uss, final int oldUserId, final int newUserId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
+
EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
final int observerCount = mUserSwitchObservers.beginBroadcast();
@@ -1901,27 +1917,36 @@ class UserController implements Handler.Callback {
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceEnd(); // end dispatchUserSwitch-
}
@GuardedBy("mLock")
- void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
+ private void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) {
mCurWaitingUserSwitchCallbacks = null;
mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG,
oldUserId, newUserId, uss));
}
+ @VisibleForTesting
void continueUserSwitch(UserState uss, int oldUserId, int newUserId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("continueUserSwitch-" + oldUserId + "-to-" + newUserId);
+
EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
if (isUserSwitchUiEnabled()) {
+ t.traceBegin("stopFreezingScreen");
mInjector.getWindowManager().stopFreezingScreen();
+ t.traceEnd();
}
uss.switching = false;
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
stopGuestOrEphemeralUserIfBackground(oldUserId);
stopBackgroundUsersOnSwitchIfEnforced(oldUserId);
+
+ t.traceEnd(); // end continueUserSwitch
}
private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
@@ -2354,7 +2379,7 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- UserInfo getCurrentUserLU() {
+ private UserInfo getCurrentUserLU() {
int userId = getCurrentOrTargetUserIdLU();
return getUserInfo(userId);
}
@@ -2366,12 +2391,12 @@ class UserController implements Handler.Callback {
}
@GuardedBy("mLock")
- int getCurrentOrTargetUserIdLU() {
+ private int getCurrentOrTargetUserIdLU() {
return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
}
@GuardedBy("mLock")
- int getCurrentUserIdLU() {
+ private int getCurrentUserIdLU() {
return mCurrentUserId;
}
@@ -2670,7 +2695,11 @@ class UserController implements Handler.Callback {
USER_LIFECYCLE_EVENT_STATE_FINISH);
logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_UNLOCKED_USER,
USER_LIFECYCLE_EVENT_STATE_BEGIN);
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("finishUserUnlocked-" + userId);
finishUserUnlocked((UserState) msg.obj);
+ t.traceEnd();
break;
case USER_UNLOCKED_MSG:
mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index af8d7a6a282b..3003c520254b 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -272,6 +272,13 @@ public final class GameManagerService extends IGameManagerService.Stub {
"com.android.graphics.intervention.wm.allowDownscale";
/**
+ * Metadata that can be included in the app manifest to allow/disallow any ANGLE
+ * interventions. Default value is TRUE.
+ */
+ public static final String METADATA_ANGLE_ALLOW_ANGLE =
+ "com.android.graphics.intervention.angle.allowAngle";
+
+ /**
* Metadata that needs to be included in the app manifest to OPT-IN to PERFORMANCE mode.
* This means the app will assume full responsibility for the experience provided by this
* mode and the system will enable no window manager downscaling.
@@ -294,6 +301,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
+ private boolean mAllowAngle;
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
@@ -305,10 +313,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
+ mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
} else {
mPerfModeOptedIn = false;
mBatteryModeOptedIn = false;
mAllowDownscale = true;
+ mAllowAngle = true;
}
} catch (PackageManager.NameNotFoundException e) {
// Not all packages are installed, hence ignore those that are not installed yet.
@@ -340,14 +350,26 @@ public final class GameManagerService extends IGameManagerService.Stub {
public static final String MODE_KEY = "mode";
public static final String SCALING_KEY = "downscaleFactor";
public static final String DEFAULT_SCALING = "1.0";
+ public static final String ANGLE_KEY = "useAngle";
private final @GameMode int mGameMode;
private final String mScaling;
+ private final boolean mUseAngle;
GameModeConfiguration(KeyValueListParser parser) {
mGameMode = parser.getInt(MODE_KEY, GameManager.GAME_MODE_UNSUPPORTED);
- mScaling = !mAllowDownscale || isGameModeOptedIn(mGameMode)
+ // isGameModeOptedIn() returns if an app will handle all of the changes necessary
+ // for a particular game mode. If so, the Android framework (i.e.
+ // GameManagerService) will not do anything for the app (like window scaling or
+ // using ANGLE).
+ mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
+ // We only want to use ANGLE if:
+ // - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
+ // - The app has not opted in to performing the work itself AND
+ // - The Phenotype config has enabled it.
+ mUseAngle = mAllowAngle && !willGamePerformOptimizations(mGameMode)
+ && parser.getBoolean(ANGLE_KEY, false);
}
public int getGameMode() {
@@ -358,6 +380,10 @@ public final class GameManagerService extends IGameManagerService.Stub {
return mScaling;
}
+ public boolean getUseAngle() {
+ return mUseAngle;
+ }
+
public boolean isValid() {
return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
|| mGameMode == GameManager.GAME_MODE_BATTERY)
@@ -368,7 +394,8 @@ public final class GameManagerService extends IGameManagerService.Stub {
* @hide
*/
public String toString() {
- return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + "]";
+ return "[Game Mode:" + mGameMode + ",Scaling:" + mScaling + ",Use Angle:"
+ + mUseAngle + "]";
}
/**
@@ -384,13 +411,14 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
/**
- * Gets whether a package has opted into a game mode via its manifest.
+ * Returns if the app will assume full responsibility for the experience provided by this
+ * mode. If True, the system will not perform any interventions for the app.
*
* @return True if the app package has specified in its metadata either:
* "com.android.app.gamemode.performance.enabled" or
* "com.android.app.gamemode.battery.enabled" with a value of "true"
*/
- public boolean isGameModeOptedIn(@GameMode int gameMode) {
+ public boolean willGamePerformOptimizations(@GameMode int gameMode) {
return (mBatteryModeOptedIn && gameMode == GameManager.GAME_MODE_BATTERY)
|| (mPerfModeOptedIn && gameMode == GameManager.GAME_MODE_PERFORMANCE);
}
@@ -631,7 +659,34 @@ public final class GameManagerService extends IGameManagerService.Stub {
mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
}
}
- updateCompatModeDownscale(packageName, gameMode);
+ updateInterventions(packageName, gameMode);
+ }
+
+ /**
+ * Get if ANGLE is enabled for the package for the currently enabled game mode.
+ * Checks that the caller has {@link android.Manifest.permission#MANAGE_GAME_MODE}.
+ */
+ @Override
+ @RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
+ public @GameMode boolean getAngleEnabled(String packageName, int userId)
+ throws SecurityException {
+ final int gameMode = getGameMode(packageName, userId);
+ if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
+ return false;
+ }
+
+ synchronized (mDeviceConfigLock) {
+ final GamePackageConfiguration config = mConfigs.get(packageName);
+ if (config == null) {
+ return false;
+ }
+ GamePackageConfiguration.GameModeConfiguration gameModeConfiguration =
+ config.getGameModeConfiguration(gameMode);
+ if (gameModeConfiguration == null) {
+ return false;
+ }
+ return gameModeConfiguration.getUseAngle();
+ }
}
/**
@@ -753,7 +808,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (DEBUG) {
Slog.v(TAG, dumpDeviceConfigs());
}
- if (packageConfig.isGameModeOptedIn(gameMode)) {
+ if (packageConfig.willGamePerformOptimizations(gameMode)) {
disableCompatScale(packageName);
return;
}
@@ -782,6 +837,17 @@ public final class GameManagerService extends IGameManagerService.Stub {
return (bitField & modeToBitmask(gameMode)) != 0;
}
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ private void updateUseAngle(String packageName, @GameMode int gameMode) {
+ // TODO (b/188475576): Nothing to do yet. Remove if it's still empty when we're ready to
+ // ship.
+ }
+
+ private void updateInterventions(String packageName, @GameMode int gameMode) {
+ updateCompatModeDownscale(packageName, gameMode);
+ updateUseAngle(packageName, gameMode);
+ }
+
/**
* @hide
*/
@@ -839,11 +905,11 @@ public final class GameManagerService extends IGameManagerService.Stub {
if (newGameMode != gameMode) {
setGameMode(packageName, newGameMode, userId);
}
- updateCompatModeDownscale(packageName, gameMode);
+ updateInterventions(packageName, gameMode);
}
}
} catch (Exception e) {
- Slog.e(TAG, "Failed to update compat modes for user: " + userId);
+ Slog.e(TAG, "Failed to update compat modes for user " + userId + ": " + e);
}
}
@@ -851,7 +917,7 @@ public final class GameManagerService extends IGameManagerService.Stub {
final List<PackageInfo> packages =
mPackageManager.getInstalledPackagesAsUser(0, userId);
return packages.stream().filter(e -> e.applicationInfo != null && e.applicationInfo.category
- == ApplicationInfo.CATEGORY_GAME)
+ == ApplicationInfo.CATEGORY_GAME)
.map(e -> e.packageName)
.toArray(String[]::new);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 64b9bd98a2fc..cfd2978d7eeb 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1829,13 +1829,13 @@ public class AppOpsService extends IAppOpsService.Stub {
for (int attributionNum = 0; attributionNum < numAttributions;
attributionNum++) {
ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
- attributionTags.add(attribution.tag);
+ attributionTags.add(attribution.getTag());
- int numInheritFrom = attribution.inheritFrom.size();
+ int numInheritFrom = attribution.getInheritFrom().size();
for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
inheritFromNum++) {
- dstAttributionTags.put(attribution.inheritFrom.get(inheritFromNum),
- attribution.tag);
+ dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
+ attribution.getTag());
}
}
}
@@ -4636,7 +4636,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (pkg.getAttributions() != null) {
int numAttributions = pkg.getAttributions().size();
for (int i = 0; i < numAttributions; i++) {
- if (pkg.getAttributions().get(i).tag.equals(attributionTag)) {
+ if (pkg.getAttributions().get(i).getTag().equals(attributionTag)) {
return true;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8961a5a05546..5ecdfe49cdf7 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1719,11 +1719,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (client == null) {
return;
}
- Log.w(TAG, "Speaker client died");
- if (removeCommunicationRouteClient(client.getBinder(), false)
- != null) {
- onUpdateCommunicationRoute("onCommunicationRouteClientDied");
- }
+ Log.w(TAG, "Communication client died");
+ setCommunicationRouteForClient(
+ client.getBinder(), client.getPid(), null, BtHelper.SCO_MODE_UNDEFINED,
+ "onCommunicationRouteClientDied");
}
/**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 34e2578f7855..43fd7ba54cec 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -64,6 +64,7 @@ import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.hdmi.HdmiAudioSystemClient;
+import android.hardware.hdmi.HdmiClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiTvClient;
@@ -93,11 +94,13 @@ import android.media.ICommunicationDeviceDispatcher;
import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
+import android.media.ISpatializerCallback;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.MediaMetrics;
import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
+import android.media.Spatializer;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
@@ -585,11 +588,12 @@ public class AudioService extends IAudioService.Stub
Set<Integer> mFixedVolumeDevices = new HashSet<>(Arrays.asList(
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
- AudioSystem.DEVICE_OUT_HDMI_ARC,
- AudioSystem.DEVICE_OUT_HDMI_EARC,
AudioSystem.DEVICE_OUT_AUX_LINE));
// Devices for which the volume is always max, no volume panel
- Set<Integer> mFullVolumeDevices = new HashSet<>();
+ Set<Integer> mFullVolumeDevices = new HashSet<>(Arrays.asList(
+ AudioSystem.DEVICE_OUT_HDMI_ARC,
+ AudioSystem.DEVICE_OUT_HDMI_EARC
+ ));
// Devices for the which use the "absolute volume" concept (framework sends audio signal
// full scale, and volume control separately) and can be used for multiple use cases reflected
// by the audio mode (e.g. media playback in MODE_NORMAL, and phone calls in MODE_IN_CALL).
@@ -2314,7 +2318,6 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.d(TAG, String.format("Master mute %s, user=%d", masterMute, currentUser));
}
- setSystemAudioMute(masterMute);
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
@@ -2616,14 +2619,6 @@ public class AudioService extends IAudioService.Stub
}
}
- /** @see AudioManager#adjustVolume(int, int) */
- public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
- String callingPackage, String caller) {
- adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
- caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
- VOL_ADJUST_NORMAL);
- }
-
public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) {
mNavigationRepeatSoundEffectsEnabled = enabled;
}
@@ -2646,6 +2641,7 @@ public class AudioService extends IAudioService.Stub
return mHomeSoundEffectEnabled;
}
+ /** All callers come from platform apps/system server, so no attribution tag is needed */
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
int keyEventMode) {
@@ -2721,7 +2717,7 @@ public class AudioService extends IAudioService.Stub
}
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid,
- hasModifyAudioSettings, keyEventMode);
+ null, hasModifyAudioSettings, keyEventMode);
}
private boolean notifyExternalVolumeController(int direction) {
@@ -2739,10 +2735,16 @@ public class AudioService extends IAudioService.Stub
return true;
}
- /** @see AudioManager#adjustStreamVolume(int, int, int)
- * Part of service interface, check permissions here */
+ /** Retain API for unsupported app usage */
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
+ adjustStreamVolumeWithAttribution(streamType, direction, flags, callingPackage, null);
+ }
+
+ /** @see AudioManager#adjustStreamVolume(int, int, int)
+ * Part of service interface, check permissions here */
+ public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
+ String callingPackage, String attributionTag) {
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+ "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
@@ -2752,13 +2754,13 @@ public class AudioService extends IAudioService.Stub
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
direction/*val1*/, flags/*val2*/, callingPackage));
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+ Binder.getCallingUid(), attributionTag, callingHasAudioSettingsPermission(),
VOL_ADJUST_NORMAL);
}
protected void adjustStreamVolume(int streamType, int direction, int flags,
- String callingPackage, String caller, int uid, boolean hasModifyAudioSettings,
- int keyEventMode) {
+ String callingPackage, String caller, int uid, String attributionTag,
+ boolean hasModifyAudioSettings, int keyEventMode) {
if (mUseFixedVolume) {
return;
}
@@ -2823,8 +2825,8 @@ public class AudioService extends IAudioService.Stub
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -2896,9 +2898,6 @@ public class AudioService extends IAudioService.Stub
} else {
state = direction == AudioManager.ADJUST_MUTE;
}
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioMute(state);
- }
for (int stream = 0; stream < mStreamStates.length; stream++) {
if (streamTypeAlias == mStreamVolumeAlias[stream]) {
if (!(readCameraSoundForced()
@@ -2964,11 +2963,6 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
}
}
-
- // Check if volume update should be sent to Hdmi system audio.
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
- }
}
final int newIndex = mStreamStates[streamType].getIndex(device);
@@ -2976,7 +2970,13 @@ public class AudioService extends IAudioService.Stub
if (adjustVolume) {
synchronized (mHdmiClientLock) {
if (mHdmiManager != null) {
- if (mHdmiPlaybackClient != null
+ // At most one of mHdmiPlaybackClient and mHdmiTvClient should be non-null
+ HdmiClient fullVolumeHdmiClient = mHdmiPlaybackClient;
+ if (mHdmiTvClient != null) {
+ fullVolumeHdmiClient = mHdmiTvClient;
+ }
+
+ if (fullVolumeHdmiClient != null
&& mHdmiCecVolumeControlEnabled
&& streamTypeAlias == AudioSystem.STREAM_MUSIC
// vol change on a full volume device
@@ -2990,6 +2990,10 @@ public class AudioService extends IAudioService.Stub
keyCode = KeyEvent.KEYCODE_VOLUME_DOWN;
break;
case AudioManager.ADJUST_TOGGLE_MUTE:
+ case AudioManager.ADJUST_MUTE:
+ case AudioManager.ADJUST_UNMUTE:
+ // Many CEC devices only support toggle mute. Therefore, we send the
+ // same keycode for all three mute options.
keyCode = KeyEvent.KEYCODE_VOLUME_MUTE;
break;
default:
@@ -2998,17 +3002,16 @@ public class AudioService extends IAudioService.Stub
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
final long ident = Binder.clearCallingIdentity();
try {
- final long time = java.lang.System.currentTimeMillis();
switch (keyEventMode) {
case VOL_ADJUST_NORMAL:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
case VOL_ADJUST_START:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, true);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, true);
break;
case VOL_ADJUST_END:
- mHdmiPlaybackClient.sendVolumeKeyEvent(keyCode, false);
+ fullVolumeHdmiClient.sendVolumeKeyEvent(keyCode, false);
break;
default:
Log.e(TAG, "Invalid keyEventMode " + keyEventMode);
@@ -3063,27 +3066,6 @@ public class AudioService extends IAudioService.Stub
Binder.restoreCallingIdentity(identity);
}
- private void setSystemAudioVolume(int oldVolume, int newVolume, int maxVolume, int flags) {
- // Sets the audio volume of AVR when we are in system audio mode. The new volume info
- // is tranformed to HDMI-CEC commands and passed through CEC bus.
- synchronized (mHdmiClientLock) {
- if (mHdmiManager == null
- || mHdmiTvClient == null
- || oldVolume == newVolume
- || (flags & AudioManager.FLAG_HDMI_SYSTEM_AUDIO_VOLUME) != 0
- || !mHdmiSystemAudioSupported
- || !mHdmiCecVolumeControlEnabled) {
- return;
- }
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioVolume(oldVolume, newVolume, maxVolume);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
// StreamVolumeCommand contains the information needed to defer the process of
// setStreamVolume() in case the user has to acknowledge the safe volume warning message.
static class StreamVolumeCommand {
@@ -3190,7 +3172,7 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */
public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
- String callingPackage) {
+ String callingPackage, String attributionTag) {
enforceModifyAudioRoutingPermission();
Objects.requireNonNull(attr, "attr must not be null");
final int volumeGroup = getVolumeGroupIdForAttributes(attr);
@@ -3215,7 +3197,7 @@ public class AudioService extends IAudioService.Stub
continue;
}
setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
+ attributionTag, Binder.getCallingUid(), true /*hasModifyAudioSettings*/);
}
}
@@ -3257,9 +3239,15 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.getMinVolumeIndexForAttributes(attr);
}
+ /** Retain API for unsupported app usage */
+ public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+ setStreamVolumeWithAttribution(streamType, index, flags, callingPackage, null);
+ }
+
/** @see AudioManager#setStreamVolume(int, int, int)
* Part of service interface, check permissions here */
- public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+ public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
+ String callingPackage, String attributionTag) {
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
@@ -3285,7 +3273,7 @@ public class AudioService extends IAudioService.Stub
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
index/*val1*/, flags/*val2*/, callingPackage));
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
- Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
+ attributionTag, Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
private boolean canChangeAccessibilityVolume() {
@@ -3520,7 +3508,8 @@ public class AudioService extends IAudioService.Stub
}
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
- String caller, int uid, boolean hasModifyAudioSettings) {
+ String caller, String attributionTag, int uid,
+ boolean hasModifyAudioSettings) {
if (DEBUG_VOL) {
Log.d(TAG, "setStreamVolume(stream=" + streamType+", index=" + index
+ ", calling=" + callingPackage + ")");
@@ -3547,8 +3536,8 @@ public class AudioService extends IAudioService.Stub
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
- if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -3587,10 +3576,6 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
}
- if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags);
- }
-
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
@@ -3634,7 +3619,7 @@ public class AudioService extends IAudioService.Stub
}
// The default volume group is the one hosted by default product strategy, i.e.
// supporting Default Attributes
- return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes);
+ return getVolumeGroupIdForAttributesInt(AudioProductStrategy.getDefaultAttributes());
}
private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
@@ -3852,18 +3837,6 @@ public class AudioService extends IAudioService.Stub
}
}
- private void setSystemAudioMute(boolean state) {
- synchronized (mHdmiClientLock) {
- if (mHdmiManager == null || mHdmiTvClient == null || !mHdmiSystemAudioSupported) return;
- final long token = Binder.clearCallingIdentity();
- try {
- mHdmiTvClient.setSystemAudioMute(state);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
/** get stream mute state. */
public boolean isStreamMute(int streamType) {
if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -3976,15 +3949,15 @@ public class AudioService extends IAudioService.Stub
}
private void setMasterMuteInternal(boolean mute, int flags, String callingPackage, int uid,
- int userId) {
+ int userId, String attributionTag) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
}
// If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
- if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
return;
}
if (userId != UserHandle.getCallingUserId() &&
@@ -4014,7 +3987,6 @@ public class AudioService extends IAudioService.Stub
if ((isPlatformAutomotive() && userId == UserHandle.USER_SYSTEM)
|| (getCurrentUserId() == userId)) {
if (mute != AudioSystem.getMasterMute()) {
- setSystemAudioMute(mute);
AudioSystem.setMasterMute(mute);
sendMasterMuteUpdate(mute, flags);
}
@@ -4026,10 +3998,12 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.getMasterMute();
}
- public void setMasterMute(boolean mute, int flags, String callingPackage, int userId) {
+ /** @see AudioManager#setMasterMute(boolean, int) */
+ public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
+ String attributionTag) {
enforceModifyAudioRoutingPermission();
- setMasterMuteInternal(mute, flags, callingPackage, Binder.getCallingUid(),
- userId);
+ setMasterMuteInternal(mute, flags, callingPackage,
+ Binder.getCallingUid(), userId, attributionTag);
}
/** @see AudioManager#getStreamVolume(int) */
@@ -4100,7 +4074,8 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setMicrophoneMute(boolean) */
@Override
- public void setMicrophoneMute(boolean on, String callingPackage, int userId) {
+ public void setMicrophoneMute(boolean on, String callingPackage, int userId,
+ String attributionTag) {
// If we are being called by the system check for user we are going to change
// so we handle user restrictions correctly.
int uid = Binder.getCallingUid();
@@ -4115,8 +4090,8 @@ public class AudioService extends IAudioService.Stub
? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE);
// If OP_MUTE_MICROPHONE is set, disallow unmuting.
- if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
+ if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid,
+ callingPackage, attributionTag, null) != AppOpsManager.MODE_ALLOWED) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record();
return;
}
@@ -4952,7 +4927,7 @@ public class AudioService extends IAudioService.Stub
}
adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
- hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
+ null, hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
}
/** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4964,7 +4939,7 @@ public class AudioService extends IAudioService.Stub
throw new SecurityException("Should only be called from system process");
}
- setStreamVolume(streamType, index, flags, packageName, packageName, uid,
+ setStreamVolume(streamType, index, flags, packageName, packageName, null, uid,
hasAudioSettingsPermission(uid, pid));
}
@@ -6374,7 +6349,7 @@ public class AudioService extends IAudioService.Stub
private void ensureValidAttributes(AudioVolumeGroup avg) {
boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
- .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+ .anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
if (!hasAtLeastOneValidAudioAttributes) {
throw new IllegalArgumentException("Volume Group " + avg.name()
+ " has no valid audio attributes");
@@ -6422,7 +6397,7 @@ public class AudioService extends IAudioService.Stub
private int mIndexMax;
private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
- private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+ private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
// No API in AudioSystem to get a device from strategy or from attributes.
// Need a valid public stream type to use current API getDeviceForStream
@@ -6435,8 +6410,9 @@ public class AudioService extends IAudioService.Stub
if (DEBUG_VOL) {
Log.v(TAG, "VolumeGroupState for " + avg.toString());
}
+ // mAudioAttributes is the default at this point
for (final AudioAttributes aa : avg.getAudioAttributes()) {
- if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ if (!aa.equals(mAudioAttributes)) {
mAudioAttributes = aa;
break;
}
@@ -8086,8 +8062,8 @@ public class AudioService extends IAudioService.Stub
}
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
- IAudioPolicyCallback pcb, int sdk) {
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName,
+ String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
final int uid = Binder.getCallingUid();
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
.setUid(uid)
@@ -8139,7 +8115,7 @@ public class AudioService extends IAudioService.Stub
}
mmi.record();
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, flags, sdk,
+ clientId, callingPackageName, attributionTag, flags, sdk,
forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/);
}
@@ -8156,7 +8132,7 @@ public class AudioService extends IAudioService.Stub
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
- clientId, callingPackageName, AudioManager.AUDIOFOCUS_FLAG_TEST,
+ clientId, callingPackageName, null, AudioManager.AUDIOFOCUS_FLAG_TEST,
sdk, false /*forceDuck*/, fakeUid);
}
@@ -8228,6 +8204,82 @@ public class AudioService extends IAudioService.Stub
}
//==========================================================================================
+ private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
+
+ /** @see AudioManager#getSpatializerImmersiveAudioLevel() */
+ public int getSpatializerImmersiveAudioLevel() {
+ return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+
+ /** @see Spatializer#isEnabled() */
+ public boolean isSpatializerEnabled() {
+ return false;
+ }
+
+ /** @see Spatializer#canBeSpatialized() */
+ public boolean canBeSpatialized(
+ @NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
+ Objects.requireNonNull(attributes);
+ Objects.requireNonNull(format);
+ return mSpatializerHelper.canBeSpatialized(attributes, format);
+ }
+
+ /** @see Spatializer.SpatializerInfoDispatcherStub */
+ public void registerSpatializerCallback(
+ @NonNull ISpatializerCallback dispatcher) {
+ Objects.requireNonNull(dispatcher);
+ mSpatializerHelper.registerStateCallback(dispatcher);
+ }
+
+ /** @see Spatializer.SpatializerInfoDispatcherStub */
+ public void unregisterSpatializerCallback(
+ @NonNull ISpatializerCallback dispatcher) {
+ Objects.requireNonNull(dispatcher);
+ mSpatializerHelper.unregisterStateCallback(dispatcher);
+ }
+
+ /** @see Spatializer#getSpatializerCompatibleAudioDevices() */
+ public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
+ enforceModifyAudioRoutingPermission();
+ return mSpatializerHelper.getCompatibleAudioDevices();
+ }
+
+ /** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
+ public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ enforceModifyAudioRoutingPermission();
+ Objects.requireNonNull(ada);
+ mSpatializerHelper.addCompatibleAudioDevice(ada);
+ }
+
+ /** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
+ public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ enforceModifyAudioRoutingPermission();
+ Objects.requireNonNull(ada);
+ mSpatializerHelper.removeCompatibleAudioDevice(ada);
+ }
+
+ /** @see AudioManager#setSpatializerFeatureEnabled(boolean) */
+ public void setSpatializerFeatureEnabled(boolean enabled) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
+ }
+ mSpatializerHelper.setEnabled(enabled);
+ }
+
+ /** @see Spatializer#setEnabledForDevice(boolean, AudioDeviceAttributes)
+ * @see Spatializer#setEnabled(boolean)
+ */
+ public void setSpatializerEnabledForDevice(boolean enabled,
+ @NonNull AudioDeviceAttributes ada) {
+ enforceModifyAudioRoutingPermission();
+ Objects.requireNonNull(ada);
+ mSpatializerHelper.setEnabledForDevice(enabled, ada);
+ }
+
+
+ //==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
mContext.getResources().getBoolean(
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index e6c4abfa2086..6fe129527b2d 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -857,6 +857,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* @param fd
* @param clientId
* @param callingPackageName
+ * @param attributionTag
* @param flags
* @param sdk
* @param forceDuck only true if
@@ -868,7 +869,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
*/
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
- int flags, int sdk, boolean forceDuck, int testUid) {
+ String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) {
new MediaMetrics.Item(mMetricsId)
.setUid(Binder.getCallingUid())
.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
@@ -903,7 +904,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
// note we're using the real uid for appOp evaluation
&& (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName) != AppOpsManager.MODE_ALLOWED)) {
+ callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
new file mode 100644
index 000000000000..1b68f840e298
--- /dev/null
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.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 com.android.server.audio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioFormat;
+import android.media.ISpatializerCallback;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class to manage Spatializer related functionality
+ */
+public class SpatializerHelper {
+
+ private static final String TAG = "AS.NotificationHelper";
+
+ //---------------------------------------------------------------
+ // audio device compatibility / enabled
+
+ private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+ private final ArrayList<AudioDeviceAttributes> mEnabledAudioDevices = new ArrayList<>(0);
+
+ /**
+ * @return a shallow copy of the list of compatible audio devices
+ */
+ synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
+ return (List<AudioDeviceAttributes>) mCompatibleAudioDevices.clone();
+ }
+
+ synchronized void addCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ if (!mCompatibleAudioDevices.contains(ada)) {
+ mCompatibleAudioDevices.add(ada);
+ }
+ // by default, adding a compatible device enables it
+ if (!mEnabledAudioDevices.contains(ada)) {
+ mEnabledAudioDevices.add(ada);
+ }
+ }
+
+ synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
+ mCompatibleAudioDevices.remove(ada);
+ mEnabledAudioDevices.remove(ada);
+ }
+
+ synchronized void setEnabledForDevice(boolean enabled, @NonNull AudioDeviceAttributes ada) {
+ if (enabled) {
+ if (!mEnabledAudioDevices.contains(ada)) {
+ mEnabledAudioDevices.add(ada);
+ }
+ } else {
+ mEnabledAudioDevices.remove(ada);
+ }
+ }
+
+ //------------------------------------------------------
+ // enabled state
+
+ // global state of feature
+ boolean mFeatureEnabled = false;
+
+ synchronized void setEnabled(boolean enabled) {
+ final boolean oldState = mFeatureEnabled;
+ mFeatureEnabled = enabled;
+ if (oldState != enabled) {
+ dispatchState();
+ }
+ }
+
+ final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
+ new RemoteCallbackList<ISpatializerCallback>();
+
+ synchronized void registerStateCallback(
+ @NonNull ISpatializerCallback callback) {
+ mStateCallbacks.register(callback);
+ }
+
+ synchronized void unregisterStateCallback(
+ @NonNull ISpatializerCallback callback) {
+ mStateCallbacks.unregister(callback);
+ }
+
+ private synchronized void dispatchState() {
+ // TODO check enabled state based on available devices
+ // (current implementation takes state as-is and dispatches it to listeners
+ final int nbCallbacks = mStateCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mStateCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerStateChanged(mFeatureEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerStateChanged", e);
+ }
+ }
+ mStateCallbacks.finishBroadcast();
+ }
+
+ //------------------------------------------------------
+ // virtualization capabilities
+ synchronized boolean canBeSpatialized(
+ @NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
+ // TODO hook up to spatializer effect for query
+ return false;
+ }
+}
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 183fabdc2a7b..85817be9746b 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
@@ -263,7 +263,7 @@ public class FingerprintService extends SystemService {
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
// Clear calling identity when checking LockPatternUtils for StrongAuth flags.
- long identity = Binder.clearCallingIdentity();
+ final long identity1 = Binder.clearCallingIdentity();
try {
if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -273,7 +273,7 @@ public class FingerprintService extends SystemService {
return;
}
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity1);
}
final boolean restricted = getContext().checkCallingPermission(MANAGE_FINGERPRINT)
@@ -297,11 +297,11 @@ public class FingerprintService extends SystemService {
provider.second.getSensorProperties(sensorId);
if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
&& sensorProps != null && sensorProps.isAnyUdfpsType()) {
- identity = Binder.clearCallingIdentity();
+ final long identity2 = Binder.clearCallingIdentity();
try {
authenticateWithPrompt(operationId, sensorProps, userId, receiver);
} finally {
- Binder.restoreCallingIdentity(identity);
+ Binder.restoreCallingIdentity(identity2);
}
} else {
provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 54a4ad42fd99..23f0ffbbf0b8 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -52,11 +52,11 @@ public class BroadcastRadioService extends SystemService {
public BroadcastRadioService(Context context) {
super(context);
- mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+ mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
mV1Modules = mHal1.loadModules();
OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
- max.isPresent() ? max.getAsInt() + 1 : 0);
+ max.isPresent() ? max.getAsInt() + 1 : 0, mLock);
}
@Override
@@ -111,7 +111,7 @@ public class BroadcastRadioService extends SystemService {
synchronized (mLock) {
if (!mHal2.hasAnyModules()) {
Slog.i(TAG, "There are no HAL 2.x modules registered");
- return new AnnouncementAggregator(listener);
+ return new AnnouncementAggregator(listener, mLock);
}
return mHal2.addAnnouncementListener(enabledTypes, listener);
diff --git a/services/core/java/com/android/server/broadcastradio/OWNERS b/services/core/java/com/android/server/broadcastradio/OWNERS
index ea4421eae96a..3e360e7e992c 100644
--- a/services/core/java/com/android/server/broadcastradio/OWNERS
+++ b/services/core/java/com/android/server/broadcastradio/OWNERS
@@ -1,2 +1,3 @@
+keunyoung@google.com
+oscarazu@google.com
twasilczyk@google.com
-randolphs@google.com
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index e8ac5477469b..5da60328cd70 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -17,16 +17,9 @@
package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.radio.IRadioService;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
-import android.os.ParcelableException;
-
-import com.android.server.SystemService;
import java.util.List;
import java.util.Objects;
@@ -37,7 +30,7 @@ public class BroadcastRadioService {
*/
private final long mNativeContext = nativeInit();
- private final Object mLock = new Object();
+ private final Object mLock;
@Override
protected void finalize() throws Throwable {
@@ -51,6 +44,14 @@ public class BroadcastRadioService {
private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
+ /**
+ * Constructor. should pass
+ * {@code com.android.server.broadcastradio.BroadcastRadioService#mLock} for lock.
+ */
+ public BroadcastRadioService(@NonNull Object lock) {
+ mLock = lock;
+ }
+
public @NonNull List<RadioManager.ModuleProperties> loadModules() {
synchronized (mLock) {
return Objects.requireNonNull(nativeLoadModules(mNativeContext));
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index f2e44ee132ed..e7118ad72bf4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -28,10 +28,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
class Tuner extends ITuner.Stub {
private static final String TAG = "BroadcastRadioService.Tuner";
@@ -292,12 +290,12 @@ class Tuner extends ITuner.Stub {
}
@Override
- public Map setParameters(Map parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
throw new UnsupportedOperationException("Not supported by HAL 1.x");
}
@Override
- public Map getParameters(List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
throw new UnsupportedOperationException("Not supported by HAL 1.x");
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 7a6d9647bf43..867d5b4b6f49 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -18,12 +18,10 @@ package com.android.server.broadcastradio.hal1;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
-import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
import android.os.IBinder;
import android.os.RemoteException;
@@ -31,7 +29,6 @@ import android.util.Slog;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@@ -176,7 +173,7 @@ class TunerCallback implements ITunerCallback {
}
@Override
- public void onParametersUpdated(Map parameters) {
+ public void onParametersUpdated(Map<String, String> parameters) {
Slog.e(TAG, "Not applicable for HAL 1.x");
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
index 53076975849b..42e296f3e4ec 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
@@ -35,7 +35,7 @@ import java.util.Objects;
public class AnnouncementAggregator extends ICloseHandle.Stub {
private static final String TAG = "BcRadio2Srv.AnnAggr";
- private final Object mLock = new Object();
+ private final Object mLock;
@NonNull private final IAnnouncementListener mListener;
private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();
@@ -45,8 +45,9 @@ public class AnnouncementAggregator extends ICloseHandle.Stub {
@GuardedBy("mLock")
private boolean mIsClosed = false;
- public AnnouncementAggregator(@NonNull IAnnouncementListener listener) {
+ public AnnouncementAggregator(@NonNull IAnnouncementListener listener, @NonNull Object lock) {
mListener = Objects.requireNonNull(listener);
+ mLock = Objects.requireNonNull(lock);
try {
listener.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5e79c5943d7b..5c07f76e5011 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -42,7 +42,7 @@ import java.util.stream.Collectors;
public class BroadcastRadioService {
private static final String TAG = "BcRadio2Srv";
- private final Object mLock = new Object();
+ private final Object mLock;
@GuardedBy("mLock")
private int mNextModuleId = 0;
@@ -68,7 +68,7 @@ public class BroadcastRadioService {
moduleId = mNextModuleId;
}
- RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName);
+ RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock);
if (module == null) {
return;
}
@@ -116,8 +116,9 @@ public class BroadcastRadioService {
}
};
- public BroadcastRadioService(int nextModuleId) {
+ public BroadcastRadioService(int nextModuleId, Object lock) {
mNextModuleId = nextModuleId;
+ mLock = lock;
try {
IServiceManager manager = IServiceManager.getService();
if (manager == null) {
@@ -174,7 +175,7 @@ public class BroadcastRadioService {
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull IAnnouncementListener listener) {
- AnnouncementAggregator aggregator = new AnnouncementAggregator(listener);
+ AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
boolean anySupported = false;
synchronized (mLock) {
for (RadioModule module : mModules.values()) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index b7e188c73eab..ef7f4c9fc919 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -58,7 +58,7 @@ class RadioModule {
@NonNull private final IBroadcastRadio mService;
@NonNull public final RadioManager.ModuleProperties mProperties;
- private final Object mLock = new Object();
+ private final Object mLock;
@NonNull private final Handler mHandler;
@GuardedBy("mLock")
@@ -132,13 +132,15 @@ class RadioModule {
@VisibleForTesting
RadioModule(@NonNull IBroadcastRadio service,
- @NonNull RadioManager.ModuleProperties properties) {
+ @NonNull RadioManager.ModuleProperties properties, @NonNull Object lock) {
mProperties = Objects.requireNonNull(properties);
mService = Objects.requireNonNull(service);
+ mLock = Objects.requireNonNull(lock);
mHandler = new Handler(Looper.getMainLooper());
}
- public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+ public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
+ Object lock) {
try {
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
if (service == null) return null;
@@ -156,7 +158,7 @@ class RadioModule {
RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
service.getProperties(), amfmConfig.value, dabConfig.value);
- return new RadioModule(service, prop);
+ return new RadioModule(service, prop, lock);
} catch (RemoteException ex) {
Slog.e(TAG, "failed to load module " + fqName, ex);
return null;
@@ -178,7 +180,8 @@ class RadioModule {
});
mHalTunerSession = Objects.requireNonNull(hwSession.value);
}
- TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
+ TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
+ mLock);
mAidlTunerSessions.add(tunerSession);
// Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -377,7 +380,7 @@ class RadioModule {
}
};
- synchronized (mService) {
+ synchronized (mLock) {
mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> {
halResult.value = result;
hwCloseHandle.value = closeHnd;
@@ -401,7 +404,7 @@ class RadioModule {
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
- synchronized (mService) {
+ synchronized (mLock) {
List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
rawImage = new byte[rawList.size()];
for (int i = 0; i < rawList.size(); i++) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 7ab3bdd859e4..d476fd60b3ee 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -40,7 +40,7 @@ class TunerSession extends ITuner.Stub {
private static final String TAG = "BcRadio2Srv.session";
private static final String kAudioDeviceName = "Radio tuner source";
- private final Object mLock = new Object();
+ private final Object mLock;
private final RadioModule mModule;
private final ITunerSession mHwSession;
@@ -53,10 +53,12 @@ class TunerSession extends ITuner.Stub {
private RadioManager.BandConfig mDummyConfig = null;
TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
- @NonNull android.hardware.radio.ITunerCallback callback) {
+ @NonNull android.hardware.radio.ITunerCallback callback,
+ @NonNull Object lock) {
mModule = Objects.requireNonNull(module);
mHwSession = Objects.requireNonNull(hwSession);
mCallback = Objects.requireNonNull(callback);
+ mLock = Objects.requireNonNull(lock);
}
@Override
@@ -299,7 +301,7 @@ class TunerSession extends ITuner.Stub {
}
@Override
- public Map setParameters(Map parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
synchronized (mLock) {
checkNotClosedLocked();
return Convert.vendorInfoFromHal(Utils.maybeRethrow(
@@ -308,7 +310,7 @@ class TunerSession extends ITuner.Stub {
}
@Override
- public Map getParameters(List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
synchronized (mLock) {
checkNotClosedLocked();
return Convert.vendorInfoFromHal(Utils.maybeRethrow(
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index b2d35f45ab44..6610e8c4392b 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -20,9 +20,14 @@ import static android.os.Build.VERSION_CODES.M;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.compat.CompatChanges;
import android.app.TaskStackListener;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -35,17 +40,21 @@ import android.hardware.CameraStreamStats;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManager;
import android.media.AudioManager;
import android.nfc.INfcAdapter;
import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.stats.camera.nano.CameraProtos.CameraStreamProto;
import android.util.ArrayMap;
@@ -57,8 +66,8 @@ import android.view.IDisplayWindowListener;
import android.view.Surface;
import android.view.WindowManagerGlobal;
-import com.android.internal.annotations.GuardedBy;
import com.android.framework.protobuf.nano.MessageNano;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -94,6 +103,95 @@ public class CameraServiceProxy extends SystemService
public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
+ /**
+ * When enabled this change id forces the packages it is applied to override the default
+ * camera rotate & crop behavior. The default behavior along with all possible override
+ * combinations is discussed in the table below.
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS = 189229956L; // buganizer id
+
+ /**
+ * When enabled this change id forces the packages it is applied to ignore the current value of
+ * 'android:resizeableActivity' as well as target SDK equal to or below M and consider the
+ * activity as non-resizeable. In this case, the value of camera rotate & crop will only depend
+ * on potential mismatches between the orientation of the camera and the fixed orientation of
+ * the activity. You can check the table below for further details on the possible override
+ * combinations.
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK = 191513214L; // buganizer id
+
+ /**
+ * This change id forces the packages it is applied to override the default camera rotate & crop
+ * behavior. Enabling it will set the crop & rotate parameter to
+ * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_90} and disabling it
+ * will reset the parameter to
+ * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_NONE} as long as camera
+ * clients include {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_AUTO}
+ * in their capture requests.
+ *
+ * This treatment only takes effect if OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS is also enabled.
+ * The table below includes further information about the possible override combinations.
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP = 190069291L; //buganizer id
+
+ /**
+ * Possible override combinations
+ *
+ * |OVERRIDE | |OVERRIDE_
+ * |CAMERA_ |OVERRIDE |CAMERA_
+ * |ROTATE_ |CAMERA_ |RESIZEABLE_
+ * |AND_CROP_ |ROTATE_ |AND_SDK_
+ * |DEFAULTS |AND_CROP |CHECK
+ * ______________________________________________
+ * Default | | |
+ * Behavior | D |D |D
+ * ______________________________________________
+ * Ignore | | |
+ * SDK&Resize | D |D |E
+ * ______________________________________________
+ * Default | | |
+ * Behavior | D |E |D
+ * ______________________________________________
+ * Ignore | | |
+ * SDK&Resize | D |E |E
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * disabled | E |D |D
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * disabled | E |D |E
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * enabled | E |E |D
+ * ______________________________________________
+ * Rotate&Crop| | |
+ * enabled | E |E |E
+ * ______________________________________________
+ * Where:
+ * E -> Override enabled
+ * D -> Override disabled
+ * Default behavior -> Rotate&crop will be enabled only in cases
+ * where the fixed app orientation mismatches
+ * with the orientation of the camera.
+ * Additionally the app must either target M (or below)
+ * or is declared as non-resizeable.
+ * Ignore SDK&Resize -> Rotate&crop will be enabled only in cases
+ * where the fixed app orientation mismatches
+ * with the orientation of the camera.
+ */
+
// Flags arguments to NFC adapter to enable/disable NFC
public static final int DISABLE_POLLING_FLAGS = 0x1000;
public static final int ENABLE_POLLING_FLAGS = 0x0000;
@@ -254,6 +352,7 @@ public class CameraServiceProxy extends SystemService
private boolean isFixedOrientationLandscape;
private boolean isFixedOrientationPortrait;
private int displayId;
+ private int userId;
}
private final class TaskStateHandler extends TaskStackListener {
@@ -270,6 +369,7 @@ public class CameraServiceProxy extends SystemService
info.frontTaskId = taskInfo.taskId;
info.isResizeable = taskInfo.isResizeable;
info.displayId = taskInfo.displayId;
+ info.userId = taskInfo.userId;
info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
taskInfo.topActivityInfo.screenOrientation);
info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
@@ -300,7 +400,7 @@ public class CameraServiceProxy extends SystemService
Log.e(TAG, "Top task with package name: " + packageName + " not found!");
return null;
}
- };
+ }
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -342,7 +442,8 @@ public class CameraServiceProxy extends SystemService
* Gets whether crop-rotate-scale is needed.
*/
private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
- @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
+ @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing,
+ boolean ignoreResizableAndSdkCheck) {
if (taskInfo == null) {
return false;
}
@@ -354,9 +455,11 @@ public class CameraServiceProxy extends SystemService
return false;
}
- // Only enable the crop-rotate-scale workaround if the app targets M or below and is not
+ // In case the activity behavior is not explicitly overridden, enable the
+ // crop-rotate-scale workaround if the app targets M (or below) or is not
// resizeable.
- if (!isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
+ if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
+ taskInfo.isResizeable) {
Slog.v(TAG,
"The activity is N or above and claims to support resizeable-activity. "
+ "Crop-rotate-scale is disabled.");
@@ -364,22 +467,32 @@ public class CameraServiceProxy extends SystemService
}
DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(taskInfo.displayId);
- int rotation = display.getRotation();
int rotationDegree = 0;
- switch (rotation) {
- case Surface.ROTATION_0:
- rotationDegree = 0;
- break;
- case Surface.ROTATION_90:
- rotationDegree = 90;
- break;
- case Surface.ROTATION_180:
- rotationDegree = 180;
- break;
- case Surface.ROTATION_270:
- rotationDegree = 270;
- break;
+ if (displayManager != null) {
+ Display display = displayManager.getDisplay(taskInfo.displayId);
+ if (display == null) {
+ Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
+ return false;
+ }
+
+ int rotation = display.getRotation();
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ rotationDegree = 0;
+ break;
+ case Surface.ROTATION_90:
+ rotationDegree = 90;
+ break;
+ case Surface.ROTATION_180:
+ rotationDegree = 180;
+ break;
+ case Surface.ROTATION_270:
+ rotationDegree = 270;
+ break;
+ }
+ } else {
+ Slog.e(TAG, "Failed to query display manager!");
+ return false;
}
// Here we only need to know whether the camera is landscape or portrait. Therefore we
@@ -411,9 +524,28 @@ public class CameraServiceProxy extends SystemService
// regions in capture requests/results to account for thea physical rotation. The
// former is somewhat tricky as it assumes that camera clients always check for the
// current value by retrieving the camera characteristics from the camera device.
- return getNeedCropRotateScale(mContext, packageName,
- mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
- lensFacing);
+ TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
+ if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
+ OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
+ UserHandle.getUserHandleForUid(taskInfo.userId)))) {
+ if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
+ UserHandle.getUserHandleForUid(taskInfo.userId))) {
+ Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
+ return true;
+ } else {
+ Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
+ return false;
+ }
+ }
+ boolean ignoreResizableAndSdkCheck = false;
+ if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
+ OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK, packageName,
+ UserHandle.getUserHandleForUid(taskInfo.userId)))) {
+ Slog.v(TAG, "OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK enabled!");
+ ignoreResizableAndSdkCheck = true;
+ }
+ return getNeedCropRotateScale(mContext, packageName, taskInfo, sensorOrientation,
+ lensFacing, ignoreResizableAndSdkCheck);
}
@Override
@@ -447,6 +579,8 @@ public class CameraServiceProxy extends SystemService
}
};
+ private final FoldStateListener mFoldStateListener;
+
public CameraServiceProxy(Context context) {
super(context);
mContext = context;
@@ -459,6 +593,14 @@ public class CameraServiceProxy extends SystemService
// Don't keep any extra logging threads if not needed
mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS);
mLogWriterService.allowCoreThreadTimeOut(true);
+
+ mFoldStateListener = new FoldStateListener(mContext, folded -> {
+ if (folded) {
+ setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ } else {
+ clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+ }
+ });
}
/**
@@ -471,7 +613,7 @@ public class CameraServiceProxy extends SystemService
*
* @see #clearDeviceStateFlags(int)
*/
- public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ private void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
synchronized (mLock) {
mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
mDeviceState |= deviceStateFlags;
@@ -491,7 +633,7 @@ public class CameraServiceProxy extends SystemService
*
* @see #setDeviceStateFlags(int)
*/
- public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+ private void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
synchronized (mLock) {
mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
mDeviceState &= ~deviceStateFlags;
@@ -555,6 +697,9 @@ public class CameraServiceProxy extends SystemService
} catch (RemoteException e) {
Log.e(TAG, "Failed to register display window listener!");
}
+
+ mContext.getSystemService(DeviceStateManager.class)
+ .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
}
}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
new file mode 100644
index 000000000000..a81213df6fe3
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesParser.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A utility class for parsing App Compat Overrides flags.
+ *
+ * @hide
+ */
+final class AppCompatOverridesParser {
+ /**
+ * Flag for specifying all compat change IDs owned by a namespace. See {@link
+ * #parseOwnedChangeIds} for information on how this flag is parsed.
+ */
+ static final String FLAG_OWNED_CHANGE_IDS = "owned_change_ids";
+
+ /**
+ * Flag for immediately removing overrides for certain packages and change IDs (from the compat
+ * platform), as well as stopping to apply them, in case of an emergency. See {@link
+ * #parseRemoveOverrides} for information on how this flag is parsed.
+ */
+ static final String FLAG_REMOVE_OVERRIDES = "remove_overrides";
+
+ private static final String TAG = "AppCompatOverridesParser";
+
+ private static final String WILDCARD_SYMBOL = "*";
+
+ private static final Pattern BOOLEAN_PATTERN =
+ Pattern.compile("true|false", Pattern.CASE_INSENSITIVE);
+
+ private static final String WILDCARD_NO_OWNED_CHANGE_IDS_WARNING =
+ "Wildcard can't be used in '" + FLAG_REMOVE_OVERRIDES + "' flag with an empty "
+ + FLAG_OWNED_CHANGE_IDS + "' flag";
+
+ private final PackageManager mPackageManager;
+
+ AppCompatOverridesParser(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Parses the given {@code configStr} and returns a map from package name to a set of change
+ * IDs to remove for that package.
+ *
+ * <p>The given {@code configStr} is expected to either be:
+ *
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds}, for all installed packages should be removed.
+ * <li>A comma separated key value list, where the key is a package name and the value is
+ * either:
+ * <ul>
+ * <li>'*' (wildcard), to indicate that all owned overrides, specified in {@code
+ * ownedChangeIds} for that package should be removed.
+ * <li>A colon separated list of change IDs to remove for that package.
+ * </ul>
+ * </ul>
+ *
+ * <p>If the given {@code configStr} doesn't match the expected format, an empty map will be
+ * returned. If a specific change ID isn't a valid long, it will be ignored.
+ */
+ Map<String, Set<Long>> parseRemoveOverrides(String configStr, Set<Long> ownedChangeIds) {
+ if (configStr.isEmpty()) {
+ return emptyMap();
+ }
+
+ Map<String, Set<Long>> result = new ArrayMap<>();
+ if (configStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ return emptyMap();
+ }
+ List<ApplicationInfo> installedApps = mPackageManager.getInstalledApplications(
+ MATCH_ANY_USER);
+ for (ApplicationInfo appInfo : installedApps) {
+ result.put(appInfo.packageName, ownedChangeIds);
+ }
+ return result;
+ }
+
+ KeyValueListParser parser = new KeyValueListParser(',');
+ try {
+ parser.setString(configStr);
+ } catch (IllegalArgumentException e) {
+ Slog.w(
+ TAG,
+ "Invalid format in '" + FLAG_REMOVE_OVERRIDES + "' flag: " + configStr, e);
+ return emptyMap();
+ }
+ for (int i = 0; i < parser.size(); i++) {
+ String packageName = parser.keyAt(i);
+ String changeIdsStr = parser.getString(packageName, /* def= */ "");
+ if (changeIdsStr.equals(WILDCARD_SYMBOL)) {
+ if (ownedChangeIds.isEmpty()) {
+ Slog.w(TAG, WILDCARD_NO_OWNED_CHANGE_IDS_WARNING);
+ continue;
+ }
+ result.put(packageName, ownedChangeIds);
+ } else {
+ for (String changeIdStr : changeIdsStr.split(":")) {
+ try {
+ long changeId = Long.parseLong(changeIdStr);
+ result.computeIfAbsent(packageName, k -> new ArraySet<>()).add(changeId);
+ } catch (NumberFormatException e) {
+ Slog.w(
+ TAG,
+ "Invalid change ID in '" + FLAG_REMOVE_OVERRIDES + "' flag: "
+ + changeIdStr, e);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of change
+ * IDs, into a set.
+ *
+ * <p>If any of the change IDs isn't a valid long, it will be ignored.
+ */
+ static Set<Long> parseOwnedChangeIds(String configStr) {
+ if (configStr.isEmpty()) {
+ return emptySet();
+ }
+
+ Set<Long> result = new ArraySet<>();
+ for (String changeIdStr : configStr.split(",")) {
+ try {
+ result.add(Long.parseLong(changeIdStr));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid change ID in '" + FLAG_OWNED_CHANGE_IDS + "' flag: " + changeIdStr,
+ e);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Parses the given {@code configStr}, that is expected to be a comma separated list of changes
+ * overrides, and returns a {@link PackageOverrides}.
+ *
+ * <p>Each change override is in the following format:
+ * '<change-id>:<min-version-code?>:<max-version-code?>:<enabled?>'. If <enabled> is empty,
+ * this indicates that any override for the specified change ID should be removed.
+ *
+ * <p>If there are multiple overrides that should be added with the same change ID, the one
+ * that best fits the given {@code versionCode} is added.
+ *
+ * <p>Any overrides whose change ID is in {@code changeIdsToSkip} are ignored.
+ *
+ * <p>If a change override entry in {@code configStr} is invalid, it will be ignored. If the
+ * same change ID is both added and removed, i.e., has a change override entry with an empty
+ * enabled and another with a non-empty enabled, the change ID will only be removed.
+ */
+ static PackageOverrides parsePackageOverrides(
+ String configStr, long versionCode, Set<Long> changeIdsToSkip) {
+ if (configStr.isEmpty()) {
+ return new PackageOverrides();
+ }
+ PackageOverrideComparator comparator = new PackageOverrideComparator(versionCode);
+ Map<Long, PackageOverride> overridesToAdd = new ArrayMap<>();
+ Set<Long> overridesToRemove = new ArraySet<>();
+ for (String overrideEntryString : configStr.split(",")) {
+ List<String> changeIdAndVersions = Arrays.asList(overrideEntryString.split(":", 4));
+ if (changeIdAndVersions.size() != 4) {
+ Slog.w(TAG, "Invalid change override entry: " + overrideEntryString);
+ continue;
+ }
+ long changeId;
+ try {
+ changeId = Long.parseLong(changeIdAndVersions.get(0));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid change ID in override entry: " + overrideEntryString, e);
+ continue;
+ }
+
+ if (changeIdsToSkip.contains(changeId)) {
+ continue;
+ }
+
+ String minVersionCodeStr = changeIdAndVersions.get(1);
+ String maxVersionCodeStr = changeIdAndVersions.get(2);
+
+ String enabledStr = changeIdAndVersions.get(3);
+ if (enabledStr.isEmpty()) {
+ if (!minVersionCodeStr.isEmpty() || !maxVersionCodeStr.isEmpty()) {
+ Slog.w(
+ TAG,
+ "min/max version code should be empty if enabled is empty: "
+ + overrideEntryString);
+ }
+ overridesToRemove.add(changeId);
+ continue;
+ }
+ if (!BOOLEAN_PATTERN.matcher(enabledStr).matches()) {
+ Slog.w(TAG, "Invalid enabled string in override entry: " + overrideEntryString);
+ continue;
+ }
+ boolean enabled = Boolean.parseBoolean(enabledStr);
+ PackageOverride.Builder overrideBuilder = new PackageOverride.Builder().setEnabled(
+ enabled);
+ try {
+ if (!minVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMinVersionCode(Long.parseLong(minVersionCodeStr));
+ }
+ if (!maxVersionCodeStr.isEmpty()) {
+ overrideBuilder.setMaxVersionCode(Long.parseLong(maxVersionCodeStr));
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG,
+ "Invalid min/max version code in override entry: " + overrideEntryString,
+ e);
+ continue;
+ }
+
+ try {
+ PackageOverride override = overrideBuilder.build();
+ if (!overridesToAdd.containsKey(changeId)
+ || comparator.compare(override, overridesToAdd.get(changeId)) < 0) {
+ overridesToAdd.put(changeId, override);
+ }
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed to build PackageOverride", e);
+ }
+ }
+
+ for (Long changeId : overridesToRemove) {
+ if (overridesToAdd.containsKey(changeId)) {
+ Slog.w(
+ TAG,
+ "Change ID ["
+ + changeId
+ + "] is both added and removed in package override flag: "
+ + configStr);
+ overridesToAdd.remove(changeId);
+ }
+ }
+
+ return new PackageOverrides(overridesToAdd, overridesToRemove);
+ }
+
+ /**
+ * A container for a map from change ID to {@link PackageOverride} to add and a set of change
+ * IDs to remove overrides for.
+ *
+ * <p>The map of overrides to add and the set of overrides to remove are mutually exclusive.
+ */
+ static final class PackageOverrides {
+ public final Map<Long, PackageOverride> overridesToAdd;
+ public final Set<Long> overridesToRemove;
+
+ PackageOverrides() {
+ this(emptyMap(), emptySet());
+ }
+
+ PackageOverrides(Map<Long, PackageOverride> overridesToAdd, Set<Long> overridesToRemove) {
+ this.overridesToAdd = overridesToAdd;
+ this.overridesToRemove = overridesToRemove;
+ }
+ }
+
+ /**
+ * A {@link Comparator} that compares @link PackageOverride} instances with respect to a
+ * specified {@code versionCode} as follows:
+ *
+ * <ul>
+ * <li>Prefer the {@link PackageOverride} whose version range contains {@code versionCode}.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from below.
+ * <li>Otherwise, prefer the {@link PackageOverride} whose version range is closest to {@code
+ * versionCode} from above.
+ * </ul>
+ */
+ private static final class PackageOverrideComparator implements Comparator<PackageOverride> {
+ private final long mVersionCode;
+
+ PackageOverrideComparator(long versionCode) {
+ this.mVersionCode = versionCode;
+ }
+
+ @Override
+ public int compare(PackageOverride o1, PackageOverride o2) {
+ // Prefer overrides whose version range contains versionCode.
+ boolean isVersionInRange1 = isVersionInRange(o1, mVersionCode);
+ boolean isVersionInRange2 = isVersionInRange(o2, mVersionCode);
+ if (isVersionInRange1 != isVersionInRange2) {
+ return isVersionInRange1 ? -1 : 1;
+ }
+
+ // Otherwise, prefer overrides whose version range is before versionCode.
+ boolean isVersionAfterRange1 = isVersionAfterRange(o1, mVersionCode);
+ boolean isVersionAfterRange2 = isVersionAfterRange(o2, mVersionCode);
+ if (isVersionAfterRange1 != isVersionAfterRange2) {
+ return isVersionAfterRange1 ? -1 : 1;
+ }
+
+ // If both overrides' version ranges are either before or after versionCode, prefer
+ // those whose version range is closer to versionCode.
+ return Long.compare(
+ getVersionProximity(o1, mVersionCode), getVersionProximity(o2, mVersionCode));
+ }
+
+ /**
+ * Returns true if the version range in the given {@code override} contains {@code
+ * versionCode}.
+ */
+ private static boolean isVersionInRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() <= versionCode
+ && versionCode <= override.getMaxVersionCode();
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly after the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionAfterRange(PackageOverride override, long versionCode) {
+ return override.getMaxVersionCode() < versionCode;
+ }
+
+ /**
+ * Returns true if the given {@code versionCode} is strictly before the version range in the
+ * given {@code override}.
+ */
+ private static boolean isVersionBeforeRange(PackageOverride override, long versionCode) {
+ return override.getMinVersionCode() > versionCode;
+ }
+
+ /**
+ * In case the given {@code versionCode} is strictly before or after the version range in
+ * the given {@code override}, returns the distance from it, otherwise returns zero.
+ */
+ private static long getVersionProximity(PackageOverride override, long versionCode) {
+ if (isVersionAfterRange(override, versionCode)) {
+ return versionCode - override.getMaxVersionCode();
+ }
+ if (isVersionBeforeRange(override, versionCode)) {
+ return override.getMinVersionCode() - versionCode;
+ }
+
+ // Version is in range. Note that when two overrides have a zero version proximity
+ // they will be ordered arbitrarily.
+ return 0;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
new file mode 100644
index 000000000000..63ae1af42f08
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/AppCompatOverridesService.java
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.provider.DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES;
+
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+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.net.Uri;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.SystemService;
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service for applying per-app compat overrides delivered via Device Config.
+ *
+ * <p>The service listens both on changes to supported Device Config namespaces and on package
+ * added/changed/removed events, and applies overrides accordingly.
+ *
+ * @hide
+ */
+public final class AppCompatOverridesService {
+ private static final String TAG = "AppCompatOverridesService";
+
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(
+ NAMESPACE_APP_COMPAT_OVERRIDES);
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+ private final IPlatformCompat mPlatformCompat;
+ private final List<String> mSupportedNamespaces;
+ private final AppCompatOverridesParser mOverridesParser;
+ private final PackageReceiver mPackageReceiver;
+ private final List<DeviceConfigListener> mDeviceConfigListeners;
+
+ private AppCompatOverridesService(Context context) {
+ this(context, IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)), SUPPORTED_NAMESPACES);
+ }
+
+ @VisibleForTesting
+ AppCompatOverridesService(Context context, IPlatformCompat platformCompat,
+ List<String> supportedNamespaces) {
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mPlatformCompat = platformCompat;
+ mSupportedNamespaces = supportedNamespaces;
+ mOverridesParser = new AppCompatOverridesParser(mPackageManager);
+ mPackageReceiver = new PackageReceiver(mContext);
+ mDeviceConfigListeners = new ArrayList<>();
+ for (String namespace : mSupportedNamespaces) {
+ mDeviceConfigListeners.add(new DeviceConfigListener(mContext, namespace));
+ }
+ }
+
+ @Override
+ public void finalize() {
+ unregisterDeviceConfigListeners();
+ unregisterPackageReceiver();
+ }
+
+ @VisibleForTesting
+ void registerDeviceConfigListeners() {
+ for (DeviceConfigListener listener : mDeviceConfigListeners) {
+ listener.register();
+ }
+ }
+
+ private void unregisterDeviceConfigListeners() {
+ for (DeviceConfigListener listener : mDeviceConfigListeners) {
+ listener.unregister();
+ }
+ }
+
+ @VisibleForTesting
+ void registerPackageReceiver() {
+ mPackageReceiver.register();
+ }
+
+ private void unregisterPackageReceiver() {
+ mPackageReceiver.unregister();
+ }
+
+ /**
+ * Same as {@link #applyOverrides(Properties, Map)} except all properties of the given {@code
+ * namespace} are fetched via {@link DeviceConfig#getProperties}.
+ */
+ private void applyAllOverrides(String namespace,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ applyOverrides(DeviceConfig.getProperties(namespace), packageToChangeIdsToSkip);
+ }
+
+ /**
+ * Iterates all package override flags in the given {@code properties}, and for each flag whose
+ * package is installed on the device, parses its value and applies the overrides in it with
+ * respect to the package's current installed version.
+ */
+ private void applyOverrides(Properties properties,
+ Map<String, Set<Long>> packageToChangeIdsToSkip) {
+ Set<String> packageNames = new ArraySet<>(properties.getKeyset());
+ packageNames.remove(FLAG_OWNED_CHANGE_IDS);
+ packageNames.remove(FLAG_REMOVE_OVERRIDES);
+ for (String packageName : packageNames) {
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode == null) {
+ // Package isn't installed yet.
+ continue;
+ }
+
+ applyPackageOverrides(properties.getString(packageName, /* defaultValue= */ ""),
+ packageName, versionCode,
+ packageToChangeIdsToSkip.getOrDefault(packageName, emptySet()));
+ }
+ }
+
+ /**
+ * Applies all overrides in all supported namespaces for the given {@code packageName}.
+ */
+ private void applyAllPackageOverrides(String packageName) {
+ Long versionCode = getVersionCodeOrNull(packageName);
+ if (versionCode == null) {
+ return;
+ }
+
+ for (String namespace : mSupportedNamespaces) {
+ // We apply overrides for each namespace separately so that if there is a failure for
+ // one namespace, the other namespaces won't be affected.
+ applyPackageOverrides(
+ DeviceConfig.getString(namespace, packageName, /* defaultValue= */ ""),
+ packageName, versionCode,
+ getOverridesToRemove(namespace).getOrDefault(packageName, emptySet()));
+ }
+ }
+
+ /**
+ * Calls {@link AppCompatOverridesParser#parsePackageOverrides} on the given arguments, adds the
+ * resulting {@link PackageOverrides#overridesToAdd} via {@link
+ * IPlatformCompat#putOverridesOnReleaseBuilds}, and removes the resulting {@link
+ * PackageOverrides#overridesToRemove} via {@link
+ * IPlatformCompat#removeOverridesOnReleaseBuilds}.
+ */
+ private void applyPackageOverrides(String configStr, String packageName,
+ long versionCode, Set<Long> changeIdsToSkip) {
+ PackageOverrides packageOverrides = AppCompatOverridesParser.parsePackageOverrides(
+ configStr, versionCode, changeIdsToSkip);
+ putPackageOverrides(packageName, packageOverrides.overridesToAdd);
+ removePackageOverrides(packageName, packageOverrides.overridesToRemove);
+ }
+
+ /**
+ * Removes all owned overrides in all supported namespaces for the given {@code packageName}.
+ *
+ * <p>If a certain namespace doesn't have a package override flag for the given {@code
+ * packageName}, that namespace is skipped.</p>
+ */
+ private void removeAllPackageOverrides(String packageName) {
+ for (String namespace : mSupportedNamespaces) {
+ if (DeviceConfig.getString(namespace, packageName, /* defaultValue= */ "").isEmpty()) {
+ // No overrides for this package in this namespace.
+ continue;
+ }
+ // We remove overrides for each namespace separately so that if there is a failure for
+ // one namespace, the other namespaces won't be affected.
+ removePackageOverrides(packageName, getOwnedChangeIds(namespace));
+ }
+ }
+
+ /**
+ * Calls {@link IPlatformCompat#removeOverridesOnReleaseBuilds} on each package name and
+ * respective change IDs in {@code overridesToRemove}.
+ */
+ private void removeOverrides(Map<String, Set<Long>> overridesToRemove) {
+ for (Map.Entry<String, Set<Long>> packageNameAndOverrides : overridesToRemove.entrySet()) {
+ removePackageOverrides(packageNameAndOverrides.getKey(),
+ packageNameAndOverrides.getValue());
+ }
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_REMOVE_OVERRIDES} for the given
+ * {@code namespace} and parses it into a map from package name to a set of change IDs to
+ * remove for that package.
+ */
+ private Map<String, Set<Long>> getOverridesToRemove(String namespace) {
+ return mOverridesParser.parseRemoveOverrides(
+ DeviceConfig.getString(namespace, FLAG_REMOVE_OVERRIDES, /* defaultValue= */ ""),
+ getOwnedChangeIds(namespace));
+ }
+
+ /**
+ * Fetches the value of {@link AppCompatOverridesParser#FLAG_OWNED_CHANGE_IDS} for the given
+ * {@code namespace} and parses it into a set of change IDs.
+ */
+ private static Set<Long> getOwnedChangeIds(String namespace) {
+ return AppCompatOverridesParser.parseOwnedChangeIds(
+ DeviceConfig.getString(namespace, FLAG_OWNED_CHANGE_IDS, /* defaultValue= */ ""));
+ }
+
+ private void putPackageOverrides(String packageName,
+ Map<Long, PackageOverride> overridesToAdd) {
+ if (overridesToAdd.isEmpty()) {
+ return;
+ }
+ CompatibilityOverrideConfig config = new CompatibilityOverrideConfig(overridesToAdd);
+ try {
+ mPlatformCompat.putOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#putOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ private void removePackageOverrides(String packageName, Set<Long> overridesToRemove) {
+ if (overridesToRemove.isEmpty()) {
+ return;
+ }
+ CompatibilityOverridesToRemoveConfig config = new CompatibilityOverridesToRemoveConfig(
+ overridesToRemove);
+ try {
+ mPlatformCompat.removeOverridesOnReleaseBuilds(config, packageName);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call IPlatformCompat#removeOverridesOnReleaseBuilds", e);
+ }
+ }
+
+ private boolean isInstalledForAnyUser(String packageName) {
+ return getVersionCodeOrNull(packageName) != null;
+ }
+
+ @Nullable
+ private Long getVersionCodeOrNull(String packageName) {
+ try {
+ ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName,
+ MATCH_ANY_USER);
+ return applicationInfo.longVersionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Package isn't installed for any user.
+ return null;
+ }
+ }
+
+ /**
+ * SystemService lifecycle for AppCompatOverridesService.
+ *
+ * @hide
+ */
+ public static final class Lifecycle extends SystemService {
+ private AppCompatOverridesService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new AppCompatOverridesService(getContext());
+ mService.registerDeviceConfigListeners();
+ mService.registerPackageReceiver();
+ }
+ }
+
+ /**
+ * A {@link DeviceConfig.OnPropertiesChangedListener} that listens on changes to a given
+ * namespace and adds/removes overrides according to the changed flags.
+ */
+ private final class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
+ private final Context mContext;
+ private final String mNamespace;
+
+ private DeviceConfigListener(Context context, String namespace) {
+ mContext = context;
+ mNamespace = namespace;
+ }
+
+ private void register() {
+ DeviceConfig.addOnPropertiesChangedListener(mNamespace, mContext.getMainExecutor(),
+ this);
+ }
+
+ private void unregister() {
+ DeviceConfig.removeOnPropertiesChangedListener(this);
+ }
+
+ @Override
+ public void onPropertiesChanged(Properties properties) {
+ boolean removeOverridesFlagChanged = properties.getKeyset().contains(
+ FLAG_REMOVE_OVERRIDES);
+ boolean ownedChangedIdsFlagChanged = properties.getKeyset().contains(
+ FLAG_OWNED_CHANGE_IDS);
+
+ Map<String, Set<Long>> overridesToRemove = getOverridesToRemove(mNamespace);
+ if (removeOverridesFlagChanged || ownedChangedIdsFlagChanged) {
+ // In both cases it's possible that overrides that weren't removed before should
+ // now be removed.
+ removeOverrides(overridesToRemove);
+ }
+
+ if (removeOverridesFlagChanged) {
+ // We need to re-apply all overrides in the namespace since the remove overrides
+ // flag might have blocked some of them from being applied before.
+ applyAllOverrides(mNamespace, overridesToRemove);
+ } else {
+ applyOverrides(properties, overridesToRemove);
+ }
+ }
+ }
+
+ /**
+ * A {@link BroadcastReceiver} that listens on package added/changed/removed events and
+ * adds/removes overrides according to the corresponding Device Config flags.
+ */
+ private final class PackageReceiver extends BroadcastReceiver {
+ private final Context mContext;
+ private final IntentFilter mIntentFilter;
+
+ private PackageReceiver(Context context) {
+ mContext = context;
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(ACTION_PACKAGE_ADDED);
+ mIntentFilter.addAction(ACTION_PACKAGE_CHANGED);
+ mIntentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ mIntentFilter.addDataScheme("package");
+ }
+
+ private void register() {
+ mContext.registerReceiverForAllUsers(this, mIntentFilter, /* broadcastPermission= */
+ null, /* scheduler= */ null);
+ }
+
+ private void unregister() {
+ mContext.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+ Uri data = intent.getData();
+ if (data == null) {
+ Slog.w(TAG, "Failed to get package name in package receiver");
+ return;
+ }
+ String packageName = data.getSchemeSpecificPart();
+ String action = intent.getAction();
+ if (action == null) {
+ Slog.w(TAG, "Failed to get action in package receiver");
+ return;
+ }
+ switch (action) {
+ case ACTION_PACKAGE_ADDED:
+ case ACTION_PACKAGE_CHANGED:
+ applyAllPackageOverrides(packageName);
+ break;
+ case ACTION_PACKAGE_REMOVED:
+ if (!isInstalledForAnyUser(packageName)) {
+ removeAllPackageOverrides(packageName);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unsupported action in package receiver: " + action);
+ break;
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 000000000000..b80f3402c19d
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1,2 @@
+tomnatan@google.com
+mariiasand@google.com
diff --git a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
new file mode 100644
index 000000000000..4b8f08ec9164
--- /dev/null
+++ b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.compat.overrides"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1acbde91b353..565c9ae2142c 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -24,8 +24,7 @@ import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
import android.Manifest;
import android.annotation.NonNull;
@@ -1098,13 +1097,14 @@ public class Vpn {
return Process.myUid();
}
PackageManager pm = mContext.getPackageManager();
- return Binder.withCleanCallingIdentity(() -> {
- try {
- return pm.getPackageUidAsUser(app, userId);
- } catch (NameNotFoundException e) {
- return -1;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUidAsUser(app, userId);
+ } catch (NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private boolean doesPackageTargetAtLeastQ(String packageName) {
@@ -1280,15 +1280,16 @@ public class Vpn {
// We are user controlled, not driven by NetworkRequest.
}
};
- Binder.withCleanCallingIdentity(() -> {
- try {
- mNetworkAgent.register();
- } catch (final Exception e) {
- // If register() throws, don't keep an unregistered agent.
- mNetworkAgent = null;
- throw e;
- }
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkAgent.register();
+ } catch (final Exception e) {
+ // If register() throws, don't keep an unregistered agent.
+ mNetworkAgent = null;
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
? Arrays.asList(mConfig.underlyingNetworks) : null);
updateState(DetailedState.CONNECTED, "agentConnect");
@@ -2026,13 +2027,16 @@ public class Vpn {
}
private void enforceNotRestrictedUser() {
- Binder.withCleanCallingIdentity(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
}
- });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -2825,8 +2829,10 @@ public class Vpn {
LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
super(TAG);
- checkArgument(racoon != null || mtpd != null, "Arguments to racoon and mtpd "
- + "must not both be null");
+ if (racoon == null && mtpd == null) {
+ throw new IllegalArgumentException(
+ "Arguments to racoon and mtpd must not both be null");
+ }
mConfig = config;
mDaemons = new String[] {"racoon", "mtpd"};
// TODO: clear arguments from memory once launched
@@ -3151,8 +3157,8 @@ public class Vpn {
*/
public synchronized boolean provisionVpnProfile(
@NonNull String packageName, @NonNull VpnProfile profile) {
- checkNotNull(packageName, "No package name provided");
- checkNotNull(profile, "No profile provided");
+ requireNonNull(packageName, "No package name provided");
+ requireNonNull(profile, "No profile provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
@@ -3169,12 +3175,12 @@ public class Vpn {
}
// Permissions checked during startVpnProfile()
- Binder.withCleanCallingIdentity(
- () -> {
- getVpnProfileStore().put(
- getProfileNameForPackage(packageName),
- encodedProfile);
- });
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getVpnProfileStore().put(getProfileNameForPackage(packageName), encodedProfile);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
// TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop.
// This mirrors the prepareAndAuthorize that is used by VpnService.
@@ -3194,26 +3200,28 @@ public class Vpn {
*/
public synchronized void deleteVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
verifyCallingUidAndPackage(packageName);
enforceNotRestrictedUser();
- Binder.withCleanCallingIdentity(
- () -> {
- // If this profile is providing the current VPN, turn it off, disabling
- // always-on as well if enabled.
- if (isCurrentIkev2VpnLocked(packageName)) {
- if (mAlwaysOn) {
- // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
- setAlwaysOnPackage(null, false, null);
- } else {
- prepareInternal(VpnConfig.LEGACY_VPN);
- }
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
- getVpnProfileStore().remove(getProfileNameForPackage(packageName));
- });
+ getVpnProfileStore().remove(getProfileNameForPackage(packageName));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3247,7 +3255,7 @@ public class Vpn {
*/
public synchronized void startVpnProfile(
@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
@@ -3256,15 +3264,17 @@ public class Vpn {
throw new SecurityException("User consent not granted for package " + packageName);
}
- Binder.withCleanCallingIdentity(
- () -> {
- final VpnProfile profile = getVpnProfilePrivileged(packageName);
- if (profile == null) {
- throw new IllegalArgumentException("No profile found for " + packageName);
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final VpnProfile profile = getVpnProfilePrivileged(packageName);
+ if (profile == null) {
+ throw new IllegalArgumentException("No profile found for " + packageName);
+ }
- startVpnProfilePrivileged(profile, packageName);
- });
+ startVpnProfilePrivileged(profile, packageName);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
private synchronized void startVpnProfilePrivileged(
@@ -3325,7 +3335,7 @@ public class Vpn {
* @param packageName the package name of the app provisioning this profile
*/
public synchronized void stopVpnProfile(@NonNull String packageName) {
- checkNotNull(packageName, "No package name provided");
+ requireNonNull(packageName, "No package name provided");
enforceNotRestrictedUser();
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c3c42bae55b0..fadcce9ee9b3 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -23,6 +23,7 @@ import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
@@ -823,6 +824,21 @@ public final class ContentService extends IContentService.Stub {
}
@Override
+ public String getSyncAdapterPackageAsUser(@NonNull String accountType,
+ @NonNull String authority, @UserIdInt int userId) {
+ enforceCrossUserPermission(userId,
+ "no permission to read sync settings for user " + userId);
+ final int callingUid = Binder.getCallingUid();
+ final long identityToken = clearCallingIdentity();
+ try {
+ return getSyncManager().getSyncAdapterPackageAsUser(accountType, authority,
+ callingUid, userId);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ @Override
public boolean getSyncAutomatically(Account account, String providerName) {
return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId());
}
@@ -1205,7 +1221,7 @@ public final class ContentService extends IContentService.Stub {
SyncManager syncManager = getSyncManager();
if (syncManager != null && callback != null) {
syncManager.getSyncStorageEngine().addStatusChangeListener(
- mask, UserHandle.getUserId(callingUid), callback);
+ mask, callingUid, callback);
}
} finally {
restoreCallingIdentity(identityToken);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 53c13c7a1268..cfd0a2d04981 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1245,6 +1245,26 @@ public class SyncManager {
return filteredResult.toArray(new String[] {});
}
+ public String getSyncAdapterPackageAsUser(String accountType, String authority,
+ int callingUid, int userId) {
+ if (accountType == null || authority == null) {
+ return null;
+ }
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(authority, accountType),
+ userId);
+ if (syncAdapterInfo == null) {
+ return null;
+ }
+ final String packageName = syncAdapterInfo.type.getPackageName();
+ if (TextUtils.isEmpty(packageName) || mPackageManagerInternal.filterAppAccess(
+ packageName, callingUid, userId)) {
+ return null;
+ }
+ return packageName;
+ }
+
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
SyncResult syncResult) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
@@ -3402,7 +3422,7 @@ public class SyncManager {
scheduleSyncOperationH(op);
mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
- target.userId);
+ op.owningPackage, target.userId);
}
/**
@@ -3921,7 +3941,7 @@ public class SyncManager {
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
resultMessage, downstreamActivity, upstreamActivity,
- syncOperation.target.userId);
+ syncOperation.owningPackage, syncOperation.target.userId);
}
}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 668142b7e8ee..1894c0fc6045 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -33,6 +33,7 @@ import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -59,6 +60,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IntPair;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -166,6 +168,8 @@ public class SyncStorageEngine {
private static HashMap<String, String> sAuthorityRenames;
private static PeriodicSyncAddedListener mPeriodicSyncAddedListener;
+ private final PackageManagerInternal mPackageManagerInternal;
+
private volatile boolean mIsClockValid;
static {
@@ -525,6 +529,8 @@ public class SyncStorageEngine {
mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
File systemDir = new File(dataDir, "system");
mSyncDir = new File(systemDir, SYNC_DIR_NAME);
mSyncDir.mkdirs();
@@ -609,9 +615,9 @@ public class SyncStorageEngine {
return mSyncRandomOffset;
}
- public void addStatusChangeListener(int mask, int userId, ISyncStatusObserver callback) {
+ public void addStatusChangeListener(int mask, int callingUid, ISyncStatusObserver callback) {
synchronized (mAuthorities) {
- final long cookie = IntPair.of(userId, mask);
+ final long cookie = IntPair.of(callingUid, mask);
mChangeListeners.register(callback, cookie);
}
}
@@ -644,16 +650,32 @@ public class SyncStorageEngine {
}
}
- void reportChange(int which, int callingUserId) {
+ void reportChange(int which, EndPoint target) {
+ final String syncAdapterPackageName;
+ if (target.account == null || target.provider == null) {
+ syncAdapterPackageName = null;
+ } else {
+ syncAdapterPackageName = ContentResolver.getSyncAdapterPackageAsUser(
+ target.account.type, target.provider, target.userId);
+ }
+ reportChange(which, syncAdapterPackageName, target.userId);
+ }
+
+ void reportChange(int which, String callingPackageName, int callingUserId) {
ArrayList<ISyncStatusObserver> reports = null;
synchronized (mAuthorities) {
int i = mChangeListeners.beginBroadcast();
while (i > 0) {
i--;
final long cookie = (long) mChangeListeners.getBroadcastCookie(i);
- final int userId = IntPair.first(cookie);
+ final int registerUid = IntPair.first(cookie);
+ final int registerUserId = UserHandle.getUserId(registerUid);
final int mask = IntPair.second(cookie);
- if ((which & mask) == 0 || callingUserId != userId) {
+ if ((which & mask) == 0 || callingUserId != registerUserId) {
+ continue;
+ }
+ if (callingPackageName != null && mPackageManagerInternal.filterAppAccess(
+ callingPackageName, registerUid, callingUserId)) {
continue;
}
if (reports == null) {
@@ -716,12 +738,12 @@ public class SyncStorageEngine {
" cuid=", callingUid,
" cpid=", callingPid
);
+ final AuthorityInfo authority;
synchronized (mAuthorities) {
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(
- new EndPoint(account, providerName, userId),
- -1 /* ident */,
- false);
+ authority = getOrCreateAuthorityLocked(
+ new EndPoint(account, providerName, userId),
+ -1 /* ident */,
+ false);
if (authority.enabled == sync) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
@@ -743,7 +765,7 @@ public class SyncStorageEngine {
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, authority.target);
queueBackup();
}
@@ -811,7 +833,7 @@ public class SyncStorageEngine {
requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
ContentResolver.SYNC_EXEMPTION_NONE, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target.userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
}
public Pair<Long, Long> getBackoff(EndPoint info) {
@@ -857,7 +879,7 @@ public class SyncStorageEngine {
}
}
if (changed) {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
}
}
@@ -919,7 +941,8 @@ public class SyncStorageEngine {
}
for (int i = changedUserIds.size() - 1; i > 0; i--) {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, changedUserIds.valueAt(i));
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+ null /* callingPackageName */, changedUserIds.valueAt(i));
}
}
@@ -945,7 +968,7 @@ public class SyncStorageEngine {
}
authority.delayUntil = delayUntil;
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info.userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, info);
}
/**
@@ -988,7 +1011,8 @@ public class SyncStorageEngine {
new Bundle(),
syncExemptionFlag, callingUid, callingPid);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
+ null /* callingPackageName */, userId);
mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
queueBackup();
}
@@ -1039,7 +1063,7 @@ public class SyncStorageEngine {
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = pendingValue;
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info.userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING, info);
}
/**
@@ -1129,7 +1153,7 @@ public class SyncStorageEngine {
activeSyncContext.mStartTime);
getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
}
- reportActiveChange(activeSyncContext.mSyncOperation.target.userId);
+ reportActiveChange(activeSyncContext.mSyncOperation.target);
return syncInfo;
}
@@ -1146,14 +1170,14 @@ public class SyncStorageEngine {
getCurrentSyncs(userId).remove(syncInfo);
}
- reportActiveChange(userId);
+ reportActiveChange(new EndPoint(syncInfo.account, syncInfo.authority, userId));
}
/**
* To allow others to send active change reports, to poke clients.
*/
- public void reportActiveChange(int userId) {
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, userId);
+ public void reportActiveChange(EndPoint target) {
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, target);
}
/**
@@ -1188,12 +1212,13 @@ public class SyncStorageEngine {
if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.target.userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, op.owningPackage, op.target.userId);
return id;
}
public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
- long downstreamActivity, long upstreamActivity, int userId) {
+ long downstreamActivity, long upstreamActivity, String opPackageName,
+ int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
@@ -1333,7 +1358,7 @@ public class SyncStorageEngine {
}
}
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, userId);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS, opPackageName, userId);
}
/**
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 56b68b73cb57..eed68f8b1300 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -27,7 +27,9 @@ import android.os.Binder;
import android.os.ShellCommand;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.Optional;
+import java.util.stream.Collectors;
/**
* ShellCommands for {@link DeviceStateManagerService}.
@@ -56,14 +58,18 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
switch (cmd) {
case "state":
return runState(pw);
+ case "print-state":
+ return runPrintState(pw);
case "print-states":
return runPrintStates(pw);
+ case "print-states-simple":
+ return runPrintStatesSimple(pw);
default:
return handleDefaultCommands(cmd);
}
}
- private void printState(PrintWriter pw) {
+ private void printAllStates(PrintWriter pw) {
Optional<DeviceState> committedState = mService.getCommittedState();
Optional<DeviceState> baseState = mService.getBaseState();
Optional<DeviceState> overrideState = mService.getOverrideState();
@@ -79,7 +85,8 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
private int runState(PrintWriter pw) {
final String nextArg = getNextArg();
if (nextArg == null) {
- printState(pw);
+ printAllStates(pw);
+ return 0;
}
final Context context = mService.getContext();
@@ -123,6 +130,16 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runPrintState(PrintWriter pw) {
+ Optional<DeviceState> deviceState = mService.getCommittedState();
+ if (deviceState.isPresent()) {
+ pw.println(deviceState.get().getIdentifier());
+ return 0;
+ }
+ getErrPrintWriter().println("Error: device state not available.");
+ return 1;
+ }
+
private int runPrintStates(PrintWriter pw) {
DeviceState[] states = mService.getSupportedStates();
pw.print("Supported states: [\n");
@@ -133,6 +150,14 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runPrintStatesSimple(PrintWriter pw) {
+ pw.print(Arrays.stream(mService.getSupportedStates())
+ .map(DeviceState::getIdentifier)
+ .map(Object::toString)
+ .collect(Collectors.joining(",")));
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -141,8 +166,12 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
pw.println(" Print this help text.");
pw.println(" state [reset|OVERRIDE_DEVICE_STATE]");
pw.println(" Return or override device state.");
+ pw.println(" print-state");
+ pw.println(" Return the current device state.");
pw.println(" print-states");
pw.println(" Return list of currently supported device states.");
+ pw.println(" print-states-simple");
+ pw.println(" Return the currently supported device states in comma separated format.");
}
private static String toString(@NonNull Optional<DeviceState> state) {
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 1acd5d097525..9dd2f8408c56 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -111,8 +111,8 @@ class DeviceStateToLayoutMap {
for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
layout.createDisplayLocked(
DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
- d.getIsDefault(),
- d.getEnabled());
+ d.isDefaultDisplay(),
+ d.isEnabled());
}
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 35f29579b417..806bcc29f305 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -16,7 +16,9 @@
package com.android.server.display;
+import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayViewport;
import android.os.IBinder;
@@ -43,6 +45,7 @@ abstract class DisplayDevice {
// The display device does not manage these properties itself, they are set by
// the display manager service. The display device shouldn't really be looking at these.
private int mCurrentLayerStack = -1;
+ private int mCurrentFlags = 0;
private int mCurrentOrientation = -1;
private Rect mCurrentLayerStackRect;
private Rect mCurrentDisplayRect;
@@ -104,6 +107,34 @@ abstract class DisplayDevice {
}
/**
+ * Returns the window token of the level of the WindowManager hierarchy to mirror, or null
+ * if layer mirroring by SurfaceFlinger should not be performed.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ @Nullable
+ public IBinder getWindowTokenClientToMirrorLocked() {
+ return null;
+ }
+
+ /**
+ * Updates the window token of the level of the level of the WindowManager hierarchy to mirror.
+ * If windowToken is null, then no layer mirroring by SurfaceFlinger to should be performed.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
+ }
+
+ /**
+ * Returns the default size of the surface associated with the display, or null if the surface
+ * is not provided for layer mirroring by SurfaceFlinger.
+ * For now, only used for mirroring started from MediaProjection.
+ */
+ @Nullable
+ public Point getDisplaySurfaceDefaultSize() {
+ return null;
+ }
+
+ /**
* Gets the name of the display device.
*
* @return The display device name.
@@ -212,6 +243,19 @@ abstract class DisplayDevice {
}
/**
+ * Sets the display flags while in a transaction.
+ *
+ * Valid display flags:
+ * {@link SurfaceControl#DISPLAY_RECEIVES_INPUT}
+ */
+ public final void setDisplayFlagsLocked(SurfaceControl.Transaction t, int flags) {
+ if (mCurrentFlags != flags) {
+ mCurrentFlags = flags;
+ t.setDisplayFlags(mDisplayToken, flags);
+ }
+ }
+
+ /**
* Sets the display projection while in a transaction.
*
* @param orientation defines the display's orientation
@@ -298,6 +342,7 @@ abstract class DisplayDevice {
pw.println("mUniqueId=" + mUniqueId);
pw.println("mDisplayToken=" + mDisplayToken);
pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentFlags=" + mCurrentFlags);
pw.println("mCurrentOrientation=" + mCurrentOrientation);
pw.println("mCurrentLayerStackRect=" + mCurrentLayerStackRect);
pw.println("mCurrentDisplayRect=" + mCurrentDisplayRect);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 57f44864d2c0..2b52350f0634 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -123,6 +123,17 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener {
return null;
}
+ // String uniqueId -> DisplayDevice object with that given uniqueId
+ public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) {
+ for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
+ final DisplayDevice displayDevice = mDisplayDevices.get(i);
+ if (displayDevice.getUniqueId().equals(uniqueId)) {
+ return displayDevice;
+ }
+ }
+ return null;
+ }
+
private void handleDisplayDeviceAdded(DisplayDevice device) {
synchronized (mSyncRoot) {
DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index afd18894f531..73bcea6de115 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -63,8 +63,6 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
-import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
-import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -192,14 +190,16 @@ public final class DisplayManagerService extends SystemService {
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
- private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
+ // This value needs to be in sync with the threshold
+ // in RefreshRateConfigs::getFrameRateDivider.
+ private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.0009f;
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
private static final int MSG_REQUEST_TRAVERSAL = 4;
private static final int MSG_UPDATE_VIEWPORT = 5;
- private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
+ private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6;
private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
@@ -525,16 +525,29 @@ public final class DisplayManagerService extends SystemService {
final int newUserId = to.getUserIdentifier();
final int userSerial = getUserManager().getUserSerialNumber(newUserId);
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
- if (mCurrentUserId != newUserId) {
+ boolean userSwitching = mCurrentUserId != newUserId;
+ if (userSwitching) {
mCurrentUserId = newUserId;
- BrightnessConfiguration config =
- mPersistentDataStore.getBrightnessConfiguration(userSerial);
- displayPowerController.setBrightnessConfiguration(config);
- handleSettingsChange();
}
- displayPowerController.onSwitchUser(newUserId);
+ mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+ if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+ return;
+ }
+ final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ logicalDisplay.getDisplayIdLocked());
+ if (dpc == null) {
+ return;
+ }
+ if (userSwitching) {
+ BrightnessConfiguration config =
+ getBrightnessConfigForDisplayWithPdsFallbackLocked(
+ logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(),
+ userSerial);
+ dpc.setBrightnessConfiguration(config);
+ }
+ dpc.onSwitchUser(newUserId);
+ });
+ handleSettingsChange();
}
}
@@ -636,6 +649,9 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
if (display != null) {
+ // Do not let constrain be overwritten by override from WindowManager.
+ info.shouldConstrainMetricsForLauncher =
+ display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher;
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
handleLogicalDisplayChangedLocked(display);
scheduleTraversalLocked(false);
@@ -812,7 +828,7 @@ public final class DisplayManagerService extends SystemService {
// Override the refresh rate only if it is a divider of the current
// refresh rate. This calculation needs to be in sync with the native code
- // in RefreshRateConfigs::getRefreshRateDividerForUid
+ // in RefreshRateConfigs::getFrameRateDivider
Display.Mode currentMode = info.getMode();
float numPeriods = currentMode.getRefreshRate() / frameRateHz;
float numPeriodsRound = Math.round(numPeriods);
@@ -1315,6 +1331,13 @@ public final class DisplayManagerService extends SystemService {
if (work != null) {
mHandler.post(work);
}
+ final int displayId = display.getDisplayIdLocked();
+ DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ if (dpc != null) {
+ dpc.onDisplayChanged();
+ }
+ mPersistentDataStore.saveIfNeeded();
+ mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
handleLogicalDisplayChangedLocked(display);
}
@@ -1423,24 +1446,42 @@ public final class DisplayManagerService extends SystemService {
return mDisplayModeDirector.getModeSwitchingType();
}
- private void setBrightnessConfigurationForUserInternal(
- @Nullable BrightnessConfiguration c, @UserIdInt int userId,
- @Nullable String packageName) {
+ private void setBrightnessConfigurationForDisplayInternal(
+ @Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
+ String packageName) {
validateBrightnessConfiguration(c);
final int userSerial = getUserManager().getUserSerialNumber(userId);
synchronized (mSyncRoot) {
try {
- mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial,
- packageName);
+ DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
+ if (displayDevice == null) {
+ return;
+ }
+ mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice,
+ userSerial, packageName);
} finally {
mPersistentDataStore.saveIfNeeded();
}
- if (userId == mCurrentUserId) {
- mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration(c);
+ if (userId != mCurrentUserId) {
+ return;
+ }
+ DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ if (dpc != null) {
+ dpc.setBrightnessConfiguration(c);
}
}
}
+ private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
+ final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
+ final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
+ if (logicalDisplay != null) {
+ final int displayId = logicalDisplay.getDisplayIdLocked();
+ return mDisplayPowerControllers.get(displayId);
+ }
+ return null;
+ }
+
@VisibleForTesting
void validateBrightnessConfiguration(BrightnessConfiguration config) {
if (config == null) {
@@ -1463,13 +1504,22 @@ public final class DisplayManagerService extends SystemService {
return false;
}
- private void loadBrightnessConfiguration() {
+ private void loadBrightnessConfigurations() {
+ int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
synchronized (mSyncRoot) {
- final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
- BrightnessConfiguration config =
- mPersistentDataStore.getBrightnessConfiguration(userSerial);
- mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration(
- config);
+ mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> {
+ final String uniqueId =
+ logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final BrightnessConfiguration config =
+ getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
+ if (config != null) {
+ final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ logicalDisplay.getDisplayIdLocked());
+ if (dpc != null) {
+ dpc.setBrightnessConfiguration(config);
+ }
+ }
+ });
}
}
@@ -1680,9 +1730,17 @@ public final class DisplayManagerService extends SystemService {
return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
}
- void resetBrightnessConfiguration() {
- setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
+ void resetBrightnessConfigurations() {
+ mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
mContext.getPackageName());
+ mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+ if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+ return;
+ }
+ final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
+ mContext.getPackageName());
+ }));
}
void setAutoBrightnessLoggingEnabled(boolean enabled) {
@@ -1723,6 +1781,21 @@ public final class DisplayManagerService extends SystemService {
}
}
+ void setShouldConstrainMetricsForLauncher(boolean constrain) {
+ // Apply constrain for every display.
+ synchronized (mSyncRoot) {
+ int[] displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(Process.myUid());
+ for (int i : displayIds) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(i);
+ if (display == null) {
+ return;
+ }
+ display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher = constrain;
+ setDisplayInfoOverrideFromWindowManagerInternal(i, display.getDisplayInfoLocked());
+ }
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
@@ -1749,10 +1822,13 @@ public final class DisplayManagerService extends SystemService {
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
+ // Mirror the part of WM hierarchy that corresponds to the provided window token.
+ IBinder windowTokenClientToMirror = device.getWindowTokenClientToMirrorLocked();
+
// Find the logical display that the display device is showing.
// Certain displays only ever show their own content.
LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
- if (!ownContent) {
+ if (!ownContent && windowTokenClientToMirror == null) {
if (display != null && !display.hasContentLocked()) {
// If the display does not have any content of its own, then
// automatically mirror the requested logical display contents if possible.
@@ -2113,6 +2189,18 @@ public final class DisplayManagerService extends SystemService {
return display == null ? null : display.getPrimaryDisplayDeviceLocked();
}
+ private BrightnessConfiguration getBrightnessConfigForDisplayWithPdsFallbackLocked(
+ String uniqueId, int userSerial) {
+ BrightnessConfiguration config =
+ mPersistentDataStore.getBrightnessConfigurationForDisplayLocked(
+ uniqueId, userSerial);
+ if (config == null) {
+ // Get from global configurations
+ config = mPersistentDataStore.getBrightnessConfiguration(userSerial);
+ }
+ return config;
+ }
+
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -2154,8 +2242,8 @@ public final class DisplayManagerService extends SystemService {
break;
}
- case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
- loadBrightnessConfiguration();
+ case MSG_LOAD_BRIGHTNESS_CONFIGURATIONS:
+ loadBrightnessConfigurations();
break;
case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
@@ -2770,6 +2858,19 @@ public final class DisplayManagerService extends SystemService {
@Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
+ mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+ if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+ return;
+ }
+ final DisplayDevice displayDevice = logicalDisplay.getPrimaryDisplayDeviceLocked();
+ setBrightnessConfigurationForDisplay(c, displayDevice.getUniqueId(), userId,
+ packageName);
+ });
+ }
+
+ @Override // Binder call
+ public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
+ String uniqueId, int userId, String packageName) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to change the display's brightness configuration");
@@ -2777,21 +2878,19 @@ public final class DisplayManagerService extends SystemService {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS,
"Permission required to change the display brightness"
- + " configuration of another user");
- }
- if (packageName != null && !validatePackageName(getCallingUid(), packageName)) {
- packageName = null;
+ + " configuration of another user");
}
final long token = Binder.clearCallingIdentity();
try {
- setBrightnessConfigurationForUserInternal(c, userId, packageName);
+ setBrightnessConfigurationForDisplayInternal(c, uniqueId, userId, packageName);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
- public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+ public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueId,
+ int userId) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
"Permission required to read the display's brightness configuration");
@@ -2802,14 +2901,19 @@ public final class DisplayManagerService extends SystemService {
+ " configuration of another user");
}
final long token = Binder.clearCallingIdentity();
+ final int userSerial = getUserManager().getUserSerialNumber(userId);
try {
- final int userSerial = getUserManager().getUserSerialNumber(userId);
synchronized (mSyncRoot) {
+ // Get from per-display configurations
BrightnessConfiguration config =
- mPersistentDataStore.getBrightnessConfiguration(userSerial);
+ getBrightnessConfigForDisplayWithPdsFallbackLocked(
+ uniqueId, userSerial);
if (config == null) {
- config = mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
- .getDefaultBrightnessConfiguration();
+ // Get default configuration
+ DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ if (dpc != null) {
+ config = dpc.getDefaultBrightnessConfiguration();
+ }
}
return config;
}
@@ -2818,6 +2922,21 @@ public final class DisplayManagerService extends SystemService {
}
}
+
+
+ @Override // Binder call
+ public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+ final String uniqueId;
+ synchronized (mSyncRoot) {
+ DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
+ Display.DEFAULT_DISPLAY).getPrimaryDisplayDeviceLocked();
+ uniqueId = displayDevice.getUniqueId();
+ }
+ return getBrightnessConfigurationForDisplay(uniqueId, userId);
+
+
+ }
+
@Override // Binder call
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
mContext.enforceCallingOrSelfPermission(
@@ -3086,7 +3205,7 @@ public final class DisplayManagerService extends SystemService {
initializeDisplayPowerControllersLocked();
}
- mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
+ mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
}
@Override
@@ -3314,6 +3433,40 @@ public final class DisplayManagerService extends SystemService {
}
return config.getRefreshRateLimitations();
}
+
+ @Override
+ public IBinder getWindowTokenClientToMirror(int displayId) {
+ final DisplayDevice device;
+ synchronized (mSyncRoot) {
+ device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ }
+ return device.getWindowTokenClientToMirrorLocked();
+ }
+
+ @Override
+ public void setWindowTokenClientToMirror(int displayId, IBinder windowToken) {
+ synchronized (mSyncRoot) {
+ final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+ if (device != null) {
+ device.setWindowTokenClientToMirrorLocked(windowToken);
+ }
+ }
+ }
+
+ @Override
+ public Point getDisplaySurfaceDefaultSize(int displayId) {
+ final DisplayDevice device;
+ synchronized (mSyncRoot) {
+ device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ }
+ return device.getDisplaySurfaceDefaultSize();
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 48edb73ac81d..9412c938f934 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -58,6 +58,8 @@ class DisplayManagerShellCommand extends ShellCommand {
return setDisplayModeDirectorLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
+ case "constrain-launcher-metrics":
+ return setConstrainLauncherMetrics();
default:
return handleDefaultCommands(cmd);
}
@@ -88,6 +90,9 @@ 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(" constrain-launcher-metrics [true|false]");
+ pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for ");
+ pw.println(" Launcher.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -115,7 +120,7 @@ class DisplayManagerShellCommand extends ShellCommand {
}
private int resetBrightnessConfiguration() {
- mService.resetBrightnessConfiguration();
+ mService.resetBrightnessConfigurations();
return 0;
}
@@ -150,4 +155,15 @@ class DisplayManagerShellCommand extends ShellCommand {
mService.setAmbientColorTemperatureOverride(cct);
return 0;
}
+
+ private int setConstrainLauncherMetrics() {
+ String constrainText = getNextArg();
+ if (constrainText == null) {
+ getErrPrintWriter().println("Error: no value specified");
+ return 1;
+ }
+ boolean constrain = Boolean.parseBoolean(constrainText);
+ mService.setShouldConstrainMetricsForLauncher(constrain);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index d66d7ee99f2e..a25cfd8f3ba4 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -2058,7 +2058,9 @@ public class DisplayModeDirector {
public void observe() {
StatusBarManagerInternal statusBar =
LocalServices.getService(StatusBarManagerInternal.class);
- statusBar.setUdfpsHbmListener(this);
+ if (statusBar != null) {
+ statusBar.setUdfpsHbmListener(this);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index f953cc8c8a27..06d331a57122 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -326,7 +326,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
if (mActiveModeId != NO_DISPLAY_MODE_ID
&& mActiveModeId != activeRecord.mMode.getModeId()) {
Slog.d(TAG, "The active mode was changed from SurfaceFlinger or the display"
- + "device.");
+ + " device to " + activeRecord.mMode);
mActiveModeId = activeRecord.mMode.getModeId();
activeModeChanged = true;
sendTraversalRequestLocked();
@@ -601,12 +601,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
&& SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
}
- if (res.getBoolean(
- com.android.internal.R.bool.config_maskMainBuiltInDisplayCutout)) {
- mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
- }
- mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
- mInfo.width, mInfo.height);
mInfo.roundedCorners = RoundedCorners.fromResources(
res, mInfo.width, mInfo.height);
} else {
@@ -620,6 +614,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
}
+ if (DisplayCutout.getMaskBuiltInDisplayCutout(res, mInfo.uniqueId)) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT;
+ }
+ mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
+ mInfo.uniqueId, mInfo.width, mInfo.height);
+
if (mStaticDisplayInfo.isInternal) {
mInfo.type = Display.TYPE_INTERNAL;
mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 9acb4c8f471a..86c9ca937482 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static com.android.server.display.DisplayDeviceInfo.TOUCH_NONE;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -231,6 +233,8 @@ final class LogicalDisplay {
info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
+ info.shouldConstrainMetricsForLauncher =
+ mOverrideDisplayInfo.shouldConstrainMetricsForLauncher;
}
mInfo.set(info);
}
@@ -512,6 +516,11 @@ final class LogicalDisplay {
boolean isBlanked) {
// Set the layer stack.
device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
+ // Also inform whether the device is the same one sent to inputflinger for its layerstack.
+ // TODO(b/188914255): Remove once input can dispatch against device vs layerstack.
+ device.setDisplayFlagsLocked(t,
+ device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE
+ ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0);
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 4c9a2d702114..a93171835365 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -195,6 +195,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
public LogicalDisplay getDisplayLocked(DisplayDevice device) {
+ if (device == null) {
+ return null;
+ }
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
final LogicalDisplay display = mLogicalDisplays.valueAt(i);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index c90ddf48a091..4b0d43b3d1d4 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -63,6 +63,15 @@ import java.util.Objects;
* &lt;display unique-id="XXXXXXX">
* &lt;color-mode>0&lt;/color-mode>
* &lt;brightness-value>0&lt;/brightness-value>
+ * &lt;brightness-configurations>
+ * &lt;brightness-configuration user-serial="0" package-name="com.example"
+ * timestamp="1234">
+ * &lt;brightness-curve description="some text">
+ * &lt;brightness-point lux="0" nits="13.25"/>
+ * &lt;brightness-point lux="20" nits="35.94"/>
+ * &lt;/brightness-curve>
+ * &lt;/brightness-configuration>
+ * &lt;/brightness-configurations>
* &lt;/display>
* &lt;/display-states>
* &lt;stable-device-values>
@@ -120,7 +129,8 @@ final class PersistentDataStore {
private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
// Brightness configuration by user
- private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
+ private BrightnessConfigurations mGlobalBrightnessConfigurations =
+ new BrightnessConfigurations();
// True if the data has been loaded.
private boolean mLoaded;
@@ -293,18 +303,44 @@ final class PersistentDataStore {
}
}
+ // Used for testing & reset
public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial,
@Nullable String packageName) {
loadIfNeeded();
- if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
+ if (mGlobalBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
packageName)) {
+
setDirty();
}
}
+ public boolean setBrightnessConfigurationForDisplayLocked(BrightnessConfiguration configuration,
+ DisplayDevice device, int userSerial, String packageName) {
+ if (device == null || !device.hasStableUniqueId()) {
+ return false;
+ }
+ DisplayState state = getDisplayState(device.getUniqueId(), /*createIfAbsent*/ true);
+ if (state.setBrightnessConfiguration(configuration, userSerial, packageName)) {
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
+
+ public BrightnessConfiguration getBrightnessConfigurationForDisplayLocked(
+ String uniqueDisplayId, int userSerial) {
+ loadIfNeeded();
+ DisplayState state = mDisplayStates.get(uniqueDisplayId);
+ if (state != null) {
+ return state.getBrightnessConfiguration(userSerial);
+ }
+ return null;
+ }
+
public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
loadIfNeeded();
- return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
+ return mGlobalBrightnessConfigurations.getBrightnessConfiguration(userSerial);
}
private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
@@ -391,7 +427,7 @@ final class PersistentDataStore {
mStableDeviceValues.loadFromXml(parser);
}
if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
- mBrightnessConfigurations.loadFromXml(parser);
+ mGlobalBrightnessConfigurations.loadFromXml(parser);
}
}
}
@@ -470,7 +506,7 @@ final class PersistentDataStore {
mStableDeviceValues.saveToXml(serializer);
serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
- mBrightnessConfigurations.saveToXml(serializer);
+ mGlobalBrightnessConfigurations.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
serializer.endDocument();
@@ -493,14 +529,18 @@ final class PersistentDataStore {
}
pw.println(" StableDeviceValues:");
mStableDeviceValues.dump(pw, " ");
- pw.println(" BrightnessConfigurations:");
- mBrightnessConfigurations.dump(pw, " ");
+ pw.println(" GlobalBrightnessConfigurations:");
+ mGlobalBrightnessConfigurations.dump(pw, " ");
}
private static final class DisplayState {
private int mColorMode;
private float mBrightness;
+ // Brightness configuration by user
+ private BrightnessConfigurations mDisplayBrightnessConfigurations =
+ new BrightnessConfigurations();
+
public boolean setColorMode(int colorMode) {
if (colorMode == mColorMode) {
return false;
@@ -525,6 +565,16 @@ final class PersistentDataStore {
return mBrightness;
}
+ public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
+ int userSerial, String packageName) {
+ mDisplayBrightnessConfigurations.setBrightnessConfigurationForUser(
+ configuration, userSerial, packageName);
+ return true;
+ }
+
+ public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
+ return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial);
+ }
public void loadFromXml(TypedXmlPullParser parser)
throws IOException, XmlPullParserException {
@@ -540,6 +590,9 @@ final class PersistentDataStore {
String brightness = parser.nextText();
mBrightness = Float.parseFloat(brightness);
break;
+ case TAG_BRIGHTNESS_CONFIGURATIONS:
+ mDisplayBrightnessConfigurations.loadFromXml(parser);
+ break;
}
}
}
@@ -548,15 +601,21 @@ final class PersistentDataStore {
serializer.startTag(null, TAG_COLOR_MODE);
serializer.text(Integer.toString(mColorMode));
serializer.endTag(null, TAG_COLOR_MODE);
+
serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
serializer.text(Float.toString(mBrightness));
serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
+ serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
+ mDisplayBrightnessConfigurations.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
}
public void dump(final PrintWriter pw, final String prefix) {
pw.println(prefix + "ColorMode=" + mColorMode);
pw.println(prefix + "BrightnessValue=" + mBrightness);
+ pw.println(prefix + "DisplayBrightnessConfigurations: ");
+ mDisplayBrightnessConfigurations.dump(pw, prefix);
}
}
@@ -621,11 +680,11 @@ final class PersistentDataStore {
private static final class BrightnessConfigurations {
// Maps from a user ID to the users' given brightness configuration
- private SparseArray<BrightnessConfiguration> mConfigurations;
+ private final SparseArray<BrightnessConfiguration> mConfigurations;
// Timestamp of time the configuration was set.
- private SparseLongArray mTimeStamps;
+ private final SparseLongArray mTimeStamps;
// Package that set the configuration.
- private SparseArray<String> mPackageNames;
+ private final SparseArray<String> mPackageNames;
public BrightnessConfigurations() {
mConfigurations = new SparseArray<>();
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index b7931c8a8424..34d2b0160c3c 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -31,7 +31,9 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUST
import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
+import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Point;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
@@ -231,6 +233,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
private Display.Mode mMode;
private boolean mIsDisplayOn;
private int mDisplayIdToMirror;
+ private IBinder mWindowTokenClientToMirror;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -253,6 +256,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
mUniqueIndex = uniqueIndex;
mIsDisplayOn = surface != null;
mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
+ mWindowTokenClientToMirror = virtualDisplayConfig.getWindowTokenClientToMirror();
}
@Override
@@ -282,6 +286,26 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
return mDisplayIdToMirror;
}
+ @Override
+ @Nullable
+ public IBinder getWindowTokenClientToMirrorLocked() {
+ return mWindowTokenClientToMirror;
+ }
+
+ @Override
+ public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
+ if (mWindowTokenClientToMirror != windowToken) {
+ mWindowTokenClientToMirror = windowToken;
+ sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+ sendTraversalRequestLocked();
+ }
+ }
+
+ @Override
+ public Point getDisplaySurfaceDefaultSize() {
+ return mSurface.getDefaultSize();
+ }
+
@VisibleForTesting
Surface getSurfaceLocked() {
return mSurface;
@@ -362,6 +386,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
pw.println("mStopped=" + mStopped);
pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
+ pw.println("mWindowTokenClientToMirror=" + mWindowTokenClientToMirror);
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index bccd4c42ff12..f9a1368ff3e8 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -162,7 +162,8 @@ public final class ColorDisplayService extends SystemService {
private final ReduceBrightColorsTintController mReduceBrightColorsTintController =
new ReduceBrightColorsTintController();
- private final Handler mHandler;
+ @VisibleForTesting
+ final Handler mHandler;
private final AppSaturationController mAppSaturationController = new AppSaturationController();
@@ -404,13 +405,13 @@ public final class ColorDisplayService extends SystemService {
// existing activated state. This ensures consistency of tint across the color mode change.
onDisplayColorModeChanged(getColorModeInternal());
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
if (mNightDisplayTintController.isAvailable(getContext())) {
// Reset the activated state.
mNightDisplayTintController.setActivated(null);
// Prepare the night display color transformation matrix.
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
@@ -432,8 +433,7 @@ public final class ColorDisplayService extends SystemService {
}
if (mReduceBrightColorsTintController.isAvailable(getContext())) {
- mReduceBrightColorsTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix());
+ mReduceBrightColorsTintController.setUp(getContext(), dtm.needsLinearColorMatrix());
onReduceBrightColorsStrengthLevelChanged();
final boolean reset = resetReduceBrightColors();
if (!reset) {
@@ -540,8 +540,8 @@ public final class ColorDisplayService extends SystemService {
mDisplayWhiteBalanceTintController.cancelAnimator();
if (mNightDisplayTintController.isAvailable(getContext())) {
- mNightDisplayTintController
- .setUp(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
+ mNightDisplayTintController.setUp(getContext(), dtm.needsLinearColorMatrix(mode));
mNightDisplayTintController
.setMatrix(mNightDisplayTintController.getColorTemperatureSetting());
}
@@ -736,10 +736,11 @@ public final class ColorDisplayService extends SystemService {
@VisibleForTesting
void updateDisplayWhiteBalanceStatus() {
boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
+ final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled()
&& !mNightDisplayTintController.isActivated()
&& !isAccessibilityEnabled()
- && DisplayTransformManager.needsLinearColorMatrix());
+ && dtm.needsLinearColorMatrix());
boolean activated = mDisplayWhiteBalanceTintController.isActivated();
if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 5c68c51ad7a3..0dba9e1b0af1 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -239,7 +239,7 @@ public class DisplayTransformManager {
/**
* Return true when the color matrix works in linear space.
*/
- public static boolean needsLinearColorMatrix() {
+ public boolean needsLinearColorMatrix() {
return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
}
@@ -247,7 +247,7 @@ public class DisplayTransformManager {
/**
* Return true when the specified colorMode requires the color matrix to work in linear space.
*/
- public static boolean needsLinearColorMatrix(int colorMode) {
+ public boolean needsLinearColorMatrix(int colorMode) {
return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED;
}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 8405bbe38b12..d422d51d4087 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -88,7 +88,7 @@ final class ActiveSourceHandler {
tv.updateActiveSource(current, "ActiveSourceHandler");
invokeCallback(HdmiControlManager.RESULT_SUCCESS);
} else {
- tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
+ tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress,
mCallback);
}
}
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
index f6828d129728..ff1a74af02e2 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
@@ -33,7 +33,7 @@ import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
* for a new active source. It does its best to wake up the target in standby mode
* before issuing the command &gt;Set Stream path&lt;.
*/
-final class DeviceSelectAction extends HdmiCecFeatureAction {
+final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
private static final String TAG = "DeviceSelect";
// Time in milliseconds we wait for the device power status to switch to 'Standby'
@@ -74,7 +74,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
* @param target target logical device that will be a new active source
* @param callback callback object
*/
- DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target,
+ DeviceSelectActionFromTv(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target,
IHdmiControlCallback callback) {
this(source, target, callback,
source.getDeviceInfo().getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0
@@ -82,7 +82,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction {
}
@VisibleForTesting
- DeviceSelectAction(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target,
+ DeviceSelectActionFromTv(HdmiCecLocalDeviceTv source, HdmiDeviceInfo target,
IHdmiControlCallback callback, boolean isCec20) {
super(source, callback);
mTarget = target;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index fefe9530fa83..5de89c9a9cff 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -317,12 +317,25 @@ public class HdmiCecConfig {
R.bool.config_cecHdmiCecVersion20_allowed,
R.bool.config_cecHdmiCecVersion20_default);
+ Setting routingControlControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ R.bool.config_cecRoutingControl_userConfigurable);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_ENABLED,
+ R.bool.config_cecRoutingControlEnabled_allowed,
+ R.bool.config_cecRoutingControlEnabled_default);
+ routingControlControl.registerValue(HdmiControlManager.ROUTING_CONTROL_DISABLED,
+ R.bool.config_cecRoutingControlDisabled_allowed,
+ R.bool.config_cecRoutingControlDisabled_default);
+
Setting powerControlMode = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
R.bool.config_cecPowerControlMode_userConfigurable);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
R.bool.config_cecPowerControlModeTv_allowed,
R.bool.config_cecPowerControlModeTv_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
R.bool.config_cecPowerControlModeBroadcast_allowed,
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -342,6 +355,16 @@ public class HdmiCecConfig {
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+ Setting systemAudioControl = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED,
+ R.bool.config_cecSystemAudioControlEnabled_allowed,
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ systemAudioControl.registerValue(HdmiControlManager.SYSTEM_AUDIO_CONTROL_DISABLED,
+ R.bool.config_cecSystemAudioControlDisabled_allowed,
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
Setting systemAudioModeMuting = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
@@ -498,12 +521,16 @@ public class HdmiCecConfig {
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return STORAGE_GLOBAL_SETTINGS;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
@@ -535,12 +562,16 @@ public class HdmiCecConfig {
return Global.HDMI_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL:
+ return Global.HDMI_CEC_SWITCH_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE:
return Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP;
case HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE:
return Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL:
+ return Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED;
case HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING:
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index ad2ef2a2b665..05e764b92797 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -94,6 +94,21 @@ final class HdmiCecController {
private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+ /*
+ * The three flags below determine the action when a message is received. If CEC_DISABLED_IGNORE
+ * bit is set in ACTION_ON_RECEIVE_MSG, then the message is forwarded irrespective of whether
+ * CEC is enabled or disabled. The other flags/bits are also ignored.
+ */
+ private static final int CEC_DISABLED_IGNORE = 1 << 0;
+
+ /* If CEC_DISABLED_LOG_WARNING bit is set, a warning message is printed if CEC is disabled. */
+ private static final int CEC_DISABLED_LOG_WARNING = 1 << 1;
+
+ /* If CEC_DISABLED_DROP_MSG bit is set, the message is dropped if CEC is disabled. */
+ private static final int CEC_DISABLED_DROP_MSG = 1 << 2;
+
+ private static final int ACTION_ON_RECEIVE_MSG = CEC_DISABLED_LOG_WARNING;
+
/** Cookie for matching the right end point. */
protected static final int HDMI_CEC_HAL_DEATH_COOKIE = 353;
@@ -568,6 +583,16 @@ final class HdmiCecController {
@VisibleForTesting
void onReceiveCommand(HdmiCecMessage message) {
assertRunOnServiceThread();
+ if (((ACTION_ON_RECEIVE_MSG & CEC_DISABLED_IGNORE) == 0)
+ && !mService.isControlEnabled()
+ && !HdmiCecMessage.isCecTransportMessage(message.getOpcode())) {
+ if ((ACTION_ON_RECEIVE_MSG & CEC_DISABLED_LOG_WARNING) != 0) {
+ HdmiLogger.warning("Message " + message + " received when cec disabled");
+ }
+ if ((ACTION_ON_RECEIVE_MSG & CEC_DISABLED_DROP_MSG) != 0) {
+ return;
+ }
+ }
if (mService.isAddressAllocated() && !isAcceptableAddress(message.getDestination())) {
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 23b5c1411b0e..698ee0bb6220 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -481,4 +481,113 @@ final class HdmiCecKeycode {
// return muting ? CEC_KEYCODE_MUTE_FUNCTION : CEC_KEYCODE_RESTORE_VOLUME_FUNCTION;
return CEC_KEYCODE_MUTE;
}
+
+ public static String getKeycodeType(byte keycode) {
+ switch (keycode) {
+ case CEC_KEYCODE_UP:
+ case CEC_KEYCODE_DOWN:
+ case CEC_KEYCODE_LEFT:
+ case CEC_KEYCODE_RIGHT:
+ case CEC_KEYCODE_RIGHT_UP:
+ case CEC_KEYCODE_RIGHT_DOWN:
+ case CEC_KEYCODE_LEFT_UP:
+ case CEC_KEYCODE_LEFT_DOWN:
+ case CEC_KEYCODE_PAGE_UP:
+ case CEC_KEYCODE_PAGE_DOWN:
+ case CEC_KEYCODE_EXIT:
+ return "Navigation";
+ case CEC_KEYCODE_ROOT_MENU:
+ case CEC_KEYCODE_SETUP_MENU:
+ case CEC_KEYCODE_CONTENTS_MENU:
+ case CEC_KEYCODE_FAVORITE_MENU:
+ case CEC_KEYCODE_MEDIA_TOP_MENU:
+ case CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU:
+ return "Menu";
+ case CEC_KEYCODE_VOLUME_UP:
+ return "Volume up";
+ case CEC_KEYCODE_VOLUME_DOWN:
+ return "Volume down";
+ case CEC_KEYCODE_MUTE:
+ return "Volume mute";
+ case CEC_KEYCODE_POWER:
+ return "Power";
+ case CEC_KEYCODE_POWER_TOGGLE_FUNCTION:
+ return "Power toggle";
+ case CEC_KEYCODE_POWER_OFF_FUNCTION:
+ return "Power off";
+ case CEC_KEYCODE_POWER_ON_FUNCTION:
+ return "Power on";
+ case CEC_KEYCODE_F1_BLUE:
+ case CEC_KEYCODE_F2_RED:
+ case CEC_KEYCODE_F3_GREEN:
+ case CEC_KEYCODE_F4_YELLOW:
+ case CEC_KEYCODE_F5:
+ return "Function key";
+ case CEC_KEYCODE_NEXT_FAVORITE:
+ case CEC_KEYCODE_CHANNEL_UP:
+ case CEC_KEYCODE_CHANNEL_DOWN:
+ case CEC_KEYCODE_PREVIOUS_CHANNEL:
+ return "Channel";
+ case CEC_KEYCODE_NUMBER_11:
+ case CEC_KEYCODE_NUMBER_12:
+ case CEC_KEYCODE_NUMBER_0_OR_NUMBER_10:
+ case CEC_KEYCODE_NUMBERS_1:
+ case CEC_KEYCODE_NUMBERS_2:
+ case CEC_KEYCODE_NUMBERS_3:
+ case CEC_KEYCODE_NUMBERS_4:
+ case CEC_KEYCODE_NUMBERS_5:
+ case CEC_KEYCODE_NUMBERS_6:
+ case CEC_KEYCODE_NUMBERS_7:
+ case CEC_KEYCODE_NUMBERS_8:
+ case CEC_KEYCODE_NUMBERS_9:
+ return "Number";
+ case CEC_KEYCODE_PLAY:
+ case CEC_KEYCODE_STOP:
+ case CEC_KEYCODE_PAUSE:
+ case CEC_KEYCODE_RECORD:
+ case CEC_KEYCODE_REWIND:
+ case CEC_KEYCODE_FAST_FORWARD:
+ case CEC_KEYCODE_EJECT:
+ case CEC_KEYCODE_FORWARD:
+ case CEC_KEYCODE_BACKWARD:
+ case CEC_KEYCODE_STOP_RECORD:
+ case CEC_KEYCODE_PAUSE_RECORD:
+ case CEC_KEYCODE_ANGLE:
+ case CEC_KEYCODE_SUB_PICTURE:
+ case CEC_KEYCODE_VIDEO_ON_DEMAND:
+ return "Media";
+ case CEC_KEYCODE_PLAY_FUNCTION:
+ case CEC_KEYCODE_PAUSE_PLAY_FUNCTION:
+ case CEC_KEYCODE_RECORD_FUNCTION:
+ case CEC_KEYCODE_PAUSE_RECORD_FUNCTION:
+ case CEC_KEYCODE_STOP_FUNCTION:
+ case CEC_KEYCODE_MUTE_FUNCTION:
+ case CEC_KEYCODE_RESTORE_VOLUME_FUNCTION:
+ case CEC_KEYCODE_TUNE_FUNCTION:
+ case CEC_KEYCODE_SELECT_MEDIA_FUNCTION:
+ case CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION:
+ case CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION:
+ return "Functional";
+ case CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE:
+ case CEC_KEYCODE_TIMER_PROGRAMMING:
+ return "Timer";
+ case CEC_KEYCODE_SOUND_SELECT:
+ case CEC_KEYCODE_SELECT_SOUND_PRESENTATION:
+ case CEC_KEYCODE_SELECT_BROADCAST_TYPE:
+ case CEC_KEYCODE_INPUT_SELECT:
+ case CEC_KEYCODE_SELECT:
+ return "Select";
+ case CEC_KEYCODE_NUMBER_ENTRY_MODE:
+ case CEC_KEYCODE_DOT:
+ case CEC_KEYCODE_CLEAR:
+ case CEC_KEYCODE_ENTER:
+ case CEC_KEYCODE_DISPLAY_INFORMATION:
+ case CEC_KEYCODE_HELP:
+ case CEC_KEYCODE_DATA:
+ case CEC_KEYCODE_INITIAL_CONFIGURATION:
+ return "General";
+ default:
+ return "Unknown";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a2cb78d27a57..35090627cb2a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -17,11 +17,11 @@
package com.android.server.hdmi;
import android.annotation.CallSuper;
-import android.annotation.Nullable;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.input.InputManager;
+import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Handler;
@@ -68,7 +68,6 @@ abstract class HdmiCecLocalDevice {
protected final HdmiControlService mService;
protected final int mDeviceType;
- protected int mAddress;
protected int mPreferredAddress;
@GuardedBy("mLock")
protected HdmiDeviceInfo mDeviceInfo;
@@ -187,7 +186,6 @@ abstract class HdmiCecLocalDevice {
protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
mService = service;
mDeviceType = deviceType;
- mAddress = Constants.ADDR_UNREGISTERED;
mLock = service.getServiceLock();
}
@@ -254,7 +252,7 @@ abstract class HdmiCecLocalDevice {
protected int dispatchMessage(HdmiCecMessage message) {
assertRunOnServiceThread();
int dest = message.getDestination();
- if (dest != mAddress && dest != Constants.ADDR_BROADCAST) {
+ if (dest != mDeviceInfo.getLogicalAddress() && dest != Constants.ADDR_BROADCAST) {
return Constants.NOT_HANDLED;
}
// Cache incoming message if it is included in the list of cacheable opcodes.
@@ -281,11 +279,11 @@ abstract class HdmiCecLocalDevice {
case Constants.MESSAGE_SET_MENU_LANGUAGE:
return handleSetMenuLanguage(message);
case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
- return handleGivePhysicalAddress(null);
+ return handleGivePhysicalAddress(message);
case Constants.MESSAGE_GIVE_OSD_NAME:
return handleGiveOsdName(message);
case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
- return handleGiveDeviceVendorId(null);
+ return handleGiveDeviceVendorId(message);
case Constants.MESSAGE_CEC_VERSION:
return handleCecVersion();
case Constants.MESSAGE_GET_CEC_VERSION:
@@ -380,25 +378,33 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly
@Constants.HandleMessageResult
- protected int handleGivePhysicalAddress(@Nullable SendMessageCallback callback) {
+ protected int handleGivePhysicalAddress(HdmiCecMessage message) {
assertRunOnServiceThread();
-
int physicalAddress = mService.getPhysicalAddress();
- HdmiCecMessage cecMessage =
- HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, physicalAddress, mDeviceType);
- mService.sendCecCommand(cecMessage, callback);
+ if (physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+ } else {
+ HdmiCecMessage cecMessage =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mDeviceInfo.getLogicalAddress(), physicalAddress, mDeviceType);
+ mService.sendCecCommand(cecMessage);
+ }
return Constants.HANDLED;
}
@ServiceThreadOnly
@Constants.HandleMessageResult
- protected int handleGiveDeviceVendorId(@Nullable SendMessageCallback callback) {
+ protected int handleGiveDeviceVendorId(HdmiCecMessage message) {
assertRunOnServiceThread();
int vendorId = mService.getVendorId();
- HdmiCecMessage cecMessage =
- HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, vendorId);
- mService.sendCecCommand(cecMessage, callback);
+ if (vendorId == Result.FAILURE_UNKNOWN) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNABLE_TO_DETERMINE);
+ } else {
+ HdmiCecMessage cecMessage =
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mDeviceInfo.getLogicalAddress(), vendorId);
+ mService.sendCecCommand(cecMessage);
+ }
return Constants.HANDLED;
}
@@ -469,8 +475,8 @@ abstract class HdmiCecLocalDevice {
protected void buildAndSendSetOsdName(int dest) {
HdmiCecMessage cecMessage =
- HdmiCecMessageBuilder.buildSetOsdNameCommand(
- mAddress, dest, mDeviceInfo.getDisplayName());
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ mDeviceInfo.getLogicalAddress(), dest, mDeviceInfo.getDisplayName());
if (cecMessage != null) {
mService.sendCecCommand(cecMessage, new SendMessageCallback() {
@Override
@@ -518,7 +524,8 @@ abstract class HdmiCecLocalDevice {
if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals(
HdmiUtils.getDefaultDeviceName(address))) {
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
+ HdmiCecMessageBuilder.buildGiveOsdNameCommand(
+ mDeviceInfo.getLogicalAddress(), address));
}
return Constants.HANDLED;
@@ -623,8 +630,13 @@ abstract class HdmiCecLocalDevice {
List<Integer> deviceFeatures = getDeviceFeatures();
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildReportFeatures(mAddress, mService.getCecVersion(),
- localDeviceTypes, rcProfile, rcFeatures, deviceFeatures));
+ HdmiCecMessageBuilder.buildReportFeatures(
+ mDeviceInfo.getLogicalAddress(),
+ mService.getCecVersion(),
+ localDeviceTypes,
+ rcProfile,
+ rcFeatures,
+ deviceFeatures));
}
@ServiceThreadOnly
@@ -786,7 +798,9 @@ abstract class HdmiCecLocalDevice {
protected int handleGiveDevicePowerStatus(HdmiCecMessage message) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportPowerStatus(
- mAddress, message.getSource(), mService.getPowerStatus()));
+ mDeviceInfo.getLogicalAddress(),
+ message.getSource(),
+ mService.getPowerStatus()));
return Constants.HANDLED;
}
@@ -795,7 +809,9 @@ abstract class HdmiCecLocalDevice {
// Always report menu active to receive Remote Control.
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportMenuStatus(
- mAddress, message.getSource(), Constants.MENU_STATE_ACTIVATED));
+ mDeviceInfo.getLogicalAddress(),
+ message.getSource(),
+ Constants.MENU_STATE_ACTIVATED));
return Constants.HANDLED;
}
@@ -877,7 +893,7 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly
final void handleAddressAllocated(int logicalAddress, int reason) {
assertRunOnServiceThread();
- mAddress = mPreferredAddress = logicalAddress;
+ mPreferredAddress = logicalAddress;
if (mService.getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0) {
reportFeatures();
}
@@ -907,14 +923,7 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly
boolean isAddressOf(int addr) {
assertRunOnServiceThread();
- return addr == mAddress;
- }
-
- // Resets the logical address to unregistered(15), meaning the logical device is invalid.
- @ServiceThreadOnly
- void clearAddress() {
- assertRunOnServiceThread();
- mAddress = Constants.ADDR_UNREGISTERED;
+ return addr == mDeviceInfo.getLogicalAddress();
}
@ServiceThreadOnly
@@ -1177,7 +1186,8 @@ abstract class HdmiCecLocalDevice {
}
List<SendKeyAction> action = getActions(SendKeyAction.class);
int logicalAddress = findKeyReceiverAddress();
- if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
+ if (logicalAddress == Constants.ADDR_INVALID
+ || logicalAddress == mDeviceInfo.getLogicalAddress()) {
// Don't send key event to invalid device or itself.
Slog.w(
TAG,
@@ -1215,7 +1225,8 @@ abstract class HdmiCecLocalDevice {
}
List<SendKeyAction> action = getActions(SendKeyAction.class);
int logicalAddress = findAudioReceiverAddress();
- if (logicalAddress == Constants.ADDR_INVALID || logicalAddress == mAddress) {
+ if (logicalAddress == Constants.ADDR_INVALID
+ || logicalAddress == mDeviceInfo.getLogicalAddress()) {
// Don't send key event to invalid device or itself.
Slog.w(
TAG,
@@ -1269,9 +1280,11 @@ abstract class HdmiCecLocalDevice {
void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildUserControlPressed(mAddress, targetAddress, cecKeycode));
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mDeviceInfo.getLogicalAddress(), targetAddress, cecKeycode));
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildUserControlReleased(mAddress, targetAddress));
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mDeviceInfo.getLogicalAddress(), targetAddress));
}
void addActiveSourceHistoryItem(ActiveSource activeSource, boolean isActiveSource,
@@ -1291,7 +1304,6 @@ abstract class HdmiCecLocalDevice {
/** Dump internal status of HdmiCecLocalDevice object. */
protected void dump(final IndentingPrintWriter pw) {
pw.println("mDeviceType: " + mDeviceType);
- pw.println("mAddress: " + mAddress);
pw.println("mPreferredAddress: " + mPreferredAddress);
pw.println("mDeviceInfo: " + mDeviceInfo);
pw.println("mActiveSource: " + getActiveSource());
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 0bb128536b42..1fa6241e8b94 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -34,7 +34,6 @@ import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.os.SystemProperties;
-import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -108,10 +107,12 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
- mRoutingControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false);
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mRoutingControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ mSystemAudioControlFeatureEnabled = mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
}
private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml";
@@ -253,9 +254,12 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mService.getPhysicalAddress(), mDeviceType));
+ getDeviceInfo().getLogicalAddress(),
+ mService.getPhysicalAddress(),
+ mDeviceType));
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId()));
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ getDeviceInfo().getLogicalAddress(), mService.getVendorId()));
mService.registerTvInputCallback(mTvInputCallback);
// Some TVs, for example Mi TV, need ARC on before turning System Audio Mode on
// to request Short Audio Descriptor. Since ARC and SAM are independent,
@@ -405,7 +409,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportSystemAudioMode(
- mAddress, message.getSource(), isSystemAudioModeOnOrTurningOn));
+ getDeviceInfo().getLogicalAddress(),
+ message.getSource(),
+ isSystemAudioModeOnOrTurningOn));
return Constants.HANDLED;
}
@@ -487,7 +493,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
} else {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
- mAddress, message.getSource(), sadBytes));
+ getDeviceInfo().getLogicalAddress(), message.getSource(), sadBytes));
return Constants.HANDLED;
}
}
@@ -655,7 +661,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, systemAudioStatusOn));
+ getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ systemAudioStatusOn));
if (systemAudioStatusOn) {
// If TV sends out SAM Request with a path of a non-CEC device, which should not show
@@ -753,7 +761,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildReportAudioStatus(
- mAddress, source, scaledVolume, mute));
+ getDeviceInfo().getLogicalAddress(), source, scaledVolume, mute));
}
/**
@@ -857,7 +865,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
}
- void onSystemAduioControlFeatureSupportChanged(boolean enabled) {
+ void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
setSystemAudioControlFeatureEnabled(enabled);
if (enabled) {
addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
@@ -873,7 +881,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
@ServiceThreadOnly
- void setRoutingControlFeatureEnables(boolean enabled) {
+ void setRoutingControlFeatureEnabled(boolean enabled) {
assertRunOnServiceThread();
synchronized (mLock) {
mRoutingControlFeatureEnabled = enabled;
@@ -907,7 +915,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
setRoutingPort(portId);
setLocalActivePort(portId);
HdmiCecMessage routingChange =
- HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+ HdmiCecMessageBuilder.buildRoutingChange(
+ getDeviceInfo().getLogicalAddress(), oldPath, newPath);
mService.sendCecCommand(routingChange);
}
@@ -932,7 +941,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// send <Set System Audio Mode> [“Off”]
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, false));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, false));
}
}
@@ -980,7 +989,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
setSystemAudioMode(true);
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, true));
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST, true));
return Constants.HANDLED;
}
// Check if TV supports System Audio Control.
@@ -991,7 +1000,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
setSystemAudioMode(true);
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- mAddress, Constants.ADDR_BROADCAST, true));
+ getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ true));
} else {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
@@ -1149,8 +1160,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return;
}
// Otherwise will switch to the current active port and broadcast routing information.
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingInformation(
- mAddress, routingInformationPath));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildRoutingInformation(
+ getDeviceInfo().getLogicalAddress(), routingInformationPath));
routeToInputFromPortId(getRoutingPort());
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 37ee76b7c615..4376c9a9d22b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -75,10 +75,14 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
getDeviceInfo().getDeviceType(), Constants.ADDR_BROADCAST,
"HdmiCecLocalDevicePlayback#onAddressAllocated()");
}
- mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mService.getPhysicalAddress(), mDeviceType));
- mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
- mAddress, mService.getVendorId()));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ getDeviceInfo().getLogicalAddress(),
+ mService.getPhysicalAddress(),
+ mDeviceType));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ getDeviceInfo().getLogicalAddress(), mService.getVendorId()));
// Actively send out an OSD name to the TV to update the TV panel in case the TV
// does not query the OSD name on time. This is not a required behavior by the spec.
// It is used for some TVs that need the OSD name update but don't query it themselves.
@@ -87,8 +91,10 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// If current device is not a functional audio system device,
// send message to potential audio system device in the system to get the system
// audio mode status. If no response, set to false.
- mService.sendCecCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mAddress, Constants.ADDR_AUDIO_SYSTEM), new SendMessageCallback() {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_AUDIO_SYSTEM),
+ new SendMessageCallback() {
@Override
public void onSendCompleted(int error) {
if (error != SendMessageResult.SUCCESS) {
@@ -140,36 +146,46 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
// Invalidate the internal active source record when going to standby
mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
"HdmiCecLocalDevicePlayback#onStandby()");
- boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
- == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
if (!wasActiveSource) {
return;
}
- if (initiatedByCec || !mTvSendStandbyOnSleep) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
- mService.getPhysicalAddress()));
+ if (initiatedByCec) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildInactiveSource(
+ getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress()));
return;
}
switch (standbyAction) {
case HdmiControlService.STANDBY_SCREEN_OFF:
// Get latest setting value
@HdmiControlManager.PowerControlMode
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- switch (sendStandbyOnSleep) {
+ switch (powerControlMode) {
case HdmiControlManager.POWER_CONTROL_MODE_TV:
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
+ break;
+ case HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM:
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM));
break;
case HdmiControlManager.POWER_CONTROL_MODE_BROADCAST:
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress,
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(),
Constants.ADDR_BROADCAST));
break;
case HdmiControlManager.POWER_CONTROL_MODE_NONE:
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+ HdmiCecMessageBuilder.buildInactiveSource(
+ getDeviceInfo().getLogicalAddress(),
mService.getPhysicalAddress()));
break;
}
@@ -177,7 +193,8 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
case HdmiControlService.STANDBY_SHUTDOWN:
// ACTION_SHUTDOWN is taken as a signal to power off all the devices.
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
break;
}
}
@@ -326,7 +343,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
protected int handleSystemAudioModeStatus(HdmiCecMessage message) {
// Only directly addressed System Audio Mode Status message can change internal
// system audio mode status.
- if (message.getDestination() == mAddress
+ if (message.getDestination() == getDeviceInfo().getLogicalAddress()
&& message.getSource() == Constants.ADDR_AUDIO_SYSTEM) {
boolean setSystemAudioModeOn = HdmiUtils.parseCommandParamSystemAudioStatus(message);
if (mService.isSystemAudioActivated() != setSystemAudioModeOn) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 1c726e0a8fbd..d4fa1df9dda7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -107,14 +107,22 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected void sendStandby(int deviceId) {
assertRunOnServiceThread();
- String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ String powerControlMode = mService.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- if (sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST)) {
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
return;
}
- mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_TV));
+ if (powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_AUDIO_SYSTEM));
+ }
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index acfeb6c77db2..bfb387f1c3f9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -38,11 +38,9 @@ import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
-import android.provider.Settings.Global;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -155,8 +153,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiCecLocalDeviceTv(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_TV);
mPrevPortId = Constants.INVALID_PORT_ID;
- mSystemAudioControlFeatureEnabled =
- mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
+ mSystemAudioControlFeatureEnabled = service.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
}
@@ -169,10 +168,14 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
mArcFeatureEnabled.put(port.getId(), port.isArcSupported());
}
mService.registerTvInputCallback(mTvInputCallback);
- mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mService.getPhysicalAddress(), mDeviceType));
- mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
- mAddress, mService.getVendorId()));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ getDeviceInfo().getLogicalAddress(),
+ mService.getPhysicalAddress(),
+ mDeviceType));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ getDeviceInfo().getLogicalAddress(), mService.getVendorId()));
mService.getHdmiCecNetwork().addCecSwitch(
mService.getHdmiCecNetwork().getPhysicalAddress()); // TV is a CEC switch too.
mTvInputs.clear();
@@ -183,7 +186,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
launchDeviceDiscovery();
startQueuedActions();
if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRequestActiveSource(mAddress));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildRequestActiveSource(
+ getDeviceInfo().getLogicalAddress()));
}
}
@@ -258,23 +263,27 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
return;
}
- removeAction(DeviceSelectAction.class);
- addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
+ removeAction(DeviceSelectActionFromTv.class);
+ addAndStartAction(new DeviceSelectActionFromTv(this, targetDevice, callback));
}
@ServiceThreadOnly
private void handleSelectInternalSource() {
assertRunOnServiceThread();
// Seq #18
- if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) {
- updateActiveSource(mAddress, mService.getPhysicalAddress(),
+ if (mService.isControlEnabled()
+ && getActiveSource().logicalAddress != getDeviceInfo().getLogicalAddress()) {
+ updateActiveSource(
+ getDeviceInfo().getLogicalAddress(),
+ mService.getPhysicalAddress(),
"HdmiCecLocalDeviceTv#handleSelectInternalSource()");
if (mSkipRoutingControl) {
mSkipRoutingControl = false;
return;
}
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- mAddress, mService.getPhysicalAddress());
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ getDeviceInfo().getLogicalAddress(), mService.getPhysicalAddress());
mService.sendCecCommand(activeSource);
}
}
@@ -295,7 +304,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
setActiveSource(newActive, caller);
int logicalAddress = newActive.logicalAddress;
if (mService.getHdmiCecNetwork().getCecDeviceInfo(logicalAddress) != null
- && logicalAddress != mAddress) {
+ && logicalAddress != getDeviceInfo().getLogicalAddress()) {
if (mService.pathToPortId(newActive.physicalAddress) == getActivePortId()) {
setPrevPortId(getActivePortId());
}
@@ -374,22 +383,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
int newPath = mService.portIdToPath(portId);
- startRoutingControl(oldPath, newPath, true, callback);
+ startRoutingControl(oldPath, newPath, callback);
}
@ServiceThreadOnly
- void startRoutingControl(int oldPath, int newPath, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (oldPath == newPath) {
return;
}
HdmiCecMessage routingChange =
- HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+ HdmiCecMessageBuilder.buildRoutingChange(
+ getDeviceInfo().getLogicalAddress(), oldPath, newPath);
mService.sendCecCommand(routingChange);
removeAction(RoutingControlAction.class);
addAndStartAction(
- new RoutingControlAction(this, newPath, queryDevicePowerStatus, callback));
+ new RoutingControlAction(this, newPath, callback));
}
@ServiceThreadOnly
@@ -411,6 +420,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
}
@Override
+ protected int findAudioReceiverAddress() {
+ return Constants.ADDR_AUDIO_SYSTEM;
+ }
+
+ @Override
@ServiceThreadOnly
@Constants.HandleMessageResult
protected int handleActiveSource(HdmiCecMessage message) {
@@ -482,9 +496,10 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
protected int handleRequestActiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
// Seq #19
- if (mAddress == getActiveSource().logicalAddress) {
+ if (getDeviceInfo().getLogicalAddress() == getActiveSource().logicalAddress) {
mService.sendCecCommand(
- HdmiCecMessageBuilder.buildActiveSource(mAddress, getActivePath()));
+ HdmiCecMessageBuilder.buildActiveSource(
+ getDeviceInfo().getLogicalAddress(), getActivePath()));
}
return Constants.HANDLED;
}
@@ -503,8 +518,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly
boolean broadcastMenuLanguage(String language) {
assertRunOnServiceThread();
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
- mAddress, language);
+ HdmiCecMessage command =
+ HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
+ getDeviceInfo().getLogicalAddress(), language);
if (command != null) {
mService.sendCecCommand(command);
return true;
@@ -567,7 +583,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, false, null);
+ startRoutingControl(getActivePath(), newPath, null);
return true;
}
return false;
@@ -612,7 +628,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
getActiveSource().invalidate();
removeAction(RoutingControlAction.class);
int newPath = HdmiUtils.twoBytesToInt(params, 2);
- addAndStartAction(new RoutingControlAction(this, newPath, true, null));
+ addAndStartAction(new RoutingControlAction(this, newPath, null));
}
return Constants.HANDLED;
}
@@ -926,10 +942,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
synchronized (mLock) {
mSystemAudioMute = mute;
mSystemAudioVolume = volume;
- int maxVolume = mService.getAudioManager().getStreamMaxVolume(
- AudioManager.STREAM_MUSIC);
- mService.setAudioStatus(mute,
- VolumeControlAction.scaleToCustomVolume(volume, maxVolume));
displayOsd(HdmiControlManager.OSD_MESSAGE_AVR_VOLUME_CHANGED,
mute ? HdmiControlManager.AVR_VOLUME_MUTED : volume);
}
@@ -1189,7 +1201,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Seq #23
if (isTailOfActivePath(path, getActivePath())) {
int newPath = mService.portIdToPath(getActivePortId());
- startRoutingControl(getActivePath(), newPath, true, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
}
@@ -1207,15 +1219,16 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
if (!routingForBootup && !isProhibitMode()) {
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- startRoutingControl(getActivePath(), newPath, routingForBootup, null);
+ startRoutingControl(getActivePath(), newPath, null);
}
} else {
int activePath = mService.getPhysicalAddress();
setActivePath(activePath);
if (!routingForBootup
&& !mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(mAddress,
- activePath));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildActiveSource(
+ getDeviceInfo().getLogicalAddress(), activePath));
}
}
}
@@ -1337,8 +1350,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
== HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
if (!initiatedByCec && sendStandbyOnSleep) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(
- mAddress, Constants.ADDR_BROADCAST));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), Constants.ADDR_BROADCAST));
}
}
@@ -1411,7 +1425,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// Remove one touch record action so that other one touch record can be started.
removeAction(OneTouchRecordAction.class);
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRecordOff(mAddress, recorderAddress));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildRecordOff(
+ getDeviceInfo().getLogicalAddress(), recorderAddress));
Slog.i(TAG, "Stop [One Touch Record]-Target:" + recorderAddress);
}
@@ -1492,16 +1508,19 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
HdmiCecMessage message = null;
switch (sourceType) {
case TIMER_RECORDING_TYPE_DIGITAL:
- message = HdmiCecMessageBuilder.buildClearDigitalTimer(mAddress, recorderAddress,
- recordSource);
+ message =
+ HdmiCecMessageBuilder.buildClearDigitalTimer(
+ getDeviceInfo().getLogicalAddress(), recorderAddress, recordSource);
break;
case TIMER_RECORDING_TYPE_ANALOGUE:
- message = HdmiCecMessageBuilder.buildClearAnalogueTimer(mAddress, recorderAddress,
- recordSource);
+ message =
+ HdmiCecMessageBuilder.buildClearAnalogueTimer(
+ getDeviceInfo().getLogicalAddress(), recorderAddress, recordSource);
break;
case TIMER_RECORDING_TYPE_EXTERNAL:
- message = HdmiCecMessageBuilder.buildClearExternalTimer(mAddress, recorderAddress,
- recordSource);
+ message =
+ HdmiCecMessageBuilder.buildClearExternalTimer(
+ getDeviceInfo().getLogicalAddress(), recorderAddress, recordSource);
break;
default:
Slog.w(TAG, "Invalid source type:" + recorderAddress);
@@ -1569,7 +1588,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return;
}
int targetAddress = targetDevice.getLogicalAddress();
- mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress));
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(
+ getDeviceInfo().getLogicalAddress(), targetAddress));
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
index c85fd50c62fe..e3292a35bf6b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessage.java
@@ -119,6 +119,10 @@ public final class HdmiCecMessage {
if (mParams.length > 0) {
if (filterMessageParameters(mOpcode)) {
s.append(String.format(" <Redacted len=%d>", mParams.length));
+ } else if (isUserControlPressedMessage(mOpcode)) {
+ s.append(
+ String.format(
+ " <Keycode type = %s>", HdmiCecKeycode.getKeycodeType(mParams[0])));
} else {
for (byte data : mParams) {
s.append(String.format(":%02X", data));
@@ -287,7 +291,6 @@ public final class HdmiCecMessage {
private static boolean filterMessageParameters(int opcode) {
switch (opcode) {
- case Constants.MESSAGE_USER_CONTROL_PRESSED:
case Constants.MESSAGE_USER_CONTROL_RELEASED:
case Constants.MESSAGE_SET_OSD_NAME:
case Constants.MESSAGE_SET_OSD_STRING:
@@ -300,5 +303,20 @@ public final class HdmiCecMessage {
return false;
}
}
+
+ private static boolean isUserControlPressedMessage(int opcode) {
+ return Constants.MESSAGE_USER_CONTROL_PRESSED == opcode;
+ }
+
+ static boolean isCecTransportMessage(int opcode) {
+ switch (opcode) {
+ case Constants.MESSAGE_REQUEST_CURRENT_LATENCY:
+ case Constants.MESSAGE_REPORT_CURRENT_LATENCY:
+ case Constants.MESSAGE_CDC_MESSAGE:
+ return true;
+ default:
+ return false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 7ceaa959212e..225785a4401d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -166,18 +166,6 @@ public class HdmiCecNetwork {
}
return false;
}
- /**
- * Clear all logical addresses registered in the device.
- *
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
- */
- @ServiceThreadOnly
- void clearLogicalAddress() {
- assertRunOnServiceThread();
- for (int i = 0; i < mLocalDevices.size(); ++i) {
- mLocalDevices.valueAt(i).clearAddress();
- }
- }
@ServiceThreadOnly
void clearLocalDevices() {
@@ -862,7 +850,7 @@ public class HdmiCecNetwork {
private boolean isLocalDeviceAddress(int address) {
for (int i = 0; i < mLocalDevices.size(); i++) {
int key = mLocalDevices.keyAt(i);
- if (mLocalDevices.get(key).mAddress == address) {
+ if (mLocalDevices.get(key).getDeviceInfo().getLogicalAddress() == address) {
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java
index c4dadaa19bd4..552ff376a2d6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecPowerStatusController.java
@@ -77,8 +77,10 @@ class HdmiCecPowerStatusController {
private void sendReportPowerStatus(int powerStatus) {
for (HdmiCecLocalDevice localDevice : mHdmiControlService.getAllLocalDevices()) {
mHdmiControlService.sendCecCommand(
- HdmiCecMessageBuilder.buildReportPowerStatus(localDevice.mAddress,
- Constants.ADDR_BROADCAST, powerStatus));
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ localDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ powerStatus));
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a086bdaa6ec9..f993b5d91fdb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -405,7 +405,7 @@ public class HdmiControlService extends SystemService {
private TvInputManager mTvInputManager;
@Nullable
- private PowerManager mPowerManager;
+ private PowerManagerWrapper mPowerManager;
@Nullable
private Looper mIoLooper;
@@ -618,6 +618,53 @@ public class HdmiControlService extends SystemService {
initializeCec(INITIATED_BY_ENABLE_CEC);
}
}, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL)
+ == HdmiControlManager.ROUTING_CONTROL_ENABLED;
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.w(TAG, "Switch device has not registered yet."
+ + " Can't turn routing on.");
+ } else {
+ audioSystem().setRoutingControlFeatureEnabled(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean enabled = mHdmiCecConfig.getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL)
+ == HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED;
+ if (isTvDeviceEnabled()) {
+ tv().setSystemAudioControlFeatureEnabled(enabled);
+ }
+ if (isAudioSystemDevice()) {
+ if (audioSystem() == null) {
+ Slog.e(TAG, "Audio System device has not registered yet."
+ + " Can't turn system audio mode on.");
+ } else {
+ audioSystem().onSystemAudioControlFeatureSupportChanged(enabled);
+ }
+ }
+ }
+ }, mServiceThreadExecutor);
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
+ }
+ }, mServiceThreadExecutor);
mHdmiCecConfig.registerChangeListener(
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
new HdmiCecConfig.SettingChangeListener() {
@@ -634,6 +681,12 @@ public class HdmiControlService extends SystemService {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
+ // Start all actions that were queued because the device was in standby
+ if (mAddressAllocated) {
+ for (HdmiCecLocalDevice localDevice : getAllLocalDevices()) {
+ localDevice.startQueuedActions();
+ }
+ }
}
}
@@ -681,7 +734,7 @@ public class HdmiControlService extends SystemService {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mTvInputManager = (TvInputManager) getContext().getSystemService(
Context.TV_INPUT_SERVICE);
- mPowerManager = getContext().getSystemService(PowerManager.class);
+ mPowerManager = new PowerManagerWrapper(getContext());
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
runOnServiceThread(this::bootCompleted);
}
@@ -702,7 +755,11 @@ public class HdmiControlService extends SystemService {
}
@VisibleForTesting
- protected PowerManager getPowerManager() {
+ void setPowerManager(PowerManagerWrapper powerManager) {
+ mPowerManager = powerManager;
+ }
+
+ PowerManagerWrapper getPowerManager() {
return mPowerManager;
}
@@ -757,11 +814,8 @@ public class HdmiControlService extends SystemService {
private void registerContentObserver() {
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
- Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
- Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Global.MHL_INPUT_SWITCHING_ENABLED,
Global.MHL_POWER_CHARGE_ENABLED,
- Global.HDMI_CEC_SWITCH_ENABLED,
Global.DEVICE_NAME
};
for (String s : settings) {
@@ -781,33 +835,6 @@ public class HdmiControlService extends SystemService {
String option = uri.getLastPathSegment();
boolean enabled = readBooleanSetting(option, true);
switch (option) {
- case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
- setHdmiCecVolumeControlEnabledInternal(getHdmiCecConfig().getIntValue(
- HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE));
- break;
- case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
- if (isTvDeviceEnabled()) {
- tv().setSystemAudioControlFeatureEnabled(enabled);
- }
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.e(TAG, "Audio System device has not registered yet."
- + " Can't turn system audio mode on.");
- break;
- }
- audioSystem().onSystemAduioControlFeatureSupportChanged(enabled);
- }
- break;
- case Global.HDMI_CEC_SWITCH_ENABLED:
- if (isAudioSystemDevice()) {
- if (audioSystem() == null) {
- Slog.w(TAG, "Switch device has not registered yet."
- + " Can't turn routing on.");
- break;
- }
- audioSystem().setRoutingControlFeatureEnables(enabled);
- }
- break;
case Global.MHL_INPUT_SWITCHING_ENABLED:
setMhlInputChangeEnabled(enabled);
break;
@@ -1222,13 +1249,11 @@ public class HdmiControlService extends SystemService {
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
+ // initPortInfo at hotplug event.
+ mHdmiCecNetwork.initPortInfo();
if (connected && !isTvDevice()
&& getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) {
- if (isSwitchDevice()) {
- mHdmiCecNetwork.initPortInfo();
- HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
- }
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
HdmiCecLocalDevice localDevice = mHdmiCecNetwork.getLocalDevice(type);
@@ -1378,8 +1403,9 @@ public class HdmiControlService extends SystemService {
deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(),
deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(),
newDisplayName, deviceInfo.getDevicePowerStatus(), deviceInfo.getCecVersion()));
- sendCecCommand(HdmiCecMessageBuilder.buildSetOsdNameCommand(
- device.mAddress, Constants.ADDR_TV, newDisplayName));
+ sendCecCommand(
+ HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ deviceInfo.getLogicalAddress(), Constants.ADDR_TV, newDisplayName));
}
}
@@ -1658,11 +1684,6 @@ public class HdmiControlService extends SystemService {
Slog.e(TAG, "Callback cannot be null");
return;
}
- if (isPowerStandby()) {
- Slog.e(TAG, "Device is in standby. Not handling deviceSelect");
- invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
- return;
- }
HdmiCecLocalDeviceTv tv = tv();
if (tv == null) {
if (!mAddressAllocated) {
@@ -1705,11 +1726,6 @@ public class HdmiControlService extends SystemService {
Slog.e(TAG, "Callback cannot be null");
return;
}
- if (isPowerStandby()) {
- Slog.e(TAG, "Device is in standby. Not handling portSelect");
- invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
- return;
- }
HdmiCecLocalDeviceTv tv = tv();
if (tv != null) {
tv.doManualPortSwitching(portId, callback);
@@ -2277,7 +2293,9 @@ public class HdmiControlService extends SystemService {
}
sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- audioSystem().mAddress, Constants.ADDR_BROADCAST, true));
+ audioSystem().getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ true));
}
});
}
@@ -2359,7 +2377,7 @@ public class HdmiControlService extends SystemService {
@Override
public List<String> getUserCecSettings() {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getUserSettings();
} finally {
@@ -2370,7 +2388,7 @@ public class HdmiControlService extends SystemService {
@Override
public List<String> getAllowedCecSettingStringValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getAllowedStringValues(name);
} finally {
@@ -2381,7 +2399,7 @@ public class HdmiControlService extends SystemService {
@Override
public int[] getAllowedCecSettingIntValues(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
List<Integer> allowedValues =
HdmiControlService.this.getHdmiCecConfig().getAllowedIntValues(name);
@@ -2394,7 +2412,7 @@ public class HdmiControlService extends SystemService {
@Override
public String getCecSettingStringValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getStringValue(name);
} finally {
@@ -2405,7 +2423,7 @@ public class HdmiControlService extends SystemService {
@Override
public void setCecSettingStringValue(String name, String value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setStringValue(name, value);
} finally {
@@ -2416,7 +2434,7 @@ public class HdmiControlService extends SystemService {
@Override
public int getCecSettingIntValue(String name) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
return HdmiControlService.this.getHdmiCecConfig().getIntValue(name);
} finally {
@@ -2427,7 +2445,7 @@ public class HdmiControlService extends SystemService {
@Override
public void setCecSettingIntValue(String name, int value) {
initBinderCall();
- long token = Binder.clearCallingIdentity();
+ final long token = Binder.clearCallingIdentity();
try {
HdmiControlService.this.getHdmiCecConfig().setIntValue(name, value);
} finally {
@@ -3134,7 +3152,7 @@ public class HdmiControlService extends SystemService {
});
}
- private boolean canGoToStandby() {
+ boolean canGoToStandby() {
for (HdmiCecLocalDevice device : mHdmiCecNetwork.getLocalDeviceList()) {
if (!device.canGoToStandby()) return false;
}
@@ -3183,7 +3201,6 @@ public class HdmiControlService extends SystemService {
return;
}
mCecController.clearLogicalAddress();
- mHdmiCecNetwork.clearLogicalAddress();
mHdmiCecNetwork.clearLocalDevices();
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 4c4c9783fab0..6fd7a72fc287 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -237,15 +237,15 @@ final class HotplugDetectionAction extends HdmiCecFeatureAction {
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectAction> actions = getActions(DeviceSelectAction.class);
+ List<DeviceSelectActionFromTv> actions = getActions(DeviceSelectActionFromTv.class);
if (actions.isEmpty()) {
return;
}
// Should have only one Device Select Action
- DeviceSelectAction action = actions.get(0);
+ DeviceSelectActionFromTv action = actions.get(0);
if (action.getTargetAddress() == address) {
- removeAction(DeviceSelectAction.class);
+ removeAction(DeviceSelectActionFromTv.class);
}
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 979e7a452e43..3dcf72eb38d6 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -178,10 +178,11 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
if (service.isAudioSystemDevice()) {
return false;
}
- @HdmiControlManager.PowerControlMode String sendStandbyOnSleep =
+ @HdmiControlManager.PowerControlMode String powerControlMode =
service.getHdmiCecConfig().getStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
- return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+ return powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM)
+ || powerControlMode.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
diff --git a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
new file mode 100644
index 000000000000..f0810687290e
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+/**
+ * Abstraction around {@link PowerManager} to allow faking PowerManager in tests.
+ */
+public class PowerManagerWrapper {
+ private static final String TAG = "PowerManagerWrapper";
+
+ private final PowerManager mPowerManager;
+
+ public PowerManagerWrapper(Context context) {
+ mPowerManager = context.getSystemService(PowerManager.class);
+ }
+
+ boolean isInteractive() {
+ return mPowerManager.isInteractive();
+ }
+
+ void wakeUp(long time, int reason, String details) {
+ mPowerManager.wakeUp(time, reason, details);
+ }
+
+ void goToSleep(long time, int reason, int flags) {
+ mPowerManager.goToSleep(time, reason, flags);
+ }
+
+ WakeLock newWakeLock(int levelAndFlags, String tag) {
+ return mPowerManager.newWakeLock(levelAndFlags, tag);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/RequestSadAction.java b/services/core/java/com/android/server/hdmi/RequestSadAction.java
new file mode 100644
index 000000000000..4d36078e3359
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/RequestSadAction.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Feature action that queries the Short Audio Descriptor (SAD) of another device. This action is
+ * initiated from the Android system working as TV device to get the SAD of the connected audio
+ * system device.
+ * <p>
+ * Package-private
+ */
+final class RequestSadAction extends HdmiCecFeatureAction {
+ private static final String TAG = "RequestSadAction";
+
+ // State in which the action is waiting for <Report Short Audio Descriptor>.
+ private static final int STATE_WAITING_FOR_REPORT_SAD = 1;
+
+ private static final List<Integer> ALL_CEC_CODECS = new ArrayList<Integer>(Arrays.asList(
+ Constants.AUDIO_CODEC_LPCM,
+ Constants.AUDIO_CODEC_DD,
+ Constants.AUDIO_CODEC_MPEG1,
+ Constants.AUDIO_CODEC_MP3,
+ Constants.AUDIO_CODEC_MPEG2,
+ Constants.AUDIO_CODEC_AAC,
+ Constants.AUDIO_CODEC_DTS,
+ Constants.AUDIO_CODEC_ATRAC,
+ Constants.AUDIO_CODEC_ONEBITAUDIO,
+ Constants.AUDIO_CODEC_DDP,
+ Constants.AUDIO_CODEC_DTSHD,
+ Constants.AUDIO_CODEC_TRUEHD,
+ Constants.AUDIO_CODEC_DST,
+ Constants.AUDIO_CODEC_WMAPRO,
+ Constants.AUDIO_CODEC_MAX));
+ private static final int MAX_SAD_PER_REQUEST = 4;
+ private static final int RETRY_COUNTER_MAX = 1;
+ private final int mTargetAddress;
+ private final RequestSadCallback mCallback;
+ // List of all valid SADs reported by the target device. Not parsed nor deduplicated.
+ private final List<byte[]> mSupportedSads = new ArrayList<>();
+ private int mQueriedSadCount = 0; // Number of SADs queries that has already been completed
+ private int mTimeoutRetry = 0; // Number of times we have already retried on time-out
+
+ /**
+ * Constructor.
+ *
+ * @param source an instance of {@link HdmiCecLocalDevice}.
+ * @param targetAddress the logical address the SAD is directed at.
+ */
+ RequestSadAction(HdmiCecLocalDevice source, int targetAddress, RequestSadCallback callback) {
+ super(source);
+ mTargetAddress = targetAddress;
+ mCallback = Objects.requireNonNull(callback);
+ }
+
+
+ @Override
+ boolean start() {
+ querySad();
+ return true;
+ }
+
+ private void querySad() {
+ if (mQueriedSadCount >= ALL_CEC_CODECS.size()) {
+ wrapUpAndFinish();
+ return;
+ }
+ int[] codecsToQuery = ALL_CEC_CODECS.subList(mQueriedSadCount,
+ Math.min(ALL_CEC_CODECS.size(), mQueriedSadCount + MAX_SAD_PER_REQUEST))
+ .stream().mapToInt(i -> i).toArray();
+ sendCommand(HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(getSourceAddress(),
+ mTargetAddress, codecsToQuery));
+ mState = STATE_WAITING_FOR_REPORT_SAD;
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_REPORT_SAD
+ || mTargetAddress != cmd.getSource()) {
+ return false;
+ }
+ if (cmd.getOpcode() == Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR) {
+ if (cmd.getParams() == null || cmd.getParams().length == 0
+ || cmd.getParams().length % 3 != 0) {
+ // Invalid message. Wait for time-out and query again.
+ return true;
+ }
+ for (int i = 0; i < cmd.getParams().length - 2; i += 3) {
+ if (isValidCodec(cmd.getParams()[i])) {
+ byte[] sad = new byte[]{cmd.getParams()[i], cmd.getParams()[i + 1],
+ cmd.getParams()[i + 2]};
+ updateResult(sad);
+ }
+ // Don't include invalid codecs in the result. Don't query again.
+ Slog.w(TAG, "Received invalid codec " + cmd.getParams()[i] + ".");
+ }
+ mQueriedSadCount += MAX_SAD_PER_REQUEST;
+ mTimeoutRetry = 0;
+ querySad();
+ return true;
+ }
+ if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT
+ && (cmd.getParams()[0] & 0xFF) == Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR
+ && (cmd.getParams()[1] & 0xFF) == Constants.ABORT_INVALID_OPERAND) {
+ // Queried SADs are not supported
+ mQueriedSadCount += MAX_SAD_PER_REQUEST;
+ mTimeoutRetry = 0;
+ querySad();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isValidCodec(byte codec) {
+ return Constants.AUDIO_CODEC_NONE < (codec & 0xFF)
+ && (codec & 0xFF) <= Constants.AUDIO_CODEC_MAX;
+ }
+
+ private void updateResult(byte[] sad) {
+ mSupportedSads.add(sad);
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+ if (state == STATE_WAITING_FOR_REPORT_SAD) {
+ if (++mTimeoutRetry <= RETRY_COUNTER_MAX) {
+ querySad();
+ return;
+ }
+ mQueriedSadCount += MAX_SAD_PER_REQUEST;
+ mTimeoutRetry = 0;
+ querySad();
+ }
+ }
+
+ private void wrapUpAndFinish() {
+ mCallback.onRequestSadDone(mSupportedSads);
+ finish();
+ }
+
+ /**
+ * Interface used to report result of SAD request.
+ */
+ interface RequestSadCallback {
+ /**
+ * Called when SAD request is done.
+ *
+ * @param sads a list of all supported SADs. It can be an empty list.
+ */
+ void onRequestSadDone(List<byte[]> supportedSads);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index b9404e407b88..0c4fb26ce3b1 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -17,11 +17,10 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.util.Slog;
-import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Feature action for routing control. Exchanges routing-related commands with other devices
@@ -41,23 +40,12 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
// State in which we wait for <Routing Information> to arrive. If timed out, we use the
// latest routing path to set the new active source.
- private static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
-
- // State in which we wait for <Report Power Status> in response to <Give Device Power Status>
- // we have sent. If the response tells us the device power is on, we send <Set Stream Path>
- // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly
- // just show the blank screen.
- private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+ @VisibleForTesting
+ static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
// Time out in millseconds used for <Routing Information>
private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
- // Time out in milliseconds used for <Report Power Status>
- private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
-
- // true if <Give Power Status> should be sent once the new active routing path is determined.
- private final boolean mQueryDevicePowerStatus;
-
// If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
// the routing control/active source change happens. The listener should be called if
// the events are triggered by external events such as manual switch port change or incoming
@@ -67,11 +55,9 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
// The latest routing path. Updated by each <Routing Information> from CEC switches.
private int mCurrentRoutingPath;
- RoutingControlAction(HdmiCecLocalDevice localDevice, int path, boolean queryDevicePowerStatus,
- IHdmiControlCallback callback) {
+ RoutingControlAction(HdmiCecLocalDevice localDevice, int path, IHdmiControlCallback callback) {
super(localDevice, callback);
mCurrentRoutingPath = path;
- mQueryDevicePowerStatus = queryDevicePowerStatus;
// Callback is non-null when routing control action is brought up by binder API. Use
// this as an indicator for the input change notification. These API calls will get
// the result through this callback, not through notification. Any other events that
@@ -104,39 +90,16 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
removeActionExcept(RoutingControlAction.class, this);
addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
return true;
- } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
- && opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
- handleReportPowerStatus(cmd.getParams()[0]);
- return true;
}
return false;
}
- private void handleReportPowerStatus(int devicePowerStatus) {
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- if (isPowerOnOrTransient(devicePowerStatus)) {
- sendSetStreamPath();
- }
- }
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
-
private void updateActiveInput() {
HdmiCecLocalDeviceTv tv = tv();
tv.setPrevPortId(tv.getActivePortId());
tv.updateActiveInput(mCurrentRoutingPath, mNotifyInputChange);
}
- private int getTvPowerStatus() {
- return tv().getPowerStatus();
- }
-
- private static boolean isPowerOnOrTransient(int status) {
- return status == HdmiControlManager.POWER_STATUS_ON
- || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
- }
-
private void sendSetStreamPath() {
sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(getSourceAddress(),
mCurrentRoutingPath));
@@ -150,46 +113,13 @@ final class RoutingControlAction extends HdmiCecFeatureAction {
}
switch (timeoutState) {
case STATE_WAIT_FOR_ROUTING_INFORMATION:
- HdmiDeviceInfo device =
- localDevice().mService.getHdmiCecNetwork().getDeviceInfoByPath(
- mCurrentRoutingPath);
- if (device != null && mQueryDevicePowerStatus) {
- int deviceLogicalAddress = device.getLogicalAddress();
- queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
- @Override
- public void onSendCompleted(int error) {
- handlDevicePowerStatusAckResult(
- error == HdmiControlManager.RESULT_SUCCESS);
- }
- });
- } else {
- updateActiveInput();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
- }
- return;
- case STATE_WAIT_FOR_REPORT_POWER_STATUS:
- if (isPowerOnOrTransient(getTvPowerStatus())) {
- updateActiveInput();
- sendSetStreamPath();
- }
+ updateActiveInput();
+ sendSetStreamPath();
finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
return;
- }
- }
-
- private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
- sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(getSourceAddress(), address),
- callback);
- }
-
- private void handlDevicePowerStatusAckResult(boolean acked) {
- if (acked) {
- mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
- addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
- } else {
- updateActiveInput();
- sendSetStreamPath();
- finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ default:
+ Slog.e("CEC", "Invalid timeoutState (" + timeoutState + ").");
+ return;
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 5ad7fab888fd..adcef667545f 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -20,6 +20,7 @@ import static com.android.server.hdmi.HdmiConfig.IRT_MS;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.util.Slog;
import android.view.KeyEvent;
+
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
/**
@@ -152,7 +153,7 @@ final class SendKeyAction extends HdmiCecFeatureAction {
// audio system device is still plugged in. Framework checks if the volume key forwarding is
// successful or not every time to make sure the System Audio Mode status is still updated.
if (mTargetAddress == Constants.ADDR_AUDIO_SYSTEM
- && localDevice().mAddress != Constants.ADDR_TV) {
+ && localDevice().getDeviceInfo().getLogicalAddress() != Constants.ADDR_TV) {
sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(),
mTargetAddress, cecKeycodeAndParams), new SendMessageCallback() {
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6fb9e58a49d1..c905fe07cf6d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -189,7 +189,7 @@ public class InputManagerService extends IInputManager.Stub
private final InputManagerHandler mHandler;
// Context cache used for loading pointer resources.
- private Context mDisplayContext;
+ private Context mPointerIconDisplayContext;
private final File mDoubleTouchGestureEnableFile;
@@ -839,21 +839,31 @@ public class InputManagerService extends IInputManager.Stub
throw new IllegalArgumentException("mode is invalid");
}
if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
- if (event instanceof MotionEvent) {
- final Context dispCtx = getContextForDisplay(event.getDisplayId());
- final Display display = dispCtx.getDisplay();
+ // Motion events that are pointer events or relative mouse events will need to have the
+ // inverse display rotation applied to them.
+ if (event instanceof MotionEvent
+ && (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
+ || event.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE))) {
+ Context displayContext = getContextForDisplay(event.getDisplayId());
+ if (displayContext == null) {
+ displayContext = Objects.requireNonNull(
+ getContextForDisplay(Display.DEFAULT_DISPLAY));
+ }
+ final Display display = displayContext.getDisplay();
final int rotation = display.getRotation();
if (rotation != ROTATION_0) {
final MotionEvent motion = (MotionEvent) event;
// Injections are currently expected to be in the space of the injector (ie.
- // usually assumed to be post-rotated). Thus we need to unrotate into raw
+ // usually assumed to be post-rotated). Thus we need to un-rotate into raw
// input coordinates for dispatch.
final Point sz = new Point();
- display.getRealSize(sz);
- if ((rotation % 2) != 0) {
- final int tmpX = sz.x;
- sz.x = sz.y;
- sz.y = tmpX;
+ if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ display.getRealSize(sz);
+ if ((rotation % 2) != 0) {
+ final int tmpX = sz.x;
+ sz.x = sz.y;
+ sz.y = tmpX;
+ }
}
motion.applyTransform(MotionEvent.createRotateMatrix(
(4 - rotation), sz.x, sz.y));
@@ -1742,6 +1752,11 @@ public class InputManagerService extends IInputManager.Stub
/** Clean up input window handles of the given display. */
public void onDisplayRemoved(int displayId) {
+ if (mPointerIconDisplayContext != null
+ && mPointerIconDisplayContext.getDisplay().getDisplayId() == displayId) {
+ mPointerIconDisplayContext = null;
+ }
+
nativeDisplayRemoved(mPtr, displayId);
}
@@ -2856,8 +2871,7 @@ public class InputManagerService extends IInputManager.Stub
};
for (File baseDir: baseDirs) {
File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
- try {
- InputStream stream = new FileInputStream(confFile);
+ try (InputStream stream = new FileInputStream(confFile)) {
names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
@@ -2890,8 +2904,7 @@ public class InputManagerService extends IInputManager.Stub
final File baseDir = Environment.getVendorDirectory();
final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
- try {
- final InputStream stream = new FileInputStream(confFile);
+ try (final InputStream stream = new FileInputStream(confFile)) {
return ConfigurationProcessor.processInputPortAssociations(stream);
} catch (FileNotFoundException e) {
// Most of the time, file will not exist, which is expected.
@@ -2971,24 +2984,43 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
private PointerIcon getPointerIcon(int displayId) {
- return PointerIcon.getDefaultIcon(getContextForDisplay(displayId));
+ return PointerIcon.getDefaultIcon(getContextForPointerIcon(displayId));
}
- private Context getContextForDisplay(int displayId) {
- if (mDisplayContext != null && mDisplayContext.getDisplay().getDisplayId() == displayId) {
- return mDisplayContext;
+ @NonNull
+ private Context getContextForPointerIcon(int displayId) {
+ if (mPointerIconDisplayContext != null
+ && mPointerIconDisplayContext.getDisplay().getDisplayId() == displayId) {
+ return mPointerIconDisplayContext;
}
+ // Create and cache context for non-default display.
+ mPointerIconDisplayContext = getContextForDisplay(displayId);
+
+ // Fall back to default display if the requested displayId does not exist.
+ if (mPointerIconDisplayContext == null) {
+ mPointerIconDisplayContext = getContextForDisplay(Display.DEFAULT_DISPLAY);
+ }
+ return mPointerIconDisplayContext;
+ }
+
+ @Nullable
+ private Context getContextForDisplay(int displayId) {
+ if (displayId == Display.INVALID_DISPLAY) {
+ return null;
+ }
if (mContext.getDisplay().getDisplayId() == displayId) {
- mDisplayContext = mContext;
- return mDisplayContext;
+ return mContext;
}
- // Create and cache context for non-default display.
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
final Display display = displayManager.getDisplay(displayId);
- mDisplayContext = mContext.createDisplayContext(display);
- return mDisplayContext;
+ if (display == null) {
+ return null;
+ }
+
+ return mContext.createDisplayContext(display);
}
// Native callback.
@@ -3012,10 +3044,10 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void visitKeyboardLayout(Resources resources,
int keyboardLayoutResId, KeyboardLayout layout) {
- try {
+ try (final InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
result[0] = layout.getDescriptor();
- result[1] = Streams.readFully(new InputStreamReader(
- resources.openRawResource(keyboardLayoutResId)));
+ result[1] = Streams.readFully(stream);
} catch (IOException ex) {
} catch (NotFoundException ex) {
}
diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java
index 720be826fd38..29aa2a873cc9 100644
--- a/services/core/java/com/android/server/input/InputShellCommand.java
+++ b/services/core/java/com/android/server/input/InputShellCommand.java
@@ -313,8 +313,13 @@ public class InputShellCommand extends ShellCommand {
injectKeyEvent(event);
if (longpress) {
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
// Some long press behavior would check the event time, we set a new event time here.
- final long nextEventTime = now + ViewConfiguration.getGlobalActionKeyTimeout();
+ final long nextEventTime = now + ViewConfiguration.getLongPressTimeout();
injectKeyEvent(KeyEvent.changeTimeRepeat(event, nextEventTime, 1 /* repeatCount */,
KeyEvent.FLAG_LONG_PRESS));
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index dc955337fdbc..62b36d0ab468 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -44,7 +44,6 @@ import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_FOR
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_IME_WITH_HARD_KEYBOARD;
import static android.server.inputmethod.InputMethodManagerServiceProto.SHOW_REQUESTED;
import static android.server.inputmethod.InputMethodManagerServiceProto.SYSTEM_READY;
-import static android.util.imetracing.ImeTracing.IME_TRACING_FROM_IMMS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
@@ -59,10 +58,10 @@ import android.annotation.BinderThread;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.UiThread;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -124,7 +123,6 @@ import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
-import android.util.imetracing.ImeTracing;
import android.util.proto.ProtoOutputStream;
import android.view.IWindowManager;
import android.view.InputChannel;
@@ -156,10 +154,12 @@ import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.CallbackUtils;
import com.android.internal.inputmethod.IBooleanResultCallback;
-import com.android.internal.inputmethod.IIInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IInputContentUriTokenResultCallback;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.IVoidResultCallback;
+import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.inputmethod.StartInputFlags;
@@ -180,9 +180,9 @@ import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputBindResult;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -263,6 +263,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
+ private static final String HANDLER_THREAD_NAME = "android.imms";
/**
* Binding flags for establishing connection to the {@link InputMethodService}.
@@ -1593,7 +1594,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mIPackageManager = AppGlobals.getPackageManager();
mContext = context;
mRes = context.getResources();
- mHandler = new Handler(this);
+ // TODO(b/196206770): Disallow I/O on this thread. Currently it's needed for loading
+ // additional subtypes in switchUserOnHandlerLocked().
+ final ServiceThread thread = new ServiceThread(
+ HANDLER_THREAD_NAME, Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+ thread.start();
+ mHandler = Handler.createAsync(thread.getLooper(), this);
// Note: SettingsObserver doesn't register observers in its constructor.
mSettingsObserver = new SettingsObserver(mHandler);
mIWindowManager = IWindowManager.Stub.asInterface(
@@ -1602,7 +1608,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mImeDisplayValidator = displayId -> mWindowManagerInternal.getDisplayImePolicy(displayId);
- mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
+ mCaller = new HandlerCaller(context, thread.getLooper(), new HandlerCaller.Callback() {
@Override
public void executeMessage(Message msg) {
handleMessage(msg);
@@ -2528,9 +2534,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
// Dispatch display id for InputMethodService to update context display.
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
- MSG_INITIALIZE_IME, mCurTokenDisplayId, mCurMethod, mCurToken,
- mMethodMap.get(mCurMethodId).getConfigChanges()));
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_INITIALIZE_IME,
+ mMethodMap.get(mCurMethodId).getConfigChanges(), mCurMethod, mCurToken));
scheduleNotifyImeUidToAudioService(mCurMethodUid);
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
@@ -3928,7 +3933,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void startProtoDump(byte[] protoDump, int source, String where) {
- if (protoDump == null && source != IME_TRACING_FROM_IMMS) {
+ if (protoDump == null && source != ImeTracing.IME_TRACING_FROM_IMMS) {
// Dump not triggered from IMMS, but no proto information provided.
return;
}
@@ -3955,7 +3960,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
proto.write(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE, protoDump);
proto.end(service_token);
break;
- case IME_TRACING_FROM_IMMS:
+ case ImeTracing.IME_TRACING_FROM_IMMS:
final long managerservice_token =
proto.start(InputMethodManagerServiceTraceFileProto.ENTRY);
proto.write(InputMethodManagerServiceTraceProto.ELAPSED_REALTIME_NANOS,
@@ -4123,6 +4128,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ /** Called right after {@link IInputMethod#showSoftInput}. */
+ private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
+ @SoftInputShowHideReason int reason) {
+ final WindowManagerInternal.ImeTargetInfo info =
+ mWindowManagerInternal.onToggleImeRequested(
+ show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
+ mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
+ mCurFocusedWindowClient, mCurAttribute, info.focusedWindowName,
+ mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
+ info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName));
+ }
+
@BinderThread
private void hideMySoftInput(@NonNull IBinder token, int flags) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
@@ -4161,7 +4178,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- void setEnabledSessionInMainThread(SessionState session) {
+ void setEnabledSessionInHandlerThread(SessionState session) {
if (mEnabledSession != session) {
if (mEnabledSession != null && mEnabledSession.session != null) {
try {
@@ -4181,7 +4198,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- @MainThread
+ @UiThread
@Override
public boolean handleMessage(Message msg) {
SomeArgs args;
@@ -4241,18 +4258,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
+ args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
+ final IBinder token = (IBinder) args.arg3;
((IInputMethod) args.arg1).showSoftInput(
- (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
- mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
- mCurFocusedWindowClient, mCurAttribute,
- mWindowManagerInternal.getWindowName(mCurFocusedWindow),
- mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
- mWindowManagerInternal.getWindowName(
- mShowRequestWindowMap.get(args.arg3)),
- mWindowManagerInternal.getImeControlTargetNameForLogging(
- mCurTokenDisplayId),
- mWindowManagerInternal.getImeTargetNameForLogging(
- mCurTokenDisplayId)));
+ token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
+ final IBinder requestToken = mShowRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(true /* show */, requestToken, reason);
} catch (RemoteException e) {
}
args.recycle();
@@ -4264,18 +4274,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
+ args.arg3 + ", " + args.arg2 + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
+ final IBinder token = (IBinder) args.arg3;
((IInputMethod)args.arg1).hideSoftInput(
- (IBinder) args.arg3, 0, (ResultReceiver)args.arg2);
- mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
- mCurFocusedWindowClient, mCurAttribute,
- mWindowManagerInternal.getWindowName(mCurFocusedWindow),
- mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
- mWindowManagerInternal.getWindowName(
- mHideRequestWindowMap.get(args.arg3)),
- mWindowManagerInternal.getImeControlTargetNameForLogging(
- mCurTokenDisplayId),
- mWindowManagerInternal.getImeTargetNameForLogging(
- mCurTokenDisplayId)));
+ token, 0 /* flags */, (ResultReceiver) args.arg2);
+ final IBinder requestToken = mHideRequestWindowMap.get(token);
+ onShowHideSoftInputRequested(false /* show */, requestToken, reason);
} catch (RemoteException e) {
}
args.recycle();
@@ -4292,12 +4295,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
try {
if (DEBUG) {
Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
- + msg.arg1);
+ + mCurTokenDisplayId);
}
final IBinder token = (IBinder) args.arg2;
- ((IInputMethod) args.arg1).initializeInternal(token, msg.arg1,
- new InputMethodPrivilegedOperationsImpl(this, token),
- (int) args.arg3);
+ ((IInputMethod) args.arg1).initializeInternal(token,
+ new InputMethodPrivilegedOperationsImpl(this, token), msg.arg1);
} catch (RemoteException e) {
}
args.recycle();
@@ -4359,7 +4361,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final IInputContext inputContext = (IInputContext) args.arg3;
final EditorInfo editorInfo = (EditorInfo) args.arg4;
try {
- setEnabledSessionInMainThread(session);
+ setEnabledSessionInHandlerThread(session);
session.method.startInput(startInputToken, inputContext, missingMethods,
editorInfo, restarting);
} catch (RemoteException e) {
@@ -5840,7 +5842,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@BinderThread
@Override
public void createInputContentUriToken(Uri contentUri, String packageName,
- IIInputContentUriTokenResultCallback resultCallback) {
+ IInputContentUriTokenResultCallback resultCallback) {
CallbackUtils.onResult(resultCallback,
() -> mImms.createInputContentUriToken(mToken, contentUri, packageName));
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 73baf79ea4b1..b8bb399a4442 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -200,6 +200,7 @@ public class InputMethodMenuController {
final Window w = mSwitchingDialog.getWindow();
final WindowManager.LayoutParams attrs = w.getAttributes();
w.setType(WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+ w.setHideOverlayWindows(true);
// Use an alternate token for the dialog for that window manager can group the token
// with other IME windows based on type vs. grouping based on whichever token happens
// to get selected by the system later on.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java b/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
deleted file mode 100644
index a6a68934accd..000000000000
--- a/services/core/java/com/android/server/inputmethod/InputMethodSystemProperty.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.inputmethod;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Build;
-import android.os.SystemProperties;
-
-/**
- * Various (pseudo) constants about IME behaviors.
- */
-public class InputMethodSystemProperty {
- /**
- * System property key for the production use. The value must be either empty or a valid
- * (flattened) component name of the multi-client IME.
- */
- private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
-
- /**
- * System property key for debugging purpose. The value must be either empty or a valid
- * (flattened) component name of the multi-client IME.
- *
- * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
- */
- private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
-
- @Nullable
- private static ComponentName getMultiClientImeComponentName() {
- if (Build.IS_DEBUGGABLE) {
- // If debuggable, allow developers to override the multi-client IME component name
- // with a different (writable) key.
- final ComponentName debugIme = ComponentName.unflattenFromString(
- SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
- if (debugIme != null) {
- return debugIme;
- }
- }
- return ComponentName.unflattenFromString(
- SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
- }
-
- /**
- * {@link ComponentName} of multi-client IME to be used.
- */
- @Nullable
- static final ComponentName sMultiClientImeComponentName = getMultiClientImeComponentName();
-
- /**
- * {@code true} when multi-client IME is enabled.
- */
- public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null);
-}
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
deleted file mode 100644
index aa4fa7c6f470..000000000000
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ /dev/null
@@ -1,1859 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.inputmethod;
-
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.AnyThread;
-import android.annotation.BinderThread;
-import android.annotation.IntDef;
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.annotation.WorkerThread;
-import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.inputmethodservice.MultiClientInputMethodServiceDelegate;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.IMultiClientInputMethod;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-import com.android.internal.inputmethod.IMultiClientInputMethodSession;
-import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.inputmethod.StartInputFlags;
-import com.android.internal.inputmethod.StartInputReason;
-import com.android.internal.inputmethod.UnbindReason;
-import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodClient;
-import com.android.internal.view.IInputMethodManager;
-import com.android.internal.view.IInputMethodSession;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.internal.view.InputBindResult;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
-import com.android.server.wm.WindowManagerInternal;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.util.Collections;
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * Actual implementation of multi-client InputMethodManagerService.
- *
- * <p>This system service is intentionally compatible with {@link InputMethodManagerService} so that
- * we can switch the implementation at the boot time.</p>
- */
-public final class MultiClientInputMethodManagerService {
- private static final String TAG = "MultiClientInputMethodManagerService";
- private static final boolean DEBUG = false;
-
- private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE =
- "config_perDisplayFocusEnabled is not true.";
-
- private static final String PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG =
- "Consider rebuilding the system image after enabling config_perDisplayFocusEnabled to "
- + "make IME focus compatible with multi-client IME mode.";
-
- private static final long RECONNECT_DELAY_MSEC = 1000;
-
- /**
- * Unlike {@link InputMethodManagerService}, {@link MultiClientInputMethodManagerService}
- * always binds to the IME with {@link Context#BIND_FOREGROUND_SERVICE} for now for simplicity.
- */
- private static final int IME_CONNECTION_UNIFIED_BIND_FLAGS =
- Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_VISIBLE
- | Context.BIND_NOT_FOREGROUND
- | Context.BIND_FOREGROUND_SERVICE;
-
- private static final ComponentName sImeComponentName =
- InputMethodSystemProperty.sMultiClientImeComponentName;
-
- private static void reportNotSupported() {
- if (DEBUG) {
- Slog.d(TAG, "non-supported operation. callers=" + Debug.getCallers(3));
- }
- }
-
- /**
- * {@link MultiClientInputMethodManagerService} is not intended to be instantiated.
- */
- private MultiClientInputMethodManagerService() {
- }
-
- /**
- * The implementation of {@link SystemService} for multi-client IME.
- */
- public static final class Lifecycle extends SystemService {
- private final ApiCallbacks mApiCallbacks;
- private final OnWorkerThreadCallback mOnWorkerThreadCallback;
-
- @MainThread
- public Lifecycle(Context context) {
- super(context);
-
- final UserToInputMethodInfoMap userIdToInputMethodInfoMapper =
- new UserToInputMethodInfoMap();
- final UserDataMap userDataMap = new UserDataMap();
- final HandlerThread workerThread = new HandlerThread(TAG);
- workerThread.start();
- mApiCallbacks = new ApiCallbacks(context, userDataMap, userIdToInputMethodInfoMapper);
- mOnWorkerThreadCallback = new OnWorkerThreadCallback(
- context, userDataMap, userIdToInputMethodInfoMapper,
- new Handler(workerThread.getLooper(), msg -> false, true));
-
- LocalServices.addService(InputMethodManagerInternal.class,
- new InputMethodManagerInternal() {
- @Override
- public void setInteractive(boolean interactive) {
- reportNotSupported();
- }
-
- @Override
- public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
- reportNotSupported();
- }
-
- @Override
- public List<InputMethodInfo> getInputMethodListAsUser(
- @UserIdInt int userId) {
- return userIdToInputMethodInfoMapper.getAsList(userId);
- }
-
- @Override
- public List<InputMethodInfo> getEnabledInputMethodListAsUser(
- @UserIdInt int userId) {
- return userIdToInputMethodInfoMapper.getAsList(userId);
- }
-
- @Override
- public void onCreateInlineSuggestionsRequest(int userId,
- InlineSuggestionsRequestInfo requestInfo,
- IInlineSuggestionsRequestCallback cb) {
- try {
- cb.onInlineSuggestionsUnsupported();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
- }
- }
-
- @Override
- public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- reportNotSupported();
- return false;
- }
-
- @Override
- public void registerInputMethodListListener(
- InputMethodListListener listener) {
- reportNotSupported();
- }
-
- @Override
- public boolean transferTouchFocusToImeWindow(
- @NonNull IBinder sourceInputToken, int displayId) {
- reportNotSupported();
- return false;
- }
-
- @Override
- public void reportImeControl(@Nullable IBinder windowToken,
- boolean imeParentChanged) {
- }
-
- @Override
- public void removeImeSurface() {
- reportNotSupported();
- }
-
- @Override
- public void updateImeWindowStatus(boolean disableImeIcon) {
- }
- });
- }
-
- @MainThread
- @Override
- public void onBootPhase(int phase) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onBootPhase, mOnWorkerThreadCallback, phase));
- }
-
- @MainThread
- @Override
- public void onStart() {
- publishBinderService(Context.INPUT_METHOD_SERVICE, mApiCallbacks);
- }
-
- @MainThread
- @Override
- public void onUserStarting(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onStartUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
-
- @MainThread
- @Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onUnlockUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
-
- @MainThread
- @Override
- public void onUserStopping(@NonNull TargetUser user) {
- mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::onStopUser, mOnWorkerThreadCallback,
- user.getUserIdentifier()));
- }
- }
-
- private static final class OnWorkerThreadCallback {
- private final Context mContext;
- private final UserDataMap mUserDataMap;
- private final UserToInputMethodInfoMap mInputMethodInfoMap;
- private final Handler mHandler;
-
- OnWorkerThreadCallback(Context context, UserDataMap userDataMap,
- UserToInputMethodInfoMap inputMethodInfoMap, Handler handler) {
- mContext = context;
- mUserDataMap = userDataMap;
- mInputMethodInfoMap = inputMethodInfoMap;
- mHandler = handler;
- }
-
- @AnyThread
- Handler getHandler() {
- return mHandler;
- }
-
- @WorkerThread
- private void tryBindInputMethodService(@UserIdInt int userId) {
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.i(TAG, "tryBindInputMethodService is called for an unknown user=" + userId);
- return;
- }
-
- final InputMethodInfo imi = queryInputMethod(mContext, userId, sImeComponentName);
- if (imi == null) {
- Slog.w(TAG, "Multi-client InputMethod is not found. component="
- + sImeComponentName);
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- // Safe to clean up.
- mInputMethodInfoMap.remove(userId);
- break;
- }
- }
- return;
- }
-
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- // If the user is still locked, we currently do not try to start IME.
- return;
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- break;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED:
- // OK, nothing to do.
- return;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- data.mState = PerUserState.SERVICE_RECOGNIZED;
- data.mCurrentInputMethodInfo = imi;
- mInputMethodInfoMap.put(userId, imi);
- final boolean bindResult = data.bindServiceLocked(mContext, userId);
- if (!bindResult) {
- Slog.e(TAG, "Failed to bind Multi-client InputMethod.");
- return;
- }
- data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
- }
- }
-
- @WorkerThread
- void onStartUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onStartUser userId=" + userId);
- }
- final PerUserData data = new PerUserData(userId, null, PerUserState.USER_LOCKED, this);
- mUserDataMap.put(userId, data);
- }
-
- @WorkerThread
- void onUnlockUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onUnlockUser() userId=" + userId);
- }
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.i(TAG, "onUnlockUser is called for an unknown user=" + userId);
- return;
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- data.mState = PerUserState.SERVICE_NOT_QUERIED;
- tryBindInputMethodService(userId);
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- break;
- }
- }
- }
-
- @WorkerThread
- void onStopUser(@UserIdInt int userId) {
- if (DEBUG) {
- Slog.v(TAG, "onStopUser() userId=" + userId);
- }
- mInputMethodInfoMap.remove(userId);
- final PerUserData data = mUserDataMap.removeReturnOld(userId);
- if (data == null) {
- Slog.i(TAG, "onStopUser is called for an unknown user=" + userId);
- return;
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.UNBIND_CALLED:
- // OK, nothing to do.
- return;
- case PerUserState.SERVICE_CONNECTED:
- case PerUserState.WAITING_SERVICE_CONNECTED:
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- break;
- }
- data.unbindServiceLocked(mContext);
- data.mState = PerUserState.UNBIND_CALLED;
- data.mCurrentInputMethod = null;
-
- // When a Service is explicitly unbound with Context.unbindService(),
- // onServiceDisconnected() will not be triggered. Hence here we explicitly call
- // onInputMethodDisconnectedLocked() as if the Service is already gone.
- data.onInputMethodDisconnectedLocked();
- }
- }
-
- @WorkerThread
- void onServiceConnected(PerUserData data, IMultiClientInputMethod service) {
- if (DEBUG) {
- Slog.v(TAG, "onServiceConnected() data.mUserId=" + data.mUserId);
- }
- synchronized (data.mLock) {
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- // OK.
- data.mState = PerUserState.SERVICE_CONNECTED;
- data.mCurrentInputMethod = service;
- try {
- data.mCurrentInputMethod.initialize(new ImeCallbacks(data));
- } catch (RemoteException e) {
- }
- data.onInputMethodConnectedLocked();
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onServiceDisconnected(PerUserData data) {
- if (DEBUG) {
- Slog.v(TAG, "onServiceDisconnected() data.mUserId=" + data.mUserId);
- }
- final WindowManagerInternal windowManagerInternal =
- LocalServices.getService(WindowManagerInternal.class);
- synchronized (data.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
- windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
- }
- data.mDisplayIdToImeWindowTokenMap.clear();
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED:
- // onServiceDisconnected() means the biding is still alive.
- data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
- data.mCurrentInputMethod = null;
- data.onInputMethodDisconnectedLocked();
- break;
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onBindingDied(PerUserData data) {
- if (DEBUG) {
- Slog.v(TAG, "onBindingDied() data.mUserId=" + data.mUserId);
- }
- final WindowManagerInternal windowManagerInternal =
- LocalServices.getService(WindowManagerInternal.class);
- synchronized (data.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
- windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
- }
- data.mDisplayIdToImeWindowTokenMap.clear();
- switch (data.mState) {
- case PerUserState.UNBIND_CALLED:
- // We should ignore this callback.
- return;
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.SERVICE_CONNECTED: {
- // onBindingDied() means the biding is dead.
- data.mState = PerUserState.UNBIND_CALLED;
- data.mCurrentInputMethod = null;
- data.onInputMethodDisconnectedLocked();
- // Schedule a retry
- mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
- OnWorkerThreadCallback::tryBindInputMethodService,
- this, data.mUserId), RECONNECT_DELAY_MSEC);
- break;
- }
- default:
- Slog.wtf(TAG, "Unknown state=" + data.mState);
- return;
- }
- }
- }
-
- @WorkerThread
- void onBootPhase(int phase) {
- if (DEBUG) {
- Slog.v(TAG, "onBootPhase() phase=" + phase);
- }
- switch (phase) {
- case SystemService.PHASE_ACTIVITY_MANAGER_READY: {
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addDataScheme("package");
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- onPackageAdded(intent);
- }
- }, filter, null, mHandler);
- break;
- }
- case SystemService.PHASE_BOOT_COMPLETED: {
- final boolean perDisplayFocusEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_perDisplayFocusEnabled);
- if (!perDisplayFocusEnabled) {
- final Bundle extras = new Bundle();
- extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
- mContext.getSystemService(NotificationManager.class).notifyAsUser(TAG,
- SystemMessageProto.SystemMessage.NOTE_SELECT_INPUT_METHOD,
- new Notification.Builder(mContext,
- SystemNotificationChannels.VIRTUAL_KEYBOARD)
- .setContentTitle(PER_DISPLAY_FOCUS_DISABLED_WARNING_TITLE)
- .setStyle(new Notification.BigTextStyle()
- .bigText(PER_DISPLAY_FOCUS_DISABLED_WARNING_MSG))
- .setSmallIcon(R.drawable.ic_notification_ime_default)
- .setWhen(0)
- .setOngoing(true)
- .setLocalOnly(true)
- .addExtras(extras)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setColor(mContext.getColor(
- R.color.system_notification_accent_color))
- .build(), UserHandle.ALL);
- }
- break;
- }
- }
- }
-
- @WorkerThread
- void onPackageAdded(Intent intent) {
- if (DEBUG) {
- Slog.v(TAG, "onPackageAdded() intent=" + intent);
- }
- final Uri uri = intent.getData();
- if (uri == null) {
- return;
- }
- if (!intent.hasExtra(Intent.EXTRA_UID)) {
- return;
- }
- final String packageName = uri.getSchemeSpecificPart();
- if (sImeComponentName == null
- || packageName == null
- || !TextUtils.equals(sImeComponentName.getPackageName(), packageName)) {
- return;
- }
- final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0));
- tryBindInputMethodService(userId);
- }
- }
-
- private static final class WindowInfo {
- final IBinder mWindowToken;
- final int mWindowHandle;
-
- WindowInfo(IBinder windowToken, int windowCookie) {
- mWindowToken = windowToken;
- mWindowHandle = windowCookie;
- }
- }
-
- /**
- * Describes the state of each IME client.
- */
- @Retention(SOURCE)
- @IntDef({InputMethodClientState.REGISTERED,
- InputMethodClientState.WAITING_FOR_IME_SESSION,
- InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT,
- InputMethodClientState.ALREADY_SENT_BIND_RESULT,
- InputMethodClientState.UNREGISTERED})
- private @interface InputMethodClientState {
- /**
- * {@link IInputMethodManager#addClient(IInputMethodClient, IInputContext, int)} is called
- * and this client is now recognized by the system. When the system lost the connection to
- * the current IME, all the clients need to be re-initialized from this state.
- */
- int REGISTERED = 1;
- /**
- * This client is notified to the current IME with {@link
- * IMultiClientInputMethod#addClient(int, int, int, int)} but the IME is not yet responded
- * with {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
- * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
- */
- int WAITING_FOR_IME_SESSION = 2;
- /**
- * This client is already accepted by the IME but a valid {@link InputBindResult} has not
- * been returned to the client yet.
- */
- int READY_TO_SEND_FIRST_BIND_RESULT = 3;
- /**
- * This client has already received a valid {@link InputBindResult} at least once. This
- * means that the client can directly call {@link IInputMethodSession} IPCs and key events
- * via {@link InputChannel}. When the current IME is unbound, these client end points also
- * need to be cleared.
- */
- int ALREADY_SENT_BIND_RESULT = 4;
- /**
- * The client process is dying.
- */
- int UNREGISTERED = 5;
- }
-
- private static final class InputMethodClientIdSource {
- @GuardedBy("InputMethodClientIdSource.class")
- private static int sNextValue = 0;
-
- private InputMethodClientIdSource() {
- }
-
- static synchronized int getNext() {
- final int result = sNextValue;
- sNextValue++;
- if (sNextValue < 0) {
- sNextValue = 0;
- }
- return result;
- }
- }
-
- private static final class WindowHandleSource {
- @GuardedBy("WindowHandleSource.class")
- private static int sNextValue = 0;
-
- private WindowHandleSource() {
- }
-
- static synchronized int getNext() {
- final int result = sNextValue;
- sNextValue++;
- if (sNextValue < 0) {
- sNextValue = 0;
- }
- return result;
- }
- }
-
- private static final class InputMethodClientInfo {
- final IInputMethodClient mClient;
- final int mUid;
- final int mPid;
- final int mSelfReportedDisplayId;
- final int mClientId;
-
- @GuardedBy("PerUserData.mLock")
- @InputMethodClientState
- int mState;
- @GuardedBy("PerUserData.mLock")
- int mBindingSequence;
- @GuardedBy("PerUserData.mLock")
- InputChannel mWriteChannel;
- @GuardedBy("PerUserData.mLock")
- IInputMethodSession mInputMethodSession;
- @GuardedBy("PerUserData.mLock")
- IMultiClientInputMethodSession mMSInputMethodSession;
- @GuardedBy("PerUserData.mLock")
- final WeakHashMap<IBinder, WindowInfo> mWindowMap = new WeakHashMap<>();
-
- InputMethodClientInfo(IInputMethodClient client, int uid, int pid,
- int selfReportedDisplayId) {
- mClient = client;
- mUid = uid;
- mPid = pid;
- mSelfReportedDisplayId = selfReportedDisplayId;
- mClientId = InputMethodClientIdSource.getNext();
- }
-
- @GuardedBy("PerUserData.mLock")
- void dumpLocked(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- ipw.println("mState=" + mState + ",mBindingSequence=" + mBindingSequence
- + ",mWriteChannel=" + mWriteChannel
- + ",mInputMethodSession=" + mInputMethodSession
- + ",mMSInputMethodSession=" + mMSInputMethodSession);
- }
- }
-
- private static final class UserDataMap {
- @GuardedBy("mMap")
- private final SparseArray<PerUserData> mMap = new SparseArray<>();
-
- @AnyThread
- @Nullable
- PerUserData get(@UserIdInt int userId) {
- synchronized (mMap) {
- return mMap.get(userId);
- }
- }
-
- @AnyThread
- void put(@UserIdInt int userId, PerUserData data) {
- synchronized (mMap) {
- mMap.put(userId, data);
- }
- }
-
- @AnyThread
- @Nullable
- PerUserData removeReturnOld(@UserIdInt int userId) {
- synchronized (mMap) {
- return mMap.removeReturnOld(userId);
- }
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mMap) {
- for (int i = 0; i < mMap.size(); i++) {
- int userId = mMap.keyAt(i);
- PerUserData data = mMap.valueAt(i);
- ipw.println("userId=" + userId + ", data=");
- if (data != null) {
- ipw.increaseIndent();
- data.dump(fd, ipw, args);
- ipw.decreaseIndent();
- }
- }
- }
- }
- }
-
- private static final class TokenInfo {
- final Binder mToken;
- final int mDisplayId;
- TokenInfo(Binder token, int displayId) {
- mToken = token;
- mDisplayId = displayId;
- }
- }
-
- @Retention(SOURCE)
- @IntDef({
- PerUserState.USER_LOCKED,
- PerUserState.SERVICE_NOT_QUERIED,
- PerUserState.SERVICE_RECOGNIZED,
- PerUserState.WAITING_SERVICE_CONNECTED,
- PerUserState.SERVICE_CONNECTED,
- PerUserState.UNBIND_CALLED})
- private @interface PerUserState {
- /**
- * The user is still locked.
- */
- int USER_LOCKED = 1;
- /**
- * The system has not queried whether there is a multi-client IME or not.
- */
- int SERVICE_NOT_QUERIED = 2;
- /**
- * A multi-client IME specified in {@link #PROP_DEBUG_MULTI_CLIENT_IME} is found in the
- * system, but not bound yet.
- */
- int SERVICE_RECOGNIZED = 3;
- /**
- * {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle)} is
- * already called for the IME but
- * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is not yet called
- * back. This includes once the IME is bound but temporarily disconnected as notified with
- * {@link ServiceConnection#onServiceDisconnected(ComponentName)}.
- */
- int WAITING_SERVICE_CONNECTED = 4;
- /**
- * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is already called
- * back. The IME is ready to be used.
- */
- int SERVICE_CONNECTED = 5;
- /**
- * The binding is gone. Either {@link Context#unbindService(ServiceConnection)} is
- * explicitly called or the system decided to destroy the binding as notified with
- * {@link ServiceConnection#onBindingDied(ComponentName)}.
- */
- int UNBIND_CALLED = 6;
- }
-
- /**
- * Takes care of per-user state separation.
- */
- private static final class PerUserData {
- final Object mLock = new Object();
-
- /**
- * User ID (not UID) that is associated with this data.
- */
- @UserIdInt
- private final int mUserId;
-
- /**
- * {@link IMultiClientInputMethod} of the currently connected multi-client IME. This
- * must be non-{@code null} only while {@link #mState} is
- * {@link PerUserState#SERVICE_CONNECTED}.
- */
- @Nullable
- @GuardedBy("mLock")
- IMultiClientInputMethod mCurrentInputMethod;
-
- /**
- * {@link InputMethodInfo} of the currently selected multi-client IME. This must be
- * non-{@code null} unless {@link #mState} is {@link PerUserState#SERVICE_NOT_QUERIED}.
- */
- @GuardedBy("mLock")
- @Nullable
- InputMethodInfo mCurrentInputMethodInfo;
-
- /**
- * Describes the current service state.
- */
- @GuardedBy("mLock")
- @PerUserState
- int mState;
-
- /**
- * A {@link SparseArray} that maps display ID to IME Window token that is already issued to
- * the IME.
- */
- @GuardedBy("mLock")
- final ArraySet<TokenInfo> mDisplayIdToImeWindowTokenMap = new ArraySet<>();
-
- @GuardedBy("mLock")
- private final ArrayMap<IBinder, InputMethodClientInfo> mClientMap = new ArrayMap<>();
-
- @GuardedBy("mLock")
- private SparseArray<InputMethodClientInfo> mClientIdToClientMap = new SparseArray<>();
-
- private final OnWorkerThreadServiceConnection mOnWorkerThreadServiceConnection;
-
- /**
- * A {@link ServiceConnection} that is designed to run on a certain worker thread with
- * which {@link OnWorkerThreadCallback} is associated.
- *
- * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle).
- */
- private static final class OnWorkerThreadServiceConnection implements ServiceConnection {
- private final PerUserData mData;
- private final OnWorkerThreadCallback mCallback;
-
- OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback) {
- mData = data;
- mCallback = callback;
- }
-
- @WorkerThread
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mCallback.onServiceConnected(mData,
- IMultiClientInputMethod.Stub.asInterface(service));
- }
-
- @WorkerThread
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mCallback.onServiceDisconnected(mData);
- }
-
- @WorkerThread
- @Override
- public void onBindingDied(ComponentName name) {
- mCallback.onBindingDied(mData);
- }
-
- Handler getHandler() {
- return mCallback.getHandler();
- }
- }
-
- PerUserData(@UserIdInt int userId, @Nullable InputMethodInfo inputMethodInfo,
- @PerUserState int initialState, OnWorkerThreadCallback callback) {
- mUserId = userId;
- mCurrentInputMethodInfo = inputMethodInfo;
- mState = initialState;
- mOnWorkerThreadServiceConnection =
- new OnWorkerThreadServiceConnection(this, callback);
- }
-
- @GuardedBy("mLock")
- boolean bindServiceLocked(Context context, @UserIdInt int userId) {
- final Intent intent =
- new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
- .setComponent(mCurrentInputMethodInfo.getComponent())
- .putExtra(Intent.EXTRA_CLIENT_LABEL,
- com.android.internal.R.string.input_method_binding_label)
- .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- context, 0,
- new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
- PendingIntent.FLAG_MUTABLE));
-
- // Note: Instead of re-dispatching callback from the main thread to the worker thread
- // where OnWorkerThreadCallback is running, we pass the Handler object here so that
- // the callbacks will be directly dispatched to the worker thread.
- return context.bindServiceAsUser(intent, mOnWorkerThreadServiceConnection,
- IME_CONNECTION_UNIFIED_BIND_FLAGS,
- mOnWorkerThreadServiceConnection.getHandler(), UserHandle.of(userId));
- }
-
- @GuardedBy("mLock")
- void unbindServiceLocked(Context context) {
- context.unbindService(mOnWorkerThreadServiceConnection);
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo getClientLocked(IInputMethodClient client) {
- return mClientMap.get(client.asBinder());
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo getClientFromIdLocked(int clientId) {
- return mClientIdToClientMap.get(clientId);
- }
-
- @GuardedBy("mLock")
- @Nullable
- InputMethodClientInfo removeClientLocked(IInputMethodClient client) {
- final InputMethodClientInfo info = mClientMap.remove(client.asBinder());
- if (info != null) {
- mClientIdToClientMap.remove(info.mClientId);
- }
- return info;
- }
-
- @GuardedBy("mLock")
- void addClientLocked(int uid, int pid, IInputMethodClient client,
- int selfReportedDisplayId) {
- if (getClientLocked(client) != null) {
- Slog.wtf(TAG, "The same client is added multiple times");
- return;
- }
- final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
- try {
- client.asBinder().linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- final InputMethodClientInfo clientInfo =
- new InputMethodClientInfo(client, uid, pid, selfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.REGISTERED;
- mClientMap.put(client.asBinder(), clientInfo);
- mClientIdToClientMap.put(clientInfo.mClientId, clientInfo);
- switch (mState) {
- case PerUserState.SERVICE_CONNECTED:
- try {
- mCurrentInputMethod.addClient(
- clientInfo.mClientId, clientInfo.mPid, clientInfo.mUid,
- clientInfo.mSelfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
- } catch (RemoteException e) {
- // TODO(yukawa): Need logging and expected behavior
- }
- break;
- }
- }
-
- @GuardedBy("mLock")
- void onInputMethodConnectedLocked() {
- final int numClients = mClientMap.size();
- for (int i = 0; i < numClients; ++i) {
- final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- // OK
- break;
- default:
- Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
- return;
- }
- try {
- mCurrentInputMethod.addClient(
- clientInfo.mClientId, clientInfo.mUid, clientInfo.mPid,
- clientInfo.mSelfReportedDisplayId);
- clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
- } catch (RemoteException e) {
- }
- }
- }
-
- @GuardedBy("mLock")
- void onInputMethodDisconnectedLocked() {
- final int numClients = mClientMap.size();
- for (int i = 0; i < numClients; ++i) {
- final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- // Disconnected before onInputMethodConnectedLocked().
- break;
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- // Disconnected between addClient() and acceptClient().
- clientInfo.mState = InputMethodClientState.REGISTERED;
- break;
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- clientInfo.mState = InputMethodClientState.REGISTERED;
- clientInfo.mInputMethodSession = null;
- clientInfo.mMSInputMethodSession = null;
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- break;
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mClient.onUnbindMethod(clientInfo.mBindingSequence,
- UnbindReason.DISCONNECT_IME);
- } catch (RemoteException e) {
- }
- clientInfo.mState = InputMethodClientState.REGISTERED;
- clientInfo.mInputMethodSession = null;
- clientInfo.mMSInputMethodSession = null;
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- break;
- }
- }
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mLock) {
- ipw.println("mState=" + mState
- + ",mCurrentInputMethod=" + mCurrentInputMethod
- + ",mCurrentInputMethodInfo=" + mCurrentInputMethodInfo);
-
- if (mCurrentInputMethod != null) {
- // indentation will not be kept. So add visual separator here.
- ipw.println(">>Dump CurrentInputMethod>>");
- ipw.flush();
- try {
- TransferPipe.dumpAsync(mCurrentInputMethod.asBinder(), fd, args);
- } catch (IOException | RemoteException e) {
- ipw.println("Failed to dump input method service: " + e);
- }
- ipw.println("<<Dump CurrentInputMethod<<");
- }
-
- ipw.println("mDisplayIdToImeWindowTokenMap=");
- for (TokenInfo info : mDisplayIdToImeWindowTokenMap) {
- ipw.println(" display=" + info.mDisplayId + ",token="
- + info.mToken);
- }
- ipw.println("mClientMap=");
- ipw.increaseIndent();
- for (int i = 0; i < mClientMap.size(); i++) {
-
- ipw.println("binder=" + mClientMap.keyAt(i));
- ipw.println(" InputMethodClientInfo=");
- InputMethodClientInfo info = mClientMap.valueAt(i);
- if (info != null) {
- ipw.increaseIndent();
- info.dumpLocked(fd, ipw, args);
- ipw.decreaseIndent();
- }
- }
- ipw.decreaseIndent();
- ipw.println("mClientIdToClientMap=");
- ipw.increaseIndent();
- for (int i = 0; i < mClientIdToClientMap.size(); i++) {
- ipw.println("clientId=" + mClientIdToClientMap.keyAt(i));
- ipw.println(" InputMethodClientInfo=");
- InputMethodClientInfo info = mClientIdToClientMap.valueAt(i);
- if (info != null) {
- ipw.increaseIndent();
- info.dumpLocked(fd, ipw, args);
- ipw.decreaseIndent();
- }
- if (info.mClient != null) {
- // indentation will not be kept. So add visual separator here.
- ipw.println(">>DumpClientStart>>");
- ipw.flush(); // all writes should be flushed to guarantee order.
- try {
- TransferPipe.dumpAsync(info.mClient.asBinder(), fd, args);
- } catch (IOException | RemoteException e) {
- ipw.println(" Failed to dump client:" + e);
- }
- ipw.println("<<DumpClientEnd<<");
- }
- }
- ipw.decreaseIndent();
- }
- }
-
- private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
- private final PerUserData mPerUserData;
- private final IInputMethodClient mClient;
-
- ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client) {
- mPerUserData = perUserData;
- mClient = client;
- }
-
- @BinderThread
- @Override
- public void binderDied() {
- synchronized (mPerUserData.mLock) {
- mClient.asBinder().unlinkToDeath(this, 0);
-
- final InputMethodClientInfo clientInfo =
- mPerUserData.removeClientLocked(mClient);
- if (clientInfo == null) {
- return;
- }
-
- if (clientInfo.mWriteChannel != null) {
- clientInfo.mWriteChannel.dispose();
- clientInfo.mWriteChannel = null;
- }
- if (clientInfo.mInputMethodSession != null) {
- try {
- clientInfo.mInputMethodSession.finishSession();
- } catch (RemoteException e) {
- }
- clientInfo.mInputMethodSession = null;
- }
- clientInfo.mMSInputMethodSession = null;
- clientInfo.mState = InputMethodClientState.UNREGISTERED;
- switch (mPerUserData.mState) {
- case PerUserState.SERVICE_CONNECTED:
- try {
- mPerUserData.mCurrentInputMethod.removeClient(clientInfo.mClientId);
- } catch (RemoteException e) {
- // TODO(yukawa): Need logging and expected behavior
- }
- break;
- }
- }
- }
- }
- }
-
- /**
- * Queries for multi-client IME specified with {@code componentName}.
- *
- * @param context {@link Context} to be used to query component.
- * @param userId User ID for which the multi-client IME is queried.
- * @param componentName {@link ComponentName} to be queried.
- * @return {@link InputMethodInfo} when multi-client IME is found. Otherwise {@code null}.
- */
- @Nullable
- private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
- @Nullable ComponentName componentName) {
- if (componentName == null) {
- Slog.w(TAG, "queryInputMethod invoked with null componentName");
- return null;
- }
-
- // Use for queryIntentServicesAsUser
- final PackageManager pm = context.getPackageManager();
- final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
- new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
- .setComponent(componentName),
- PackageManager.GET_META_DATA, userId);
- try {
- return new InputMethodInfo(context, resolveMultiClientImeService(services));
- } catch (Exception e) {
- Slog.wtf(TAG, "Unable to load input method from services (" + services + ")", e);
- }
- return null;
- }
-
- /**
- * Determines the multi-client IME from the specified {@link List<ResolveInfo>}.
- *
- * @return {@link ResolveInfo} when an appropriate multi-client IME is found.
- * Otherwise {@code null}.
- */
- @Nullable
- @VisibleForTesting
- static ResolveInfo resolveMultiClientImeService(@NonNull List<ResolveInfo> services) {
- if (services.isEmpty()) {
- Slog.e(TAG, "No IME found");
- return null;
- }
- if (services.size() > 1) {
- Slog.e(TAG, "Only one IME service is supported.");
- return null;
- }
- final ResolveInfo ri = services.get(0);
- ServiceInfo si = ri.serviceInfo;
- final String imeId = InputMethodInfo.computeId(ri);
- if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
- Slog.e(TAG, imeId + " must have required"
- + android.Manifest.permission.BIND_INPUT_METHOD);
- return null;
- }
- if (!Build.IS_DEBUGGABLE && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
- return null;
- }
- return ri;
- }
-
- /**
- * Manages the mapping rule from user ID to {@link InputMethodInfo}.
- */
- private static final class UserToInputMethodInfoMap {
- @GuardedBy("mArray")
- private final SparseArray<InputMethodInfo> mArray = new SparseArray<>();
-
- @AnyThread
- void put(@UserIdInt int userId, InputMethodInfo imi) {
- synchronized (mArray) {
- mArray.put(userId, imi);
- }
- }
-
- @AnyThread
- void remove(@UserIdInt int userId) {
- synchronized (mArray) {
- mArray.remove(userId);
- }
- }
-
- @AnyThread
- @Nullable
- InputMethodInfo get(@UserIdInt int userId) {
- synchronized (mArray) {
- return mArray.get(userId);
- }
- }
-
- @AnyThread
- List<InputMethodInfo> getAsList(@UserIdInt int userId) {
- final InputMethodInfo info = get(userId);
- if (info == null) {
- return Collections.emptyList();
- }
- return Collections.singletonList(info);
- }
-
- @AnyThread
- void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
- synchronized (mArray) {
- for (int i = 0; i < mArray.size(); i++) {
- ipw.println("userId=" + mArray.keyAt(i));
- ipw.println(" InputMethodInfo=" + mArray.valueAt(i));
- }
- }
- }
- }
-
- /**
- * Takes care of IPCs exposed to the multi-client IME.
- */
- private static final class ImeCallbacks
- extends IMultiClientInputMethodPrivilegedOperations.Stub {
- private final PerUserData mPerUserData;
- private final WindowManagerInternal mIWindowManagerInternal;
-
- ImeCallbacks(PerUserData perUserData) {
- mPerUserData = perUserData;
- mIWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- }
-
- @BinderThread
- @Override
- public IBinder createInputMethodWindowToken(int displayId) {
- synchronized (mPerUserData.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo tokenInfo =
- mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
- // Currently we issue up to one window token per display.
- if (tokenInfo.mDisplayId == displayId) {
- return tokenInfo.mToken;
- }
- }
-
- final Binder token = new Binder();
- Binder.withCleanCallingIdentity(
- PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
- mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
- null /* options */));
- mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
- return token;
- }
- }
-
- @BinderThread
- @Override
- public void deleteInputMethodWindowToken(IBinder token) {
- synchronized (mPerUserData.mLock) {
- // We assume the number of tokens would not be that large (up to 10 or so) hence
- // linear search should be acceptable.
- final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
- for (int i = 0; i < numTokens; ++i) {
- final TokenInfo tokenInfo =
- mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
- if (tokenInfo.mToken == token) {
- mPerUserData.mDisplayIdToImeWindowTokenMap.remove(tokenInfo);
- break;
- }
- }
- }
- }
-
- @BinderThread
- @Override
- public void acceptClient(int clientId, IInputMethodSession inputMethodSession,
- IMultiClientInputMethodSession multiSessionInputMethodSession,
- InputChannel writeChannel) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- try {
- clientInfo.mClient.setActive(true, false, false);
- } catch (RemoteException e) {
- // TODO(yukawa): Remove this client.
- return;
- }
- clientInfo.mState = InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT;
- clientInfo.mWriteChannel = writeChannel;
- clientInfo.mInputMethodSession = inputMethodSession;
- clientInfo.mMSInputMethodSession = multiSessionInputMethodSession;
- break;
- default:
- Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
- break;
- }
- }
- }
-
- @BinderThread
- @Override
- public void reportImeWindowTarget(int clientId, int targetWindowHandle,
- IBinder imeWindowToken) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) {
- if (windowInfo.mWindowHandle == targetWindowHandle) {
- final IBinder targetWindowToken = windowInfo.mWindowToken;
- if (DEBUG) {
- Slog.v(TAG, "reportImeWindowTarget"
- + " clientId=" + clientId
- + " imeWindowToken=" + imeWindowToken
- + " targetWindowToken=" + targetWindowToken);
- }
- mIWindowManagerInternal.updateInputMethodTargetWindow(
- imeWindowToken, targetWindowToken);
- }
- }
- // not found.
- }
- }
-
- @BinderThread
- @Override
- public boolean isUidAllowedOnDisplay(int displayId, int uid) {
- return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid);
- }
-
- @BinderThread
- @Override
- public void setActive(int clientId, boolean active) {
- synchronized (mPerUserData.mLock) {
- final InputMethodClientInfo clientInfo =
- mPerUserData.getClientFromIdLocked(clientId);
- if (clientInfo == null) {
- Slog.e(TAG, "Unknown clientId=" + clientId);
- return;
- }
- try {
- clientInfo.mClient.setActive(active, false /* fullscreen */, false);
- } catch (RemoteException e) {
- return;
- }
- }
- }
- }
-
- /**
- * Takes care of IPCs exposed to the IME client.
- */
- private static final class ApiCallbacks extends IInputMethodManager.Stub {
- private final Context mContext;
- private final UserDataMap mUserDataMap;
- private final UserToInputMethodInfoMap mInputMethodInfoMap;
- private final AppOpsManager mAppOpsManager;
- private final WindowManagerInternal mWindowManagerInternal;
-
- ApiCallbacks(Context context, UserDataMap userDataMap,
- UserToInputMethodInfoMap inputMethodInfoMap) {
- mContext = context;
- mUserDataMap = userDataMap;
- mInputMethodInfoMap = inputMethodInfoMap;
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
- }
-
- @AnyThread
- private boolean checkFocus(int uid, int pid, int displayId) {
- return mWindowManagerInternal.isInputMethodClientFocus(uid, pid, displayId);
- }
-
- @BinderThread
- @Override
- public void addClient(IInputMethodClient client, IInputContext inputContext,
- int selfReportedDisplayId) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "addClient() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return;
- }
- synchronized (data.mLock) {
- data.addClientLocked(callingUid, callingPid, client, selfReportedDisplayId);
- }
- }
-
- @BinderThread
- @Override
- public List<InputMethodInfo> getInputMethodList(@UserIdInt int userId) {
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
- }
- return mInputMethodInfoMap.getAsList(userId);
- }
-
- @BinderThread
- @Override
- public List<InputMethodInfo> getEnabledInputMethodList(@UserIdInt int userId) {
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingPermission(INTERACT_ACROSS_USERS_FULL, null);
- }
- return mInputMethodInfoMap.getAsList(userId);
- }
-
- @BinderThread
- @Override
- public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
- boolean allowsImplicitlySelectedSubtypes) {
- reportNotSupported();
- return Collections.emptyList();
- }
-
- @BinderThread
- @Override
- public InputMethodSubtype getLastInputMethodSubtype() {
- reportNotSupported();
- return null;
- }
-
- @BinderThread
- @Override
- public void removeImeSurface() {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void removeImeSurfaceFromWindowAsync(IBinder windowToken) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public boolean showSoftInput(
- IInputMethodClient client, IBinder token, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
- return showSoftInputInternal(client, token, flags, resultReceiver);
- }
-
- @BinderThread
- private boolean showSoftInputInternal(
- IInputMethodClient client, IBinder token, int flags,
- ResultReceiver resultReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "showSoftInput() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return false;
- }
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- Slog.e(TAG, "showSoftInput. client not found. ignoring.");
- return false;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return false;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver);
-
- // Forcing WM to show IME on imeTargetWindow
- mWindowManagerInternal.showImePostLayout(token);
- } catch (RemoteException e) {
- }
- break;
- default:
- if (DEBUG) {
- Slog.e(TAG, "Ignoring showSoftInput(). clientState="
- + clientInfo.mState);
- }
- break;
- }
- return true;
- }
- }
-
- @BinderThread
- @Override
- public boolean hideSoftInput(
- IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
- return hideSoftInputInternal(client, windowToken, flags, resultReceiver);
- }
-
- @BinderThread
- private boolean hideSoftInputInternal(
- IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "hideSoftInput() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return false;
- }
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- return false;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return false;
- }
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- try {
- clientInfo.mMSInputMethodSession.hideSoftInput(flags, resultReceiver);
- } catch (RemoteException e) {
- }
- break;
- default:
- if (DEBUG) {
- Slog.e(TAG, "Ignoring hideSoftInput(). clientState="
- + clientInfo.mState);
- }
- break;
- }
- return true;
- }
- }
-
- @BinderThread
- @Override
- public InputBindResult startInputOrWindowGainedFocus(
- @StartInputReason int startInputReason,
- @Nullable IInputMethodClient client,
- @Nullable IBinder windowToken,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int windowFlags,
- @Nullable EditorInfo editorInfo,
- @Nullable IInputContext inputContext,
- @MissingMethodFlags int missingMethods,
- int unverifiedTargetSdkVersion) {
- return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
- startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
- missingMethods, unverifiedTargetSdkVersion);
- }
-
- @BinderThread
- private InputBindResult startInputOrWindowGainedFocusInternal(
- @StartInputReason int startInputReason,
- @Nullable IInputMethodClient client,
- @Nullable IBinder windowToken,
- @StartInputFlags int startInputFlags,
- @SoftInputModeFlags int softInputMode,
- int windowFlags,
- @Nullable EditorInfo editorInfo,
- @Nullable IInputContext inputContext,
- @MissingMethodFlags int missingMethods,
- int unverifiedTargetSdkVersion) {
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final int userId = UserHandle.getUserId(callingUid);
-
- if (client == null) {
- return InputBindResult.INVALID_CLIENT;
- }
-
- final boolean packageNameVerified =
- editorInfo != null && InputMethodUtils.checkIfPackageBelongsToUid(
- mAppOpsManager, callingUid, editorInfo.packageName);
- if (editorInfo != null && !packageNameVerified) {
- Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
- + " uid=" + callingUid + " package=" + editorInfo.packageName);
- return InputBindResult.INVALID_PACKAGE_NAME;
- }
-
- final PerUserData data = mUserDataMap.get(userId);
- if (data == null) {
- Slog.e(TAG, "startInputOrWindowGainedFocus() from unknown userId=" + userId
- + " uid=" + callingUid + " pid=" + callingPid);
- return InputBindResult.INVALID_USER;
- }
-
- synchronized (data.mLock) {
- final InputMethodClientInfo clientInfo = data.getClientLocked(client);
- if (clientInfo == null) {
- return InputBindResult.INVALID_CLIENT;
- }
- if (clientInfo.mUid != callingUid) {
- Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
- + " actual=" + callingUid);
- return InputBindResult.INVALID_CLIENT;
- }
-
- switch (data.mState) {
- case PerUserState.USER_LOCKED:
- case PerUserState.SERVICE_NOT_QUERIED:
- case PerUserState.SERVICE_RECOGNIZED:
- case PerUserState.WAITING_SERVICE_CONNECTED:
- case PerUserState.UNBIND_CALLED:
- return InputBindResult.IME_NOT_CONNECTED;
- case PerUserState.SERVICE_CONNECTED:
- // OK
- break;
- default:
- Slog.wtf(TAG, "Unexpected state=" + data.mState);
- return InputBindResult.IME_NOT_CONNECTED;
- }
-
- WindowInfo windowInfo = null;
- if (windowToken != null) {
- windowInfo = clientInfo.mWindowMap.get(windowToken);
- if (windowInfo == null) {
- windowInfo = new WindowInfo(windowToken, WindowHandleSource.getNext());
- clientInfo.mWindowMap.put(windowToken, windowInfo);
- }
- }
-
- if (!checkFocus(clientInfo.mUid, clientInfo.mPid,
- clientInfo.mSelfReportedDisplayId)) {
- return InputBindResult.NOT_IME_TARGET_WINDOW;
- }
-
- if (editorInfo == null) {
- // So-called fallback InputConnection scenario. For app compatibility, we still
- // notify this to the IME.
- switch (clientInfo.mState) {
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- final int windowHandle = windowInfo != null
- ? windowInfo.mWindowHandle
- : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
- try {
- clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
- inputContext, missingMethods, editorInfo, startInputFlags,
- softInputMode, windowHandle);
- } catch (RemoteException ignored) { }
- break;
- }
- return InputBindResult.NULL_EDITOR_INFO;
- }
-
- switch (clientInfo.mState) {
- case InputMethodClientState.REGISTERED:
- case InputMethodClientState.WAITING_FOR_IME_SESSION:
- clientInfo.mBindingSequence++;
- if (clientInfo.mBindingSequence < 0) {
- clientInfo.mBindingSequence = 0;
- }
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, false);
- case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
- case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
- clientInfo.mBindingSequence++;
- if (clientInfo.mBindingSequence < 0) {
- clientInfo.mBindingSequence = 0;
- }
- // Successful start input.
- final int windowHandle = windowInfo != null
- ? windowInfo.mWindowHandle
- : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
- try {
- clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
- inputContext, missingMethods, editorInfo, startInputFlags,
- softInputMode, windowHandle);
- } catch (RemoteException ignored) { }
- clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
- return new InputBindResult(
- InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
- clientInfo.mInputMethodSession,
- clientInfo.mWriteChannel.dup(),
- data.mCurrentInputMethodInfo.getId(),
- clientInfo.mBindingSequence, false);
- case InputMethodClientState.UNREGISTERED:
- Slog.e(TAG, "The client is already unregistered.");
- return InputBindResult.INVALID_CLIENT;
- }
- }
- return null;
- }
-
- @BinderThread
- @Override
- public void showInputMethodPickerFromClient(IInputMethodClient client,
- int auxiliarySubtypeMode) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void showInputMethodPickerFromSystem(IInputMethodClient client,
- int auxiliarySubtypeMode, int displayId) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void showInputMethodAndSubtypeEnablerFromClient(IInputMethodClient client,
- String inputMethodId) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public boolean isInputMethodPickerShownForTest() {
- reportNotSupported();
- return false;
- }
-
- @BinderThread
- @Override
- public InputMethodSubtype getCurrentInputMethodSubtype() {
- reportNotSupported();
- return null;
- }
-
- @BinderThread
- @Override
- public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public int getInputMethodWindowVisibleHeight() {
- reportNotSupported();
- return 0;
- }
-
- @BinderThread
- @Override
- public void reportPerceptibleAsync(IBinder windowClient, boolean perceptible) {
- reportNotSupported();
- }
-
- @BinderThread
- @Override
- public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
- ResultReceiver resultReceiver) {
- }
-
- @BinderThread
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- final String prefixChild = " ";
- pw.println("Current Multi Client Input Method Manager state:");
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
- ipw.println("mUserDataMap=");
- if (mUserDataMap != null) {
- ipw.increaseIndent();
- mUserDataMap.dump(fd, ipw, args);
- }
- }
-
- @BinderThread
- @Override
- public void startProtoDump(byte[] clientProtoDump, int source, String where) {
- }
-
- @BinderThread
- @Override
- public boolean isImeTraceEnabled() {
- return false;
- }
-
- @BinderThread
- @Override
- public void startImeTrace() {
- }
-
- @BinderThread
- @Override
- public void stopImeTrace() {
- }
- }
-}
diff --git a/services/core/java/com/android/server/inputmethod/multi-client-ime.md b/services/core/java/com/android/server/inputmethod/multi-client-ime.md
deleted file mode 100644
index ba92cd2d4690..000000000000
--- a/services/core/java/com/android/server/inputmethod/multi-client-ime.md
+++ /dev/null
@@ -1,194 +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.
--->
-
-# Multi Client Input Method Editors
-
-## History of Multi Client Input Method Editors (Multi Client IMEs)
-
-An advanced multi-display support is requested for certain Android form-factors so that user(s) can type text on each display at the same time without losing software keyboard focus in other displays (hereafter called "multi-client scenario"). This is not possible in Android IMEs built on top of `InputMethodService` class. The assumption that a single IME client can be focused at the same time was made before Android IME APIs were introduced in Android 1.5 and many public APIs in `InputMethodService` have already relied heavily on that assumption (hereafter called "single-client scenario"). Updating `InputMethodService` class to support multi-client scenario is, however, quite challenging because:
-
- 1. doing so would introduce an unacceptable amount of complexity into `InputMethodService`, which is already hard to maintain,
- 2. IME developers still need to update their implementation to be able to support parallel requests from multiple focused IME client, which may require non-trivial redesign in their side (e.g. input decoder, typing history database, ...), and
- 3. actual use cases for multi IME clients are expected to be evolved rapidly hence the new protocol is not yet stable and not yet ready to be exposed as public APIs.
-
-Thus the first decision we made was that to support such special multi-display environments a new type of IME (hereafter called "multi-client IME") needs to be designed and implemented rather than reusing `InputMethodService` public class. On top of this decision, following decisions were also made:
-
- * Multi-client IME V1 will be built on top of private APIs. This means:
- * Multi-client IME must be pre-installed into the system. They cannot be distributed via application store since protocol compatibility is not guaranteed across devices and releases.
- * The system should trust multi-client IME to some extent. System integrators are responsible for making sure that the pre-installed multi-client IME works as expected.
- * Unlike `InputMethodService`, multiple multi-client IMEs cannot be enabled. The system pre-installs only one multi-client IME.
- * Punt some special features of Android IMEs (e.g. fullscreen mode, InputMethodSubtype, ...) from V1 goal unless someone actually requests those features for multi-client IME scenario.
- * Introduce `MultiClientInputMethodManagerService` (MCIMMS) for multi-client IME scenario and use it instead of `InputMethodManagerService` (IMMS) when a certain runtime flag is enabled at the device boot time. This means:
- * basically no risk for single-client scenario,
- * the feature can be easily deprecated, and
- * it forces us to rewrite IME system server, which is expected to be a good chance to reconsider what Android IME protocol should look like.
- * Most of form-factors such as Phones and TVs continue to use IMMS and support at most one focused IME client even under multi-display environment.
-
-
-## How to test
-
-For multi-client IME to properly work, an internal boolean resource `com.android.internal.R.bool.config_perDisplayFocusEnabled` needs to be `true`. Since this value cannot be overridden at the run time, you may need to rebuild the system image to enable per-display focus mode.
-
-As for multi-client IME mode itself, you can enable multi-client IME mode just by setting a valid component name that supports multi-client IME protocol to the system property `persist.debug.multi_client_ime`, as long as `android.os.Build.IS_DEBUGGABLE` returns `true` and you can have root access. Reboot is required for this to take effect.
-
-```shell
-# Build and install a sample multi-client IME
-make -j MultiClientInputMethod
-adb install -r $OUT/system/priv-app/MultiClientInputMethod/MultiClientInputMethod.apk
-
-# Enable multi-client IME for the side-loaded sample multi-client IME
-adb root
-adb shell setprop persist.debug.multi_client_ime com.example.android.multiclientinputmethod/.MultiClientInputMethod
-adb reboot
-```
-
-To disable multi-client IME on non-supported devices again, just clear `persist.debug.multi_client_ime` as follows. Reboot is still required for this to take effect.
-
-```shell
-# Disable multi-client IME again
-adb root
-adb shell "setprop persist.debug.multi_client_ime ''"
-adb reboot
-```
-
-## How to develop multi-client IMEs
-
-There is a sample multi-client IME in `development/samples/MultiClientInputMethod/`.
-
-## Versioning
-
-Neither forward nor backward compatibility is guaranteed in multi-client IME APIs. The system integrator is responsible for making sure that both the system and pre-installed multi-client IME are compatible with each other every time the system image is updated.
-
-## Implementation note
-
-### Unsupported features
-
- * VR IME
- * `VrManager#setVrInputMethod()` system API is not supported.
- * InputMethodSubtype
- * Following APIs are not supported
- * `InputMethodManager#getEnabledInputMethodSubtypeList()`
- * `InputMethodManager#getCurrentInputMethodSubtype()`
- * `InputMethodManager#setCurrentInputMethodSubtype()`
- * `InputMethodManager#getShortcutInputMethodsAndSubtypes()`
- * `InputMethodManager#setAdditionalInputMethodSubtypes()`
- * `InputMethodManager#getLastInputMethodSubtype()`
- * `Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE`
- * IME switching
- * Following APIs are not supported
- * `InputMethodManager#showInputMethodPicker()`
- * `InputMethodManager#showInputMethodAndSubtypeEnabler()`
- * `InputMethodManager#setInputMethod()`
- * `InputMethodManager#setInputMethodAndSubtype()`
- * `InputMethodManager#switchToLastInputMethod()`
- * `InputMethodManager#switchToNextInputMethod()`
- * `InputMethodManager#shouldOfferSwitchingToNextInputMethod()`
- * `Settings.Secure#DEFAULT_INPUT_METHOD`
- * `Settings.Secure#ENABLED_INPUT_METHODS`
- * Direct-boot aware multi-client IME
- * Device manufacturer can work around this by integrating in-app keyboard into the initial unlock screen.
- * Full-screen mode
- * Following API always returns `false`.
- * `InputMethodManager#isFullscreenMode()`
- * Custom inset
- * For instance, floating IME cannot be implemented right now.
- * Custom touchable region (`InputMethodService.Insets#touchableRegion`)
- * Image Insertion API
- * `InputConnection#commitContent()` API is silently ignored.
- * `adb shell dumpsys` does not include any log from MCIMMS yet.
-
-### Security
-
-#### Root permission is required to enable MCIMMS on non-supported devices
-
-In order to override `persist.debug.multi_client_ime` device property, an explicit root permission is needed.
-
-#### Multi-client IME must be pre-installed
-
-Multi-client IME must be pre-installed since it is considered as part of the system component. This is verified by checking `ApplicationInfo.FLAG_SYSTEM` bit. This security check can be bypassed when `Build.IS_DEBUGGABLE` is `true` so that IME developers can easily side-load their APKs during development phase.
-
-```java
-public final class MultiClientInputMethodManagerService {
- ...
- @Nullable
- private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
- @Nullable ComponentName componentName) {
-
- ...
-
- if (! && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
- return null;
- }
-```
-[services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java](MultiClientInputMethodManagerService.java)
-
-
-#### Integer handle vs IBinder token
-
-Sometimes MCIMMS needs to issue certain types of identifiers to the multi-client IME so that the IME can later specify to which entity or resource it intends to access. A good example is the IME client identifier. Multi-client IME definitely need to be able to specify which IME client to be interacted with for certain operations. The problem is that MCIMMS cannot simply pass `IInputMethodClient` to the multi-client IME as an ID because it would allow the IME to make IPC calls to the IME client. For this kind of situations, we usually use `Binder` object just as a non-spoofable token. For instance, IMMS creates another 'Binder' token then pass it to the IME, instead of directly passing 'IWindow' Binder token.
-
-```java
-public class InputMethodManagerService extends IInputMethodManager.Stub
- implements ServiceConnection, Handler.Callback {
- ...
- @GuardedBy("mMethodMap")
- private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
-
- ...
-
- @GuardedBy("mMethodMap")
- @NonNull
- InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
- ...
- final Binder startInputToken = new Binder();
- final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
- !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
- mCurSeq);
- mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
- ...
- }
-
- ...
-
- @BinderThread
- private void reportStartInput(IBinder token, IBinder startInputToken) {
- if (!calledWithValidToken(token)) {
- return;
- }
-
- synchronized (mMethodMap) {
- final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
- if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
- mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
- }
- mLastImeTargetWindow = targetWindow;
- }
- }
-```
-[services/core/java/com/android/server/inputmethod/InputMethodManagerService.java](InputMethodManagerService.java)
-
-However, in MCIMMS, for certain cases we decided to use a simple integer token, which can be spoofable and can be messed up if integer overflow happens. This is because:
-
- * It does not make much sense to worry about malicious multi-client IMEs, because it is guaranteed to be a pre-installed system component.
- * Integer token is expected to be a more lightweight that `Binder` token.
- * For that use case, integer overflow is unrealistic.
- * Strict user separation is still enforced. Multi-client IMEs are still not allowed to interact with other users' resources by any means.
-
-Currently the following IDs are implemented as integer tokens:
-
- * Client ID
- * Window Handle
- * Note that each IME client has its own Window Handle mapping table. Window Handle is valid only within the associated IME client.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index b61c6a7ca569..f2ccf9ff802d 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -44,8 +44,11 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -580,8 +583,14 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
// APK signatures is already verified elsewhere in PackageManager. We do not need to
// verify it again since it could cause a timeout for large APKs.
- pkg.setSigningDetails(
- ParsingPackageUtils.getSigningDetails(pkg, /* skipVerify= */ true));
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, pkg, /* skipVerify= */ true);
+ if (result.isError()) {
+ Slog.w(TAG, result.getErrorMessage(), result.getException());
+ return null;
+ }
+ pkg.setSigningDetails(result.getResult());
return PackageInfoUtils.generate(
pkg,
null,
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index dc3596b6c2a7..57a7620a3b32 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -56,7 +56,7 @@ public class GeocoderProxy {
private GeocoderProxy(Context context) {
mServiceWatcher = ServiceWatcher.create(context, "GeocoderProxy",
- new CurrentUserServiceSupplier(context, SERVICE_ACTION,
+ CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION,
com.android.internal.R.bool.config_enableGeocoderOverlay,
com.android.internal.R.string.config_geocoderProviderPackageName),
null);
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index 6ac6e77342be..f63ba583fdf0 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -73,8 +73,8 @@ public class HardwareActivityRecognitionProxy implements ServiceListener<BoundSe
mServiceWatcher = ServiceWatcher.create(context,
"HardwareActivityRecognitionProxy",
- new CurrentUserServiceSupplier(context, SERVICE_ACTION, useOverlayResId,
- nonOverlayPackageResId),
+ CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION,
+ useOverlayResId, nonOverlayPackageResId),
this);
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index e6210b2a8cc9..7b5a635d6c87 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -771,14 +771,6 @@ public class LocationManagerService extends ILocationManager.Stub implements
// sanitize request
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- && GPS_PROVIDER.equals(provider)
- && ArrayUtils.contains(mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationDriverAssistancePackageNames),
- identity.getPackageName())) {
- sanitized.setAdasGnssBypass(true);
- }
-
if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
!= PERMISSION_GRANTED) {
@@ -914,14 +906,6 @@ public class LocationManagerService extends ILocationManager.Stub implements
// sanitize request
LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- && GPS_PROVIDER.equals(provider)
- && ArrayUtils.contains(mContext.getResources().getStringArray(
- com.android.internal.R.array.config_locationDriverAssistancePackageNames),
- identity.getPackageName())) {
- sanitized.setAdasGnssBypass(true);
- }
-
request = sanitized.build();
// validate request
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 fa33338a61e7..3da5a43ee95b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -210,11 +210,6 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
/*
- * True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
- */
- private final boolean mHasAccessContextHubPermission;
-
- /*
* 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.
@@ -327,8 +322,6 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
mPid = Binder.getCallingPid();
mUid = Binder.getCallingUid();
- mHasAccessContextHubPermission = context.checkCallingPermission(
- Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED;
mAppOpsManager = context.getSystemService(AppOpsManager.class);
startMonitoringOpChanges();
@@ -827,9 +820,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub
*/
private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) {
try {
- String requiredPermission = mHasAccessContextHubPermission
- ? Manifest.permission.ACCESS_CONTEXT_HUB
- : Manifest.permission.LOCATION_HARDWARE;
+ String requiredPermission = Manifest.permission.ACCESS_CONTEXT_HUB;
pendingIntent.send(
mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
requiredPermission, null /* options */);
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 8361253dcd4a..70f50c3c60f8 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -30,28 +30,20 @@ import android.hardware.location.ContextHubTransaction;
import android.hardware.location.NanoAppBinary;
import android.hardware.location.NanoAppMessage;
import android.hardware.location.NanoAppState;
-import android.os.Binder;
-import android.os.Build;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* A class encapsulating helper functions used by the ContextHubService class
*/
/* package */ class ContextHubServiceUtil {
private static final String TAG = "ContextHubServiceUtil";
- private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
private static final String CONTEXT_HUB_PERMISSION = Manifest.permission.ACCESS_CONTEXT_HUB;
- // A set of packages that have already been warned regarding the ACCESS_CONTEXT_HUB permission.
- private static final Set<String> PERMISSION_WARNED_PACKAGES = new HashSet<String>();
-
/**
* Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an
* ArrayList of HIDL ContextHub objects.
@@ -209,25 +201,12 @@ import java.util.Set;
*/
/* package */
static void checkPermissions(Context context) {
- boolean hasLocationHardwarePermission = (context.checkCallingPermission(HARDWARE_PERMISSION)
- == PERMISSION_GRANTED);
boolean hasAccessContextHubPermission = (context.checkCallingPermission(
CONTEXT_HUB_PERMISSION) == PERMISSION_GRANTED);
- if (!hasLocationHardwarePermission && !hasAccessContextHubPermission) {
+ if (!hasAccessContextHubPermission) {
throw new SecurityException(
- "LOCATION_HARDWARE or ACCESS_CONTEXT_HUB permission required to use Context "
- + "Hub");
- }
-
- if (!hasAccessContextHubPermission && !Build.IS_USER) {
- String pkgName = context.getPackageManager().getNameForUid(Binder.getCallingUid());
- if (!PERMISSION_WARNED_PACKAGES.contains(pkgName)) {
- Log.w(TAG, pkgName
- + ": please use the ACCESS_CONTEXT_HUB permission rather than "
- + "LOCATION_HARDWARE (will be removed for Context Hub APIs in T)");
- PERMISSION_WARNED_PACKAGES.add(pkgName);
- }
+ "ACCESS_CONTEXT_HUB permission required to use Context Hub");
}
}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
index 90b446ecb15d..d0e8dcf05c9b 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
@@ -67,7 +67,7 @@ public final class GeofenceProxy implements ServiceListener<BoundServiceInfo> {
mGpsGeofenceHardware = Objects.requireNonNull(gpsGeofence);
mServiceWatcher = ServiceWatcher.create(context,
"GeofenceProxy",
- new CurrentUserServiceSupplier(context, SERVICE_ACTION,
+ CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION,
com.android.internal.R.bool.config_enableGeofenceOverlay,
com.android.internal.R.string.config_geofenceProviderPackageName),
this);
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index 1967e026e46c..e3750074168c 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -82,7 +82,7 @@ public class GnssAntennaInfoProvider extends
}
public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
putRegistration(listener.asBinder(),
new AntennaInfoListenerRegistration(callerIdentity, listener));
@@ -92,7 +92,7 @@ public class GnssAntennaInfoProvider extends
}
public void removeListener(IGnssAntennaInfoListener listener) {
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
removeRegistration(listener.asBinder());
} finally {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index f3dcfbbf2c0a..206682e46d47 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -68,8 +68,6 @@ import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -138,13 +136,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private static final int AGPS_SUPL_MODE_MSA = 0x02;
private static final int AGPS_SUPL_MODE_MSB = 0x01;
- // handler messages
- private static final int INJECT_NTP_TIME = 5;
- private static final int DOWNLOAD_PSDS_DATA = 6;
- private static final int REQUEST_LOCATION = 16;
- private static final int REPORT_LOCATION = 17; // HAL reports location
- private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
-
// TCP/IP constants.
// Valid TCP/UDP port range is (0, 65535].
private static final int TCP_MIN_PORT = 0;
@@ -402,7 +393,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
BatteryStats.SERVICE_NAME));
// Construct internal handler
- mHandler = new ProviderHandler(FgThread.getHandler().getLooper());
+ mHandler = FgThread.getHandler();
// Load GPS configuration and register listeners in the background:
// some operations, such as opening files and registering broadcast receivers, can take a
@@ -530,7 +521,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
if (mSupportsPsds) {
synchronized (mLock) {
for (int psdsType : mPendingDownloadPsdsTypes) {
- sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
}
mPendingDownloadPsdsTypes.clear();
}
@@ -654,9 +645,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
synchronized (mLock) {
backoffMillis = mPsdsBackOff.nextBackoffMillis();
}
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(DOWNLOAD_PSDS_DATA, psdsType, 0, null),
- backoffMillis);
+ mHandler.postDelayed(() -> handleDownloadPsdsData(psdsType), backoffMillis);
}
// Release wake lock held by task, synchronize on mLock in case multiple
@@ -976,8 +965,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
requestUtcTime();
} else if ("force_psds_injection".equals(command)) {
if (mSupportsPsds) {
- sendMessage(DOWNLOAD_PSDS_DATA, GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX,
- null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(
+ GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX));
}
} else if ("request_power_stats".equals(command)) {
mGnssNative.requestPowerStats();
@@ -1314,7 +1303,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
private void requestUtcTime() {
if (DEBUG) Log.d(TAG, "utcTimeRequest");
- sendMessage(INJECT_NTP_TIME, 0, null);
+ postWithWakeLockHeld(mNtpTimeHelper::retrieveAndInjectNtpTime);
}
private void requestRefLocation() {
@@ -1348,75 +1337,20 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
}
- boolean isInEmergencySession() {
- return mNIHandler.getInEmergency();
- }
-
- private void sendMessage(int message, int arg, Object obj) {
+ private void postWithWakeLockHeld(Runnable runnable) {
// hold a wake lock until this message is delivered
// note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
- if (DEBUG) {
- Log.d(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
- + ", " + obj + ")");
- }
- mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
- }
-
- private final class ProviderHandler extends Handler {
- ProviderHandler(Looper looper) {
- super(looper, null, true /*async*/);
- }
-
- @Override
- public void handleMessage(Message msg) {
- int message = msg.what;
- switch (message) {
- case INJECT_NTP_TIME:
- mNtpTimeHelper.retrieveAndInjectNtpTime();
- break;
- case REQUEST_LOCATION:
- handleRequestLocation(msg.arg1 == 1, (boolean) msg.obj);
- break;
- case DOWNLOAD_PSDS_DATA:
- handleDownloadPsdsData(msg.arg1);
- break;
- case REPORT_LOCATION:
- handleReportLocation(msg.arg1 == 1, (Location) msg.obj);
- break;
- case REPORT_SV_STATUS:
- handleReportSvStatus((GnssStatus) msg.obj);
- break;
- }
- if (msg.arg2 == 1) {
- // wakelock was taken for this message, release it
+ boolean success = mHandler.post(() -> {
+ try {
+ runnable.run();
+ } finally {
mWakeLock.release();
- if (DEBUG) {
- Log.d(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
- + ", " + msg.arg1 + ", " + msg.obj + ")");
- }
}
- }
- }
-
- /**
- * @return A string representing the given message ID.
- */
- private String messageIdAsString(int message) {
- switch (message) {
- case INJECT_NTP_TIME:
- return "INJECT_NTP_TIME";
- case REQUEST_LOCATION:
- return "REQUEST_LOCATION";
- case DOWNLOAD_PSDS_DATA:
- return "DOWNLOAD_PSDS_DATA";
- case REPORT_LOCATION:
- return "REPORT_LOCATION";
- case REPORT_SV_STATUS:
- return "REPORT_SV_STATUS";
- default:
- return "<Unknown>";
+ });
+ if (!success) {
+ mWakeLock.release();
}
}
@@ -1476,7 +1410,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onReportLocation(boolean hasLatLong, Location location) {
- sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
+ postWithWakeLockHeld(() -> handleReportLocation(hasLatLong, location));
}
@Override
@@ -1502,7 +1436,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onReportSvStatus(GnssStatus gnssStatus) {
- sendMessage(REPORT_SV_STATUS, 0, gnssStatus);
+ postWithWakeLockHeld(() -> handleReportSvStatus(gnssStatus));
}
@Override
@@ -1512,7 +1446,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
@Override
public void onRequestPsdsDownload(int psdsType) {
- sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+ postWithWakeLockHeld(() -> handleDownloadPsdsData(psdsType));
}
@Override
@@ -1558,7 +1492,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
+ ", isUserEmergency: "
+ isUserEmergency);
}
- sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency);
+ postWithWakeLockHeld(() -> handleRequestLocation(independentFromGnss, isUserEmergency));
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 21946ca88401..5de9cf3f5b6a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -331,7 +331,7 @@ public class GnssManagerService {
@Override
public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
GnssCapabilities newCapabilities) {
- long ident = Binder.clearCallingIdentity();
+ final long ident = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
.putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
diff --git a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
index 9874ecfdefdf..7369eac48c05 100644
--- a/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemDeviceStationaryHelper.java
@@ -41,7 +41,7 @@ public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
public void addListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.registerStationaryListener(listener);
} finally {
@@ -53,7 +53,7 @@ public class SystemDeviceStationaryHelper extends DeviceStationaryHelper {
public void removeListener(DeviceIdleInternal.StationaryListener listener) {
Preconditions.checkState(mDeviceIdle != null);
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
mDeviceIdle.unregisterStationaryListener(listener);
} finally {
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 a9641f0f1c1b..2b3f42074991 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
@@ -103,7 +103,7 @@ public class ProxyLocationProvider extends AbstractLocationProvider implements
mContext = context;
mServiceWatcher = ServiceWatcher.create(context, provider,
- new CurrentUserServiceSupplier(context, action, enableOverlayResId,
+ CurrentUserServiceSupplier.createFromConfig(context, action, enableOverlayResId,
nonOverlayPackageResId), this);
mName = provider;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7d5b7e535ca9..fab11a16f399 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1620,7 +1620,7 @@ public class LockSettingsService extends ILockSettings.Stub {
+ PERMISSION);
}
- long identity = Binder.clearCallingIdentity();
+ final long identity = Binder.clearCallingIdentity();
try {
enforceFrpResolved();
// When changing credential for profiles with unified challenge, some callers
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b477ea353c25..65a3ad0747cd 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -471,7 +471,9 @@ public class MediaSessionService extends SystemService implements Monitor {
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
SessionsListenerRecord listener = mSessionsListeners.get(i);
try {
- enforceMediaPermissions(listener.componentName, listener.pid, listener.uid,
+ String packageName = listener.componentName == null ? null :
+ listener.componentName.getPackageName();
+ enforceMediaPermissions(packageName, listener.pid, listener.uid,
listener.userId);
} catch (SecurityException e) {
Log.i(TAG, "ActiveSessionsListener " + listener.componentName
@@ -593,15 +595,13 @@ public class MediaSessionService extends SystemService implements Monitor {
* for the caller's user</li>
* </ul>
*/
- private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+ private void enforceMediaPermissions(String packageName, int pid, int uid,
int resolvedUserId) {
if (hasStatusBarServicePermission(pid, uid)) return;
- // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener
- if (mContext
- .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
- != PackageManager.PERMISSION_GRANTED
- && !isEnabledNotificationListener(compName,
- UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
+ if (hasMediaControlPermission(pid, uid)) return;
+
+ if (packageName == null || !hasEnabledNotificationListener(
+ packageName, UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
throw new SecurityException("Missing permission to control media.");
}
}
@@ -632,29 +632,26 @@ public class MediaSessionService extends SystemService implements Monitor {
}
/**
- * This checks if the component is an enabled notification listener for the
+ * This checks if the given package has an enabled notification listener for the
* specified user. Enabled components may only operate on behalf of the user
* they're running as.
*
- * @param compName The component that is enabled.
+ * @param packageName The package name.
* @param userHandle The user handle of the caller.
* @param forUserId The user id they're making the request on behalf of.
- * @return True if the component is enabled, false otherwise
+ * @return True if the app has an enabled notification listener for the user, false otherwise
*/
- private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle,
- int forUserId) {
+ private boolean hasEnabledNotificationListener(String packageName,
+ UserHandle userHandle, int forUserId) {
if (userHandle.getIdentifier() != forUserId) {
// You may not access another user's content as an enabled listener.
return false;
}
if (DEBUG) {
- Log.d(TAG, "Checking if enabled notification listener " + compName);
- }
- if (compName != null) {
- return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(),
- userHandle);
+ Log.d(TAG, "Checking whether the package " + packageName + " has an"
+ + " enabled notification listener.");
}
- return false;
+ return mNotificationManager.hasEnabledNotificationListener(packageName, userHandle);
}
/*
@@ -1237,16 +1234,16 @@ public class MediaSessionService extends SystemService implements Monitor {
}
@Override
- public MediaSession.Token getMediaKeyEventSession() {
+ public MediaSession.Token getMediaKeyEventSession(final String packageName) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- if (!hasMediaControlPermission(pid, uid)) {
- throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
- + " get the media key event session");
- }
+ enforcePackageName(packageName, uid);
+ enforceMediaPermissions(packageName, pid, uid, userId);
+
MediaSessionRecordImpl record;
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
@@ -1268,16 +1265,16 @@ public class MediaSessionService extends SystemService implements Monitor {
}
@Override
- public String getMediaKeyEventSessionPackageName() {
+ public String getMediaKeyEventSessionPackageName(final String packageName) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- if (!hasMediaControlPermission(pid, uid)) {
- throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
- + " get the media key event session package");
- }
+ enforcePackageName(packageName, uid);
+ enforceMediaPermissions(packageName, pid, uid, userId);
+
MediaSessionRecordImpl record;
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
@@ -1303,6 +1300,10 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void addSessionsListener(IActiveSessionsListener listener,
ComponentName componentName, int userId) throws RemoteException {
+ if (listener == null) {
+ Log.w(TAG, "addSessionsListener: listener is null, ignoring");
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
@@ -1349,6 +1350,10 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void addSession2TokensListener(ISession2TokensListener listener,
int userId) {
+ if (listener == null) {
+ Log.w(TAG, "addSession2TokensListener: listener is null, ignoring");
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
@@ -1359,7 +1364,8 @@ public class MediaSessionService extends SystemService implements Monitor {
synchronized (mLock) {
int index = findIndexOfSession2TokensListenerLocked(listener);
if (index >= 0) {
- Log.w(TAG, "addSession2TokensListener is already added, ignoring");
+ Log.w(TAG, "addSession2TokensListener: "
+ + "listener is already added, ignoring");
return;
}
mSession2TokensListenerRecords.add(
@@ -1516,6 +1522,10 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void addOnMediaKeyEventDispatchedListener(
final IOnMediaKeyEventDispatchedListener listener) {
+ if (listener == null) {
+ Log.w(TAG, "addOnMediaKeyEventDispatchedListener: listener is null, ignoring");
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
@@ -1544,6 +1554,10 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void removeOnMediaKeyEventDispatchedListener(
final IOnMediaKeyEventDispatchedListener listener) {
+ if (listener == null) {
+ Log.w(TAG, "removeOnMediaKeyEventDispatchedListener: listener is null, ignoring");
+ return;
+ }
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
@@ -1571,16 +1585,22 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void addOnMediaKeyEventSessionChangedListener(
- final IOnMediaKeyEventSessionChangedListener listener) {
+ final IOnMediaKeyEventSessionChangedListener listener,
+ final String packageName) {
+ if (listener == null) {
+ Log.w(TAG, "addOnMediaKeyEventSessionChangedListener: listener is null, ignoring");
+ return;
+ }
+
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ final UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
+ final int userId = userHandle.getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- if (!hasMediaControlPermission(pid, uid)) {
- throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
- + " add MediaKeyEventSessionChangedListener");
- }
+ enforcePackageName(packageName, uid);
+ enforceMediaPermissions(packageName, pid, uid, userId);
+
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
@@ -1590,7 +1610,7 @@ public class MediaSessionService extends SystemService implements Monitor {
}
user.addOnMediaKeyEventSessionChangedListenerLocked(listener, uid);
Log.d(TAG, "The MediaKeyEventSessionChangedListener (" + listener.asBinder()
- + ") is added by " + getCallingPackageName(uid));
+ + ") is added by " + packageName);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1600,15 +1620,17 @@ public class MediaSessionService extends SystemService implements Monitor {
@Override
public void removeOnMediaKeyEventSessionChangedListener(
final IOnMediaKeyEventSessionChangedListener listener) {
+ if (listener == null) {
+ Log.w(TAG, "removeOnMediaKeyEventSessionChangedListener: listener is null,"
+ + " ignoring");
+ return;
+ }
+
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final long token = Binder.clearCallingIdentity();
try {
- if (!hasMediaControlPermission(pid, uid)) {
- throw new SecurityException("MEDIA_CONTENT_CONTROL permission is required to"
- + " remove MediaKeyEventSessionChangedListener");
- }
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null || user.mFullUserId != userId) {
@@ -2091,7 +2113,7 @@ public class MediaSessionService extends SystemService implements Monitor {
int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
// Check if they have the permissions or their component is enabled for the user
// they're calling from.
- enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+ enforceMediaPermissions(packageName, pid, uid, resolvedUserId);
return resolvedUserId;
}
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 2519bbf389ba..8e7c4ff3e11c 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.media.metrics;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.MediaMetrics;
import android.media.metrics.IMediaMetricsManager;
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
@@ -65,6 +66,8 @@ public final class MediaMetricsManagerService extends SystemService {
private static final int LOGGING_LEVEL_NO_UID = 1000;
private static final int LOGGING_LEVEL_BLOCKED = 99999;
+ private static final String mMetricsId = MediaMetrics.Name.METRICS_MANAGER;
+
private static final String FAILED_TO_GET = "failed_to_get";
private final SecureRandom mSecureRandom;
@GuardedBy("mLock")
@@ -199,6 +202,12 @@ public final class MediaMetricsManagerService extends SystemService {
mSecureRandom.nextBytes(byteId);
String id = Base64.encodeToString(
byteId, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE);
+
+ // Authorize these session ids in the native mediametrics service.
+ new MediaMetrics.Item(mMetricsId)
+ .set(MediaMetrics.Property.EVENT, "create")
+ .set(MediaMetrics.Property.LOG_SESSION_ID, id)
+ .record();
return id;
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 42b7c9d388a0..8a3329913142 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -354,6 +354,12 @@ public class NotificationHistoryDatabase {
}
}
+ public void unregisterFileCleanupReceiver() {
+ if(mContext != null) {
+ mContext.unregisterReceiver(mFileCleanupReceiver);
+ }
+ }
+
private static long safeParseLong(String fileName) {
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
// over the course of its processing. If these files still exist on boot we need to clean
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 6da898acdafe..0aacd130c837 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -288,6 +288,7 @@ public class NotificationHistoryManager {
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
userHistory.disableHistory();
+ userHistory.unregisterFileCleanupReceiver();
mUserPendingHistoryDisables.put(userId, false);
mHistoryEnabled.put(userId, false);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b54e8f973bd6..5ffb754c96be 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2320,14 +2320,16 @@ public class NotificationManagerService extends SystemService {
mUserProfiles.updateCache(getContext());
- telephonyManager.listen(new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (mCallState == state) return;
- if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
- mCallState = state;
- }
- }, PhoneStateListener.LISTEN_CALL_STATE);
+ if (mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ telephonyManager.listen(new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DBG) Slog.d(TAG, "Call state changed: " + callStateToString(state));
+ mCallState = state;
+ }
+ }, PhoneStateListener.LISTEN_CALL_STATE);
+ }
mSettingsObserver = new SettingsObserver(mHandler);
@@ -4363,8 +4365,7 @@ public class NotificationManagerService extends SystemService {
final int userId = r.getSbn().getUserId();
if (userId != info.userid && userId != UserHandle.USER_ALL &&
!mUserProfiles.isCurrentProfile(userId)) {
- throw new SecurityException("Disallowed call from listener: "
- + info.service);
+ continue;
}
cancelNotificationFromListenerLocked(info, callingUid, callingPid,
r.getSbn().getPackageName(), r.getSbn().getTag(),
@@ -4431,8 +4432,7 @@ public class NotificationManagerService extends SystemService {
final int userId = r.getSbn().getUserId();
if (userId != info.userid && userId != UserHandle.USER_ALL
&& !mUserProfiles.isCurrentProfile(userId)) {
- throw new SecurityException("Disallowed call from listener: "
- + info.service);
+ continue;
}
seen.add(r);
if (!r.isSeen()) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f66cfa99ce98..7a00b86cb1a3 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -585,7 +585,8 @@ public final class NotificationRecord {
pw.println("null");
} else {
pw.print(val.getClass().getSimpleName());
- if (redact && (val instanceof CharSequence || val instanceof String)) {
+ if (redact && (val instanceof CharSequence) && shouldRedactStringExtra(key)) {
+ pw.print(String.format(" [length=%d]", ((CharSequence) val).length()));
// redact contents from bugreports
} else if (val instanceof Bitmap) {
pw.print(String.format(" (%dx%d)",
@@ -611,6 +612,19 @@ public final class NotificationRecord {
}
}
+ private boolean shouldRedactStringExtra(String key) {
+ if (key == null) return true;
+ switch (key) {
+ // none of these keys contain user-related information; they do not need to be redacted
+ case Notification.EXTRA_SUBSTITUTE_APP_NAME:
+ case Notification.EXTRA_TEMPLATE:
+ case "android.support.v4.app.extra.COMPAT_TEMPLATE":
+ return false;
+ default:
+ return true;
+ }
+ }
+
@Override
public final String toString() {
return String.format(
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0e2ff7523c85..3ca6a0d1fe10 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -429,4 +429,14 @@ public interface NotificationRecordLogger {
return NotificationChannelLogger.getLoggingImportance(channel, importance);
}
+ /**
+ * @param r NotificationRecord
+ * @return Whether the notification is a foreground service notification.
+ */
+ static boolean isForegroundService(@NonNull NotificationRecord r) {
+ if (r.getSbn() == null || r.getSbn().getNotification() == null) {
+ return false;
+ }
+ return (r.getSbn().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 1a99fb0e55f3..249910dc09fa 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -63,7 +63,12 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
/* android.stats.sysui.NotificationImportance importance_asst = 19 */
r.getAssistantImportance(),
/* int32 assistant_hash = 20 */ p.getAssistantHash(),
- /* float assistant_ranking_score = 21 */ r.getRankingScore()
+ /* float assistant_ranking_score = 21 */ r.getRankingScore(),
+ /* bool is_ongoing = 22 */ r.getSbn().isOngoing(),
+ /* bool is_foreground_service = 23 */
+ NotificationRecordLogger.isForegroundService(r),
+ /* optional int64 timeout_millis = 24 */
+ r.getSbn().getNotification().getTimeoutAfter()
);
}
diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java
index f47aa487744a..0a69aec76306 100644
--- a/services/core/java/com/android/server/notification/VibratorHelper.java
+++ b/services/core/java/com/android/server/notification/VibratorHelper.java
@@ -39,6 +39,9 @@ public final class VibratorHelper {
private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
+ private static final int CHIRP_LEVEL_DURATION_MILLIS = 100;
+ private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100;
+ private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50;
private final Vibrator mVibrator;
private final long[] mDefaultPattern;
@@ -102,6 +105,9 @@ public final class VibratorHelper {
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createFallbackVibration(boolean insistent) {
+ if (mVibrator.hasFrequencyControl()) {
+ return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent);
+ }
return createWaveformVibration(mFallbackPattern, insistent);
}
@@ -111,9 +117,32 @@ public final class VibratorHelper {
* @param insistent {@code true} if the vibration should loop until it is cancelled.
*/
public VibrationEffect createDefaultVibration(boolean insistent) {
+ if (mVibrator.hasFrequencyControl()) {
+ return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent);
+ }
return createWaveformVibration(mDefaultPattern, insistent);
}
+ private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
+ VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
+ .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
+ .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
+ .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
+ .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
+
+ if (insistent) {
+ return waveformBuilder
+ .addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
+ .build(/* repeat= */ 0);
+ }
+
+ VibrationEffect singleBeat = waveformBuilder.build();
+ return VibrationEffect.startComposition()
+ .addEffect(singleBeat)
+ .addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS)
+ .compose();
+ }
+
private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
int[] ar = resources.getIntArray(resId);
if (ar == null) {
diff --git a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
index 947405ed2a78..b276c6f37201 100644
--- a/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
+++ b/services/core/java/com/android/server/os/DeviceIdentifiersPolicyService.java
@@ -19,10 +19,13 @@ package com.android.server.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.os.IDeviceIdentifiersPolicyService;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.server.SystemService;
@@ -65,11 +68,31 @@ public final class DeviceIdentifiersPolicyService extends SystemService {
@Override
public @Nullable String getSerialForPackage(String callingPackage,
String callingFeatureId) throws RemoteException {
+ if (!checkPackageBelongsToCaller(callingPackage)) {
+ throw new IllegalArgumentException(
+ "Invalid callingPackage or callingPackage does not belong to caller's uid:"
+ + Binder.getCallingUid());
+ }
+
if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext,
callingPackage, callingFeatureId, "getSerial")) {
return Build.UNKNOWN;
}
return SystemProperties.get("ro.serialno", Build.UNKNOWN);
}
+
+ private boolean checkPackageBelongsToCaller(String callingPackage) {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ int callingPackageUid;
+ try {
+ callingPackageUid = mContext.getPackageManager().getPackageUidAsUser(
+ callingPackage, callingUserId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+
+ return callingPackageUid == callingUid;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 53bf7b8aa8ae..7889ff219b4e 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -31,10 +31,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -249,6 +250,14 @@ public abstract class ApexManager {
throws PackageManagerException;
/**
+ * Returns {@code ApeInfo} about apex sessions that have been marked ready via
+ * {@link #markStagedSessionReady(int)}
+ *
+ * Returns empty array if there is no staged apex session or if there is any error.
+ */
+ abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params);
+
+ /**
* Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
* applied at next reboot.
*
@@ -583,8 +592,8 @@ public abstract class ApexManager {
}
factoryPackagesSet.add(packageInfo.packageName);
}
- } else if (throwable instanceof PackageParserException) {
- final PackageParserException e = (PackageParserException) throwable;
+ } else if (throwable instanceof PackageManagerException) {
+ final PackageManagerException e = (PackageManagerException) throwable;
// Skip parsing non-coreApp apex file if system is in minimal boot state.
if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) {
Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath);
@@ -757,6 +766,19 @@ public abstract class ApexManager {
}
@Override
+ ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
+ try {
+ return waitForApexService().getStagedApexInfos(params);
+ } catch (RemoteException re) {
+ Slog.w(TAG, "Unable to contact apexservice" + re.getMessage());
+ throw new RuntimeException(re);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage());
+ return new ApexInfo[0];
+ }
+ }
+
+ @Override
void markStagedSessionReady(int sessionId) throws PackageManagerException {
try {
waitForApexService().markStagedSessionReady(sessionId);
@@ -988,15 +1010,17 @@ public abstract class ApexManager {
}
private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException {
- int minSignatureScheme =
+ final int minSignatureScheme =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.applicationInfo.targetSdkVersion);
- try {
- return ApkSignatureVerifier.verify(pkg.applicationInfo.sourceDir,
- minSignatureScheme);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify(
+ input, pkg.applicationInfo.sourceDir, minSignatureScheme);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ result.getException());
}
+ return result.getResult();
}
private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)
@@ -1247,6 +1271,11 @@ public abstract class ApexManager {
}
@Override
+ ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
void markStagedSessionReady(int sessionId) {
throw new UnsupportedOperationException();
}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 28511070fc8e..a44cad844202 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -35,9 +35,11 @@ import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -461,8 +463,8 @@ public class ApkChecksums {
}
// Obtaining array of certificates used for signing the installer package.
- certs = installer.getSigningDetails().signatures;
- pastCerts = installer.getSigningDetails().pastSigningCertificates;
+ certs = installer.getSigningDetails().getSignatures();
+ pastCerts = installer.getSigningDetails().getPastSigningCertificates();
}
if (certs == null || certs.length == 0 || certs[0] == null) {
Slog.e(TAG, "Can't obtain certificates.");
@@ -662,14 +664,16 @@ public class ApkChecksums {
private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
String split, String filePath, int types) {
Map<Integer, byte[]> contentDigests = null;
- try {
- contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
- false).contentDigests;
- } catch (PackageParser.PackageParserException e) {
- if (!(e.getCause() instanceof SignatureNotFoundException)) {
- Slog.e(TAG, "Signature verification error", e);
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<ApkSignatureVerifier.SigningDetailsWithDigests> result =
+ ApkSignatureVerifier.verifySignaturesInternal(input, filePath,
+ SignatureSchemeVersion.SIGNING_BLOCK_V2, false /*verifyFull*/);
+ if (result.isError()) {
+ if (!(result.getException() instanceof SignatureNotFoundException)) {
+ Slog.e(TAG, "Signature verification error", result.getException());
}
+ } else {
+ contentDigests = result.getResult().contentDigests;
}
if (contentDigests == null) {
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ed9b539c05df..56b77b594443 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -28,7 +28,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedInstrumentation;
@@ -147,7 +147,7 @@ public class AppsFilter implements Watchable, Snappable {
private final OverlayReferenceMapper mOverlayReferenceMapper;
private final StateProvider mStateProvider;
- private PackageParser.SigningDetails mSystemSigningDetails;
+ private SigningDetails mSystemSigningDetails;
private Set<String> mProtectedBroadcasts = new ArraySet<>();
private final Object mCacheLock = new Object();
@@ -951,7 +951,7 @@ public class AppsFilter implements Watchable, Snappable {
}
}
- private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
+ private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
PackageSetting pkgSetting) {
return pkgSetting.isSystem()
&& pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
diff --git a/services/core/java/com/android/server/pm/CommitRequest.java b/services/core/java/com/android/server/pm/CommitRequest.java
new file mode 100644
index 000000000000..d1a6002590f2
--- /dev/null
+++ b/services/core/java/com/android/server/pm/CommitRequest.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;
+
+import android.annotation.NonNull;
+
+import java.util.Map;
+
+/**
+ * Package state to commit to memory and disk after reconciliation has completed.
+ */
+final class CommitRequest {
+ final Map<String, ReconciledPackage> mReconciledPackages;
+ @NonNull final int[] mAllUsers;
+
+ CommitRequest(Map<String, ReconciledPackage> reconciledPackages,
+ @NonNull int[] allUsers) {
+ mReconciledPackages = reconciledPackages;
+ mAllUsers = allUsers;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 3019439a430b..117136ab982d 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -178,6 +178,27 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addDataType("*/*")
.build();
+ /** Pick images can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ 0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+ /** Pick images can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES_WITH_DATA_TYPES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ 0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("image/*")
+ .addDataType("video/*")
+ .build();
+
/** Open document intent can be forwarded to parent user. */
private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
new DefaultCrossProfileIntentFilter.Builder(
@@ -289,6 +310,8 @@ public class DefaultCrossProfileIntentFiltersUtils {
RECOGNIZE_SPEECH,
ACTION_PICK_RAW,
ACTION_PICK_DATA,
+ ACTION_PICK_IMAGES,
+ ACTION_PICK_IMAGES_WITH_DATA_TYPES,
OPEN_DOCUMENT,
GET_CONTENT,
USB_DEVICE_ATTACHED,
diff --git a/services/core/java/com/android/server/pm/DeletePackageAction.java b/services/core/java/com/android/server/pm/DeletePackageAction.java
new file mode 100644
index 000000000000..8ef6601f7684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DeletePackageAction.java
@@ -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.
+ */
+
+package com.android.server.pm;
+
+import android.os.UserHandle;
+
+final class DeletePackageAction {
+ public final PackageSetting mDeletingPs;
+ public final PackageSetting mDisabledPs;
+ public final PackageRemovedInfo mRemovedInfo;
+ public final int mFlags;
+ public final UserHandle mUser;
+
+ DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
+ PackageRemovedInfo removedInfo, int flags, UserHandle user) {
+ mDeletingPs = deletingPs;
+ mDisabledPs = disabledPs;
+ mRemovedInfo = removedInfo;
+ mFlags = flags;
+ mUser = user;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/FileInstallArgs.java b/services/core/java/com/android/server/pm/FileInstallArgs.java
new file mode 100644
index 000000000000..3e18374d3d68
--- /dev/null
+++ b/services/core/java/com/android/server/pm/FileInstallArgs.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
+
+import android.content.pm.DataLoaderType;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.SELinux;
+import android.os.Trace;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Slog;
+
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Logic to handle installation of new applications, including copying
+ * and renaming logic.
+ */
+class FileInstallArgs extends InstallArgs {
+ private File mCodeFile;
+
+ // Example topology:
+ // /data/app/com.example/base.apk
+ // /data/app/com.example/split_foo.apk
+ // /data/app/com.example/lib/arm/libfoo.so
+ // /data/app/com.example/lib/arm64/libfoo.so
+ // /data/app/com.example/dalvik/arm/base.apk@classes.dex
+
+ /** New install */
+ FileInstallArgs(InstallParams params) {
+ super(params);
+ }
+
+ /** Existing install */
+ FileInstallArgs(String codePath, String[] instructionSets, PackageManagerService pm) {
+ super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
+ null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
+ SigningDetails.UNKNOWN,
+ PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
+ false, DataLoaderType.NONE, pm);
+ mCodeFile = (codePath != null) ? new File(codePath) : null;
+ }
+
+ int copyApk() {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
+ try {
+ return doCopyApk();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private int doCopyApk() {
+ if (mOriginInfo.mStaged) {
+ if (DEBUG_INSTALL) Slog.d(TAG, mOriginInfo.mFile + " already staged; skipping copy");
+ mCodeFile = mOriginInfo.mFile;
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ try {
+ final boolean isEphemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ final File tempDir =
+ mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral);
+ mCodeFile = tempDir;
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to create copy file: " + e);
+ return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ }
+
+ int ret = PackageManagerServiceUtils.copyPackage(
+ mOriginInfo.mFile.getAbsolutePath(), mCodeFile);
+ if (ret != PackageManager.INSTALL_SUCCEEDED) {
+ Slog.e(TAG, "Failed to copy package");
+ return ret;
+ }
+
+ final boolean isIncremental = isIncrementalPath(mCodeFile.getAbsolutePath());
+ final File libraryRoot = new File(mCodeFile, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(mCodeFile);
+ ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ mAbiOverride, isIncremental);
+ } catch (IOException e) {
+ Slog.e(TAG, "Copying native libraries failed", e);
+ ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+
+ return ret;
+ }
+
+ int doPreInstall(int status) {
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp();
+ }
+ return status;
+ }
+
+ @Override
+ boolean doRename(int status, ParsedPackage parsedPackage) {
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp();
+ return false;
+ }
+
+ final File targetDir = resolveTargetDir();
+ final File beforeCodeFile = mCodeFile;
+ final File afterCodeFile = PackageManagerService.getNextCodePath(targetDir,
+ parsedPackage.getPackageName());
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+ final boolean onIncremental = mPm.mIncrementalManager != null
+ && isIncrementalPath(beforeCodeFile.getAbsolutePath());
+ try {
+ makeDirRecursive(afterCodeFile.getParentFile(), 0775);
+ if (onIncremental) {
+ // Just link files here. The stage dir will be removed when the installation
+ // session is completed.
+ mPm.mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile);
+ } else {
+ Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
+ }
+ } catch (IOException | ErrnoException e) {
+ Slog.w(TAG, "Failed to rename", e);
+ return false;
+ }
+
+ if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
+ Slog.w(TAG, "Failed to restorecon");
+ return false;
+ }
+
+ // Reflect the rename internally
+ mCodeFile = afterCodeFile;
+
+ // Reflect the rename in scanned details
+ try {
+ parsedPackage.setPath(afterCodeFile.getCanonicalPath());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
+ return false;
+ }
+ parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, parsedPackage.getBaseApkPath()));
+ parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
+ afterCodeFile, parsedPackage.getSplitCodePaths()));
+
+ return true;
+ }
+
+ // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
+ // flow, we won't need this method anymore.
+ private File resolveTargetDir() {
+ boolean isStagedInstall = (mInstallFlags & INSTALL_STAGED) != 0;
+ if (isStagedInstall) {
+ return Environment.getDataAppDirectory(null);
+ } else {
+ return mCodeFile.getParentFile();
+ }
+ }
+
+ int doPostInstall(int status, int uid) {
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp();
+ }
+ return status;
+ }
+
+ @Override
+ String getCodePath() {
+ return (mCodeFile != null) ? mCodeFile.getAbsolutePath() : null;
+ }
+
+ private boolean cleanUp() {
+ if (mCodeFile == null || !mCodeFile.exists()) {
+ return false;
+ }
+ mPm.removeCodePathLI(mCodeFile);
+ return true;
+ }
+
+ void cleanUpResourcesLI() {
+ // Try enumerating all code paths before deleting
+ List<String> allCodePaths = Collections.EMPTY_LIST;
+ if (mCodeFile != null && mCodeFile.exists()) {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), mCodeFile, /* flags */ 0);
+ if (result.isSuccess()) {
+ // Ignore error; we tried our best
+ allCodePaths = result.getResult().getAllApkPaths();
+ }
+ }
+
+ cleanUp();
+ removeDexFiles(allCodePaths, mInstructionSets);
+ }
+
+ void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
+ if (!allCodePaths.isEmpty()) {
+ if (instructionSets == null) {
+ throw new IllegalStateException("instructionSet == null");
+ }
+ String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
+ for (String codePath : allCodePaths) {
+ for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+ try {
+ mPm.mInstaller.rmdex(codePath, dexCodeInstructionSet);
+ } catch (Installer.InstallerException ignored) {
+ }
+ }
+ }
+ }
+ }
+
+ boolean doPostDeleteLI(boolean delete) {
+ // XXX err, shouldn't we respect the delete flag?
+ cleanUpResourcesLI();
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java
new file mode 100644
index 000000000000..b8c2eb87e0c5
--- /dev/null
+++ b/services/core/java/com/android/server/pm/HandlerParams.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.UserHandle;
+import android.util.Slog;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+abstract class HandlerParams {
+ /** User handle for the user requesting the information or installation. */
+ private final UserHandle mUser;
+ String mTraceMethod;
+ int mTraceCookie;
+
+ HandlerParams(UserHandle user) {
+ mUser = user;
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+
+ HandlerParams setTraceMethod(String traceMethod) {
+ mTraceMethod = traceMethod;
+ return this;
+ }
+
+ HandlerParams setTraceCookie(int traceCookie) {
+ mTraceCookie = traceCookie;
+ return this;
+ }
+
+ final void startCopy() {
+ if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
+ handleStartCopy();
+ handleReturnCode();
+ }
+
+ abstract void handleStartCopy();
+ abstract void handleReturnCode();
+}
diff --git a/services/core/java/com/android/server/pm/IncrementalProgressListener.java b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
new file mode 100644
index 000000000000..bb797cb93b92
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IncrementalProgressListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.pm.IPackageLoadingProgressCallback;
+
+/**
+ * Loading progress callback, used to listen for progress changes and update package setting
+ */
+final class IncrementalProgressListener extends IPackageLoadingProgressCallback.Stub {
+ private final String mPackageName;
+ private final PackageManagerService mPm;
+ IncrementalProgressListener(String packageName, PackageManagerService pm) {
+ mPackageName = packageName;
+ mPm = pm;
+ }
+
+ @Override
+ public void onPackageLoadingProgressChanged(float progress) {
+ final PackageSetting ps;
+ synchronized (mPm.mLock) {
+ ps = mPm.mSettings.getPackageLPr(mPackageName);
+ if (ps == null) {
+ return;
+ }
+ ps.setLoadingProgress(progress);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IncrementalStatesCallback.java b/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
new file mode 100644
index 000000000000..478c99b2445d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IncrementalStatesCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+/**
+ * Package states callback, used to listen for package state changes and send broadcasts
+ */
+final class IncrementalStatesCallback implements IncrementalStates.Callback {
+ private final String mPackageName;
+ private final PackageManagerService mPm;
+
+ IncrementalStatesCallback(String packageName, PackageManagerService pm) {
+ mPackageName = packageName;
+ mPm = pm;
+ }
+
+ @Override
+ public void onPackageFullyLoaded() {
+ final String codePath;
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(mPackageName);
+ if (ps == null) {
+ return;
+ }
+ codePath = ps.getPathString();
+ }
+ // Unregister progress listener
+ mPm.mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
+ // Make sure the information is preserved
+ mPm.scheduleWriteSettingsLocked();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
new file mode 100644
index 000000000000..07c712331cf1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import java.util.List;
+
+abstract class InstallArgs {
+ /** @see InstallParams#mOriginInfo */
+ final OriginInfo mOriginInfo;
+ /** @see InstallParams#mMoveInfo */
+ final MoveInfo mMoveInfo;
+
+ final IPackageInstallObserver2 mObserver;
+ // Always refers to PackageManager flags only
+ final int mInstallFlags;
+ @NonNull
+ final InstallSource mInstallSource;
+ final String mVolumeUuid;
+ final UserHandle mUser;
+ final String mAbiOverride;
+ final String[] mInstallGrantPermissions;
+ final List<String> mAllowlistedRestrictedPermissions;
+ final int mAutoRevokePermissionsMode;
+ /** If non-null, drop an async trace when the install completes */
+ final String mTraceMethod;
+ final int mTraceCookie;
+ final SigningDetails mSigningDetails;
+ final int mInstallReason;
+ final int mInstallScenario;
+ final boolean mForceQueryableOverride;
+ final int mDataLoaderType;
+
+ // The list of instruction sets supported by this app. This is currently
+ // only used during the rmdex() phase to clean up resources. We can get rid of this
+ // if we move dex files under the common app path.
+ @Nullable String[] mInstructionSets;
+
+ @NonNull final PackageManagerService mPm;
+
+ InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
+ int installFlags, InstallSource installSource, String volumeUuid,
+ UserHandle user, String[] instructionSets,
+ String abiOverride, String[] installGrantPermissions,
+ List<String> allowlistedRestrictedPermissions,
+ int autoRevokePermissionsMode,
+ String traceMethod, int traceCookie, SigningDetails signingDetails,
+ int installReason, int installScenario, boolean forceQueryableOverride,
+ int dataLoaderType, PackageManagerService pm) {
+ mOriginInfo = originInfo;
+ mMoveInfo = moveInfo;
+ mInstallFlags = installFlags;
+ mObserver = observer;
+ mInstallSource = Preconditions.checkNotNull(installSource);
+ mVolumeUuid = volumeUuid;
+ mUser = user;
+ mInstructionSets = instructionSets;
+ mAbiOverride = abiOverride;
+ mInstallGrantPermissions = installGrantPermissions;
+ mAllowlistedRestrictedPermissions = allowlistedRestrictedPermissions;
+ mAutoRevokePermissionsMode = autoRevokePermissionsMode;
+ mTraceMethod = traceMethod;
+ mTraceCookie = traceCookie;
+ mSigningDetails = signingDetails;
+ mInstallReason = installReason;
+ mInstallScenario = installScenario;
+ mForceQueryableOverride = forceQueryableOverride;
+ mDataLoaderType = dataLoaderType;
+ mPm = pm;
+ }
+
+ /** New install */
+ InstallArgs(InstallParams params) {
+ this(params.mOriginInfo, params.mMoveInfo, params.mObserver, params.mInstallFlags,
+ params.mInstallSource, params.mVolumeUuid,
+ params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride,
+ params.mGrantedRuntimePermissions, params.mAllowlistedRestrictedPermissions,
+ params.mAutoRevokePermissionsMode,
+ params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
+ params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
+ params.mDataLoaderType, params.mPm);
+ }
+
+ abstract int copyApk();
+ abstract int doPreInstall(int status);
+
+ /**
+ * Rename package into final resting place. All paths on the given
+ * scanned package should be updated to reflect the rename.
+ */
+ abstract boolean doRename(int status, ParsedPackage parsedPackage);
+ abstract int doPostInstall(int status, int uid);
+
+ /** @see PackageSettingBase#getPath() */
+ abstract String getCodePath();
+
+ // Need installer lock especially for dex file removal.
+ abstract void cleanUpResourcesLI();
+ abstract boolean doPostDeleteLI(boolean delete);
+
+ /**
+ * Called before the source arguments are copied. This is used mostly
+ * for MoveParams when it needs to read the source file to put it in the
+ * destination.
+ */
+ int doPreCopy() {
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ /**
+ * Called after the source arguments are copied. This is used mostly for
+ * MoveParams when it needs to read the source file to put it in the
+ * destination.
+ */
+ int doPostCopy(int uid) {
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ protected boolean isEphemeral() {
+ return (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
new file mode 100644
index 000000000000..45ce3980d543
--- /dev/null
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -0,0 +1,2162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
+import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
+import static com.android.server.pm.PackageManagerService.INIT_COPY;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.PRECOMPILE_LAYOUTS;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ApplicationPackageManager;
+import android.content.pm.DataLoaderType;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageChangeEvent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
+import android.os.storage.StorageManager;
+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 com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.F2fsUtils;
+import com.android.internal.content.PackageHelper;
+import com.android.internal.security.VerityUtils;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.Watchdog;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.Permission;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+final class InstallParams extends HandlerParams {
+ final OriginInfo mOriginInfo;
+ final MoveInfo mMoveInfo;
+ final IPackageInstallObserver2 mObserver;
+ int mInstallFlags;
+ @NonNull
+ final InstallSource mInstallSource;
+ final String mVolumeUuid;
+ int mRet;
+ final String mPackageAbiOverride;
+ final String[] mGrantedRuntimePermissions;
+ final List<String> mAllowlistedRestrictedPermissions;
+ final int mAutoRevokePermissionsMode;
+ final SigningDetails mSigningDetails;
+ final int mInstallReason;
+ final int mInstallScenario;
+ @Nullable
+ MultiPackageInstallParams mParentInstallParams;
+ final boolean mForceQueryableOverride;
+ final int mDataLoaderType;
+ final long mRequiredInstalledVersionCode;
+ final PackageLite mPackageLite;
+ @NonNull final PackageManagerService mPm;
+
+ InstallParams(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
+ int installFlags, InstallSource installSource, String volumeUuid,
+ UserHandle user, String packageAbiOverride, PackageLite packageLite,
+ PackageManagerService pm) {
+ super(user);
+ mPm = pm;
+ mOriginInfo = originInfo;
+ mMoveInfo = moveInfo;
+ mObserver = observer;
+ mInstallFlags = installFlags;
+ mInstallSource = Preconditions.checkNotNull(installSource);
+ mVolumeUuid = volumeUuid;
+ mPackageAbiOverride = packageAbiOverride;
+
+ mGrantedRuntimePermissions = null;
+ mAllowlistedRestrictedPermissions = null;
+ mAutoRevokePermissionsMode = MODE_DEFAULT;
+ mSigningDetails = SigningDetails.UNKNOWN;
+ mInstallReason = PackageManager.INSTALL_REASON_UNKNOWN;
+ mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
+ mForceQueryableOverride = false;
+ mDataLoaderType = DataLoaderType.NONE;
+ mRequiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
+ mPackageLite = packageLite;
+ }
+
+ InstallParams(File stagedDir, IPackageInstallObserver2 observer,
+ PackageInstaller.SessionParams sessionParams, InstallSource installSource,
+ UserHandle user, SigningDetails signingDetails, int installerUid,
+ PackageLite packageLite, PackageManagerService pm) {
+ super(user);
+ mPm = pm;
+ mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
+ mMoveInfo = null;
+ mInstallReason = fixUpInstallReason(
+ installSource.installerPackageName, installerUid, sessionParams.installReason);
+ mInstallScenario = sessionParams.installScenario;
+ mObserver = observer;
+ mInstallFlags = sessionParams.installFlags;
+ mInstallSource = installSource;
+ mVolumeUuid = sessionParams.volumeUuid;
+ mPackageAbiOverride = sessionParams.abiOverride;
+ mGrantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
+ mAllowlistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
+ mAutoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
+ mSigningDetails = signingDetails;
+ mForceQueryableOverride = sessionParams.forceQueryableOverride;
+ mDataLoaderType = (sessionParams.dataLoaderParams != null)
+ ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+ mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
+ mPackageLite = packageLite;
+ }
+
+ @Override
+ public String toString() {
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + mOriginInfo.mFile + "}";
+ }
+
+ private int installLocationPolicy(PackageInfoLite pkgLite) {
+ String packageName = pkgLite.packageName;
+ int installLocation = pkgLite.installLocation;
+ // reader
+ synchronized (mPm.mLock) {
+ // Currently installed package which the new package is attempting to replace or
+ // null if no such package is installed.
+ AndroidPackage installedPkg = mPm.mPackages.get(packageName);
+
+ if (installedPkg != null) {
+ if ((mInstallFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ // Check for updated system application.
+ if (installedPkg.isSystem()) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else {
+ // If current upgrade specifies particular preference
+ if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ // Application explicitly specified internal.
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else if (
+ installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ // App explicitly prefers external. Let policy decide
+ } else {
+ // Prefer previous location
+ if (installedPkg.isExternalStorage()) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ }
+ } else {
+ // Invalid install. Return error code
+ return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
+ }
+ }
+ }
+ return pkgLite.recommendedInstallLocation;
+ }
+
+ /**
+ * Override install location based on default policy if needed.
+ *
+ * Only {@link #mInstallFlags} may mutate in this method.
+ *
+ * Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in
+ * {@link #mInstallFlags}
+ */
+ private int overrideInstallLocation(PackageInfoLite pkgLite) {
+ final boolean ephemeral = (mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+ if (DEBUG_INSTANT && ephemeral) {
+ Slog.v(TAG, "pkgLite for install: " + pkgLite);
+ }
+
+ if (mOriginInfo.mStaged) {
+ // If we're already staged, we've firmly committed to an install location
+ if (mOriginInfo.mFile != null) {
+ mInstallFlags |= PackageManager.INSTALL_INTERNAL;
+ } else {
+ throw new IllegalStateException("Invalid stage location");
+ }
+ } else if (pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ /*
+ * If we are not staged and have too little free space, try to free cache
+ * before giving up.
+ */
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mPm.mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+ mOriginInfo.mResolvedPath, mPackageAbiOverride);
+ if (sizeBytes >= 0) {
+ try {
+ mPm.mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+ pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+ mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags,
+ mPackageAbiOverride);
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to free cache", e);
+ }
+ }
+
+ /*
+ * The cache free must have deleted the file we downloaded to install.
+ *
+ * TODO: fix the "freeCache" call to not delete the file we care about.
+ */
+ if (pkgLite.recommendedInstallLocation
+ == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+ pkgLite.recommendedInstallLocation =
+ PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
+
+ int ret = INSTALL_SUCCEEDED;
+ int loc = pkgLite.recommendedInstallLocation;
+ if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
+ ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
+ ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+ ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
+ ret = PackageManager.INSTALL_FAILED_INVALID_APK;
+ } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+ ret = PackageManager.INSTALL_FAILED_INVALID_URI;
+ } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
+ ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
+ } else {
+ // Override with defaults if needed.
+ loc = installLocationPolicy(pkgLite);
+
+ final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
+
+ if (!onInt) {
+ // Override install location with flags
+ if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+ // Set the flag to install on external media.
+ mInstallFlags &= ~PackageManager.INSTALL_INTERNAL;
+ } else {
+ // Make sure the flag for installing on external
+ // media is unset
+ mInstallFlags |= PackageManager.INSTALL_INTERNAL;
+ }
+ }
+ }
+ return ret;
+ }
+
+ /*
+ * Invoke remote method to get package information and install
+ * location values. Override install location based on default
+ * policy if needed and then create install arguments based
+ * on the install location.
+ */
+ public void handleStartCopy() {
+ if ((mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
+ mRet = INSTALL_SUCCEEDED;
+ return;
+ }
+ PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+ mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
+
+ // For staged session, there is a delay between its verification and install. Device
+ // state can change within this delay and hence we need to re-verify certain conditions.
+ boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
+ if (isStaged) {
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+ pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
+ mRet = ret.first;
+ if (mRet != INSTALL_SUCCEEDED) {
+ return;
+ }
+ }
+
+ mRet = overrideInstallLocation(pkgLite);
+ }
+
+ @Override
+ void handleReturnCode() {
+ processPendingInstall();
+ }
+
+ private void processPendingInstall() {
+ InstallArgs args = createInstallArgs(this);
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ mRet = args.copyApk();
+ }
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ F2fsUtils.releaseCompressedBlocks(
+ mPm.mContext.getContentResolver(), new File(args.getCodePath()));
+ }
+ if (mParentInstallParams != null) {
+ mParentInstallParams.tryProcessInstallRequest(args, mRet);
+ } else {
+ PackageInstalledInfo res = new PackageInstalledInfo(mRet);
+ processInstallRequestsAsync(
+ res.mReturnCode == PackageManager.INSTALL_SUCCEEDED,
+ Collections.singletonList(new InstallRequest(args, res)));
+ }
+ }
+
+ private InstallArgs createInstallArgs(InstallParams params) {
+ if (params.mMoveInfo != null) {
+ return new MoveInstallArgs(params);
+ } else {
+ return new FileInstallArgs(params);
+ }
+ }
+
+ // Queue up an async operation since the package installation may take a little while.
+ private void processInstallRequestsAsync(boolean success,
+ List<InstallRequest> installRequests) {
+ mPm.mHandler.post(() -> {
+ List<InstallRequest> apexInstallRequests = new ArrayList<>();
+ List<InstallRequest> apkInstallRequests = new ArrayList<>();
+ for (InstallRequest request : installRequests) {
+ if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
+ apexInstallRequests.add(request);
+ } else {
+ apkInstallRequests.add(request);
+ }
+ }
+ // Note: supporting multi package install of both APEXes and APKs might requir some
+ // thinking to ensure atomicity of the install.
+ if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
+ // This should've been caught at the validation step, but for some reason wasn't.
+ throw new IllegalStateException(
+ "Attempted to do a multi package install of both APEXes and APKs");
+ }
+ if (!apexInstallRequests.isEmpty()) {
+ if (success) {
+ // Since installApexPackages requires talking to external service (apexd), we
+ // schedule to run it async. Once it finishes, it will resume the install.
+ Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
+ "installApexPackages");
+ t.start();
+ } else {
+ // Non-staged APEX installation failed somewhere before
+ // processInstallRequestAsync. In that case just notify the observer about the
+ // failure.
+ InstallRequest request = apexInstallRequests.get(0);
+ mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
+ }
+ return;
+ }
+ if (success) {
+ for (InstallRequest request : apkInstallRequests) {
+ request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
+ }
+ synchronized (mPm.mInstallLock) {
+ installPackagesTracedLI(apkInstallRequests);
+ }
+ for (InstallRequest request : apkInstallRequests) {
+ request.mArgs.doPostInstall(
+ request.mInstallResult.mReturnCode, request.mInstallResult.mUid);
+ }
+ }
+ for (InstallRequest request : apkInstallRequests) {
+ mPm.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
+ request.mInstallResult,
+ new PackageManagerService.PostInstallData(request.mArgs,
+ request.mInstallResult, null));
+ }
+ });
+ }
+
+ private void installApexPackagesTraced(List<InstallRequest> requests) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
+ installApexPackages(requests);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ private void installApexPackages(List<InstallRequest> requests) {
+ if (requests.isEmpty()) {
+ return;
+ }
+ if (requests.size() != 1) {
+ throw new IllegalStateException(
+ "Only a non-staged install of a single APEX is supported");
+ }
+ InstallRequest request = requests.get(0);
+ try {
+ // Should directory scanning logic be moved to ApexManager for better test coverage?
+ final File dir = request.mArgs.mOriginInfo.mResolvedFile;
+ final File[] apexes = dir.listFiles();
+ if (apexes == null) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ dir.getAbsolutePath() + " is not a directory");
+ }
+ if (apexes.length != 1) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Expected exactly one .apex file under " + dir.getAbsolutePath()
+ + " got: " + apexes.length);
+ }
+ try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
+ mPm.mApexManager.installPackage(apexes[0], packageParser);
+ }
+ } catch (PackageManagerException e) {
+ request.mInstallResult.setError("APEX installation failed", e);
+ }
+ PackageManagerService.invalidatePackageInfoCache();
+ mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
+ }
+
+ @GuardedBy("mInstallLock")
+ private void installPackagesTracedLI(List<InstallRequest> requests) {
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+ installPackagesLI(requests);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ /**
+ * Installs one or more packages atomically. This operation is broken up into four phases:
+ * <ul>
+ * <li><b>Prepare</b>
+ * <br/>Analyzes any current install state, parses the package and does initial
+ * validation on it.</li>
+ * <li><b>Scan</b>
+ * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
+ * <li><b>Reconcile</b>
+ * <br/>Validates scanned packages in the context of each other and the current system
+ * state to ensure that the install will be successful.
+ * <li><b>Commit</b>
+ * <br/>Commits all scanned packages and updates system state. This is the only place
+ * that system state may be modified in the install flow and all predictable errors
+ * must be determined before this phase.</li>
+ * </ul>
+ *
+ * Failure at any phase will result in a full failure to install all packages.
+ */
+ @GuardedBy("mInstallLock")
+ private void installPackagesLI(List<InstallRequest> requests) {
+ final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
+ final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
+ final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
+ final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
+ final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
+ final Map<String, PackageSetting> lastStaticSharedLibSettings =
+ new ArrayMap<>(requests.size());
+ final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
+ boolean success = false;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+ for (InstallRequest request : requests) {
+ // TODO(b/109941548): remove this once we've pulled everything from it and into
+ // scan, reconcile or commit.
+ final PrepareResult prepareResult;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
+ prepareResult =
+ preparePackageLI(request.mArgs, request.mInstallResult);
+ } catch (PrepareFailure prepareFailure) {
+ request.mInstallResult.setError(prepareFailure.error,
+ prepareFailure.getMessage());
+ request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage;
+ request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission;
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.mInstallResult.mInstallerPackageName =
+ request.mArgs.mInstallSource.installerPackageName;
+
+ final String packageName = prepareResult.mPackageToScan.getPackageName();
+ prepareResults.put(packageName, prepareResult);
+ installResults.put(packageName, request.mInstallResult);
+ installArgs.put(packageName, request.mArgs);
+ try {
+ final ScanResult result = mPm.scanPackageTracedLI(
+ prepareResult.mPackageToScan, prepareResult.mParseFlags,
+ prepareResult.mScanFlags, System.currentTimeMillis(),
+ request.mArgs.mUser, request.mArgs.mAbiOverride);
+ if (null != preparedScans.put(result.mPkgSetting.pkg.getPackageName(),
+ result)) {
+ request.mInstallResult.setError(
+ PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Duplicate package " + result.mPkgSetting.pkg.getPackageName()
+ + " in multi-package install request.");
+ return;
+ }
+ createdAppId.put(packageName, mPm.optimisticallyRegisterAppId(result));
+ versionInfos.put(result.mPkgSetting.pkg.getPackageName(),
+ mPm.getSettingsVersionForPackage(result.mPkgSetting.pkg));
+ if (result.mStaticSharedLibraryInfo != null) {
+ final PackageSetting sharedLibLatestVersionSetting =
+ mPm.getSharedLibLatestVersionSetting(result);
+ if (sharedLibLatestVersionSetting != null) {
+ lastStaticSharedLibSettings.put(result.mPkgSetting.pkg.getPackageName(),
+ sharedLibLatestVersionSetting);
+ }
+ }
+ } catch (PackageManagerException e) {
+ request.mInstallResult.setError("Scanning Failed.", e);
+ return;
+ }
+ }
+ ReconcileRequest
+ reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
+ installResults,
+ prepareResults,
+ mPm.mSharedLibraries,
+ Collections.unmodifiableMap(mPm.mPackages), versionInfos,
+ lastStaticSharedLibSettings);
+ CommitRequest commitRequest = null;
+ synchronized (mPm.mLock) {
+ Map<String, ReconciledPackage> reconciledPackages;
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
+ reconciledPackages = PackageManagerService.reconcilePackagesLocked(
+ reconcileRequest, mPm.mSettings.getKeySetManagerService(),
+ mPm.mInjector);
+ } catch (ReconcileFailure e) {
+ for (InstallRequest request : requests) {
+ request.mInstallResult.setError("Reconciliation failed...", e);
+ }
+ return;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
+ commitRequest = new CommitRequest(reconciledPackages,
+ mPm.mUserManager.getUserIds());
+ commitPackagesLocked(commitRequest);
+ success = true;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+ executePostCommitSteps(commitRequest);
+ } finally {
+ if (success) {
+ for (InstallRequest request : requests) {
+ final InstallArgs args = request.mArgs;
+ if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+ continue;
+ }
+ if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
+ continue;
+ }
+ // For incremental installs, we bypass the verifier prior to install. Now
+ // that we know the package is valid, send a notice to the verifier with
+ // the root hash of the base.apk.
+ final String baseCodePath = request.mInstallResult.mPkg.getBaseApkPath();
+ final String[] splitCodePaths = request.mInstallResult.mPkg.getSplitCodePaths();
+ final Uri originUri = Uri.fromFile(args.mOriginInfo.mResolvedFile);
+ final int verificationId = mPm.mPendingVerificationToken++;
+ final String rootHashString = PackageManagerServiceUtils
+ .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+ mPm.broadcastPackageVerified(verificationId, originUri,
+ PackageManager.VERIFICATION_ALLOW, rootHashString,
+ args.mDataLoaderType, args.getUser());
+ }
+ } else {
+ for (ScanResult result : preparedScans.values()) {
+ if (createdAppId.getOrDefault(result.mRequest.mParsedPackage.getPackageName(),
+ false)) {
+ mPm.cleanUpAppIdCreation(result);
+ }
+ }
+ // TODO(b/194319951): create a more descriptive reason than unknown
+ // mark all non-failure installs as UNKNOWN so we do not treat them as success
+ for (InstallRequest request : requests) {
+ if (request.mInstallResult.mFreezer != null) {
+ request.mInstallResult.mFreezer.close();
+ }
+ if (request.mInstallResult.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
+ request.mInstallResult.mReturnCode = PackageManager.INSTALL_UNKNOWN;
+ }
+ }
+ }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ @GuardedBy("mInstallLock")
+ private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+ throws PrepareFailure {
+ final int installFlags = args.mInstallFlags;
+ final File tmpPackageFile = new File(args.getCodePath());
+ final boolean onExternal = args.mVolumeUuid != null;
+ final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
+ final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
+ final boolean virtualPreload =
+ ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ @PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
+ if (args.mMoveInfo != null) {
+ // moving a complete application; perform an initial scan on the new install location
+ scanFlags |= SCAN_INITIAL;
+ }
+ if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ scanFlags |= SCAN_DONT_KILL_APP;
+ }
+ if (instantApp) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (fullApp) {
+ scanFlags |= SCAN_AS_FULL_APP;
+ }
+ if (virtualPreload) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
+
+ // Validity check
+ if (instantApp && onExternal) {
+ Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+
+ // Retrieve PackageSettings and parse package
+ @ParsingPackageUtils.ParseFlags final int parseFlags =
+ mPm.mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE
+ | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
+ parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
+ AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ } catch (PackageManagerException e) {
+ throw new PrepareFailure("Failed parse during installPackageLI", e);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Instant apps have several additional install-time checks.
+ if (instantApp) {
+ if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " does not target at least O");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package must target at least O");
+ }
+ if (parsedPackage.getSharedUserId() != null) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " may not declare sharedUserId.");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package may not declare a sharedUserId");
+ }
+ }
+
+ if (parsedPackage.isStaticSharedLibrary()) {
+ // Static shared libraries have synthetic package names
+ PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
+
+ // No static shared libs on external storage
+ if (onExternal) {
+ Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Packages declaring static-shared libs cannot be updated");
+ }
+ }
+
+ String pkgName = res.mName = parsedPackage.getPackageName();
+ if (parsedPackage.isTestOnly()) {
+ if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
+ throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
+ }
+ }
+
+ // either use what we've been given or parse directly from the APK
+ if (args.mSigningDetails != SigningDetails.UNKNOWN) {
+ parsedPackage.setSigningDetails(args.mSigningDetails);
+ } else {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, false /*skipVerify*/);
+ if (result.isError()) {
+ throw new PrepareFailure("Failed collect during installPackageLI",
+ result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ }
+
+ if (instantApp && parsedPackage.getSigningDetails().getSignatureSchemeVersion()
+ < SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
+ Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
+ + " is not signed with at least APK Signature Scheme v2");
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Instant app package must be signed with APK Signature Scheme v2 or greater");
+ }
+
+ boolean systemApp = false;
+ boolean replace = false;
+ synchronized (mPm.mLock) {
+ // Check if installing already existing package
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName);
+ if (parsedPackage.getOriginalPackages().contains(oldName)
+ && mPm.mPackages.containsKey(oldName)) {
+ // This package is derived from an original package,
+ // and this device has been updating from that original
+ // name. We must continue using the original name, so
+ // rename the new package here.
+ parsedPackage.setPackageName(oldName);
+ pkgName = parsedPackage.getPackageName();
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Replacing existing renamed package: oldName="
+ + oldName + " pkgName=" + pkgName);
+ }
+ } else if (mPm.mPackages.containsKey(pkgName)) {
+ // This package, under its official name, already exists
+ // on the device; we should replace it.
+ replace = true;
+ if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
+ }
+
+ if (replace) {
+ // Prevent apps opting out from runtime permissions
+ AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
+ final int oldTargetSdk = oldPackage.getTargetSdkVersion();
+ final int newTargetSdk = parsedPackage.getTargetSdkVersion();
+ if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
+ && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
+ "Package " + parsedPackage.getPackageName()
+ + " new target SDK " + newTargetSdk
+ + " doesn't support runtime permissions but the old"
+ + " target SDK " + oldTargetSdk + " does.");
+ }
+ // Prevent persistent apps from being updated
+ if (oldPackage.isPersistent()
+ && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Package " + oldPackage.getPackageName() + " is a persistent app. "
+ + "Persistent apps are not updateable.");
+ }
+ }
+ }
+
+ PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ if (ps != null) {
+ if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
+
+ // Static shared libs have same package with different versions where
+ // we internally use a synthetic package name to allow multiple versions
+ // of the same package, therefore we need to compare signatures against
+ // the package setting for the latest library version.
+ PackageSetting signatureCheckPs = ps;
+ if (parsedPackage.isStaticSharedLibrary()) {
+ SharedLibraryInfo libraryInfo = mPm.getLatestSharedLibraVersionLPr(
+ parsedPackage);
+ if (libraryInfo != null) {
+ signatureCheckPs = mPm.mSettings.getPackageLPr(
+ libraryInfo.getPackageName());
+ }
+ }
+
+ // Quick validity check that we're signed correctly if updating;
+ // we'll check this again later when scanning, but we want to
+ // bail early here before tripping over redefined permissions.
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
+ + parsedPackage.getPackageName() + " upgrade keys do not match the "
+ + "previously installed version");
+ }
+ } else {
+ try {
+ final boolean compareCompat = mPm.isCompatSignatureUpdateNeeded(
+ parsedPackage);
+ final boolean compareRecover = mPm.isRecoverSignatureUpdateNeeded(
+ parsedPackage);
+ // We don't care about disabledPkgSetting on install for now.
+ final boolean compatMatch = verifySignatures(signatureCheckPs, null,
+ parsedPackage.getSigningDetails(), compareCompat, compareRecover,
+ isRollback);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ synchronized (mPm.mLock) {
+ ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
+ }
+ }
+ } catch (PackageManagerException e) {
+ throw new PrepareFailure(e.error, e.getMessage());
+ }
+ }
+
+ if (ps.pkg != null) {
+ systemApp = ps.pkg.isSystem();
+ }
+ res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ }
+
+ final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ final ParsedPermissionGroup group =
+ parsedPackage.getPermissionGroups().get(groupNum);
+ final PermissionGroupInfo sourceGroup = mPm.getPermissionGroupInfo(group.getName(),
+ 0);
+
+ if (sourceGroup != null && cannotInstallWithBadPermissionGroups(parsedPackage)) {
+ final String sourcePackageName = sourceGroup.packageName;
+
+ if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
+ && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission group "
+ + group.getName() + " already owned by "
+ + sourcePackageName);
+ }
+ }
+ }
+
+ // TODO: Move logic for checking permission compatibility into PermissionManagerService
+ final int n = ArrayUtils.size(parsedPackage.getPermissions());
+ for (int i = n - 1; i >= 0; i--) {
+ final ParsedPermission perm = parsedPackage.getPermissions().get(i);
+ final Permission bp = mPm.mPermissionManager.getPermissionTEMP(perm.getName());
+
+ // Don't allow anyone but the system to define ephemeral permissions.
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
+ && !systemApp) {
+ Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
+ + " attempting to delcare ephemeral permission "
+ + perm.getName() + "; Removing ephemeral.");
+ perm.setProtectionLevel(
+ perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
+ }
+
+ // Check whether the newly-scanned package wants to define an already-defined perm
+ if (bp != null) {
+ final String sourcePackageName = bp.getPackageName();
+
+ if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
+ scanFlags)) {
+ // If the owning package is the system itself, we log but allow
+ // install to proceed; we fail the install on all other permission
+ // redefinitions.
+ if (!sourcePackageName.equals("android")) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to redeclare permission "
+ + perm.getName() + " already owned by "
+ + sourcePackageName)
+ .conflictsWithExistingPermission(perm.getName(),
+ sourcePackageName);
+ } else {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " attempting to redeclare system permission "
+ + perm.getName() + "; ignoring new declaration");
+ parsedPackage.removePermission(i);
+ }
+ } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
+ // Prevent apps to change protection level to dangerous from any other
+ // type as this would allow a privilege escalation where an app adds a
+ // normal/signature permission in other app's group and later redefines
+ // it as dangerous leading to the group auto-grant.
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS) {
+ if (bp != null && !bp.isRuntime()) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " trying to change a non-runtime permission "
+ + perm.getName()
+ + " to runtime; keeping old protection level");
+ perm.setProtectionLevel(bp.getProtectionLevel());
+ }
+ }
+ }
+ }
+
+ if (perm.getGroup() != null
+ && cannotInstallWithBadPermissionGroups(parsedPackage)) {
+ boolean isPermGroupDefinedByPackage = false;
+ for (int groupNum = 0; groupNum < numGroups; groupNum++) {
+ if (parsedPackage.getPermissionGroups().get(groupNum).getName()
+ .equals(perm.getGroup())) {
+ isPermGroupDefinedByPackage = true;
+ break;
+ }
+ }
+
+ if (!isPermGroupDefinedByPackage) {
+ final PermissionGroupInfo sourceGroup =
+ mPm.getPermissionGroupInfo(perm.getGroup(), 0);
+
+ if (sourceGroup == null) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in non-existing group "
+ + perm.getGroup());
+ } else {
+ String groupSourcePackageName = sourceGroup.packageName;
+
+ if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
+ && !doesSignatureMatchForPermissions(groupSourcePackageName,
+ parsedPackage, scanFlags)) {
+ EventLog.writeEvent(0x534e4554, "146211400", -1,
+ parsedPackage.getPackageName());
+
+ throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
+ "Package "
+ + parsedPackage.getPackageName()
+ + " attempting to declare permission "
+ + perm.getName() + " in group "
+ + perm.getGroup() + " owned by package "
+ + groupSourcePackageName
+ + " with incompatible certificate");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (systemApp) {
+ if (onExternal) {
+ // Abort update; system app can't be replaced with app on sdcard
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Cannot install updates to system apps on sdcard");
+ } else if (instantApp) {
+ // Abort update; system app can't be replaced with an instant app
+ throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
+ "Cannot update a system app with an instant app");
+ }
+ }
+
+ if (args.mMoveInfo != null) {
+ // We did an in-place move, so dex is ready to roll
+ scanFlags |= SCAN_NO_DEX;
+ scanFlags |= SCAN_MOVE;
+
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ if (ps == null) {
+ res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+ "Missing settings for moved package " + pkgName);
+ }
+
+ // We moved the entire application as-is, so bring over the
+ // previously derived ABI information.
+ parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)
+ .setSecondaryCpuAbi(ps.secondaryCpuAbiString);
+ }
+
+ } else {
+ // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
+ scanFlags |= SCAN_NO_DEX;
+
+ try {
+ PackageSetting pkgSetting;
+ synchronized (mPm.mLock) {
+ pkgSetting = mPm.mSettings.getPackageLPr(pkgName);
+ }
+ boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
+ && pkgSetting.getPkgState().isUpdatedSystemApp();
+ final String abiOverride = deriveAbiOverride(args.mAbiOverride);
+ AndroidPackage oldPackage = mPm.mPackages.get(pkgName);
+ boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
+ derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
+ abiOverride, mPm.mAppLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ } catch (PackageManagerException pme) {
+ Slog.e(TAG, "Error deriving application ABI", pme);
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Error deriving application ABI: " + pme.getMessage());
+ }
+ }
+
+ if (!args.doRename(res.mReturnCode, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
+ }
+
+ try {
+ setUpFsVerityIfPossible(parsedPackage);
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to set up verity: " + e);
+ }
+
+ final PackageFreezer freezer =
+ freezePackageForInstall(pkgName, installFlags, "installPackageLI");
+ boolean shouldCloseFreezerBeforeReturn = true;
+ try {
+ final AndroidPackage existingPackage;
+ String renamedPackage = null;
+ boolean sysPkg = false;
+ int targetScanFlags = scanFlags;
+ int targetParseFlags = parseFlags;
+ final PackageSetting ps;
+ final PackageSetting disabledPs;
+ if (replace) {
+ if (parsedPackage.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // and cannot be updated as an update would get a new package name,
+ // unless this is installed from adb which is useful for development.
+ AndroidPackage existingPkg = mPm.mPackages.get(parsedPackage.getPackageName());
+ if (existingPkg != null
+ && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {
+ throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Packages declaring "
+ + "static-shared libs cannot be updated");
+ }
+ }
+
+ final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+
+ final AndroidPackage oldPackage;
+ final String pkgName11 = parsedPackage.getPackageName();
+ final int[] allUsers;
+ final int[] installedUsers;
+ final int[] uninstalledUsers;
+
+ synchronized (mPm.mLock) {
+ oldPackage = mPm.mPackages.get(pkgName11);
+ existingPackage = oldPackage;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
+ }
+
+ ps = mPm.mSettings.getPackageLPr(pkgName11);
+ disabledPs = mPm.mSettings.getDisabledSystemPkgLPr(ps);
+
+ // verify signatures are valid
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
+ if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package not signed by keys specified by upgrade-keysets: "
+ + pkgName11);
+ }
+ } else {
+ SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
+ SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
+ // default to original signature matching
+ if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ // Allow the update to proceed if this is a rollback and the parsed
+ // package's current signing key is the current signer or in the lineage
+ // of the old package; this allows a rollback to a previously installed
+ // version after an app's signing key has been rotated without requiring
+ // the rollback capability on the previous signing key.
+ if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
+ parsedPkgSigningDetails)) {
+ throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "New package has a different signature: " + pkgName11);
+ }
+ }
+ }
+
+ // don't allow a system upgrade unless the upgrade hash matches
+ if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
+ final byte[] digestBytes;
+ try {
+ final MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ updateDigest(digest, new File(parsedPackage.getBaseApkPath()));
+ if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
+ for (String path : parsedPackage.getSplitCodePaths()) {
+ updateDigest(digest, new File(path));
+ }
+ }
+ digestBytes = digest.digest();
+ } catch (NoSuchAlgorithmException | IOException e) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "Could not compute hash: " + pkgName11);
+ }
+ if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
+ throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
+ "New package fails restrict-update check: " + pkgName11);
+ }
+ // retain upgrade restriction
+ parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
+ }
+
+ // Check for shared user id changes
+ String invalidPackageName = null;
+ if (!Objects.equals(oldPackage.getSharedUserId(),
+ parsedPackage.getSharedUserId())
+ // Don't mark as invalid if the app is trying to
+ // leave a sharedUserId
+ && parsedPackage.getSharedUserId() != null) {
+ invalidPackageName = parsedPackage.getPackageName();
+ }
+
+ if (invalidPackageName != null) {
+ throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
+ "Package " + invalidPackageName + " tried to change user "
+ + oldPackage.getSharedUserId());
+ }
+
+ // In case of rollback, remember per-user/profile install state
+ allUsers = mPm.mUserManager.getUserIds();
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+ uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
+
+
+ // don't allow an upgrade from full to ephemeral
+ if (isInstantApp) {
+ if (args.mUser == null
+ || args.mUser.getIdentifier() == UserHandle.USER_ALL) {
+ for (int currentUser : allUsers) {
+ if (!ps.getInstantApp(currentUser)) {
+ // can't downgrade from full to instant
+ Slog.w(TAG,
+ "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + currentUser);
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+ }
+ } else if (!ps.getInstantApp(args.mUser.getIdentifier())) {
+ // can't downgrade from full to instant
+ Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
+ + " for user: " + args.mUser.getIdentifier());
+ throw new PrepareFailure(
+ PackageManager.INSTALL_FAILED_SESSION_INVALID);
+ }
+ }
+ }
+
+ // Update what is removed
+ res.mRemovedInfo = new PackageRemovedInfo(mPm);
+ res.mRemovedInfo.mUid = oldPackage.getUid();
+ res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName();
+ res.mRemovedInfo.mInstallerPackageName = ps.installSource.installerPackageName;
+ res.mRemovedInfo.mIsStaticSharedLib =
+ parsedPackage.getStaticSharedLibName() != null;
+ res.mRemovedInfo.mIsUpdate = true;
+ res.mRemovedInfo.mOrigUsers = installedUsers;
+ res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length);
+ for (int i = 0; i < installedUsers.length; i++) {
+ final int userId = installedUsers[i];
+ res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId));
+ }
+ res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length);
+ for (int i = 0; i < uninstalledUsers.length; i++) {
+ final int userId = uninstalledUsers[i];
+ res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId));
+ }
+
+ sysPkg = oldPackage.isSystem();
+ if (sysPkg) {
+ // Set the system/privileged/oem/vendor/product flags as needed
+ final boolean privileged = oldPackage.isPrivileged();
+ final boolean oem = oldPackage.isOem();
+ final boolean vendor = oldPackage.isVendor();
+ final boolean product = oldPackage.isProduct();
+ final boolean odm = oldPackage.isOdm();
+ final boolean systemExt = oldPackage.isSystemExt();
+ final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags;
+ final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags
+ | SCAN_AS_SYSTEM
+ | (privileged ? SCAN_AS_PRIVILEGED : 0)
+ | (oem ? SCAN_AS_OEM : 0)
+ | (vendor ? SCAN_AS_VENDOR : 0)
+ | (product ? SCAN_AS_PRODUCT : 0)
+ | (odm ? SCAN_AS_ODM : 0)
+ | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
+
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
+ + ", old=" + oldPackage);
+ }
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ targetParseFlags = systemParseFlags;
+ targetScanFlags = systemScanFlags;
+ } else { // non system replace
+ replace = true;
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG,
+ "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
+ + oldPackage);
+ }
+ }
+ } else { // new package install
+ ps = null;
+ disabledPs = null;
+ replace = false;
+ existingPackage = null;
+ // Remember this for later, in case we need to rollback this install
+ String pkgName1 = parsedPackage.getPackageName();
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
+
+ // TODO(b/194319951): MOVE TO RECONCILE
+ synchronized (mPm.mLock) {
+ renamedPackage = mPm.mSettings.getRenamedPackageLPr(pkgName1);
+ if (renamedPackage != null) {
+ // A package with the same name is already installed, though
+ // it has been renamed to an older name. The package we
+ // are trying to install should be installed as an update to
+ // the existing one, but that has not been requested, so bail.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling package running as "
+ + renamedPackage);
+ }
+ if (mPm.mPackages.containsKey(pkgName1)) {
+ // Don't allow installation over an existing package with the same name.
+ throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
+ "Attempt to re-install " + pkgName1
+ + " without first uninstalling.");
+ }
+ }
+ }
+ // we're passing the freezer back to be closed in a later phase of install
+ shouldCloseFreezerBeforeReturn = false;
+
+ return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+ existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
+ ps, disabledPs);
+ } finally {
+ res.mFreezer = freezer;
+ if (shouldCloseFreezerBeforeReturn) {
+ freezer.close();
+ }
+ }
+ }
+
+ /*
+ * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
+ * as this only works for packages that are installed
+ *
+ * TODO: Move logic for permission group compatibility into PermissionManagerService
+ */
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
+ return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
+ }
+
+ private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
+ @NonNull ParsedPackage parsedPackage, int scanFlags) {
+ // If the defining package is signed with our cert, it's okay. This
+ // also includes the "updating the same package" case, of course.
+ // "updating same package" could also involve key-rotation.
+
+ final PackageSetting sourcePackageSetting;
+ synchronized (mPm.mLock) {
+ sourcePackageSetting = mPm.mSettings.getPackageLPr(sourcePackageName);
+ }
+
+ final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+ ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ if (sourcePackageName.equals(parsedPackage.getPackageName())
+ && (ksms.shouldCheckUpgradeKeySetLocked(
+ sourcePackageSetting, scanFlags))) {
+ return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
+ } else {
+
+ // in the event of signing certificate rotation, we need to see if the
+ // package's certificate has rotated from the current one, or if it is an
+ // older certificate with which the current is ok with sharing permissions
+ if (sourceSigningDetails.checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.PERMISSION)) {
+ return true;
+ } else if (parsedPackage.getSigningDetails().checkCapability(
+ sourceSigningDetails,
+ SigningDetails.CertCapabilities.PERMISSION)) {
+ // the scanned package checks out, has signing certificate rotation
+ // history, and is newer; bring it over
+ synchronized (mPm.mLock) {
+ sourcePackageSetting.signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set up fs-verity for the given package if possible. This requires a feature flag of system
+ * property to be enabled only if the kernel supports fs-verity.
+ *
+ * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
+ * kernel patches). In normal mode, all file format can be supported.
+ */
+ private void setUpFsVerityIfPossible(AndroidPackage pkg) throws Installer.InstallerException,
+ PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
+ final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+ final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+ if (!standardMode && !legacyMode) {
+ return;
+ }
+
+ if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
+ < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
+ return;
+ }
+
+ // Collect files we care for fs-verity setup.
+ ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
+ if (legacyMode) {
+ synchronized (mPm.mLock) {
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (ps != null && ps.isPrivileged()) {
+ fsverityCandidates.put(pkg.getBaseApkPath(), null);
+ if (pkg.getSplitCodePaths() != null) {
+ for (String splitPath : pkg.getSplitCodePaths()) {
+ fsverityCandidates.put(splitPath, null);
+ }
+ }
+ }
+ }
+ } else {
+ // NB: These files will become only accessible if the signing key is loaded in kernel's
+ // .fs-verity keyring.
+ fsverityCandidates.put(pkg.getBaseApkPath(),
+ VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
+
+ final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
+ pkg.getBaseApkPath());
+ if (new File(dmPath).exists()) {
+ fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
+ }
+
+ if (pkg.getSplitCodePaths() != null) {
+ for (String path : pkg.getSplitCodePaths()) {
+ fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
+
+ final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
+ if (new File(splitDmPath).exists()) {
+ fsverityCandidates.put(splitDmPath,
+ VerityUtils.getFsveritySignatureFilePath(splitDmPath));
+ }
+ }
+ }
+ }
+
+ for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
+ final String filePath = entry.getKey();
+ final String signaturePath = entry.getValue();
+
+ if (!legacyMode) {
+ // fs-verity is optional for now. Only set up if signature is provided.
+ if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
+ try {
+ VerityUtils.setUpFsverity(filePath, signaturePath);
+ } catch (IOException e) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to enable fs-verity: " + e);
+ }
+ }
+ continue;
+ }
+
+ // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
+ final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
+ if (result.isOk()) {
+ if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
+ final FileDescriptor fd = result.getUnownedFileDescriptor();
+ try {
+ final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
+ try {
+ // A file may already have fs-verity, e.g. when reused during a split
+ // install. If the measurement succeeds, no need to attempt to set up.
+ mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ } catch (Installer.InstallerException e) {
+ mPm.mInstaller.installApkVerity(filePath, fd, result.getContentSize());
+ mPm.mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
+ }
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ } else if (result.isFailed()) {
+ throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
+ "Failed to generate verity");
+ }
+ }
+ }
+
+ private PackageFreezer freezePackageForInstall(String packageName, int installFlags,
+ String killReason) {
+ return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);
+ }
+
+ private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
+ String killReason) {
+ if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
+ return new PackageFreezer(mPm);
+ } else {
+ return mPm.freezePackage(packageName, userId, killReason);
+ }
+ }
+
+ private static void updateDigest(MessageDigest digest, File file) throws IOException {
+ try (DigestInputStream digestStream =
+ new DigestInputStream(new FileInputStream(file), digest)) {
+ int length, total = 0;
+ while ((length = digestStream.read()) != -1) {
+ total += length;
+ } // just plow through the file
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void commitPackagesLocked(final CommitRequest request) {
+ // TODO: remove any expected failures from this method; this should only be able to fail due
+ // to unavoidable errors (I/O, etc.)
+ for (ReconciledPackage reconciledPkg : request.mReconciledPackages.values()) {
+ final ScanResult scanResult = reconciledPkg.mScanResult;
+ final ScanRequest scanRequest = scanResult.mRequest;
+ final ParsedPackage parsedPackage = scanRequest.mParsedPackage;
+ final String packageName = parsedPackage.getPackageName();
+ final PackageInstalledInfo res = reconciledPkg.mInstallResult;
+
+ if (reconciledPkg.mPrepareResult.mReplace) {
+ AndroidPackage oldPackage = mPm.mPackages.get(packageName);
+
+ // Set the update and install times
+ PackageSetting deletedPkgSetting = mPm.getPackageSetting(
+ oldPackage.getPackageName());
+ reconciledPkg.mPkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
+ reconciledPkg.mPkgSetting.lastUpdateTime = System.currentTimeMillis();
+
+ res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
+ reconciledPkg.mPkgSetting, request.mAllUsers,
+ mPm.mSettings.getPackagesLocked());
+ if (reconciledPkg.mPrepareResult.mSystem) {
+ // Remove existing system package
+ mPm.removePackageLI(oldPackage, true);
+ if (!disableSystemPackageLPw(oldPackage)) {
+ // We didn't need to disable the .apk as a current system package,
+ // which means we are replacing another update that is already
+ // installed. We need to make sure to delete the older one's .apk.
+ res.mRemovedInfo.mArgs = mPm.createInstallArgsForExisting(
+ oldPackage.getPath(),
+ getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
+ deletedPkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
+ deletedPkgSetting)));
+ } else {
+ res.mRemovedInfo.mArgs = null;
+ }
+ } else {
+ try {
+ // Settings will be written during the call to updateSettingsLI().
+ mPm.executeDeletePackageLIF(reconciledPkg.mDeletePackageAction, packageName,
+ true, request.mAllUsers, false);
+ } catch (SystemDeleteException e) {
+ if (mPm.mIsEngBuild) {
+ throw new RuntimeException("Unexpected failure", e);
+ // ignore; not possible for non-system app
+ }
+ }
+ // Successfully deleted the old package; proceed with replace.
+
+ // If deleted package lived in a container, give users a chance to
+ // relinquish resources before killing.
+ if (oldPackage.isExternalStorage()) {
+ if (DEBUG_INSTALL) {
+ Slog.i(TAG, "upgrading pkg " + oldPackage
+ + " is ASEC-hosted -> UNAVAILABLE");
+ }
+ final int[] uidArray = new int[]{oldPackage.getUid()};
+ final ArrayList<String> pkgList = new ArrayList<>(1);
+ pkgList.add(oldPackage.getPackageName());
+ mPm.sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ }
+
+ // Update the in-memory copy of the previous code paths.
+ PackageSetting ps1 = mPm.mSettings.getPackageLPr(
+ reconciledPkg.mPrepareResult.mExistingPackage.getPackageName());
+ if ((reconciledPkg.mInstallArgs.mInstallFlags & PackageManager.DONT_KILL_APP)
+ == 0) {
+ if (ps1.mOldCodePaths == null) {
+ ps1.mOldCodePaths = new ArraySet<>();
+ }
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
+ if (oldPackage.getSplitCodePaths() != null) {
+ Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
+ }
+ } else {
+ ps1.mOldCodePaths = null;
+ }
+
+ if (reconciledPkg.mInstallResult.mReturnCode
+ == PackageManager.INSTALL_SUCCEEDED) {
+ PackageSetting ps2 = mPm.mSettings.getPackageLPr(
+ parsedPackage.getPackageName());
+ if (ps2 != null) {
+ res.mRemovedInfo.mRemovedForAllUsers =
+ mPm.mPackages.get(ps2.name) == null;
+ }
+ }
+ }
+ }
+
+ AndroidPackage pkg = mPm.commitReconciledScanResultLocked(reconciledPkg,
+ request.mAllUsers);
+ updateSettingsLI(pkg, reconciledPkg.mInstallArgs, request.mAllUsers, res);
+
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ ps.setUpdateAvailable(false /*updateAvailable*/);
+ }
+ if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
+ mPm.updateSequenceNumberLP(ps, res.mNewUsers);
+ mPm.updateInstantAppInstallerLocked(packageName);
+ }
+ }
+ ApplicationPackageManager.invalidateGetPackagesForUidCache();
+ }
+
+ @GuardedBy("mLock")
+ private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
+ return mPm.mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
+ }
+
+ private void updateSettingsLI(AndroidPackage newPackage, InstallArgs installArgs,
+ int[] allUsers, PackageInstalledInfo res) {
+ updateSettingsInternalLI(newPackage, installArgs, allUsers, res);
+ }
+
+ private void updateSettingsInternalLI(AndroidPackage pkg, InstallArgs installArgs,
+ int[] allUsers, PackageInstalledInfo res) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
+
+ final String pkgName = pkg.getPackageName();
+ final int[] installedForUsers = res.mOrigUsers;
+ final int installReason = installArgs.mInstallReason;
+ InstallSource installSource = installArgs.mInstallSource;
+ final String installerPackageName = installSource.installerPackageName;
+
+ if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
+ synchronized (mPm.mLock) {
+ // For system-bundled packages, we assume that installing an upgraded version
+ // of the package implies that the user actually wants to run that new code,
+ // so we enable the package.
+ final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+ final int userId = installArgs.mUser.getIdentifier();
+ if (ps != null) {
+ if (pkg.isSystem()) {
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
+ }
+ // Enable system package for requested users
+ if (res.mOrigUsers != null) {
+ for (int origUserId : res.mOrigUsers) {
+ if (userId == UserHandle.USER_ALL || userId == origUserId) {
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
+ origUserId, installerPackageName);
+ }
+ }
+ }
+ // Also convey the prior install/uninstall state
+ if (allUsers != null && installedForUsers != null) {
+ for (int currentUserId : allUsers) {
+ final boolean installed = ArrayUtils.contains(
+ installedForUsers, currentUserId);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, " user " + currentUserId + " => " + installed);
+ }
+ ps.setInstalled(installed, currentUserId);
+ }
+ // these install state changes will be persisted in the
+ // upcoming call to mSettings.writeLPr().
+ }
+
+ if (allUsers != null) {
+ for (int currentUserId : allUsers) {
+ ps.resetOverrideComponentLabelIcon(currentUserId);
+ }
+ }
+ }
+
+ // Retrieve the overlays for shared libraries of the package.
+ if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
+ for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
+ for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
+ if (!sharedLib.isDynamic()) {
+ // TODO(146804378): Support overlaying static shared libraries
+ continue;
+ }
+ final PackageSetting libPs = mPm.mSettings.getPackageLPr(
+ sharedLib.getPackageName());
+ if (libPs == null) {
+ continue;
+ }
+ ps.setOverlayPathsForLibrary(sharedLib.getName(),
+ libPs.getOverlayPaths(currentUserId), currentUserId);
+ }
+ }
+ }
+
+ // It's implied that when a user requests installation, they want the app to be
+ // installed and enabled. (This does not apply to USER_ALL, which here means only
+ // install on users for which the app is already installed).
+ if (userId != UserHandle.USER_ALL) {
+ ps.setInstalled(true, userId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
+ }
+
+ mPm.mSettings.addInstallerPackageNames(ps.installSource);
+
+ // When replacing an existing package, preserve the original install reason for all
+ // users that had the package installed before. Similarly for uninstall reasons.
+ final Set<Integer> previousUserIds = new ArraySet<>();
+ if (res.mRemovedInfo != null && res.mRemovedInfo.mInstallReasons != null) {
+ final int installReasonCount = res.mRemovedInfo.mInstallReasons.size();
+ for (int i = 0; i < installReasonCount; i++) {
+ final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i);
+ final int previousInstallReason =
+ res.mRemovedInfo.mInstallReasons.valueAt(i);
+ ps.setInstallReason(previousInstallReason, previousUserId);
+ previousUserIds.add(previousUserId);
+ }
+ }
+ if (res.mRemovedInfo != null && res.mRemovedInfo.mUninstallReasons != null) {
+ for (int i = 0; i < res.mRemovedInfo.mUninstallReasons.size(); i++) {
+ final int previousUserId = res.mRemovedInfo.mUninstallReasons.keyAt(i);
+ final int previousReason = res.mRemovedInfo.mUninstallReasons.valueAt(i);
+ ps.setUninstallReason(previousReason, previousUserId);
+ }
+ }
+
+ // Set install reason for users that are having the package newly installed.
+ final int[] allUsersList = mPm.mUserManager.getUserIds();
+ if (userId == UserHandle.USER_ALL) {
+ // TODO(b/152629990): It appears that the package doesn't actually get newly
+ // installed in this case, so the installReason shouldn't get modified?
+ for (int currentUserId : allUsersList) {
+ if (!previousUserIds.contains(currentUserId)) {
+ ps.setInstallReason(installReason, currentUserId);
+ }
+ }
+ } else if (!previousUserIds.contains(userId)) {
+ ps.setInstallReason(installReason, userId);
+ }
+
+ // TODO(b/169721400): generalize Incremental States and create a Callback object
+ // that can be used for all the packages.
+ final String codePath = ps.getPathString();
+ if (IncrementalManager.isIncrementalPath(codePath)
+ && mPm.mIncrementalManager != null) {
+ final IncrementalStatesCallback incrementalStatesCallback =
+ new IncrementalStatesCallback(ps.name, mPm);
+ ps.setIncrementalStatesCallback(incrementalStatesCallback);
+ mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
+ new IncrementalProgressListener(ps.name, mPm));
+ }
+
+ // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
+ for (int currentUserId : allUsersList) {
+ if (ps.getInstalled(currentUserId)) {
+ ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
+ }
+ }
+
+ mPm.mSettings.writeKernelMappingLPr(ps);
+
+ final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+ permissionParamsBuilder =
+ new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
+ final boolean grantPermissions = (installArgs.mInstallFlags
+ & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
+ if (grantPermissions) {
+ final List<String> grantedPermissions =
+ installArgs.mInstallGrantPermissions != null
+ ? Arrays.asList(installArgs.mInstallGrantPermissions)
+ : pkg.getRequestedPermissions();
+ permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
+ }
+ final boolean allowlistAllRestrictedPermissions =
+ (installArgs.mInstallFlags
+ & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
+ final List<String> allowlistedRestrictedPermissions =
+ allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
+ : installArgs.mAllowlistedRestrictedPermissions;
+ if (allowlistedRestrictedPermissions != null) {
+ permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+ allowlistedRestrictedPermissions);
+ }
+ final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode;
+ permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
+ mPm.mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(),
+ userId);
+ }
+ res.mName = pkgName;
+ res.mUid = pkg.getUid();
+ res.mPkg = pkg;
+ res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ //to update install status
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
+ mPm.writeSettingsLPrTEMP();
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ /**
+ * On successful install, executes remaining steps after commit completes and the package lock
+ * is released. These are typically more expensive or require calls to installd, which often
+ * locks on {@link com.android.server.pm.PackageManagerService.mLock}.
+ */
+ private void executePostCommitSteps(CommitRequest commitRequest) {
+ final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
+ for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
+ final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
+ & SCAN_AS_INSTANT_APP) != 0);
+ final AndroidPackage pkg = reconciledPkg.mPkgSetting.pkg;
+ final String packageName = pkg.getPackageName();
+ final String codePath = pkg.getPath();
+ final boolean onIncremental = mPm.mIncrementalManager != null
+ && isIncrementalPath(codePath);
+ if (onIncremental) {
+ IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
+ if (storage == null) {
+ throw new IllegalArgumentException(
+ "Install: null storage for incremental package " + packageName);
+ }
+ incrementalStorages.add(storage);
+ }
+ mPm.prepareAppDataAfterInstallLIF(pkg);
+ if (reconciledPkg.mPrepareResult.mClearCodeCache) {
+ mPm.clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ }
+ if (reconciledPkg.mPrepareResult.mReplace) {
+ mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ pkg.getBaseApkPath(), pkg.getSplitCodePaths());
+ }
+
+ // Prepare the application profiles for the new code paths.
+ // This needs to be done before invoking dexopt so that any install-time profile
+ // can be used for optimizations.
+ mPm.mArtManagerService.prepareAppProfiles(
+ pkg,
+ mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
+ /* updateReferenceProfileContent= */ true);
+
+ // Compute the compilation reason from the installation scenario.
+ final int compilationReason =
+ mPm.getDexManager().getCompilationReasonForInstallScenario(
+ reconciledPkg.mInstallArgs.mInstallScenario);
+
+ // Construct the DexoptOptions early to see if we should skip running dexopt.
+ //
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ final boolean isBackupOrRestore =
+ reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE
+ || reconciledPkg.mInstallArgs.mInstallReason
+ == INSTALL_REASON_DEVICE_SETUP;
+
+ final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
+ | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE
+ | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
+ DexoptOptions dexoptOptions =
+ new DexoptOptions(packageName, compilationReason, dexoptFlags);
+
+ // Check whether we need to dexopt the app.
+ //
+ // NOTE: it is IMPORTANT to call dexopt:
+ // - after doRename which will sync the package data from AndroidPackage and
+ // its corresponding ApplicationInfo.
+ // - after installNewPackageLIF or replacePackageLIF which will update result with the
+ // uid of the application (pkg.applicationInfo.uid).
+ // This update happens in place!
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
+ // 2) it is not debuggable.
+ // 3) it is not on Incremental File System.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ //
+ // Furthermore, dexopt may be skipped, depending on the install scenario and current
+ // state of the device.
+ //
+ // TODO(b/174695087): instantApp and onIncremental should be removed and their install
+ // path moved to SCENARIO_FAST.
+ final boolean performDexopt =
+ (!instantApp || android.provider.Settings.Global.getInt(
+ mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
+ && !pkg.isDebuggable()
+ && (!onIncremental)
+ && dexoptOptions.isCompilationEnabled();
+
+ if (performDexopt) {
+ // Compile the layout resources.
+ if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
+ mPm.mViewCompiler.compileLayouts(pkg);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ ScanResult result = reconciledPkg.mScanResult;
+
+ // This mirrors logic from commitReconciledScanResultLocked, where the library files
+ // needed for dexopt are assigned.
+ // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
+ // setting needs to be passed to have a comparison, hide it behind an immutable
+ // interface. There's no good reason to have 3 different ways to access the real
+ // PackageSetting object, only one of which is actually correct.
+ PackageSetting realPkgSetting = result.mExistingSettingCopied
+ ? result.mRequest.mPkgSetting : result.mPkgSetting;
+ if (realPkgSetting == null) {
+ realPkgSetting = reconciledPkg.mPkgSetting;
+ }
+
+ // Unfortunately, the updated system app flag is only tracked on this PackageSetting
+ boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState()
+ .isUpdatedSystemApp();
+
+ realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+
+ mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ null /* instructionSets */,
+ mPm.getOrCreateCompilerPackageStats(pkg),
+ mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
+ dexoptOptions);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its denylist.
+ // TODO: Layering violation
+ BackgroundDexOptService.notifyPackageChanged(packageName);
+
+ notifyPackageChangeObserversOnUpdate(reconciledPkg);
+ }
+ waitForNativeBinariesExtraction(incrementalStorages);
+ }
+
+ private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+ final PackageSetting pkgSetting = reconciledPkg.mPkgSetting;
+ final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.mInstallResult;
+ final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo;
+
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
+ pkgChangeEvent.version = pkgSetting.versionCode;
+ pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+ pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
+ pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
+ pkgChangeEvent.isDeleted = false;
+
+ mPm.notifyPackageChangeObservers(pkgChangeEvent);
+ }
+
+ static void waitForNativeBinariesExtraction(
+ ArraySet<IncrementalStorage> incrementalStorages) {
+ if (incrementalStorages.isEmpty()) {
+ return;
+ }
+ try {
+ // Native library extraction may take very long time: each page could potentially
+ // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
+ // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
+ // make much sense as blocking here doesn't lock up the framework, but only blocks
+ // the installation session and the following ones.
+ Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
+ for (int i = 0; i < incrementalStorages.size(); ++i) {
+ IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
+ storage.waitForNativeBinariesExtraction();
+ }
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
+ }
+ }
+
+ /**
+ * Ensure that the install reason matches what we know about the package installer (e.g. whether
+ * it is acting on behalf on an enterprise or the user).
+ *
+ * Note that the ordering of the conditionals in this method is important. The checks we perform
+ * are as follows, in this order:
+ *
+ * 1) If the install is being performed by a system app, we can trust the app to have set the
+ * install reason correctly. Thus, we pass through the install reason unchanged, no matter
+ * what it is.
+ * 2) If the install is being performed by a device or profile owner app, the install reason
+ * should be enterprise policy. However, we cannot be sure that the device or profile owner
+ * set the install reason correctly. If the app targets an older SDK version where install
+ * reasons did not exist yet, or if the app author simply forgot, the install reason may be
+ * unset or wrong. Thus, we force the install reason to be enterprise policy.
+ * 3) In all other cases, the install is being performed by a regular app that is neither part
+ * of the system nor a device or profile owner. We have no reason to believe that this app is
+ * acting on behalf of the enterprise admin. Thus, we check whether the install reason was
+ * set to enterprise policy and if so, change it to unknown instead.
+ */
+ private int fixUpInstallReason(String installerPackageName, int installerUid,
+ int installReason) {
+ if (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+ == PERMISSION_GRANTED) {
+ // If the install is being performed by a system app, we trust that app to have set the
+ // install reason correctly.
+ return installReason;
+ }
+ final String ownerPackage = mPm.mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(
+ UserHandle.getUserId(installerUid));
+ if (ownerPackage != null && ownerPackage.equals(installerPackageName)) {
+ // If the install is being performed by a device or profile owner, the install
+ // reason should be enterprise policy.
+ return PackageManager.INSTALL_REASON_POLICY;
+ }
+
+
+ if (installReason == PackageManager.INSTALL_REASON_POLICY) {
+ // If the install is being performed by a regular app (i.e. neither system app nor
+ // device or profile owner), we have no reason to believe that the app is acting on
+ // behalf of an enterprise. If the app set the install reason to enterprise policy,
+ // change it to unknown instead.
+ return PackageManager.INSTALL_REASON_UNKNOWN;
+ }
+
+ // If the install is being performed by a regular app and the install reason was set to any
+ // value but enterprise policy, leave the install reason unchanged.
+ return installReason;
+ }
+
+ public void installStage() {
+ final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
+ setTraceMethod("installStage").setTraceCookie(System.identityHashCode(this));
+ msg.obj = this;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+
+ mPm.mHandler.sendMessage(msg);
+ }
+
+ public void installStage(List<InstallParams> children)
+ throws PackageManagerException {
+ final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
+ final MultiPackageInstallParams params =
+ new MultiPackageInstallParams(this, children);
+ params.setTraceMethod("installStageMultiPackage")
+ .setTraceCookie(System.identityHashCode(params));
+ msg.obj = params;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+ mPm.mHandler.sendMessage(msg);
+ }
+
+ public void movePackage() {
+ final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
+ setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(this));
+ msg.obj = this;
+
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
+ System.identityHashCode(msg.obj));
+ Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
+ System.identityHashCode(msg.obj));
+ mPm.mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Container for a multi-package install which refers to all install sessions and args being
+ * committed together.
+ */
+ final class MultiPackageInstallParams extends HandlerParams {
+ private final List<InstallParams> mChildParams;
+ private final Map<InstallArgs, Integer> mCurrentState;
+
+ MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams)
+ throws PackageManagerException {
+ super(parent.getUser());
+ if (childParams.size() == 0) {
+ throw new PackageManagerException("No child sessions found!");
+ }
+ mChildParams = childParams;
+ for (int i = 0; i < childParams.size(); i++) {
+ final InstallParams childParam = childParams.get(i);
+ childParam.mParentInstallParams = this;
+ }
+ this.mCurrentState = new ArrayMap<>(mChildParams.size());
+ }
+
+ @Override
+ void handleStartCopy() {
+ for (InstallParams params : mChildParams) {
+ params.handleStartCopy();
+ }
+ }
+
+ @Override
+ void handleReturnCode() {
+ for (InstallParams params : mChildParams) {
+ params.handleReturnCode();
+ }
+ }
+
+ void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
+ mCurrentState.put(args, currentStatus);
+ if (mCurrentState.size() != mChildParams.size()) {
+ return;
+ }
+ int completeStatus = PackageManager.INSTALL_SUCCEEDED;
+ for (Integer status : mCurrentState.values()) {
+ if (status == PackageManager.INSTALL_UNKNOWN) {
+ return;
+ } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+ completeStatus = status;
+ break;
+ }
+ }
+ final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
+ for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
+ installRequests.add(new InstallRequest(entry.getKey(),
+ new PackageInstalledInfo(completeStatus)));
+ }
+ processInstallRequestsAsync(
+ completeStatus == PackageManager.INSTALL_SUCCEEDED,
+ installRequests);
+ }
+ }
+}
diff --git a/core/java/android/uwb/MeasurementStatus.aidl b/services/core/java/com/android/server/pm/InstallRequest.java
index 5fa15549e84d..753d012a32e3 100644
--- a/core/java/android/uwb/MeasurementStatus.aidl
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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,26 +14,14 @@
* limitations under the License.
*/
-package android.uwb;
+package com.android.server.pm;
-/**
- * @hide
- */
-@Backing(type="int")
-enum MeasurementStatus {
- /**
- * Ranging was successful
- */
- SUCCESS,
-
- /**
- * The remote device is out of range
- */
- FAILURE_OUT_OF_RANGE,
+final class InstallRequest {
+ public final InstallArgs mArgs;
+ public final PackageInstalledInfo mInstallResult;
- /**
- * An unknown failure has occurred.
- */
- FAILURE_UNKNOWN,
+ InstallRequest(InstallArgs args, PackageInstalledInfo res) {
+ mArgs = args;
+ mInstallResult = res;
+ }
}
-
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index bf323e7d4ff2..d4ebbe38e3b6 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -23,8 +23,9 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -123,6 +124,7 @@ class InstantAppRegistry implements Watchable, Snappable {
private final PackageManagerService mService;
private final PermissionManagerServiceInternal mPermissionManager;
private final CookiePersistence mCookiePersistence;
+ private final PackageManagerInternal mPmInternal;
/** State for uninstalled instant apps */
@Watched
@@ -191,9 +193,11 @@ class InstantAppRegistry implements Watchable, Snappable {
}
public InstantAppRegistry(PackageManagerService service,
- PermissionManagerServiceInternal permissionManager) {
+ PermissionManagerServiceInternal permissionManager,
+ PackageManagerInternal pmInternal) {
mService = service;
mPermissionManager = permissionManager;
+ mPmInternal = pmInternal;
mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper());
mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>();
@@ -214,6 +218,7 @@ class InstantAppRegistry implements Watchable, Snappable {
private InstantAppRegistry(InstantAppRegistry r) {
mService = r.mService;
mPermissionManager = r.mPermissionManager;
+ mPmInternal = r.mPmInternal;
mCookiePersistence = null;
mUninstalledInstantApps = new WatchedSparseArray<List<UninstalledInstantAppState>>(
@@ -366,7 +371,7 @@ class InstantAppRegistry implements Watchable, Snappable {
@GuardedBy("mService.mLock")
public void onPackageInstalledLPw(@NonNull AndroidPackage pkg, @NonNull int[] userIds) {
- PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+ PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
if (ps == null) {
return;
}
@@ -412,14 +417,14 @@ class InstantAppRegistry implements Watchable, Snappable {
// into account but also allow the value from the old computation to avoid
// data loss.
if (pkg.getSigningDetails().checkCapability(currentCookieSha256,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
+ SigningDetails.CertCapabilities.INSTALLED_DATA)) {
return;
}
// For backwards compatibility we accept match based on any signature, since we may have
// recorded only the first for multiply-signed packages
- final String[] signaturesSha256Digests =
- PackageUtils.computeSignaturesSha256Digests(pkg.getSigningDetails().signatures);
+ final String[] signaturesSha256Digests = PackageUtils.computeSignaturesSha256Digests(
+ pkg.getSigningDetails().getSignatures());
for (String s : signaturesSha256Digests) {
if (s.equals(currentCookieSha256)) {
return;
@@ -784,7 +789,7 @@ class InstantAppRegistry implements Watchable, Snappable {
final int packageCount = mService.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+ final PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
if (ps == null) {
continue;
}
@@ -824,13 +829,13 @@ class InstantAppRegistry implements Watchable, Snappable {
} else if (rhsPkg == null) {
return 1;
} else {
- final PackageSetting lhsPs = mService.getPackageSetting(
+ final PackageSetting lhsPs = mPmInternal.getPackageSetting(
lhsPkg.getPackageName());
if (lhsPs == null) {
return 0;
}
- final PackageSetting rhsPs = mService.getPackageSetting(
+ final PackageSetting rhsPs = mPmInternal.getPackageSetting(
rhsPkg.getPackageName());
if (rhsPs == null) {
return 0;
@@ -918,7 +923,7 @@ class InstantAppRegistry implements Watchable, Snappable {
final int packageCount = mService.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final AndroidPackage pkg = mService.mPackages.valueAt(i);
- final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+ final PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
if (ps == null || !ps.getInstantApp(userId)) {
continue;
}
@@ -940,7 +945,7 @@ class InstantAppRegistry implements Watchable, Snappable {
InstantAppInfo createInstantAppInfoForPackage(
@NonNull AndroidPackage pkg, @UserIdInt int userId,
boolean addApplicationInfo) {
- PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+ PackageSetting ps = mPmInternal.getPackageSetting(pkg.getPackageName());
if (ps == null) {
return null;
}
@@ -1305,8 +1310,8 @@ class InstantAppRegistry implements Watchable, Snappable {
// We prefer the modern computation procedure where all certs are taken
// into account and delete the file derived via the legacy hash computation.
File newCookieFile = computeInstantCookieFile(pkg.getPackageName(),
- PackageUtils.computeSignaturesSha256Digest(pkg.getSigningDetails().signatures),
- userId);
+ PackageUtils.computeSignaturesSha256Digest(
+ pkg.getSigningDetails().getSignatures()), userId);
if (!pkg.getSigningDetails().hasSignatures()) {
Slog.wtf(LOG_TAG, "Parsed Instant App contains no valid signatures!");
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 34caaf52b215..1b919f964b33 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -17,10 +17,10 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
-import android.content.pm.PackageParser;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
@@ -193,7 +193,7 @@ public class KeySetManagerService {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Passed invalid package to keyset validation.");
}
- ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().publicKeys;
+ ArraySet<PublicKey> signingKeys = pkg.getSigningDetails().getPublicKeys();
if (signingKeys == null || !(signingKeys.size() > 0) || signingKeys.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package has invalid signing-key-set.");
@@ -226,7 +226,7 @@ public class KeySetManagerService {
PackageSetting ps = mPackages.get(pkg.getPackageName());
Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
+ "does not have a corresponding entry in mPackages.");
- addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
+ addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().getPublicKeys());
if (pkg.getKeySetMapping() != null) {
addDefinedKeySetsToPackageLPw(ps, pkg.getKeySetMapping());
if (pkg.getUpgradeKeySets() != null) {
@@ -371,7 +371,7 @@ public class KeySetManagerService {
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
if (upgradeSet != null
- && pkg.getSigningDetails().publicKeys.containsAll(upgradeSet)) {
+ && pkg.getSigningDetails().getPublicKeys().containsAll(upgradeSet)) {
return true;
}
}
@@ -801,7 +801,7 @@ public class KeySetManagerService {
long identifier = parser.getAttributeLong(null, "identifier");
int refCount = 0;
byte[] publicKey = parser.getAttributeBytesBase64(null, "value", null);
- PublicKey pub = PackageParser.parsePublicKey(publicKey);
+ PublicKey pub = parsePublicKey(publicKey);
if (pub != null) {
PublicKeyHandle pkh = new PublicKeyHandle(identifier, refCount, pub);
mPublicKeys.put(identifier, pkh);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 419b72675c49..6f02138bbab6 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -407,11 +407,16 @@ public class LauncherAppsService extends SystemService {
@Override
public boolean shouldHideFromSuggestions(String packageName, UserHandle user) {
- if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
+ final int userId = user.getIdentifier();
+ if (!canAccessProfile(userId, "cannot get shouldHideFromSuggestions")) {
return false;
}
- final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(packageName,
- user.getIdentifier());
+ if (mPackageManagerInternal.filterAppAccess(
+ packageName, Binder.getCallingUid(), userId)) {
+ return false;
+ }
+ final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(
+ packageName, userId);
return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
}
diff --git a/services/core/java/com/android/server/pm/MoveInfo.java b/services/core/java/com/android/server/pm/MoveInfo.java
new file mode 100644
index 000000000000..5ab86d626e8b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/MoveInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+final class MoveInfo {
+ final int mMoveId;
+ final String mFromUuid;
+ final String mToUuid;
+ final String mPackageName;
+ final int mAppId;
+ final String mSeInfo;
+ final int mTargetSdkVersion;
+ final String mFromCodePath;
+
+ MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
+ int appId, String seInfo, int targetSdkVersion,
+ String fromCodePath) {
+ mMoveId = moveId;
+ mFromUuid = fromUuid;
+ mToUuid = toUuid;
+ mPackageName = packageName;
+ mAppId = appId;
+ mSeInfo = seInfo;
+ mTargetSdkVersion = targetSdkVersion;
+ mFromCodePath = fromCodePath;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/MoveInstallArgs.java b/services/core/java/com/android/server/pm/MoveInstallArgs.java
new file mode 100644
index 000000000000..35827a196a39
--- /dev/null
+++ b/services/core/java/com/android/server/pm/MoveInstallArgs.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.util.Slog;
+
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import java.io.File;
+
+/**
+ * Logic to handle movement of existing installed applications.
+ */
+final class MoveInstallArgs extends InstallArgs {
+ private File mCodeFile;
+
+ /** New install */
+ MoveInstallArgs(InstallParams params) {
+ super(params);
+ }
+
+ int copyApk() {
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, "Moving " + mMoveInfo.mPackageName + " from "
+ + mMoveInfo.mFromUuid + " to " + mMoveInfo.mToUuid);
+ }
+ synchronized (mPm.mInstaller) {
+ try {
+ mPm.mInstaller.moveCompleteApp(mMoveInfo.mFromUuid, mMoveInfo.mToUuid,
+ mMoveInfo.mPackageName, mMoveInfo.mAppId, mMoveInfo.mSeInfo,
+ mMoveInfo.mTargetSdkVersion, mMoveInfo.mFromCodePath);
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, "Failed to move app", e);
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ }
+ }
+
+ final String toPathName = new File(mMoveInfo.mFromCodePath).getName();
+ mCodeFile = new File(Environment.getDataAppDirectory(mMoveInfo.mToUuid), toPathName);
+ if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + mCodeFile);
+
+ return PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ int doPreInstall(int status) {
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp(mMoveInfo.mToUuid);
+ }
+ return status;
+ }
+
+ @Override
+ boolean doRename(int status, ParsedPackage parsedPackage) {
+ if (status != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp(mMoveInfo.mToUuid);
+ return false;
+ }
+
+ return true;
+ }
+
+ int doPostInstall(int status, int uid) {
+ if (status == PackageManager.INSTALL_SUCCEEDED) {
+ cleanUp(mMoveInfo.mFromUuid);
+ } else {
+ cleanUp(mMoveInfo.mToUuid);
+ }
+ return status;
+ }
+
+ @Override
+ String getCodePath() {
+ return (mCodeFile != null) ? mCodeFile.getAbsolutePath() : null;
+ }
+
+ private void cleanUp(String volumeUuid) {
+ final String toPathName = new File(mMoveInfo.mFromCodePath).getName();
+ final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
+ toPathName);
+ Slog.d(TAG, "Cleaning up " + mMoveInfo.mPackageName + " on " + volumeUuid);
+ final int[] userIds = mPm.mUserManager.getUserIds();
+ synchronized (mPm.mInstallLock) {
+ // Clean up both app data and code
+ // All package moves are frozen until finished
+
+ // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
+ // this task was only focused on moving data on internal storage.
+ // We don't want ART profiles cleared, because they don't move,
+ // so we would be deleting the only copy (b/149200535).
+ final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
+ for (int userId : userIds) {
+ try {
+ mPm.mInstaller.destroyAppData(volumeUuid, mMoveInfo.mPackageName, userId, flags,
+ 0);
+ } catch (Installer.InstallerException e) {
+ Slog.w(TAG, String.valueOf(e));
+ }
+ }
+ mPm.removeCodePathLI(codeFile);
+ }
+ }
+
+ void cleanUpResourcesLI() {
+ throw new UnsupportedOperationException();
+ }
+
+ boolean doPostDeleteLI(boolean delete) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 43c5d5e4015e..3233819b543f 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -42,7 +42,7 @@ per-file KeySetHandle.java = cbrubaker@google.com, nnk@google.com
per-file KeySetManagerService.java = cbrubaker@google.com, nnk@google.com
per-file PackageKeySetData.java = cbrubaker@google.com, nnk@google.com
per-file PackageSignatures.java = cbrubaker@google.com, nnk@google.com
-per-file SELinuxMMAC.java = cbrubaker@google.com, jeffv@google.com, jgalenson@google.com, nnk@google.com
+per-file SELinuxMMAC* = alanstokes@google.com, cbrubaker@google.com, jeffv@google.com, jgalenson@google.com
# shortcuts
per-file LauncherAppsService.java = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
diff --git a/services/core/java/com/android/server/pm/OriginInfo.java b/services/core/java/com/android/server/pm/OriginInfo.java
new file mode 100644
index 000000000000..b2fbd32da983
--- /dev/null
+++ b/services/core/java/com/android/server/pm/OriginInfo.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 java.io.File;
+
+final class OriginInfo {
+ /**
+ * Location where install is coming from, before it has been
+ * copied/renamed into place. This could be a single monolithic APK
+ * file, or a cluster directory. This location may be untrusted.
+ */
+ final File mFile;
+
+ /**
+ * Flag indicating that {@link #mFile} has already been staged, meaning downstream users
+ * don't need to defensively copy the contents.
+ */
+ final boolean mStaged;
+
+ /**
+ * Flag indicating that {@link #mFile} is an already installed app that is being moved.
+ */
+ final boolean mExisting;
+
+ final String mResolvedPath;
+ final File mResolvedFile;
+
+ static OriginInfo fromNothing() {
+ return new OriginInfo(null, false, false);
+ }
+
+ static OriginInfo fromExistingFile(File file) {
+ return new OriginInfo(file, false, true);
+ }
+
+ static OriginInfo fromStagedFile(File file) {
+ return new OriginInfo(file, true, false);
+ }
+
+ private OriginInfo(File file, boolean staged, boolean existing) {
+ mFile = file;
+ mStaged = staged;
+ mExisting = existing;
+
+ if (file != null) {
+ mResolvedPath = file.getAbsolutePath();
+ mResolvedFile = file;
+ } else {
+ mResolvedPath = null;
+ mResolvedFile = null;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index d6400f3c879e..f968daf39c9b 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -23,6 +23,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.IOtaDexopt;
+import android.content.pm.PackageManagerInternal;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -33,6 +34,7 @@ import android.util.Log;
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
+import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -41,7 +43,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -371,8 +372,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
return;
}
- // Look into all packages.
- Collection<AndroidPackage> pkgs = mPackageManagerService.getPackages();
+ // Make a copy of all packages and look into each package.
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
+ final ArrayList<AndroidPackage> pkgs = new ArrayList<>();
+ pmInt.forEachPackage(pkgs::add);
int packagePaths = 0;
int pathsSuccessful = 0;
for (AndroidPackage pkg : pkgs) {
@@ -398,7 +401,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
continue;
}
- PackageSetting pkgSetting = mPackageManagerService.getPackageSetting(pkg.getPackageName());
+ PackageSetting pkgSetting = pmInt.getPackageSetting(pkg.getPackageName());
final String[] instructionSets = getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 44f7d8869322..dd22fd6f4b97 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -210,7 +210,7 @@ public class PackageDexOptimizer {
if (paths.size() != classLoaderContexts.length) {
String[] splitCodePaths = pkg.getSplitCodePaths();
throw new IllegalStateException("Inconsistent information "
- + "between PackageParser.Package and its ApplicationInfo. "
+ + "between AndroidPackage and its ApplicationInfo. "
+ "pkg.getAllCodePaths=" + paths
+ " pkg.getBaseCodePath=" + pkg.getBaseApkPath()
+ " pkg.getSplitCodePaths="
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
new file mode 100644
index 000000000000..395f3b43426f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.annotation.NonNull;
+import android.content.pm.PackageManager;
+
+import dalvik.system.CloseGuard;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class that freezes and kills the given package upon creation, and
+ * unfreezes it upon closing. This is typically used when doing surgery on
+ * app code/data to prevent the app from running while you're working.
+ */
+final class PackageFreezer implements AutoCloseable {
+ private final String mPackageName;
+
+ private final boolean mWeFroze;
+
+ private final AtomicBoolean mClosed = new AtomicBoolean();
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ @NonNull
+ private final PackageManagerService mPm;
+
+ /**
+ * Create and return a stub freezer that doesn't actually do anything,
+ * typically used when someone requested
+ * {@link PackageManager#INSTALL_DONT_KILL_APP} or
+ * {@link PackageManager#DELETE_DONT_KILL_APP}.
+ */
+ PackageFreezer(PackageManagerService pm) {
+ mPm = pm;
+ mPackageName = null;
+ mWeFroze = false;
+ mCloseGuard.open("close");
+ }
+
+ PackageFreezer(String packageName, int userId, String killReason,
+ PackageManagerService pm) {
+ mPm = pm;
+ mPackageName = packageName;
+ final PackageSetting ps;
+ synchronized (mPm.mLock) {
+ mWeFroze = mPm.mFrozenPackages.add(mPackageName);
+ ps = mPm.mSettings.getPackageLPr(mPackageName);
+ }
+ if (ps != null) {
+ mPm.killApplication(ps.name, ps.appId, userId, killReason);
+ }
+ mCloseGuard.open("close");
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ mCloseGuard.warnIfOpen();
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ public void close() {
+ mCloseGuard.close();
+ if (mClosed.compareAndSet(false, true)) {
+ synchronized (mPm.mLock) {
+ if (mWeFroze) {
+ mPm.mFrozenPackages.remove(mPackageName);
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstalledInfo.java b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
new file mode 100644
index 000000000000..d0ca9d845560
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static com.android.server.pm.PackageManagerService.TAG;
+
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.ArrayList;
+
+final class PackageInstalledInfo {
+ String mName;
+ int mUid;
+ // The set of users that originally had this package installed.
+ int[] mOrigUsers;
+ // The set of users that now have this package installed.
+ int[] mNewUsers;
+ AndroidPackage mPkg;
+ int mReturnCode;
+ String mReturnMsg;
+ String mInstallerPackageName;
+ PackageRemovedInfo mRemovedInfo;
+ // The set of packages consuming this shared library or null if no consumers exist.
+ ArrayList<AndroidPackage> mLibraryConsumers;
+ PackageFreezer mFreezer;
+
+ // In some error cases we want to convey more info back to the observer
+ String mOrigPackage;
+ String mOrigPermission;
+
+ PackageInstalledInfo(int currentStatus) {
+ mReturnCode = currentStatus;
+ mUid = -1;
+ mPkg = null;
+ mRemovedInfo = null;
+ }
+
+ public void setError(int code, String msg) {
+ setReturnCode(code);
+ setReturnMessage(msg);
+ Slog.w(TAG, msg);
+ }
+
+ public void setError(String msg, PackageManagerException e) {
+ mReturnCode = e.error;
+ setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
+ Slog.w(TAG, msg, e);
+ }
+
+ public void setReturnCode(int returnCode) {
+ mReturnCode = returnCode;
+ }
+
+ private void setReturnMessage(String returnMsg) {
+ mReturnMsg = returnMsg;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d7b244980cfc..b34a3a2b8dcb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -254,12 +254,17 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
mSessionsDir.mkdirs();
mApexManager = ApexManager.getInstance();
- mStagingManager = new StagingManager(context, apexParserSupplier);
+ mStagingManager = new StagingManager(context, apexParserSupplier,
+ mInstallThread.getLooper());
LocalServices.getService(SystemServiceManager.class).startService(
new Lifecycle(context, this));
}
+ StagingManager getStagingManager() {
+ return mStagingManager;
+ }
+
boolean okToSendBroadcasts() {
return mOkToSendBroadcasts;
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 542948491dc8..958c769ba7d1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -46,9 +46,11 @@ import static com.android.internal.util.XmlUtils.writeUriAttribute;
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
+import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.WorkerThread;
import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -82,8 +84,7 @@ import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.SigningDetails;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -263,6 +264,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
+ /**
+ * The default value of {@link #mValidatedTargetSdk} is {@link Integer#MAX_VALUE}. If {@link
+ * #mValidatedTargetSdk} is compared with {@link Build.VERSION_CODES#Q} before getting the
+ * target sdk version from a validated apk in {@link #validateApkInstallLocked()}, the compared
+ * result will not trigger any user action in
+ * {@link #checkUserActionRequirement(PackageInstallerSession)}.
+ */
+ private static final int INVALID_TARGET_SDK_VERSION = Integer.MAX_VALUE;
+
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
@@ -366,7 +376,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private long mVersionCode;
@GuardedBy("mLock")
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
@GuardedBy("mLock")
private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>();
@GuardedBy("mLock")
@@ -705,28 +715,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
/**
- * Notified by the staging manager that pre-reboot verification is about to start. The
- * return value should be checked to decide whether it is OK to start pre-reboot
- * verification. In the case of a destroyed session, {@code false} is returned and there is
- * no need to start pre-reboot verification.
+ * Called when pre-reboot verification is about to start. This shouldn't be called
+ * on a destroyed session.
*/
- @Override
- public boolean notifyStartPreRebootVerification() {
+ private void notifyStartPreRebootVerification() {
synchronized (mLock) {
+ Preconditions.checkState(!mDestroyed);
if (mInPreRebootVerification) {
throw new IllegalStateException("Pre-reboot verification has started");
}
- if (mDestroyed) {
- return false;
- }
mInPreRebootVerification = true;
- return true;
}
}
/**
- * Notified by the staging manager that pre-reboot verification has ended. Now it is safe to
- * clean up the session if {@link #abandon()} has been called previously.
+ * Notified by the staging manager or PIS that pre-reboot verification has ended.
+ * Now it is safe to clean up the session if {@link #abandon()} has been called previously.
*/
@Override
public void notifyEndPreRebootVerification() {
@@ -750,6 +754,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
assertCallerIsOwnerOrRootOrSystem();
Preconditions.checkArgument(isCommitted());
Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
+ notifyStartPreRebootVerification();
verify();
}
@@ -803,6 +808,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private PackageLite mPackageLite;
+ /**
+ * Keep the target sdk of a validated apk.
+ */
+ @GuardedBy("mLock")
+ private int mValidatedTargetSdk = INVALID_TARGET_SDK_VERSION;
+
private static final FileFilter sAddedApkFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -1736,6 +1747,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
+ @WorkerThread
private void handleStreamValidateAndCommit() {
PackageManagerException unrecoverableFailure = null;
// This will track whether the session and any children were validated and are ready to
@@ -1985,6 +1997,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* exception is thrown.
* @throws PackageManagerException on an unrecoverable error.
*/
+ @WorkerThread
private boolean streamValidateAndCommit() throws PackageManagerException {
// TODO(patb): since the work done here for a parent session in a multi-package install is
// mostly superficial, consider splitting this method for the parent and
@@ -2099,12 +2112,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
Slog.e(TAG, "Failed to verify session " + sessionId + " [" + msgWithErrorCode + "]");
// Session is sealed and committed but could not be verified, we need to destroy it.
destroyInternal();
+ if (isMultiPackage()) {
+ for (PackageInstallerSession childSession : getChildSessions()) {
+ childSession.destroyInternal();
+ }
+ }
if (isStaged()) {
mStagedSession.setSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode);
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyVerificationComplete(mStagedSession);
+ mStagedSession.notifyEndPreRebootVerification();
} else {
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, msg, null);
@@ -2148,6 +2164,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* immutable by the caller during the method call. Used to resolve child
* sessions Ids to actual object reference.
*/
+ @AnyThread
void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) {
synchronized (mLock) {
// Resolve null values to actual object references
@@ -2200,7 +2217,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void transfer(String packageName) {
Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
-
ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
if (newOwnerAppInfo == null) {
throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
@@ -2233,6 +2249,63 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ @WorkerThread
+ private static boolean checkUserActionRequirement(PackageInstallerSession session) {
+ if (session.isMultiPackage()) {
+ return false;
+ }
+
+ @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED;
+ // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
+ userActionRequirement = session.computeUserActionRequirement();
+ if (userActionRequirement == USER_ACTION_REQUIRED) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (!session.isApexSession() && userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
+ final int validatedTargetSdk;
+ synchronized (session.mLock) {
+ validatedTargetSdk = session.mValidatedTargetSdk;
+ }
+
+ if (validatedTargetSdk != INVALID_TARGET_SDK_VERSION
+ && validatedTargetSdk < Build.VERSION_CODES.Q) {
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+
+ if (session.params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
+ if (!session.mSilentUpdatePolicy.isSilentUpdateAllowed(
+ session.getInstallerPackageName(), session.getPackageName())) {
+ // Fall back to the non-silent update if a repeated installation is invoked
+ // within the throttle time.
+ session.sendPendingUserActionIntent();
+ return true;
+ }
+ session.mSilentUpdatePolicy.track(session.getInstallerPackageName(),
+ session.getPackageName());
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Find out any session needs user action.
+ *
+ * @return true if the session set requires user action for the installation, otherwise false.
+ */
+ @WorkerThread
+ private boolean sendPendingUserActionIntentIfNeeded() {
+ synchronized (mLock) {
+ assertNotChildLocked("PackageInstallerSession#sendPendingUserActionIntentIfNeeded");
+ }
+
+ return sessionContains(PackageInstallerSession::checkUserActionRequirement);
+ }
+
+ @WorkerThread
private void handleInstall() {
if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
DevicePolicyEventLogger
@@ -2241,6 +2314,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
.write();
}
+ /**
+ * Stops the installation of the whole session set if one session needs user action
+ * in its belong session set. When the user answers the yes,
+ * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
+ * handled to come back here to check again.
+ */
+ if (sendPendingUserActionIntentIfNeeded()) {
+ return;
+ }
+
+
// Check if APEX update is allowed. We do this check in handleInstall, since this is one of
// the places that:
// * Shared between staged and non-staged APEX update flows.
@@ -2278,14 +2362,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
if (params.isStaged) {
- mStagingManager.commitSession(mStagedSession);
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
// though ideally, we just need to send session committed broadcast.
dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
- return;
- }
- verify();
+ mStagedSession.verifySession();
+ } else {
+ verify();
+ }
}
private void verify() {
@@ -2297,18 +2381,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ private IntentSender getRemoteStatusReceiver() {
+ synchronized (mLock) {
+ return mRemoteStatusReceiver;
+ }
+ }
+
private void verifyNonStaged()
throws PackageManagerException {
- final PackageManagerService.VerificationParams verifyingSession =
- prepareForVerification();
- if (verifyingSession == null) {
- return;
- }
+ final VerificationParams verifyingSession = prepareForVerification();
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
+ final List<PackageInstallerSession> childSessions = getChildSessions();
// Spot check to reject a non-staged multi package install of APEXes and APKs.
if (!params.isStaged && containsApkSession()
&& sessionContains(s -> s.isApexSession())) {
@@ -2316,36 +2399,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
PackageManager.INSTALL_FAILED_SESSION_INVALID,
"Non-staged multi package install of APEX and APK packages is not supported");
}
- List<PackageManagerService.VerificationParams> verifyingChildSessions =
+ List<VerificationParams> verifyingChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
PackageManagerException failure = null;
for (int i = 0; i < childSessions.size(); ++i) {
final PackageInstallerSession session = childSessions.get(i);
try {
- final PackageManagerService.VerificationParams verifyingChildSession =
+ final VerificationParams verifyingChildSession =
session.prepareForVerification();
- if (verifyingChildSession != null) {
- verifyingChildSessions.add(verifyingChildSession);
- }
+ verifyingChildSessions.add(verifyingChildSession);
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
- mPm.verifyStage(verifyingSession, verifyingChildSessions);
+ verifyingSession.verifyStage(verifyingChildSessions);
} else {
- mPm.verifyStage(verifyingSession);
+ verifyingSession.verifyStage();
}
}
@@ -2360,24 +2437,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private void installNonStaged()
throws PackageManagerException {
- final PackageManagerService.InstallParams installingSession = makeInstallParams();
+ final InstallParams installingSession = makeInstallParams();
if (installingSession == null) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session should contain at least one apk session for installation");
}
if (isMultiPackage()) {
- final List<PackageInstallerSession> childSessions;
- synchronized (mLock) {
- childSessions = getChildSessionsLocked();
- }
- List<PackageManagerService.InstallParams> installingChildSessions =
- new ArrayList<>(childSessions.size());
+ final List<PackageInstallerSession> childSessions = getChildSessions();
+ List<InstallParams> installingChildSessions = new ArrayList<>(childSessions.size());
boolean success = true;
PackageManagerException failure = null;
for (int i = 0; i < childSessions.size(); ++i) {
final PackageInstallerSession session = childSessions.get(i);
try {
- final PackageManagerService.InstallParams installingChildSession =
+ final InstallParams installingChildSession =
session.makeInstallParams();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
@@ -2388,43 +2461,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
if (!success) {
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ sendOnPackageInstalled(mContext, getRemoteStatusReceiver(), sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
- mPm.installStage(installingSession, installingChildSessions);
+ installingSession.installStage(installingChildSessions);
} else {
- mPm.installStage(installingSession);
+ installingSession.installStage();
}
}
/**
* Stages this session for verification and returns a
- * {@link PackageManagerService.VerificationParams} representing this new staged state or null
+ * {@link VerificationParams} representing this new staged state or null
* in case permissions need to be requested before verification can proceed.
*/
- @Nullable
- private PackageManagerService.VerificationParams prepareForVerification()
- throws PackageManagerException {
+ @NonNull
+ private VerificationParams prepareForVerification() throws PackageManagerException {
assertNotLocked("makeSessionActive");
- @UserActionRequirement
- int userActionRequirement = USER_ACTION_NOT_NEEDED;
- // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
- if (!params.isMultiPackage) {
- userActionRequirement = computeUserActionRequirement();
- if (userActionRequirement == USER_ACTION_REQUIRED) {
- sendPendingUserActionIntent();
- return null;
- } // else, we'll wait until we parse to determine if we need to
- }
-
- boolean silentUpdatePolicyEnforceable = false;
synchronized (mLock) {
if (mRelinquished) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -2441,36 +2497,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
PackageLite result = parseApkLite();
if (result != null) {
mPackageLite = result;
- synchronized (mProgressLock) {
- mInternalProgress = 0.5f;
- computeProgressLocked(true);
- }
-
- extractNativeLibraries(
- mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
-
- if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
- if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
- sendPendingUserActionIntent();
- return null;
- }
- if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
- silentUpdatePolicyEnforceable = true;
+ if (!isApexSession()) {
+ synchronized (mProgressLock) {
+ mInternalProgress = 0.5f;
+ computeProgressLocked(true);
}
+
+ extractNativeLibraries(
+ mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
- }
- if (silentUpdatePolicyEnforceable) {
- if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
- getInstallerPackageName(), getPackageName())) {
- // Fall back to the non-silent update if a repeated installation is invoked within
- // the throttle time.
- sendPendingUserActionIntent();
- return null;
- }
- mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
- }
- synchronized (mLock) {
return makeVerificationParamsLocked();
}
}
@@ -2483,11 +2519,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendOnUserActionRequired(mContext, statusReceiver, sessionId, intent);
+ sendOnUserActionRequired(mContext, getRemoteStatusReceiver(), sessionId, intent);
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
@@ -2497,81 +2529,85 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* Prepares staged directory with any inherited APKs and returns the parsed package.
*/
+ @GuardedBy("mLock")
@Nullable
private PackageLite parseApkLite() throws PackageManagerException {
- // TODO(b/136257624): Some logic in this if block probably belongs in
- // makeInstallParams().
- if (!isMultiPackage() && !isApexSession()) {
- Objects.requireNonNull(mPackageName);
- Objects.requireNonNull(mSigningDetails);
- Objects.requireNonNull(mResolvedBaseFile);
+ if (isMultiPackage()) {
+ return null;
+ }
+ Objects.requireNonNull(mPackageName);
+ Objects.requireNonNull(mSigningDetails);
+ Objects.requireNonNull(mResolvedBaseFile);
- // If we haven't already parsed, inherit any packages and native libraries from existing
- // install that haven't been overridden.
- if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- try {
- final List<File> fromFiles = mResolvedInheritedFiles;
- final File toDir = stageDir;
+ // Inherit any packages and native libraries from existing install that
+ // haven't been overridden.
+ if (!isApexSession() && params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ try {
+ final List<File> fromFiles = mResolvedInheritedFiles;
+ final File toDir = stageDir;
- if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
- if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
- throw new IllegalStateException("mInheritedFilesBase == null");
- }
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
+ }
- if (isLinkPossible(fromFiles, toDir)) {
- if (!mResolvedInstructionSets.isEmpty()) {
- final File oatDir = new File(toDir, "oat");
- createOatDirs(mResolvedInstructionSets, oatDir);
- }
- // pre-create lib dirs for linking if necessary
- if (!mResolvedNativeLibPaths.isEmpty()) {
- for (String libPath : mResolvedNativeLibPaths) {
- // "/lib/arm64" -> ["lib", "arm64"]
- final int splitIndex = libPath.lastIndexOf('/');
- if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
- Slog.e(TAG,
- "Skipping native library creation for linking due"
- + " to invalid path: " + libPath);
- continue;
- }
- final String libDirPath = libPath.substring(1, splitIndex);
- final File libDir = new File(toDir, libDirPath);
- if (!libDir.exists()) {
- NativeLibraryHelper.createNativeLibrarySubdir(libDir);
- }
- final String archDirPath = libPath.substring(splitIndex + 1);
- NativeLibraryHelper.createNativeLibrarySubdir(
- new File(libDir, archDirPath));
+ if (isLinkPossible(fromFiles, toDir)) {
+ if (!mResolvedInstructionSets.isEmpty()) {
+ final File oatDir = new File(toDir, "oat");
+ createOatDirs(mResolvedInstructionSets, oatDir);
+ }
+ // pre-create lib dirs for linking if necessary
+ if (!mResolvedNativeLibPaths.isEmpty()) {
+ for (String libPath : mResolvedNativeLibPaths) {
+ // "/lib/arm64" -> ["lib", "arm64"]
+ final int splitIndex = libPath.lastIndexOf('/');
+ if (splitIndex < 0 || splitIndex >= libPath.length() - 1) {
+ Slog.e(TAG,
+ "Skipping native library creation for linking due"
+ + " to invalid path: " + libPath);
+ continue;
+ }
+ final String libDirPath = libPath.substring(1, splitIndex);
+ final File libDir = new File(toDir, libDirPath);
+ if (!libDir.exists()) {
+ NativeLibraryHelper.createNativeLibrarySubdir(libDir);
}
+ final String archDirPath = libPath.substring(splitIndex + 1);
+ NativeLibraryHelper.createNativeLibrarySubdir(
+ new File(libDir, archDirPath));
}
- linkFiles(fromFiles, toDir, mInheritedFilesBase);
- } else {
- // TODO: this should delegate to DCS so the system process
- // avoids holding open FDs into containers.
- copyFiles(fromFiles, toDir);
}
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Failed to inherit existing install", e);
+ linkFiles(fromFiles, toDir, mInheritedFilesBase);
+ } else {
+ // TODO: this should delegate to DCS so the system process
+ // avoids holding open FDs into containers.
+ copyFiles(fromFiles, toDir);
}
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
+ "Failed to inherit existing install", e);
}
+ }
+
+ if (!isApexSession()) {
// For mode inherit existing, it would link/copy existing files to stage dir in the
// above block. Therefore, we need to parse the complete package in stage dir here.
- // Besides, PackageLite may be null for staged sessions that don't complete pre-reboot
- // verification.
+ // Besides, PackageLite may be null for staged sessions that don't complete
+ // pre-reboot verification.
return getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
+ } else {
+ return getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
}
- return null;
}
@GuardedBy("mLock")
@Nullable
/**
- * Returns a {@link com.android.server.pm.PackageManagerService.VerificationParams}
+ * Returns a {@link com.android.server.pm.VerificationParams}
*/
- private PackageManagerService.VerificationParams makeVerificationParamsLocked() {
+ private VerificationParams makeVerificationParamsLocked() {
final IPackageInstallObserver2 localObserver;
if (!hasParentSessionId()) {
// Avoid attaching this observer to child session since they won't use it.
@@ -2584,11 +2620,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
- if (returnCode == INSTALL_SUCCEEDED) {
- onVerificationComplete();
- } else {
- onSessionVerificationFailure(returnCode, msg);
- }
+ mHandler.post(() -> {
+ if (returnCode == INSTALL_SUCCEEDED) {
+ onVerificationComplete();
+ } else {
+ onSessionVerificationFailure(returnCode, msg);
+ }
+ });
}
};
} else {
@@ -2604,25 +2642,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mRelinquished = true;
- // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
- PackageInstaller.SessionParams copiedParams = params.copy();
- if (params.isStaged) {
- // This is called by the pre-reboot verification. Don't enable rollback here since
- // it has been enabled when pre-reboot verification starts.
- copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
- }
- return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
- mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite);
+ return new VerificationParams(user, stageDir, localObserver, params,
+ mInstallSource, mInstallerUid, mSigningDetails, sessionId, mPackageLite, mPm);
}
+ @WorkerThread
private void onVerificationComplete() {
- // Staged sessions will be installed later during boot
+ // APK verification is done. Continue the installation depending on whether it is a
+ // staged session or not. For a staged session, we will hand it over to the staging
+ // manager to complete the installation.
if (isStaged()) {
- // TODO(b/136257624): Remove this once all verification logic has been transferred out
- // of StagingManager.
- mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
- // TODO(b/136257624): We also need to destroy internals for verified staged session,
- // otherwise file descriptors are never closed for verified staged session until reboot
+ mStagingManager.commitSession(mStagedSession);
return;
}
@@ -2631,10 +2661,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/**
* Stages this session for install and returns a
- * {@link PackageManagerService.InstallParams} representing this new staged state.
+ * {@link InstallParams} representing this new staged state.
*/
@Nullable
- private PackageManagerService.InstallParams makeInstallParams()
+ private InstallParams makeInstallParams()
throws PackageManagerException {
synchronized (mLock) {
if (mDestroyed) {
@@ -2697,8 +2727,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
synchronized (mLock) {
- return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
- mSigningDetails, mInstallerUid, mPackageLite);
+ return new InstallParams(stageDir, localObserver, params, mInstallSource, user,
+ mSigningDetails, mInstallerUid, mPackageLite, mPm);
}
}
@@ -2753,7 +2783,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private long getApksSize(String packageName) {
- final PackageSetting ps = mPm.getPackageSetting(packageName);
+ final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ final PackageSetting ps = pmi.getPackageSetting(packageName);
if (ps == null) {
return 0;
}
@@ -2886,6 +2917,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
+
+ mSigningDetails = apk.getSigningDetails();
}
/**
@@ -2904,11 +2937,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private PackageLite validateApkInstallLocked() throws PackageManagerException {
ApkLite baseApk = null;
- PackageLite packageLite = null;
+ final PackageLite packageLite;
mPackageLite = null;
mPackageName = null;
mVersionCode = -1;
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
mResolvedBaseFile = null;
mResolvedStagedFiles.clear();
@@ -2971,7 +3004,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = apk.getSigningDetails();
}
@@ -3026,7 +3059,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mPackageName = pkgInfo.packageName;
mVersionCode = pkgInfo.getLongVersionCode();
}
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
+ if (mSigningDetails == SigningDetails.UNKNOWN) {
mSigningDetails = unsafeGetCertsWithoutVerification(
pkgInfo.applicationInfo.sourceDir);
}
@@ -3086,7 +3119,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
packageLite = existing;
assertPackageConsistentLocked("Existing", existing.getPackageName(),
existing.getLongVersionCode());
- final PackageParser.SigningDetails signingDetails =
+ final SigningDetails signingDetails =
unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3211,6 +3244,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mIncrementalFileStorages.disallowReadLogs();
}
}
+
+ // {@link #sendPendingUserActionIntentIfNeeded} needs to use
+ // {@link PackageLite#getTargetSdk()}
+ mValidatedTargetSdk = packageLite.getTargetSdk();
+
return packageLite;
}
@@ -3426,15 +3464,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- private PackageParser.SigningDetails unsafeGetCertsWithoutVerification(String path)
+ private SigningDetails unsafeGetCertsWithoutVerification(String path)
throws PackageManagerException {
- try {
- return ApkSignatureVerifier.unsafeGetCertsWithoutVerification(path,
- PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
- } catch (PackageParserException e) {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result =
+ ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+ input, path, SigningDetails.SignatureSchemeVersion.JAR);
+ if (result.isError()) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Couldn't obtain signatures from APK : " + path);
}
+ return result.getResult();
}
/**
@@ -3622,8 +3662,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Mark and kick off another install pass
synchronized (mLock) {
mPermissionsManuallyAccepted = true;
- mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
+
+ PackageInstallerSession root =
+ (hasParentSessionId())
+ ? mSessionProvider.getSession(getParentSessionId())
+ : this;
+ root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
} else {
destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -3909,11 +3954,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
case IDataLoaderStatusListener.DATA_LOADER_UNAVAILABLE: {
// Don't fail or commit the session. Allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId,
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
"DataLoader unavailable");
break;
}
@@ -3927,11 +3968,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
// Just return from this method and allow caller to commit again.
- final IntentSender statusReceiver;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- }
- sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
+ sendPendingStreaming(mContext, getRemoteStatusReceiver(), sessionId,
+ e.getMessage());
}
}
};
@@ -4200,18 +4238,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
- final IntentSender statusReceiver;
- final String packageName;
- synchronized (mLock) {
- statusReceiver = mRemoteStatusReceiver;
- packageName = mPackageName;
- }
+ final IntentSender statusReceiver = getRemoteStatusReceiver();
if (statusReceiver != null) {
// Execute observer.onPackageInstalled on different thread as we don't want callers
// inside the system server have to worry about catching the callbacks while they are
// calling into the session
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = packageName;
+ args.arg1 = getPackageName();
args.arg2 = msg;
args.arg3 = extras;
args.arg4 = statusReceiver;
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index 0793b091265e..4662389737b7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -17,7 +17,6 @@
package com.android.server.pm;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
import com.android.server.pm.Installer.InstallerException;
@@ -45,11 +44,6 @@ public class PackageManagerException extends Exception {
this.error = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
}
- public static PackageManagerException from(PackageParserException e)
- throws PackageManagerException {
- throw new PackageManagerException(e.error, e.getMessage(), e.getCause());
- }
-
public static PackageManagerException from(InstallerException e)
throws PackageManagerException {
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c4775463ee75..114dec61274b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21,14 +21,10 @@ import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
-import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
-import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
-import static android.content.Intent.EXTRA_VERSION_CODE;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@@ -36,36 +32,17 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
-import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
-import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_INTERNAL;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
-import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
-import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -95,11 +72,9 @@ import static android.content.pm.PackageManager.TYPE_SERVICE;
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_PACKAGE_REPLACED;
-import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
@@ -118,7 +93,6 @@ import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATI
import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter;
import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
@@ -132,6 +106,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTi
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import static com.android.server.pm.PackageManagerServiceUtils.makeDirRecursive;
import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+import static com.android.server.pm.parsing.PackageInfoUtils.checkUseInstalledOrHidden;
import android.Manifest;
import android.annotation.AppIdInt;
@@ -147,6 +122,7 @@ import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
+import android.app.PendingIntent;
import android.app.ResourcesManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
@@ -159,6 +135,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
@@ -186,6 +163,7 @@ import android.content.pm.IPackageManager;
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
@@ -200,6 +178,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.PackageManager.ComponentType;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageManager.ModuleInfoFlags;
@@ -208,10 +187,6 @@ import android.content.pm.PackageManager.PropertyLocation;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackagePartitions;
import android.content.pm.PackagePartitions.SystemPartition;
import android.content.pm.PackageStats;
@@ -226,26 +201,26 @@ import android.content.pm.SELinuxUtil;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.content.pm.SigningInfo;
+import android.content.pm.StagedApexInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.VerifierInfo;
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.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedPermission;
-import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.parsing.component.ParsedProcess;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
@@ -276,7 +251,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.SELinux;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
@@ -285,7 +259,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
-import android.os.incremental.IncrementalStorage;
import android.os.incremental.PerUidReadTimeouts;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
@@ -300,11 +273,9 @@ import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
-import android.security.SystemKeyStore;
import android.service.pm.PackageServiceDumpProto;
import android.stats.storage.StorageEnums;
import android.system.ErrnoException;
-import android.system.Os;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -321,6 +292,7 @@ import android.util.MathUtils;
import android.util.PackageUtils;
import android.util.Pair;
import android.util.PrintStreamPrinter;
+import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -359,6 +331,7 @@ import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
+import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.PackageWatchdog;
@@ -390,7 +363,6 @@ import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
-import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
@@ -413,7 +385,6 @@ import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
-import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -428,7 +399,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -439,10 +409,8 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.charset.StandardCharsets;
import java.security.DigestException;
-import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
@@ -528,7 +496,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static final boolean DEBUG_PACKAGE_INFO = false;
private static final boolean DEBUG_INTENT_MATCHING = false;
public static final boolean DEBUG_PACKAGE_SCANNING = false;
- private static final boolean DEBUG_VERIFY = false;
+ static final boolean DEBUG_VERIFY = false;
public static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
@@ -541,7 +509,7 @@ public class PackageManagerService extends IPackageManager.Stub
public static final boolean DEBUG_DEXOPT = false;
static final boolean DEBUG_ABI_SELECTION = false;
- private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
+ public static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;
private static final boolean DEBUG_APP_DATA = false;
/** REMOVE. According to Svet, this was only used to reset permissions during development. */
@@ -549,7 +517,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static final boolean HIDE_EPHEMERAL_APIS = false;
- private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
+ static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -677,43 +645,10 @@ public class PackageManagerService extends IPackageManager.Stub
private static final long DEFAULT_MANDATORY_FSTRIM_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS;
/**
- * Whether verification is enabled by default.
- */
- private static final boolean DEFAULT_VERIFY_ENABLE = true;
-
- /**
- * Whether integrity verification is enabled by default.
- */
- private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
-
- /**
* The default maximum time to wait for the verification agent to return in
* milliseconds.
*/
- private static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
-
- /**
- * The default maximum time to wait for the integrity verification to return in
- * milliseconds.
- */
- private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;
-
- /**
- * Timeout duration in milliseconds for enabling package rollback. If we fail to enable
- * rollback within that period, the install will proceed without rollback enabled.
- *
- * <p>If flag value is negative, the default value will be assigned.
- *
- * Flag type: {@code long}
- * Namespace: NAMESPACE_ROLLBACK
- */
- private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = "enable_rollback_timeout";
-
- /**
- * The default duration to wait for rollback to be enabled in
- * milliseconds.
- */
- private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000;
+ static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
/**
* Default IncFs timeouts. Maximum values in IncFs is 1hr.
@@ -771,9 +706,23 @@ public class PackageManagerService extends IPackageManager.Stub
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088;
+ /**
+ * Components of apps targeting Android T and above will stop receiving intents from
+ * external callers that do not match its declared intent filters.
+ *
+ * When an app registers an exported component in its manifest and adds an <intent-filter>,
+ * the component can be started by any intent - even those that do not match the intent filter.
+ * This has proven to be something that many developers find counterintuitive.
+ * Without checking the intent when the component is started, in some circumstances this can
+ * allow 3P apps to trigger internal-only functionality.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
+
public static final String PLATFORM_PACKAGE_NAME = "android";
- private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
+ static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
private static final String PACKAGE_SCHEME = "package";
@@ -940,7 +889,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean mFirstBoot;
- private final boolean mIsEngBuild;
+ final boolean mIsEngBuild;
private final boolean mIsUserDebugBuild;
private final String mIncrementalVersion;
@@ -971,16 +920,11 @@ public class PackageManagerService extends IPackageManager.Stub
@GuardedBy("mLock")
final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>();
- @GuardedBy("mLock")
- private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray();
-
private final ModuleInfoProvider mModuleInfoProvider;
- private final ApexManager mApexManager;
+ final ApexManager mApexManager;
- private final Injector mInjector;
-
- private final SystemWrapper mSystemWrapper;
+ final Injector mInjector;
/**
* The list of all system partitions that may contain packages in ascending order of
@@ -1354,7 +1298,6 @@ public class PackageManagerService extends IPackageManager.Stub
public DefaultAppProvider defaultAppProvider;
public DexManager dexManager;
public List<ScanPartition> dirsToScanAsSystem;
- public @Nullable String documenterPackage;
public boolean factoryTest;
public ArrayMap<String, FeatureInfo> availableFeatures;
public Handler handler;
@@ -1400,17 +1343,16 @@ public class PackageManagerService extends IPackageManager.Stub
public ArrayMap<String, AndroidPackage> packages;
public boolean enableFreeCacheV2;
public int sdkVersion;
- public SystemWrapper systemWrapper;
public File appInstallDir;
public File appLib32InstallDir;
public boolean isEngBuild;
public boolean isUserDebugBuild;
public int sdkInt = Build.VERSION.SDK_INT;
- public String incrementalVersion = Build.VERSION.INCREMENTAL;
+ public final String incrementalVersion = Build.VERSION.INCREMENTAL;
}
@Watched
- private final AppsFilter mAppsFilter;
+ final AppsFilter mAppsFilter;
final PackageParser2.Callback mPackageParserCallback;
@@ -1458,24 +1400,24 @@ public class PackageManagerService extends IPackageManager.Stub
final ArtManagerService mArtManagerService;
- private final PackageDexOptimizer mPackageDexOptimizer;
+ final PackageDexOptimizer mPackageDexOptimizer;
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps).
private final DexManager mDexManager;
- private final ViewCompiler mViewCompiler;
+ final ViewCompiler mViewCompiler;
- private AtomicInteger mNextMoveId = new AtomicInteger();
+ private final AtomicInteger mNextMoveId = new AtomicInteger();
private final MoveCallbacks mMoveCallbacks;
// Cache of users who need badging.
private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
/** Token for keys in mPendingVerification. */
- private int mPendingVerificationToken = 0;
+ int mPendingVerificationToken = 0;
/** Token for keys in mPendingEnableRollback. */
- private int mPendingEnableRollbackToken = 0;
+ int mPendingEnableRollbackToken = 0;
@Watched(manual = true)
volatile boolean mSystemReady;
@@ -1508,7 +1450,7 @@ public class PackageManagerService extends IPackageManager.Stub
/** Activity used to install instant applications */
@Watched(manual = true)
- private ActivityInfo mInstantAppInstallerActivity;
+ ActivityInfo mInstantAppInstallerActivity;
@Watched(manual = true)
private final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
@@ -1516,7 +1458,7 @@ public class PackageManagerService extends IPackageManager.Stub
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
// Internal interface for permission manager
- private final PermissionManagerServiceInternal mPermissionManager;
+ final PermissionManagerServiceInternal mPermissionManager;
@Watched
private final ComponentResolver mComponentResolver;
@@ -1531,7 +1473,7 @@ public class PackageManagerService extends IPackageManager.Stub
private Future<?> mPrepareAppDataFuture;
- private final IncrementalManager mIncrementalManager;
+ final IncrementalManager mIncrementalManager;
private final DefaultAppProvider mDefaultAppProvider;
@@ -1643,7 +1585,7 @@ public class PackageManagerService extends IPackageManager.Stub
final UserManagerService mUserManager;
// Stores a list of users whose package restrictions file needs to be updated
- private ArraySet<Integer> mDirtyUsers = new ArraySet<>();
+ private final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
@@ -1669,31 +1611,6 @@ public class PackageManagerService extends IPackageManager.Stub
// XML tags for backup/restore of various bits of state
private static final String TAG_PREFERRED_BACKUP = "pa";
private static final String TAG_DEFAULT_APPS = "da";
- private static final String TAG_INTENT_FILTER_VERIFICATION = "iv";
-
- private static final String TAG_PERMISSION_BACKUP = "perm-grant-backup";
- private static final String TAG_ALL_GRANTS = "rt-grants";
- private static final String TAG_GRANT = "grant";
- private static final String ATTR_PACKAGE_NAME = "pkg";
-
- private static final String TAG_PERMISSION = "perm";
- private static final String ATTR_PERMISSION_NAME = "name";
- private static final String ATTR_IS_GRANTED = "g";
- private static final String ATTR_USER_SET = "set";
- private static final String ATTR_USER_FIXED = "fixed";
- private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
-
- // System/policy permission grants are not backed up
- private static final int SYSTEM_RUNTIME_GRANT_MASK =
- FLAG_PERMISSION_POLICY_FIXED
- | FLAG_PERMISSION_SYSTEM_FIXED
- | FLAG_PERMISSION_GRANTED_BY_DEFAULT;
-
- // And we back up these user-adjusted states
- private static final int USER_RUNTIME_GRANT_MASK =
- FLAG_PERMISSION_USER_SET
- | FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKED_COMPAT;
final @Nullable String mRequiredVerifierPackage;
final @NonNull String mRequiredInstallerPackage;
@@ -1703,7 +1620,6 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mStorageManagerPackage;
final @Nullable String mDefaultTextClassifierPackage;
final @Nullable String mSystemTextClassifierPackageName;
- final @Nullable String mDocumenterPackage;
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
@@ -1837,7 +1753,7 @@ public class PackageManagerService extends IPackageManager.Stub
private final Watcher mWatcher = new Watcher() {
@Override
public void onChange(@Nullable Watchable what) {
- PackageManagerService.this.onChange(what);
+ PackageManagerService.onChange(what);
}
};
@@ -1958,7 +1874,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
- public @interface LiveImplementation {
+ @interface LiveImplementation {
// A Computer method must be annotated with one of the following values:
// MANDATORY - the method must be overridden in ComputerEngineLive. The
// format of the override is a call to the super method, wrapped in a
@@ -2131,6 +2047,8 @@ public class PackageManagerService extends IPackageManager.Stub
@LiveImplementation(override = LiveImplementation.MANDATORY)
boolean filterAppAccess(String packageName, int callingUid, int userId);
@LiveImplementation(override = LiveImplementation.MANDATORY)
+ boolean filterAppAccess(int uid, int callingUid);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
}
@@ -2261,9 +2179,11 @@ public class PackageManagerService extends IPackageManager.Stub
false /* requireFullPermission */, false /* checkShell */,
"query intent activities");
final String pkgName = intent.getPackage();
+ Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
+ originalIntent = intent;
intent = intent.getSelector();
comp = intent.getComponent();
}
@@ -2273,8 +2193,9 @@ public class PackageManagerService extends IPackageManager.Stub
comp != null || pkgName != null /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
+ List<ResolveInfo> list = Collections.emptyList();
+ boolean skipPostResolution = false;
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getActivityInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -2317,36 +2238,45 @@ public class PackageManagerService extends IPackageManager.Stub
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
+ list = new ArrayList<>(1);
list.add(ri);
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, intent, resolvedType, filterCallingUid);
}
}
-
- List<ResolveInfo> result = applyPostResolutionFilter(
- list, instantAppPkgName, allowDynamicSplits, filterCallingUid,
- resolveForStart,
- userId, intent);
- return result;
+ } else {
+ QueryIntentActivitiesResult lockedResult =
+ queryIntentActivitiesInternalBody(
+ intent, resolvedType, flags, filterCallingUid, userId,
+ resolveForStart, allowDynamicSplits, pkgName, instantAppPkgName);
+ if (lockedResult.answer != null) {
+ skipPostResolution = true;
+ list = lockedResult.answer;
+ } else {
+ if (lockedResult.addInstant) {
+ String callingPkgName = getInstantAppPackageName(filterCallingUid);
+ boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
+ lockedResult.result = maybeAddInstantAppInstaller(
+ lockedResult.result, intent, resolvedType, flags,
+ userId, resolveForStart, isRequesterInstantApp);
+ }
+ if (lockedResult.sortResult) {
+ lockedResult.result.sort(RESOLVE_PRIORITY_SORTER);
+ }
+ list = lockedResult.result;
+ }
}
- QueryIntentActivitiesResult lockedResult =
- queryIntentActivitiesInternalBody(
- intent, resolvedType, flags, filterCallingUid, userId, resolveForStart,
- allowDynamicSplits, pkgName, instantAppPkgName);
- if (lockedResult.answer != null) {
- return lockedResult.answer;
+ if (originalIntent != null) {
+ // We also have to ensure all components match the original intent
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, originalIntent, resolvedType, filterCallingUid);
}
- if (lockedResult.addInstant) {
- String callingPkgName = getInstantAppPackageName(filterCallingUid);
- boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
- lockedResult.result = maybeAddInstantAppInstaller(lockedResult.result, intent,
- resolvedType, flags, userId, resolveForStart, isRequesterInstantApp);
- }
- if (lockedResult.sortResult) {
- Collections.sort(lockedResult.result, RESOLVE_PRIORITY_SORTER);
- }
- return applyPostResolutionFilter(
- lockedResult.result, instantAppPkgName, allowDynamicSplits, filterCallingUid,
+ return skipPostResolution ? list : applyPostResolutionFilter(
+ list, instantAppPkgName, allowDynamicSplits, filterCallingUid,
resolveForStart, userId, intent);
}
@@ -2369,15 +2299,17 @@ public class PackageManagerService extends IPackageManager.Stub
final String instantAppPkgName = getInstantAppPackageName(callingUid);
flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
+ Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
+ originalIntent = intent;
intent = intent.getSelector();
comp = intent.getComponent();
}
}
+ List<ResolveInfo> list = Collections.emptyList();
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<>(1);
final ServiceInfo si = getServiceInfo(comp, flags, userId);
if (si != null) {
// When specifying an explicit component, we prevent the service from being
@@ -2410,14 +2342,26 @@ public class PackageManagerService extends IPackageManager.Stub
if (!blockInstantResolution && !blockNormalResolution) {
final ResolveInfo ri = new ResolveInfo();
ri.serviceInfo = si;
+ list = new ArrayList<>(1);
list.add(ri);
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, intent, resolvedType, callingUid);
}
}
- return list;
+ } else {
+ list = queryIntentServicesInternalBody(intent, resolvedType, flags,
+ userId, callingUid, instantAppPkgName);
}
- return queryIntentServicesInternalBody(intent, resolvedType, flags,
- userId, callingUid, instantAppPkgName);
+ if (originalIntent != null) {
+ // We also have to ensure all components match the original intent
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, false, originalIntent, resolvedType, callingUid);
+ }
+
+ return list;
}
protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
@@ -2623,8 +2567,8 @@ public class PackageManagerService extends IPackageManager.Stub
a, flags, ps.readUserState(userId), userId, ps);
}
if (resolveComponentName().equals(component)) {
- return PackageParser.generateActivityInfo(
- mResolveActivity, flags, new PackageUserState(), userId);
+ return PackageInfoWithoutStateUtils.generateDelegateActivityInfo(mResolveActivity,
+ flags, new PackageUserState(), userId);
}
return null;
}
@@ -3274,8 +3218,10 @@ public class PackageManagerService extends IPackageManager.Stub
return result;
}
final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
- ephemeralInstaller.activityInfo = PackageParser.generateActivityInfo(
- instantAppInstallerActivity(), 0, ps.readUserState(userId), userId);
+ ephemeralInstaller.activityInfo =
+ PackageInfoWithoutStateUtils.generateDelegateActivityInfo(
+ instantAppInstallerActivity(), 0 /*flags*/, ps.readUserState(userId),
+ userId);
ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
| IntentFilter.MATCH_ADJUSTMENT_NORMAL;
// add a non-generic filter
@@ -3358,8 +3304,8 @@ public class PackageManagerService extends IPackageManager.Stub
ai.setVersionCode(ps.versionCode);
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
- pi.applicationInfo =
- PackageParser.generateApplicationInfo(ai, flags, state, userId);
+ pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
+ ai, flags, state, userId);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "ps.pkg is n/a for ["
+ ps.name + "]. Provides a minimum info.");
@@ -3964,10 +3910,8 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
// TODO(b/122900055) Change/Remove this and replace with new permission role.
- if (mAppPredictionServicePackage != null
- && isCallerSameApp(mAppPredictionServicePackage, callingUid)) {
- return true;
- }
+ return mAppPredictionServicePackage != null
+ && isCallerSameApp(mAppPredictionServicePackage, callingUid);
}
return false;
}
@@ -4069,10 +4013,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
return true;
}
- if (isComponentVisibleToInstantApp(component, TYPE_PROVIDER)) {
- return true;
- }
- return false;
+ return isComponentVisibleToInstantApp(component, TYPE_PROVIDER);
}
public final boolean isComponentVisibleToInstantApp(
@@ -4309,11 +4250,8 @@ public class PackageManagerService extends IPackageManager.Stub
final String instantAppPkgName = getInstantAppPackageName(callingUid);
final boolean callerIsInstantApp = instantAppPkgName != null;
if (ps == null) {
- if (callerIsInstantApp) {
- // pretend the application exists, but, needs to be filtered
- return true;
- }
- return false;
+ // pretend the application exists, but, needs to be filtered
+ return callerIsInstantApp;
}
// if the target and caller are the same application, don't filter
if (isCallerSameApp(ps.name, callingUid)) {
@@ -4402,11 +4340,11 @@ public class PackageManagerService extends IPackageManager.Stub
// reader
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
- PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return -1;
+ final PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
+ if (ps != null && ps.getInstalled(userId)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return UserHandle.getUid(userId, p.getUid());
}
- return UserHandle.getUid(userId, p.getUid());
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -4654,6 +4592,22 @@ public class PackageManagerService extends IPackageManager.Stub
userId);
}
+ public boolean filterAppAccess(int uid, int callingUid) {
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+ final Object setting = mSettings.getSettingLPr(appId);
+
+ if (setting instanceof SharedUserSetting) {
+ return shouldFilterApplicationLocked(
+ (SharedUserSetting) setting, callingUid, userId);
+ } else if (setting == null
+ || setting instanceof PackageSetting) {
+ return shouldFilterApplicationLocked(
+ (PackageSetting) setting, callingUid, userId);
+ }
+ return false;
+ }
+
public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
final String packageName = dumpState.getTargetPackageName();
final boolean checkin = dumpState.isCheckIn();
@@ -5000,6 +4954,11 @@ public class PackageManagerService extends IPackageManager.Stub
return super.filterAppAccess(packageName, callingUid, userId);
}
}
+ public final boolean filterAppAccess(int uid, int callingUid) {
+ synchronized (mLock) {
+ return super.filterAppAccess(uid, callingUid);
+ }
+ }
public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
synchronized (mLock) {
super.dump(type, fd, pw, dumpState);
@@ -5020,13 +4979,13 @@ public class PackageManagerService extends IPackageManager.Stub
// a live computer.
private final AtomicInteger mReusedLive = new AtomicInteger(0);
- private PackageManagerService mService;
+ private final PackageManagerService mService;
ComputerTracker(PackageManagerService s) {
mService = s;
}
private ThreadComputer live() {
- ThreadComputer current = mService.sThreadComputer.get();
+ ThreadComputer current = sThreadComputer.get();
if (current.mRefCount > 0) {
current.acquire();
mReusedLive.incrementAndGet();
@@ -5037,7 +4996,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
private ThreadComputer snapshot() {
- ThreadComputer current = mService.sThreadComputer.get();
+ ThreadComputer current = sThreadComputer.get();
if (current.mRefCount > 0) {
current.acquire();
mReusedSnapshot.incrementAndGet();
@@ -5370,6 +5329,14 @@ public class PackageManagerService extends IPackageManager.Stub
current.release();
}
}
+ public final boolean filterAppAccess(int uid, int callingUid) {
+ ThreadComputer current = snapshot();
+ try {
+ return current.mComputer.filterAppAccess(uid, callingUid);
+ } finally {
+ current.release();
+ }
+ }
public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
int userId, int flags) {
ThreadComputer current = live();
@@ -5589,13 +5556,13 @@ public class PackageManagerService extends IPackageManager.Stub
// set from outside classes. The attribute may be set to true anywhere, although it
// should only be set true while holding mLock. However, the attribute id guaranteed
// to be set false only while mLock and mSnapshotLock are both held.
- private static AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
+ private static final AtomicBoolean sSnapshotInvalid = new AtomicBoolean(true);
// The package manager that is using snapshots.
private static PackageManagerService sSnapshotConsumer = null;
// If true, the snapshot is corked. Do not create a new snapshot but use the live
// computer. This throttles snapshot creation during periods of churn in Package
// Manager.
- private static AtomicInteger sSnapshotCorked = new AtomicInteger(0);
+ private static final AtomicInteger sSnapshotCorked = new AtomicInteger(0);
/**
* This class records the Computer being used by a thread and the Computer's reference
@@ -5623,10 +5590,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
- private static ThreadLocal<ThreadComputer> sThreadComputer = new ThreadLocal<>() {
- @Override protected ThreadComputer initialValue() {
- return new ThreadComputer();
- }};
+ private static final ThreadLocal<ThreadComputer> sThreadComputer =
+ ThreadLocal.withInitial(ThreadComputer::new);
/**
* This lock is used to make reads from {@link #sSnapshotInvalid} and
@@ -5800,10 +5765,10 @@ public class PackageManagerService extends IPackageManager.Stub
break;
}
case SEND_PENDING_BROADCAST: {
- String packages[];
- ArrayList<String> components[];
+ String[] packages;
+ ArrayList<String>[] components;
int size = 0;
- int uids[];
+ int[] uids;
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
size = mPendingBroadcasts.size();
@@ -5849,8 +5814,8 @@ public class PackageManagerService extends IPackageManager.Stub
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
- if (data != null && data.res.freezer != null) {
- data.res.freezer.close();
+ if (data != null && data.res.mFreezer != null) {
+ data.res.mFreezer.close();
}
if (data != null && data.mPostInstallRunnable != null) {
@@ -5859,19 +5824,19 @@ public class PackageManagerService extends IPackageManager.Stub
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
- final boolean killApp = (args.installFlags
+ final boolean killApp = (args.mInstallFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
- final boolean virtualPreload = ((args.installFlags
+ final boolean virtualPreload = ((args.mInstallFlags
& PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
handlePackagePostInstall(parentRes, killApp, virtualPreload,
- didRestore, args.installSource.installerPackageName, args.observer,
- args.mDataLoaderType);
+ didRestore, args.mInstallSource.installerPackageName,
+ args.mObserver, args.mDataLoaderType);
// Log tracing if needed
- if (args.traceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
- args.traceCookie);
+ if (args.mTraceMethod != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.mTraceMethod,
+ args.mTraceCookie);
}
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
@@ -5930,9 +5895,10 @@ public class PackageManagerService extends IPackageManager.Stub
if ((state != null) && !state.isVerificationComplete()
&& !state.timeoutExtended()) {
final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
- Slog.i(TAG, "Verification timed out for " + originUri);
+ String errorMsg = "Verification timed out for " + originUri;
+ Slog.i(TAG, errorMsg);
final UserHandle user = params.getUser();
if (getDefaultVerificationResponse(user)
@@ -5948,7 +5914,7 @@ public class PackageManagerService extends IPackageManager.Stub
PackageManager.VERIFICATION_REJECT, null,
params.mDataLoaderType, user);
params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
state.setVerifierResponse(Binder.getCallingUid(),
PackageManager.VERIFICATION_REJECT);
}
@@ -5971,9 +5937,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (state != null && !state.isIntegrityVerificationComplete()) {
final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
- Slog.i(TAG, "Integrity verification timed out for " + originUri);
+ String errorMsg = "Integrity verification timed out for " + originUri;
+ Slog.i(TAG, errorMsg);
state.setIntegrityVerificationResult(
getDefaultIntegrityVerificationResponse());
@@ -5983,7 +5950,8 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, "Integrity check times out, continuing with " + originUri);
} else {
params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ errorMsg);
}
if (state.areAllVerificationsComplete()) {
@@ -6016,14 +5984,15 @@ public class PackageManagerService extends IPackageManager.Stub
if (state.isVerificationComplete()) {
final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
if (state.isInstallAllowed()) {
broadcastPackageVerified(verificationId, originUri,
response.code, null, params.mDataLoaderType, params.getUser());
} else {
params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Install not allowed");
}
if (state.areAllVerificationsComplete()) {
@@ -6050,7 +6019,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int response = (Integer) msg.obj;
final VerificationParams params = state.getVerificationParams();
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
state.setIntegrityVerificationResult(response);
@@ -6058,7 +6027,8 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.i(TAG, "Integrity check passed for " + originUri);
} else {
params.setReturnCode(
- PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE);
+ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Integrity check failed for " + originUri);
}
if (state.areAllVerificationsComplete()) {
@@ -6095,7 +6065,7 @@ public class PackageManagerService extends IPackageManager.Stub
mPendingEnableRollback.remove(enableRollbackToken);
if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
Slog.w(TAG, "Failed to enable rollback for " + originUri);
Slog.w(TAG, "Continuing with installation of " + originUri);
}
@@ -6112,7 +6082,7 @@ public class PackageManagerService extends IPackageManager.Stub
final VerificationParams params =
mPendingEnableRollback.get(enableRollbackToken);
if (params != null) {
- final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
+ final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
Slog.w(TAG, "Enable rollback timed out for " + originUri);
mPendingEnableRollback.remove(enableRollbackToken);
@@ -6153,20 +6123,21 @@ public class PackageManagerService extends IPackageManager.Stub
private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
boolean virtualPreload, boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver, int dataLoaderType) {
- boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
- final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
- final String packageName = res.name;
+ boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
+ final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final String packageName = res.mName;
final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
final boolean removedBeforeUpdate = (pkgSetting == null)
- || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(res.pkg.getPath()));
+ || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(
+ res.mPkg.getPath()));
if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
- res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
- res.returnMsg = "Package was removed before install could complete.";
+ res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
+ res.mReturnMsg = "Package was removed before install could complete.";
// Remove the update failed package's older resources safely now
- InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
if (args != null) {
synchronized (mInstallLock) {
args.doPostDeleteLI(true);
@@ -6181,19 +6152,19 @@ public class PackageManagerService extends IPackageManager.Stub
mPerUidReadTimeoutsCache = null;
// Send the removed broadcasts
- if (res.removedInfo != null) {
- res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
+ if (res.mRemovedInfo != null) {
+ res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
}
final String installerPackageName =
- res.installerPackageName != null
- ? res.installerPackageName
- : res.removedInfo != null
- ? res.removedInfo.installerPackageName
+ res.mInstallerPackageName != null
+ ? res.mInstallerPackageName
+ : res.mRemovedInfo != null
+ ? res.mRemovedInfo.mInstallerPackageName
: null;
synchronized (mLock) {
- mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
+ mInstantAppRegistry.onPackageInstalledLPw(res.mPkg, res.mNewUsers);
}
// Determine the set of users who are adding this package for
@@ -6202,10 +6173,9 @@ public class PackageManagerService extends IPackageManager.Stub
int[] firstInstantUserIds = EMPTY_INT_ARRAY;
int[] updateUserIds = EMPTY_INT_ARRAY;
int[] instantUserIds = EMPTY_INT_ARRAY;
- final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
- final PackageSetting ps = pkgSetting;
- for (int newUser : res.newUsers) {
- final boolean isInstantApp = ps.getInstantApp(newUser);
+ final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
+ for (int newUser : res.mNewUsers) {
+ final boolean isInstantApp = pkgSetting.getInstantApp(newUser);
if (allNewUsers) {
if (isInstantApp) {
firstInstantUserIds = ArrayUtils.appendInt(firstInstantUserIds, newUser);
@@ -6215,7 +6185,7 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
boolean isNew = true;
- for (int origUser : res.origUsers) {
+ for (int origUser : res.mOrigUsers) {
if (origUser == newUser) {
isNew = false;
break;
@@ -6237,20 +6207,20 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Send installed broadcasts if the package is not a static shared lib.
- if (res.pkg.getStaticSharedLibName() == null) {
- mProcessLoggingHandler.invalidateBaseApkHash(res.pkg.getBaseApkPath());
+ if (res.mPkg.getStaticSharedLibName() == null) {
+ mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
// Send added for users that see the package for the first time
// sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(res.uid);
- boolean isSystem = res.pkg.isSystem();
+ int appId = UserHandle.getAppId(res.mUid);
+ boolean isSystem = res.mPkg.isSystem();
sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds,
dataLoaderType);
// Send added for users that don't see the package for the first time
Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.uid);
+ extras.putInt(Intent.EXTRA_UID, res.mUid);
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
@@ -6260,7 +6230,7 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
- getPackageSettingInternal(res.name, Process.SYSTEM_UID),
+ getPackageSettingInternal(res.mName, Process.SYSTEM_UID),
updateUserIds, mSettings.getPackagesLocked());
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
@@ -6298,7 +6268,7 @@ public class PackageManagerService extends IPackageManager.Stub
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.removedInfo.broadcastAllowList,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
null);
if (installerPackageName != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
@@ -6319,7 +6289,7 @@ public class PackageManagerService extends IPackageManager.Stub
null /*broadcastAllowList*/,
getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
.toBundle());
- } else if (launchedForRestore && !res.pkg.isSystem()) {
+ } else if (launchedForRestore && !res.mPkg.isSystem()) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
if (DEBUG_BACKUP) {
@@ -6331,15 +6301,15 @@ public class PackageManagerService extends IPackageManager.Stub
}
// Send broadcast package appeared if external for all users
- if (res.pkg.isExternalStorage()) {
+ if (res.mPkg.isExternalStorage()) {
if (!update) {
final StorageManager storage = mInjector.getSystemService(
StorageManager.class);
VolumeInfo volume =
storage.findVolumeByUuid(
- res.pkg.getStorageUuid().toString());
+ res.mPkg.getStorageUuid().toString());
int packageExternalStorageType =
- getPackageExternalStorageType(volume, res.pkg.isExternalStorage());
+ getPackageExternalStorageType(volume, res.mPkg.isExternalStorage());
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
FrameworkStatsLog.write(
@@ -6348,17 +6318,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
+ Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
}
- final int[] uidArray = new int[]{res.pkg.getUid()};
+ final int[] uidArray = new int[]{res.mPkg.getUid()};
ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(packageName);
sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
}
- } else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
- int[] allUsers = mInjector.getUserManagerService().getUserIds();
- for (int i = 0; i < res.libraryConsumers.size(); i++) {
- AndroidPackage pkg = res.libraryConsumers.get(i);
+ } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+ for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
+ AndroidPackage pkg = res.mLibraryConsumers.get(i);
// send broadcast that all consumers of the static shared library have changed
sendPackageChangedBroadcast(pkg.getPackageName(), false /* dontKillApp */,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
@@ -6370,14 +6339,14 @@ public class PackageManagerService extends IPackageManager.Stub
if (firstUserIds != null && firstUserIds.length > 0) {
for (int userId : firstUserIds) {
restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
- pkgSetting.getInstallReason(userId), userId);
+ userId);
}
}
if (allNewUsers && !update) {
- notifyPackageAdded(packageName, res.uid);
+ notifyPackageAdded(packageName, res.mUid);
} else {
- notifyPackageChanged(packageName, res.uid);
+ notifyPackageChanged(packageName, res.mUid);
}
// Log current value of "unknown sources" setting
@@ -6385,7 +6354,7 @@ public class PackageManagerService extends IPackageManager.Stub
getUnknownSourcesSettings());
// Remove the replaced package's older resources safely now
- InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+ InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
if (args != null) {
if (!killApp) {
// If we didn't kill the app, defer the deletion of code/resource files, since
@@ -6444,7 +6413,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void notifyInstallObserver(String packageName) {
+ void notifyInstallObserver(String packageName) {
Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
mNoKillInstallObservers.remove(packageName);
@@ -6453,13 +6422,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void notifyInstallObserver(PackageInstalledInfo info,
+ void notifyInstallObserver(PackageInstalledInfo info,
IPackageInstallObserver2 installObserver) {
if (installObserver != null) {
try {
Bundle extras = extrasForInstallResult(info);
- installObserver.onPackageInstalled(info.name, info.returnCode,
- info.returnMsg, extras);
+ installObserver.onPackageInstalled(info.mName, info.mReturnCode,
+ info.mReturnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
@@ -6473,7 +6442,7 @@ public class PackageManagerService extends IPackageManager.Stub
private void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
IPackageInstallObserver2 observer) {
- String packageName = info.pkg.getPackageName();
+ String packageName = info.mPkg.getPackageName();
mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
@@ -6578,7 +6547,7 @@ public class PackageManagerService extends IPackageManager.Stub
return StorageEnums.UNKNOWN;
}
- private StorageEventListener mStorageListener = new StorageEventListener() {
+ private final StorageEventListener mStorageListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
if (vol.type == VolumeInfo.TYPE_PRIVATE) {
@@ -6632,19 +6601,19 @@ public class PackageManagerService extends IPackageManager.Stub
Bundle extrasForInstallResult(PackageInstalledInfo res) {
Bundle extras = null;
- switch (res.returnCode) {
+ switch (res.mReturnCode) {
case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: {
extras = new Bundle();
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION,
- res.origPermission);
+ res.mOrigPermission);
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE,
- res.origPackage);
+ res.mOrigPackage);
break;
}
case PackageManager.INSTALL_SUCCEEDED: {
extras = new Bundle();
extras.putBoolean(Intent.EXTRA_REPLACING,
- res.removedInfo != null && res.removedInfo.removedPackage != null);
+ res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null);
break;
}
}
@@ -6833,7 +6802,7 @@ public class PackageManagerService extends IPackageManager.Stub
* reasons. This simply requests that the copy takes place and awaits confirmation of its
* completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy.
*/
- private static void requestCopyPreoptedFiles(Injector injector) {
+ private static void requestCopyPreoptedFiles() {
final int WAIT_TIME_MS = 100;
final String CP_PREOPT_PROPERTY = "sys.cppreopt";
if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) {
@@ -6989,7 +6958,6 @@ public class PackageManagerService extends IPackageManager.Stub
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
- mDocumenterPackage = testParams.documenterPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -7009,7 +6977,6 @@ public class PackageManagerService extends IPackageManager.Stub
mPackages.putAll(testParams.packages);
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
- mSystemWrapper = testParams.systemWrapper;
mAppInstallDir = testParams.appInstallDir;
mAppLib32InstallDir = testParams.appLib32InstallDir;
mIsEngBuild = testParams.isEngBuild;
@@ -7039,7 +7006,6 @@ public class PackageManagerService extends IPackageManager.Stub
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
- mSystemWrapper = injector.getSystemWrapper();
mContext = injector.getContext();
mFactoryTest = factoryTest;
@@ -7146,7 +7112,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager);
+ mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
mDirsToScanAsSystem = new ArrayList<>();
mDirsToScanAsSystem.addAll(injector.getSystemPartitions());
@@ -7220,9 +7186,10 @@ public class PackageManagerService extends IPackageManager.Stub
t.traceEnd();
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
+ mPermissionManager.readLegacyPermissionStateTEMP();
if (!mOnlyCore && mFirstBoot) {
- requestCopyPreoptedFiles(mInjector);
+ requestCopyPreoptedFiles();
}
String customResolverActivityName = Resources.getSystem().getString(
@@ -7396,7 +7363,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "System package " + ps.name
- + " no longer exists; it's data will be wiped");
+ + " no longer exists; its data will be wiped");
removePackageDataLIF(ps, userIds, null, 0, false);
} else {
// we still have a disabled system package, but, it still might have
@@ -7578,6 +7545,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
mExpectingBetter.clear();
+ mSettings.pruneRenamedPackagesLPw();
+
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
@@ -7588,7 +7557,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
- mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
@@ -7635,7 +7603,6 @@ public class PackageManagerService extends IPackageManager.Stub
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
- mPermissionManager.readLegacyPermissionStateTEMP();
// If the build fingerprint has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
@@ -7941,7 +7908,7 @@ public class PackageManagerService extends IPackageManager.Stub
private boolean enableCompressedPackage(AndroidPackage stubPkg,
@NonNull PackageSetting stubPkgSetting) {
final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
- | PackageParser.PARSE_ENFORCE_CODE;
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE;
synchronized (mInstallLock) {
final AndroidPackage pkg;
try (PackageFreezer freezer =
@@ -8103,7 +8070,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private void updateInstantAppInstallerLocked(String modifiedPackage) {
+ void updateInstantAppInstallerLocked(String modifiedPackage) {
// we're only interested in updating the installer appliction when 1) it's not
// already set or 2) the modified package is the installer
if (mInstantAppInstallerActivity != null
@@ -8213,7 +8180,7 @@ public class PackageManagerService extends IPackageManager.Stub
final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
+ UserHandle.USER_SYSTEM, Binder.getCallingUid());
if (matches.size() == 1) {
return matches.get(0).getComponentInfo().packageName;
} else if (matches.size() == 0) {
@@ -8309,7 +8276,7 @@ public class PackageManagerService extends IPackageManager.Stub
final List<ResolveInfo> matches = queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
+ UserHandle.USER_SYSTEM, Binder.getCallingUid());
ResolveInfo best = null;
final int N = matches.size();
for (int i = 0; i < N; i++) {
@@ -8337,7 +8304,7 @@ public class PackageManagerService extends IPackageManager.Stub
Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
List<ResolveInfo> matches = queryIntentReceiversInternal(intent, null,
MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM, false /*allowDynamicSplits*/);
+ UserHandle.USER_SYSTEM, Binder.getCallingUid());
ResolveInfo best = null;
final int N = matches.size();
for (int i = 0; i < N; i++) {
@@ -8372,11 +8339,7 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
synchronized (mLock) {
- final ComponentName instantAppResolver = getInstantAppResolverLPr();
- if (instantAppResolver == null) {
- return null;
- }
- return instantAppResolver;
+ return getInstantAppResolverLPr();
}
}
@@ -8600,7 +8563,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (ps != null) {
final PackageUserState state = ps.readUserState(userId);
if (state != null) {
- return PackageParser.isAvailable(state);
+ return checkUseInstalledOrHidden(p, ps, state, 0 /*flags*/);
}
}
}
@@ -8632,16 +8595,6 @@ public class PackageManagerService extends IPackageManager.Stub
flags, filterCallingUid, userId);
}
- private boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
- return mComputer.isComponentVisibleToInstantApp(component);
- }
-
- private boolean isComponentVisibleToInstantApp(
- @Nullable ComponentName component, @ComponentType int type) {
- return mComputer.isComponentVisibleToInstantApp(
- component, type);
- }
-
/**
* Returns whether or not access to the application should be filtered.
* <p>
@@ -8765,13 +8718,11 @@ public class PackageManagerService extends IPackageManager.Stub
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
- PackageSetting ps = getPackageSetting(p.getPackageName());
- if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return null;
+ final PackageSetting ps = getPackageSetting(p.getPackageName());
+ if (ps != null && ps.getInstalled(userId)
+ && !shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
- // TODO: Shouldn't this be checking for package installed state for userId and
- // return null?
- return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -8962,14 +8913,13 @@ public class PackageManagerService extends IPackageManager.Stub
if (freeBytesRequired > 0) {
smInternal.freeCache(volumeUuid, freeBytesRequired);
}
- if (file.getUsableSpace() >= bytes) return;
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);
} catch (InstallerException ignored) {
}
- if (file.getUsableSpace() >= bytes) return;
}
+ if (file.getUsableSpace() >= bytes) return;
throw new IOException("Failed to free " + bytes + " on storage device at " + file);
}
@@ -8983,7 +8933,6 @@ public class PackageManagerService extends IPackageManager.Stub
final long now = System.currentTimeMillis();
synchronized (mLock) {
- final int[] allUsers = mUserManager.getUserIds();
final int libCount = mSharedLibraries.size();
for (int i = 0; i < libCount; i++) {
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib
@@ -9096,14 +9045,6 @@ public class PackageManagerService extends IPackageManager.Stub
wantInstantApps, isImplicitImageCaptureIntentAndNotSetByDpc);
}
- private int updateFlagsForResolve(int flags, int userId, int callingUid,
- boolean wantInstantApps, boolean onlyExposedExplicitly,
- boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
- return mComputer.updateFlagsForResolve(flags, userId, callingUid,
- wantInstantApps, onlyExposedExplicitly,
- isImplicitImageCaptureIntentAndNotSetByDpc);
- }
-
@Override
public int getTargetSdkVersion(String packageName) {
synchronized (mLock) {
@@ -9523,7 +9464,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
+ void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
for (int i = userList.length - 1; i >= 0; --i) {
final int userId = userList[i];
SparseArray<String> changedPackages = mChangedPackages.get(userId);
@@ -9781,7 +9722,8 @@ public class PackageManagerService extends IPackageManager.Stub
if (p2SigningDetails == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
+ int result = compareSignatures(p1SigningDetails.getSignatures(),
+ p2SigningDetails.getSignatures());
if (result == PackageManager.SIGNATURE_MATCH) {
return result;
}
@@ -9791,11 +9733,11 @@ public class PackageManagerService extends IPackageManager.Stub
if (p1SigningDetails.hasPastSigningCertificates()
|| p2SigningDetails.hasPastSigningCertificates()) {
Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
- : p1SigningDetails.signatures;
+ ? new Signature[]{p1SigningDetails.getPastSigningCertificates()[0]}
+ : p1SigningDetails.getSignatures();
Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
- : p2SigningDetails.signatures;
+ ? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
+ : p2SigningDetails.getSignatures();
result = compareSignatures(p1Signatures, p2Signatures);
}
return result;
@@ -9839,7 +9781,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int appId = UserHandle.getAppId(uid);
// reader
synchronized (mLock) {
- final PackageParser.SigningDetails signingDetails;
+ final SigningDetails signingDetails;
final Object obj = mSettings.getSettingLPr(appId);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
@@ -9876,7 +9818,7 @@ public class PackageManagerService extends IPackageManager.Stub
* external storage) is less than the version where package signatures
* were updated, return true.
*/
- private boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
+ boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
return isCompatSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
@@ -9884,7 +9826,7 @@ public class PackageManagerService extends IPackageManager.Stub
return ver.databaseVersion < DatabaseVersion.SIGNATURE_END_ENTITY;
}
- private boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
+ boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
return isRecoverSignatureUpdateNeeded(getSettingsVersionForPackage(pkg));
}
@@ -10022,24 +9964,26 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getUidForSharedUser(String sharedUserName) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return -1;
- }
if (sharedUserName == null) {
- return -1;
+ return Process.INVALID_UID;
+ }
+ final int callingUid = Binder.getCallingUid();
+ if (getInstantAppPackageName(callingUid) != null) {
+ return Process.INVALID_UID;
}
// reader
synchronized (mLock) {
- SharedUserSetting suid;
try {
- suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false);
- if (suid != null) {
+ final SharedUserSetting suid = mSettings.getSharedUserLPw(sharedUserName,
+ 0 /* pkgFlags */, 0 /* pkgPrivateFlags */, false /* create */);
+ if (suid != null && !shouldFilterApplicationLocked(suid, callingUid,
+ UserHandle.getUserId(callingUid))) {
return suid.userId;
}
} catch (PackageManagerException ignore) {
// can't happen, but, still need to catch it
}
- return -1;
+ return Process.INVALID_UID;
}
}
@@ -10133,7 +10077,22 @@ public class PackageManagerService extends IPackageManager.Stub
if (getInstantAppPackageName(getCallingUid()) != null) {
return EmptyArray.STRING;
}
- return mPermissionManager.getAppOpPermissionPackages(permissionName);
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ final ArraySet<String> packageNames = new ArraySet(
+ mPermissionManager.getAppOpPermissionPackages(permissionName));
+ synchronized (mLock) {
+ for (int i = packageNames.size() - 1; i >= 0; i--) {
+ final String packageName = packageNames.valueAt(i);
+ if (!shouldFilterApplicationLocked(mSettings.getPackageLPr(packageName),
+ callingUid, callingUserId)) {
+ continue;
+ }
+ packageNames.removeAt(i);
+ }
+ }
+ return packageNames.toArray(new String[packageNames.size()]);
}
@Override
@@ -10240,7 +10199,7 @@ public class PackageManagerService extends IPackageManager.Stub
userId);
// Find any earlier preferred or last chosen entries and nuke them
findPreferredActivityNotLocked(
- intent, resolvedType, flags, query, 0, false, true, false, userId);
+ intent, resolvedType, flags, query, false, true, false, userId);
// Add the new activity as the last chosen for this filter
addPreferredActivity(filter, match, null, activity, false, userId,
"Setting last chosen", false);
@@ -10256,7 +10215,7 @@ public class PackageManagerService extends IPackageManager.Stub
final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
userId);
return findPreferredActivityNotLocked(
- intent, resolvedType, flags, query, 0, false, false, false, userId);
+ intent, resolvedType, flags, query, false, false, false, userId);
}
private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
@@ -10298,7 +10257,7 @@ public class PackageManagerService extends IPackageManager.Stub
// If we have saved a preference for a preferred activity for
// this Intent, use that.
ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
- flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
+ flags, query, true, false, debug, userId, queryMayBeFiltered);
if (ri != null) {
return ri;
}
@@ -10475,17 +10434,17 @@ public class PackageManagerService extends IPackageManager.Stub
}
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
- List<ResolveInfo> query, int priority, boolean always,
+ List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId) {
return findPreferredActivityNotLocked(
- intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
+ intent, resolvedType, flags, query, always, removeMatches, debug, userId,
UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
}
// TODO: handle preferred activities missing while user has amnesia
/** <b>must not hold {@link #mLock}</b> */
ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
- List<ResolveInfo> query, int priority, boolean always,
+ List<ResolveInfo> query, boolean always,
boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
if (Thread.holdsLock(mLock)) {
Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
@@ -10793,16 +10752,6 @@ public class PackageManagerService extends IPackageManager.Stub
filterCallingUid, userId, resolveForStart, allowDynamicSplits);
}
- private @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
- Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
- boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
- String instantAppPkgName) {
- return mComputer.queryIntentActivitiesInternalBody(
- intent, resolvedType, flags, filterCallingUid, userId,
- resolveForStart, allowDynamicSplits, pkgName,
- instantAppPkgName);
- }
-
private static class CrossProfileDomainInfo {
/* ResolveInfo for IntentForwarderActivity to send the intent to the other profile */
ResolveInfo resolveInfo;
@@ -11034,30 +10983,32 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
- return new ParceledListSlice<>(
- queryIntentReceiversInternal(intent, resolvedType, flags, userId,
- false /*allowDynamicSplits*/));
+ return new ParceledListSlice<>(queryIntentReceiversInternal(intent, resolvedType,
+ flags, userId, Binder.getCallingUid()));
}
+ // In this method, we have to know the actual calling UID, but in some cases Binder's
+ // call identity is removed, so the UID has to be passed in explicitly.
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
- String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
+ String resolvedType, int flags, int userId, int filterCallingUid) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
- final int callingUid = Binder.getCallingUid();
- enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+ enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "query intent receivers");
- final String instantAppPkgName = getInstantAppPackageName(callingUid);
- flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+ final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
+ flags = updateFlagsForResolve(flags, userId, filterCallingUid, false /*includeInstantApps*/,
isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
flags));
+ Intent originalIntent = null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
+ originalIntent = intent;
intent = intent.getSelector();
comp = intent.getComponent();
}
}
+ List<ResolveInfo> list = Collections.emptyList();
if (comp != null) {
- final List<ResolveInfo> list = new ArrayList<>(1);
final ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
// When specifying an explicit component, we prevent the activity from being
@@ -11093,40 +11044,44 @@ public class PackageManagerService extends IPackageManager.Stub
if (!blockResolution) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
+ list = new ArrayList<>(1);
list.add(ri);
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, true, intent, resolvedType, filterCallingUid);
}
}
- return applyPostResolutionFilter(
- list, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
- intent);
- }
-
- // reader
- synchronized (mLock) {
- String pkgName = intent.getPackage();
- if (pkgName == null) {
- final List<ResolveInfo> result =
- mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
- if (result == null) {
- return Collections.emptyList();
+ } else {
+ // reader
+ synchronized (mLock) {
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ final List<ResolveInfo> result =
+ mComponentResolver.queryReceivers(intent, resolvedType, flags, userId);
+ if (result != null) {
+ list = result;
+ }
}
- return applyPostResolutionFilter(
- result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
- intent);
- }
- final AndroidPackage pkg = mPackages.get(pkgName);
- if (pkg != null) {
- final List<ResolveInfo> result = mComponentResolver.queryReceivers(
- intent, resolvedType, flags, pkg.getReceivers(), userId);
- if (result == null) {
- return Collections.emptyList();
+ final AndroidPackage pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ final List<ResolveInfo> result = mComponentResolver.queryReceivers(
+ intent, resolvedType, flags, pkg.getReceivers(), userId);
+ if (result != null) {
+ list = result;
+ }
}
- return applyPostResolutionFilter(
- result, instantAppPkgName, allowDynamicSplits, callingUid, false, userId,
- intent);
}
- return Collections.emptyList();
}
+
+ if (originalIntent != null) {
+ // We also have to ensure all components match the original intent
+ applyEnforceIntentFilterMatching(
+ mInjector.getCompatibility(), mComponentResolver,
+ list, true, originalIntent, resolvedType, filterCallingUid);
+ }
+
+ return applyPostResolutionFilter(
+ list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
}
@Override
@@ -11168,6 +11123,68 @@ public class PackageManagerService extends IPackageManager.Stub
includeInstantApps);
}
+ // Static to give access to ComputeEngine
+ private static void applyEnforceIntentFilterMatching(
+ PlatformCompat compat, ComponentResolver resolver,
+ List<ResolveInfo> resolveInfos, boolean isReceiver,
+ Intent intent, String resolvedType, int filterCallingUid) {
+ // Do not enforce filter matching when the caller is system or root.
+ // see ActivityManager#checkComponentPermission(String, int, int, boolean)
+ if (filterCallingUid == Process.ROOT_UID || filterCallingUid == Process.SYSTEM_UID) {
+ return;
+ }
+
+ final Printer logPrinter = DEBUG_INTENT_MATCHING
+ ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM)
+ : null;
+
+ for (int i = resolveInfos.size() - 1; i >= 0; --i) {
+ final ComponentInfo info = resolveInfos.get(i).getComponentInfo();
+
+ // Do not enforce filter matching when the caller is the same app
+ if (info.applicationInfo.uid == filterCallingUid) {
+ continue;
+ }
+
+ // Only enforce filter matching if target app's target SDK >= T
+ if (!compat.isChangeEnabledInternal(
+ ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) {
+ continue;
+ }
+
+ final ParsedMainComponent comp;
+ if (info instanceof ActivityInfo) {
+ if (isReceiver) {
+ comp = resolver.getReceiver(info.getComponentName());
+ } else {
+ comp = resolver.getActivity(info.getComponentName());
+ }
+ } else if (info instanceof ServiceInfo) {
+ comp = resolver.getService(info.getComponentName());
+ } else {
+ // This shall never happen
+ throw new IllegalArgumentException("Unsupported component type");
+ }
+
+ if (comp.getIntents().isEmpty()) {
+ continue;
+ }
+
+ final boolean match = comp.getIntents().stream().anyMatch(
+ f -> IntentResolver.intentMatchesFilter(f, intent, resolvedType));
+ if (!match) {
+ Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
+ Slog.w(TAG, "Access blocked: " + comp.getComponentName());
+ if (DEBUG_INTENT_MATCHING) {
+ Slog.v(TAG, "Component intent filters:");
+ comp.getIntents().forEach(f -> f.dump(logPrinter, " "));
+ Slog.v(TAG, "-----------------------------");
+ }
+ resolveInfos.remove(i);
+ }
+ }
+ }
+
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
String resolvedType, int flags, int userId) {
@@ -11603,14 +11620,6 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
- return resolveContentProviderInternal(name, flags, userId);
- }
-
- public ProviderInfo resolveContentProvider(String name, int flags, int userId, int callingUid) {
- return resolveContentProviderInternal(name, flags, userId, callingUid);
- }
-
- private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) {
return resolveContentProviderInternal(name, flags, userId, Binder.getCallingUid());
}
@@ -11846,9 +11855,8 @@ public class PackageManagerService extends IPackageManager.Stub
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
}
- } else if (throwable instanceof PackageParserException) {
- PackageParserException e = (PackageParserException)
- throwable;
+ } else if (throwable instanceof PackageManagerException) {
+ PackageManagerException e = (PackageManagerException) throwable;
errorCode = e.error;
errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
@@ -11888,14 +11896,14 @@ public class PackageManagerService extends IPackageManager.Stub
&& ps.timeStamp == lastModifiedTime
&& !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.signatures != null
- && ps.signatures.mSigningDetails.signatures.length != 0
- && ps.signatures.mSigningDetails.signatureSchemeVersion
+ if (ps.signatures.mSigningDetails.getSignatures() != null
+ && ps.signatures.mSigningDetails.getSignatures().length != 0
+ && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
!= SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
- new PackageParser.SigningDetails(ps.signatures.mSigningDetails));
+ new SigningDetails(ps.signatures.mSigningDetails));
return;
}
@@ -11908,10 +11916,14 @@ public class PackageManagerService extends IPackageManager.Stub
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- parsedPackage.setSigningDetails(
- ParsingPackageUtils.getSigningDetails(parsedPackage, skipVerify));
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -11931,7 +11943,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
- clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
+ clearAppProfilesLIF(pkg);
if (DEBUG_INSTALL) {
Slog.d(TAG, originalPkgSetting.name
+ " clear profile due to version change "
@@ -11968,8 +11980,6 @@ public class PackageManagerService extends IPackageManager.Stub
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -12107,11 +12117,12 @@ public class PackageManagerService extends IPackageManager.Stub
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
null, parseFlags, scanFlags, isPlatformPackage, user, null);
- applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, true);
+ applyPolicy(parsedPackage, scanFlags, mPlatformPackage, true);
final ScanResult scanResult =
scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
- if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
- scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
}
}
}
@@ -12158,11 +12169,11 @@ public class PackageManagerService extends IPackageManager.Stub
// for the package. Which means it needs to be finalized here to cache derived fields.
// This is relevant for cases where the disabled system package is used for flags or
// other metadata.
- ((ParsedPackage) parsedPackage).hideAsFinal();
+ parsedPackage.hideAsFinal();
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getPath() + " ignored: updated version "
- + pkgSetting.versionCode + " better than this "
- + parsedPackage.getLongVersionCode());
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
}
// Verify certificates against what was last scanned. Force re-collecting certificate in two
@@ -12199,10 +12210,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (!parsedPackage.getSigningDetails()
.checkCapability(pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
@@ -12210,7 +12221,7 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.getPackageName(),
"scanPackageInternalLI")) {
deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mUserManager.getUserIds(), 0, null, false, null);
+ mUserManager.getUserIds(), 0, null, false);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
@@ -12247,11 +12258,11 @@ public class PackageManagerService extends IPackageManager.Stub
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- if (scanResult.success) {
+ if (scanResult.mSuccess) {
synchronized (mLock) {
boolean appIdCreated = false;
try {
- final String pkgName = scanResult.pkgSetting.name;
+ final String pkgName = scanResult.mPkgSetting.name;
final Map<String, ReconciledPackage> reconcileResult = reconcilePackagesLocked(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
@@ -12283,19 +12294,17 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkgSetting != null && pkgSetting.isPackageLoading()) {
// Continue monitoring loading progress of active incremental packages
final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(parsedPackage.getPackageName(),
- UserHandle.getUid(UserHandle.USER_ALL, pkgSetting.appId),
- getInstalledUsers(pkgSetting, UserHandle.USER_ALL));
+ new IncrementalStatesCallback(parsedPackage.getPackageName(), this);
pkgSetting.setIncrementalStatesCallback(incrementalStatesCallback);
mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
- new IncrementalProgressListener(parsedPackage.getPackageName()));
+ new IncrementalProgressListener(parsedPackage.getPackageName(), this));
}
}
- return scanResult.pkgSetting.pkg;
+ return scanResult.mPkgSetting.pkg;
}
// TODO:(b/135203078): Move to parsing
- private static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
+ static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
// Derive the new package synthetic package name
parsedPackage.setPackageName(toStaticSharedLibraryPackageName(
parsedPackage.getPackageName(), parsedPackage.getStaticSharedLibVersion()));
@@ -12306,13 +12315,6 @@ public class PackageManagerService extends IPackageManager.Stub
return packageName + STATIC_SHARED_LIB_DELIMITER + libraryVersion;
}
- static String fixProcessName(String defProcessName, String processName) {
- if (processName == null) {
- return defProcessName;
- }
- return processName;
- }
-
/**
* Enforces that only the system UID or root's UID can call a method exposed
* via Binder.
@@ -12355,24 +12357,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
- * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
- * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
- *
- * @param checkShell whether to prevent shell from access if there's a debugging restriction
- * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
- * permission to be held even if the callingUid and userId
- * reference the same user.
- * @param message the message to log on security exception
- */
- private void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
- boolean requireFullPermission, boolean checkShell,
- boolean requirePermissionWhenSameUser, String message) {
- mComputer.enforceCrossUserPermission(callingUid, userId,
- requireFullPermission, checkShell,
- requirePermissionWhenSameUser, message);
- }
-
- /**
* Checks if the request is from the system or an app that has the appropriate cross-user
* permissions defined as follows:
* <ul>
@@ -12392,10 +12376,6 @@ public class PackageManagerService extends IPackageManager.Stub
requireFullPermission, checkShell, message);
}
- private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
- return mComputer.isSameProfileGroup(callerUserId, userId);
- }
-
private static String buildInvalidCrossUserPermissionMessage(int callingUid,
@UserIdInt int userId, String message, boolean requireFullPermission) {
StringBuilder builder = new StringBuilder();
@@ -12438,6 +12418,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
builder.append(" to access user ");
+ builder.append(userId);
builder.append(".");
return builder.toString();
}
@@ -13046,7 +13027,7 @@ public class PackageManagerService extends IPackageManager.Stub
return versionedLib.get(version);
}
- private SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
+ SharedLibraryInfo getLatestSharedLibraVersionLPr(AndroidPackage pkg) {
WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
pkg.getStaticSharedLibName());
if (versionedLib == null) {
@@ -13066,13 +13047,12 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
-
@Nullable
- private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
+ PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) {
PackageSetting sharedLibPackage = null;
synchronized (mLock) {
final SharedLibraryInfo latestSharedLibraVersionLPr =
- getLatestSharedLibraVersionLPr(scanResult.request.parsedPackage);
+ getLatestSharedLibraVersionLPr(scanResult.mRequest.mParsedPackage);
if (latestSharedLibraVersionLPr != null) {
sharedLibPackage = mSettings.getPackageLPr(
latestSharedLibraVersionLPr.getPackageName());
@@ -13212,18 +13192,18 @@ public class PackageManagerService extends IPackageManager.Stub
cacher.cleanCachedResult(codePath);
}
- private int[] resolveUserIds(int userId) {
+ int[] resolveUserIds(int userId) {
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
- private void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
+ void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
if (pkg == null) {
return;
}
clearAppDataLeafLIF(pkg, userId, flags);
if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
- clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
+ clearAppProfilesLIF(pkg);
}
}
@@ -13284,7 +13264,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void clearAppProfilesLIF(AndroidPackage pkg, int userId) {
+ private void clearAppProfilesLIF(AndroidPackage pkg) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
@@ -13503,9 +13483,9 @@ public class PackageManagerService extends IPackageManager.Stub
// For apps targeting O MR1 we require explicit enumeration of all certs.
final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1)
? PackageUtils.computeSignaturesSha256Digests(
- libPkg.signatures)
+ libPkg.getSignatures())
: PackageUtils.computeSignaturesSha256Digests(
- new Signature[]{libPkg.signatures[0]});
+ new Signature[]{libPkg.getSignatures()[0]});
// Take a shortcut if sizes don't match. Note that if an app doesn't
// target O we don't parse the "additional-certificate" tags similarly
@@ -13624,7 +13604,7 @@ public class PackageManagerService extends IPackageManager.Stub
? PackageManager.DELETE_KEEP_DATA : 0;
deletePackageLIF(pkg.getPackageName(), null, true,
mUserManager.getUserIds(), flags, null,
- true, null);
+ true);
}
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
@@ -13648,7 +13628,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy({"mInstallLock", "mLock"})
- private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
@@ -13660,107 +13640,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /** The result of a package scan. */
- @VisibleForTesting
- static class ScanResult {
- /** The request that initiated the scan that produced this result. */
- public final ScanRequest request;
- /** Whether or not the package scan was successful */
- public final boolean success;
- /**
- * Whether or not the original PackageSetting needs to be updated with this result on
- * commit.
- */
- public final boolean existingSettingCopied;
- /**
- * The final package settings. This may be the same object passed in
- * the {@link ScanRequest}, but, with modified values.
- */
- @Nullable public final PackageSetting pkgSetting;
- /** ABI code paths that have changed in the package scan */
- @Nullable public final List<String> changedAbiCodePath;
-
- public final SharedLibraryInfo staticSharedLibraryInfo;
-
- public final List<SharedLibraryInfo> dynamicSharedLibraryInfos;
-
- public ScanResult(
- ScanRequest request, boolean success,
- @Nullable PackageSetting pkgSetting,
- @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
- SharedLibraryInfo staticSharedLibraryInfo,
- List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
- this.request = request;
- this.success = success;
- this.pkgSetting = pkgSetting;
- this.changedAbiCodePath = changedAbiCodePath;
- this.existingSettingCopied = existingSettingCopied;
- this.staticSharedLibraryInfo = staticSharedLibraryInfo;
- this.dynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
- }
- }
-
- /** A package to be scanned */
- @VisibleForTesting
- static class ScanRequest {
- /** The parsed package */
- @NonNull public final ParsedPackage parsedPackage;
- /** The package this package replaces */
- @Nullable public final AndroidPackage oldPkg;
- /** Shared user settings, if the package has a shared user */
- @Nullable public final SharedUserSetting sharedUserSetting;
- /**
- * Package settings of the currently installed version.
- * <p><em>IMPORTANT:</em> The contents of this object may be modified
- * during scan.
- */
- @Nullable public final PackageSetting pkgSetting;
- /** A copy of the settings for the currently installed version */
- @Nullable public final PackageSetting oldPkgSetting;
- /** Package settings for the disabled version on the /system partition */
- @Nullable public final PackageSetting disabledPkgSetting;
- /** Package settings for the installed version under its original package name */
- @Nullable public final PackageSetting originalPkgSetting;
- /** The real package name of a renamed application */
- @Nullable public final String realPkgName;
- public final @ParseFlags int parseFlags;
- public final @ScanFlags int scanFlags;
- /** The user for which the package is being scanned */
- @Nullable public final UserHandle user;
- /** Whether or not the platform package is being scanned */
- public final boolean isPlatformPackage;
- /** Override value for package ABI if set during install */
- @Nullable
- public final String cpuAbiOverride;
- public ScanRequest(
- @NonNull ParsedPackage parsedPackage,
- @Nullable SharedUserSetting sharedUserSetting,
- @Nullable AndroidPackage oldPkg,
- @Nullable PackageSetting pkgSetting,
- @Nullable PackageSetting disabledPkgSetting,
- @Nullable PackageSetting originalPkgSetting,
- @Nullable String realPkgName,
- @ParseFlags int parseFlags,
- @ScanFlags int scanFlags,
- boolean isPlatformPackage,
- @Nullable UserHandle user,
- @Nullable String cpuAbiOverride) {
- this.parsedPackage = parsedPackage;
- this.oldPkg = oldPkg;
- this.pkgSetting = pkgSetting;
- this.sharedUserSetting = sharedUserSetting;
- this.oldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
- this.disabledPkgSetting = disabledPkgSetting;
- this.originalPkgSetting = originalPkgSetting;
- this.realPkgName = realPkgName;
- this.parseFlags = parseFlags;
- this.scanFlags = scanFlags;
- this.isPlatformPackage = isPlatformPackage;
- this.user = user;
- this.cpuAbiOverride = cpuAbiOverride;
- }
- }
-
/**
* Returns the actual scan flags depending upon the state of the other settings.
* <p>Updated system applications will not have the following flags set
@@ -13846,8 +13725,9 @@ public class PackageManagerService extends IPackageManager.Stub
// priv-apps.
synchronized (mLock) {
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
- if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures,
- pkg.getSigningDetails().signatures)
+ if ((compareSignatures(
+ platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ pkg.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
}
@@ -13892,7 +13772,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
isUpdatedSystemApp = disabledPkgSetting != null;
}
- applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
+ applyPolicy(parsedPackage, scanFlags, mPlatformPackage, isUpdatedSystemApp);
assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
@@ -13919,19 +13799,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
-
/**
* Prepares the system to commit a {@link ScanResult} in a way that will not fail by registering
* the app ID required for reconcile.
* @return {@code true} if a new app ID was registered and will need to be cleaned up on
* failure.
*/
- private boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
+ boolean optimisticallyRegisterAppId(@NonNull ScanResult result)
throws PackageManagerException {
- if (!result.existingSettingCopied) {
+ if (!result.mExistingSettingCopied || result.mNeedsNewAppId) {
// THROWS: when we can't allocate a user id. add call to check if there's
// enough space to ensure we won't throw; otherwise, don't modify state
- return mSettings.registerAppIdLPw(result.pkgSetting);
+ return mSettings.registerAppIdLPw(result.mPkgSetting, result.mNeedsNewAppId);
}
return false;
}
@@ -13941,11 +13820,11 @@ public class PackageManagerService extends IPackageManager.Stub
* {@link #optimisticallyRegisterAppId(ScanResult)}. Note: this is only necessary if the
* referenced method returned true.
*/
- private void cleanUpAppIdCreation(@NonNull ScanResult result) {
+ void cleanUpAppIdCreation(@NonNull ScanResult result) {
// iff we've acquired an app ID for a new package setting, remove it so that it can be
// acquired by another request.
- if (result.pkgSetting.appId > 0) {
- mSettings.removeAppIdLPw(result.pkgSetting.appId);
+ if (result.mPkgSetting.appId > 0) {
+ mSettings.removeAppIdLPw(result.mPkgSetting.appId);
}
}
@@ -13957,37 +13836,37 @@ public class PackageManagerService extends IPackageManager.Stub
* possible and the system is not left in an inconsistent state.
*/
@GuardedBy({"mLock", "mInstallLock"})
- private AndroidPackage commitReconciledScanResultLocked(
+ AndroidPackage commitReconciledScanResultLocked(
@NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
- final ScanResult result = reconciledPkg.scanResult;
- final ScanRequest request = result.request;
+ final ScanResult result = reconciledPkg.mScanResult;
+ final ScanRequest request = result.mRequest;
// TODO(b/135203078): Move this even further away
- ParsedPackage parsedPackage = request.parsedPackage;
+ ParsedPackage parsedPackage = request.mParsedPackage;
if ("android".equals(parsedPackage.getPackageName())) {
// TODO(b/135203078): Move this to initial parse
parsedPackage.setVersionCode(mSdkVersion)
.setVersionCodeMajor(0);
}
- final AndroidPackage oldPkg = request.oldPkg;
- final @ParseFlags int parseFlags = request.parseFlags;
- final @ScanFlags int scanFlags = request.scanFlags;
- final PackageSetting oldPkgSetting = request.oldPkgSetting;
- final PackageSetting originalPkgSetting = request.originalPkgSetting;
- final UserHandle user = request.user;
- final String realPkgName = request.realPkgName;
- final List<String> changedAbiCodePath = result.changedAbiCodePath;
+ final AndroidPackage oldPkg = request.mOldPkg;
+ final @ParseFlags int parseFlags = request.mParseFlags;
+ final @ScanFlags int scanFlags = request.mScanFlags;
+ final PackageSetting oldPkgSetting = request.mOldPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final UserHandle user = request.mUser;
+ final String realPkgName = request.mRealPkgName;
+ final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
final PackageSetting pkgSetting;
- if (request.pkgSetting != null && request.pkgSetting.sharedUser != null
- && request.pkgSetting.sharedUser != result.pkgSetting.sharedUser) {
+ if (request.mPkgSetting != null && request.mPkgSetting.sharedUser != null
+ && request.mPkgSetting.sharedUser != result.mPkgSetting.sharedUser) {
// shared user changed, remove from old shared user
- request.pkgSetting.sharedUser.removePackage(request.pkgSetting);
+ request.mPkgSetting.sharedUser.removePackage(request.mPkgSetting);
}
- if (result.existingSettingCopied) {
- pkgSetting = request.pkgSetting;
- pkgSetting.updateFrom(result.pkgSetting);
+ if (result.mExistingSettingCopied) {
+ pkgSetting = request.mPkgSetting;
+ pkgSetting.updateFrom(result.mPkgSetting);
} else {
- pkgSetting = result.pkgSetting;
+ pkgSetting = result.mPkgSetting;
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
@@ -13999,14 +13878,15 @@ public class PackageManagerService extends IPackageManager.Stub
if (pkgSetting.sharedUser != null) {
pkgSetting.sharedUser.addPackage(pkgSetting);
}
- if (reconciledPkg.installArgs != null && reconciledPkg.installArgs.forceQueryableOverride) {
+ if (reconciledPkg.mInstallArgs != null
+ && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
pkgSetting.forceQueryableOverride = true;
}
// If this is part of a standard install, set the initiating package name, else rely on
// previous device state.
- if (reconciledPkg.installArgs != null) {
- InstallSource installSource = reconciledPkg.installArgs.installSource;
+ if (reconciledPkg.mInstallArgs != null) {
+ InstallSource installSource = reconciledPkg.mInstallArgs.mInstallSource;
if (installSource.initiatingPackageName != null) {
final PackageSetting ips = mSettings.getPackageLPr(
installSource.initiatingPackageName);
@@ -14033,20 +13913,20 @@ public class PackageManagerService extends IPackageManager.Stub
mTransferredPackages.add(pkg.getPackageName());
}
- if (reconciledPkg.collectedSharedLibraryInfos != null) {
+ if (reconciledPkg.mCollectedSharedLibraryInfos != null) {
executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
- reconciledPkg.collectedSharedLibraryInfos, allUsers);
+ reconciledPkg.mCollectedSharedLibraryInfos, allUsers);
}
final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- if (reconciledPkg.removeAppKeySetData) {
+ if (reconciledPkg.mRemoveAppKeySetData) {
ksms.removeAppKeySetDataLPw(pkg.getPackageName());
}
- if (reconciledPkg.sharedUserSignaturesChanged) {
+ if (reconciledPkg.mSharedUserSignaturesChanged) {
pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
- pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.signingDetails;
+ pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
}
- pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
+ pkgSetting.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
@@ -14235,7 +14115,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
-
/**
* Just scans the package without any side effects.
* <p>Not entirely true at the moment. There is still one side effect -- this
@@ -14256,17 +14135,16 @@ public class PackageManagerService extends IPackageManager.Stub
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- final UserManagerInternal userManager = injector.getUserManagerInternal();
- ParsedPackage parsedPackage = request.parsedPackage;
- PackageSetting pkgSetting = request.pkgSetting;
- final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
- final PackageSetting originalPkgSetting = request.originalPkgSetting;
- final @ParseFlags int parseFlags = request.parseFlags;
- final @ScanFlags int scanFlags = request.scanFlags;
- final String realPkgName = request.realPkgName;
- final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
- final UserHandle user = request.user;
- final boolean isPlatformPackage = request.isPlatformPackage;
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParseFlags int parseFlags = request.mParseFlags;
+ final @ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
List<String> changedAbiCodePath = null;
@@ -14302,15 +14180,25 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ boolean leavingSharedUser = false;
+
if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.sharedUser != null
- ? pkgSetting.sharedUser.name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
+ if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
+ leavingSharedUser = true;
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.sharedUser.name + " to " + "<nothing>.");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.sharedUser != null
+ ? pkgSetting.sharedUser.name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
}
String[] usesStaticLibraries = null;
@@ -14403,7 +14291,7 @@ public class PackageManagerService extends IPackageManager.Stub
configurePackageComponents(parsedPackage);
}
- final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
final File appLib32InstallDir = getAppLib32InstallDir();
@@ -14586,7 +14474,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,
+ !createNewPackage /* existingSettingCopied */,
+ leavingSharedUser /* needsNewAppId */, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
@@ -14644,7 +14533,7 @@ public class PackageManagerService extends IPackageManager.Stub
* Implementation detail: This method must NOT have any side effect. It would
* ideally be static, but, it requires locks to read system state.
*/
- private static void applyPolicy(ParsedPackage parsedPackage, final @ParseFlags int parseFlags,
+ private static void applyPolicy(ParsedPackage parsedPackage,
final @ScanFlags int scanFlags, AndroidPackage platformPkg,
boolean isUpdatedSystemApp) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
@@ -14686,8 +14575,8 @@ public class PackageManagerService extends IPackageManager.Stub
parsedPackage.setSignedWithPlatformKey(
(PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
|| (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().signatures,
- parsedPackage.getSigningDetails().signatures
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
) == PackageManager.SIGNATURE_MATCH))
);
@@ -14701,14 +14590,6 @@ public class PackageManagerService extends IPackageManager.Stub
PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
}
- private static @NonNull <T> T assertNotNull(@Nullable T object, String message)
- throws PackageManagerException {
- if (object == null) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, message);
- }
- return object;
- }
-
private <T extends ParsedMainComponent>
void assertPackageProcesses(AndroidPackage pkg, List<T> components,
Map<String, ParsedProcess> procs, String compName)
@@ -14983,7 +14864,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Exempt SharedUsers signed with the platform key.
PackageSetting platformPkgSetting = mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Apps that share a user with a " +
"privileged app must themselves be marked as privileged. " +
pkg.getPackageName() + " shares privileged user " +
@@ -15031,7 +14912,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting platformPkgSetting =
mSettings.getPackageLPr("android");
if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName()
+ " must target Q or later, "
@@ -15051,7 +14932,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSettings.getPackageLPr(pkg.getOverlayTarget());
if (targetPkgSetting != null) {
if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
// check reference signature
if (mOverlayConfigSignaturePackage == null) {
throw new PackageManagerException("Overlay "
@@ -15063,7 +14944,7 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting refPkgSetting =
mSettings.getPackageLPr(mOverlayConfigSignaturePackage);
if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().signatures)) {
+ pkg.getSigningDetails().getSignatures())) {
throw new PackageManagerException("Overlay "
+ pkg.getPackageName() + " signed with a different "
+ "certificate than both the reference package and "
@@ -15082,7 +14963,8 @@ public class PackageManagerService extends IPackageManager.Stub
int minSignatureSchemeVersion =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No signature found in package of version " + minSignatureSchemeVersion
+ " or newer for package " + pkg.getPackageName());
@@ -15092,19 +14974,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private boolean addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
+ private void addBuiltInSharedLibraryLocked(SystemConfig.SharedLibraryEntry entry) {
if (nonStaticSharedLibExistsLocked(entry.name)) {
- return false;
+ return;
}
SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null,
- entry.name, (long) SharedLibraryInfo.VERSION_UNDEFINED,
+ entry.name, SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_BUILTIN,
new VersionedPackage(PLATFORM_PACKAGE_NAME, (long)0), null, null,
entry.isNative);
commitSharedLibraryInfoLocked(libraryInfo);
- return true;
}
@GuardedBy("mLock")
@@ -15115,10 +14996,7 @@ public class PackageManagerService extends IPackageManager.Stub
private static boolean sharedLibExists(final String name, final long version,
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
WatchedLongSparseArray<SharedLibraryInfo> versionedLib = librarySource.get(name);
- if (versionedLib != null && versionedLib.indexOfKey(version) >= 0) {
- return true;
- }
- return false;
+ return versionedLib != null && versionedLib.indexOfKey(version) >= 0;
}
@GuardedBy("mLock")
@@ -15263,8 +15141,8 @@ public class PackageManagerService extends IPackageManager.Stub
ArrayList<AndroidPackage> clientLibPkgs = null;
// writer
synchronized (mLock) {
- if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) {
- for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) {
+ if (!ArrayUtils.isEmpty(reconciledPkg.mAllowedSharedLibraryInfos)) {
+ for (SharedLibraryInfo info : reconciledPkg.mAllowedSharedLibraryInfos) {
commitSharedLibraryInfoLocked(info);
}
final Map<String, AndroidPackage> combinedSigningDetails =
@@ -15284,8 +15162,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
- if (reconciledPkg.installResult != null) {
- reconciledPkg.installResult.libraryConsumers = clientLibPkgs;
+ if (reconciledPkg.mInstallResult != null) {
+ reconciledPkg.mInstallResult.mLibraryConsumers = clientLibPkgs;
}
if ((scanFlags & SCAN_BOOTING) != 0) {
@@ -15329,7 +15207,7 @@ public class PackageManagerService extends IPackageManager.Stub
mComponentResolver.addAllComponents(pkg, chatty);
final boolean isReplace =
- reconciledPkg.prepareResult != null && reconciledPkg.prepareResult.replace;
+ reconciledPkg.mPrepareResult != null && reconciledPkg.mPrepareResult.mReplace;
mAppsFilter.addPackage(pkgSetting, isReplace);
mPackageProperty.addAllProperties(pkg);
@@ -15439,7 +15317,7 @@ public class PackageManagerService extends IPackageManager.Stub
killApplication(pkgName, appId, UserHandle.USER_ALL, reason);
}
- private void killApplication(String pkgName, @AppIdInt int appId,
+ void killApplication(String pkgName, @AppIdInt int appId,
@UserIdInt int userId, String reason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
@@ -15458,7 +15336,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void removePackageLI(AndroidPackage pkg, boolean chatty) {
+ void removePackageLI(AndroidPackage pkg, boolean chatty) {
// Remove the parent package setting
PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null) {
@@ -15564,10 +15442,10 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
resolvedUserIds = userIds;
}
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
resolvedUserIds, false, broadcastAllowList, bOptions);
if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
- doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
+ doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
instantUserIds, true, null, bOptions);
}
} catch (RemoteException ex) {
@@ -15636,7 +15514,7 @@ public class PackageManagerService extends IPackageManager.Stub
* the system and applications allowed to see instant applications to receive package
* lifecycle events for instant applications.
*/
- private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras,
+ private void doSendBroadcast(String action, String pkg, Bundle extras,
int flags, String targetPkg, IIntentReceiver finishedReceiver,
int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
@Nullable Bundle bOptions) {
@@ -15677,109 +15555,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- /**
- * Check if the external storage media is available. This is true if there
- * is a mounted external storage medium or if the external storage is
- * emulated.
- */
- private boolean isExternalMediaAvailable() {
- return mMediaMounted || Environment.isExternalStorageEmulated();
- }
-
- /**
- * Ensure that the install reason matches what we know about the package installer (e.g. whether
- * it is acting on behalf on an enterprise or the user).
- *
- * Note that the ordering of the conditionals in this method is important. The checks we perform
- * are as follows, in this order:
- *
- * 1) If the install is being performed by a system app, we can trust the app to have set the
- * install reason correctly. Thus, we pass through the install reason unchanged, no matter
- * what it is.
- * 2) If the install is being performed by a device or profile owner app, the install reason
- * should be enterprise policy. However, we cannot be sure that the device or profile owner
- * set the install reason correctly. If the app targets an older SDK version where install
- * reasons did not exist yet, or if the app author simply forgot, the install reason may be
- * unset or wrong. Thus, we force the install reason to be enterprise policy.
- * 3) In all other cases, the install is being performed by a regular app that is neither part
- * of the system nor a device or profile owner. We have no reason to believe that this app is
- * acting on behalf of the enterprise admin. Thus, we check whether the install reason was
- * set to enterprise policy and if so, change it to unknown instead.
- */
- private int fixUpInstallReason(String installerPackageName, int installerUid,
- int installReason) {
- if (checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
- == PERMISSION_GRANTED) {
- // If the install is being performed by a system app, we trust that app to have set the
- // install reason correctly.
- return installReason;
- }
- final String ownerPackage = mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(
- UserHandle.getUserId(installerUid));
- if (ownerPackage != null && ownerPackage.equals(installerPackageName)) {
- // If the install is being performed by a device or profile owner, the install
- // reason should be enterprise policy.
- return PackageManager.INSTALL_REASON_POLICY;
- }
-
-
- if (installReason == PackageManager.INSTALL_REASON_POLICY) {
- // If the install is being performed by a regular app (i.e. neither system app nor
- // device or profile owner), we have no reason to believe that the app is acting on
- // behalf of an enterprise. If the app set the install reason to enterprise policy,
- // change it to unknown instead.
- return PackageManager.INSTALL_REASON_UNKNOWN;
- }
-
- // If the install is being performed by a regular app and the install reason was set to any
- // value but enterprise policy, leave the install reason unchanged.
- return installReason;
- }
-
- void installStage(InstallParams params) {
- final Message msg = mHandler.obtainMessage(INIT_COPY);
- params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
- msg.obj = params;
-
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
- System.identityHashCode(msg.obj));
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(msg.obj));
-
- mHandler.sendMessage(msg);
- }
-
- void installStage(InstallParams parent, List<InstallParams> children)
- throws PackageManagerException {
- final Message msg = mHandler.obtainMessage(INIT_COPY);
- final MultiPackageInstallParams params =
- new MultiPackageInstallParams(parent, children);
- params.setTraceMethod("installStageMultiPackage")
- .setTraceCookie(System.identityHashCode(params));
- msg.obj = params;
-
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStageMultiPackage",
- System.identityHashCode(msg.obj));
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(msg.obj));
- mHandler.sendMessage(msg);
- }
-
- void verifyStage(VerificationParams params) {
- mHandler.post(()-> {
- params.startCopy();
- });
- }
-
- void verifyStage(VerificationParams parent, List<VerificationParams> children)
- throws PackageManagerException {
- final MultiPackageVerificationParams params =
- new MultiPackageVerificationParams(parent, children);
- mHandler.post(()-> {
- params.startCopy();
- });
- }
-
private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
int userId, int dataLoaderType) {
final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
@@ -16024,11 +15799,11 @@ public class PackageManagerService extends IPackageManager.Stub
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
- info.removedPackage = packageName;
- info.installerPackageName = pkgSetting.installSource.installerPackageName;
- info.removedUsers = new int[] {userId};
- info.broadcastUsers = new int[] {userId};
- info.uid = UserHandle.getUid(userId, pkgSetting.appId);
+ info.mRemovedPackage = packageName;
+ info.mInstallerPackageName = pkgSetting.installSource.installerPackageName;
+ info.mRemovedUsers = new int[] {userId};
+ info.mBroadcastUsers = new int[] {userId};
+ info.mUid = UserHandle.getUid(userId, pkgSetting.appId);
info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
}
@@ -16228,17 +16003,17 @@ public class PackageManagerService extends IPackageManager.Stub
updateSequenceNumberLP(pkgSetting, new int[]{ userId });
}
// start async restore with no post-install since we finish install here
- PackageInstalledInfo res =
- createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED);
- res.pkg = pkgSetting.pkg;
- res.newUsers = new int[]{ userId };
+ PackageInstalledInfo res = new PackageInstalledInfo(
+ PackageManager.INSTALL_SUCCEEDED);
+ res.mPkg = pkgSetting.pkg;
+ res.mNewUsers = new int[]{ userId };
PostInstallData postInstallData =
new PostInstallData(null, res, () -> {
restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
- pkgSetting.getInstallReason(userId), userId);
+ userId);
if (intentSender != null) {
- onRestoreComplete(res.returnCode, mContext, intentSender);
+ onRestoreComplete(res.mReturnCode, mContext, intentSender);
}
});
restoreAndPostInstall(userId, res, postInstallData);
@@ -16513,7 +16288,7 @@ public class PackageManagerService extends IPackageManager.Stub
} else {
intentExtras = null;
}
- doSendBroadcast(am, action, null, intentExtras,
+ doSendBroadcast(action, null, intentExtras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
targetUserIds, false, null, null);
}
@@ -16813,7 +16588,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private void broadcastPackageVerified(int verificationId, Uri packageUri,
+ void broadcastPackageVerified(int verificationId, Uri packageUri,
int verificationCode, @Nullable String rootHashString, int dataLoaderType,
UserHandle user) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED);
@@ -16830,101 +16605,6 @@ public class PackageManagerService extends IPackageManager.Stub
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT);
}
- private ComponentName matchComponentForVerifier(String packageName,
- List<ResolveInfo> receivers) {
- ActivityInfo targetReceiver = null;
-
- final int NR = receivers.size();
- for (int i = 0; i < NR; i++) {
- final ResolveInfo info = receivers.get(i);
- if (info.activityInfo == null) {
- continue;
- }
-
- if (packageName.equals(info.activityInfo.packageName)) {
- targetReceiver = info.activityInfo;
- break;
- }
- }
-
- if (targetReceiver == null) {
- return null;
- }
-
- return new ComponentName(targetReceiver.packageName, targetReceiver.name);
- }
-
- private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
- List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
- if (pkgInfo.verifiers.length == 0) {
- return null;
- }
-
- final int N = pkgInfo.verifiers.length;
- final List<ComponentName> sufficientVerifiers = new ArrayList<>(N + 1);
- for (int i = 0; i < N; i++) {
- final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
-
- final ComponentName comp = matchComponentForVerifier(verifierInfo.packageName,
- receivers);
- if (comp == null) {
- continue;
- }
-
- final int verifierUid = getUidForVerifier(verifierInfo);
- if (verifierUid == -1) {
- continue;
- }
-
- if (DEBUG_VERIFY) {
- Slog.d(TAG, "Added sufficient verifier " + verifierInfo.packageName
- + " with the correct signature");
- }
- sufficientVerifiers.add(comp);
- verificationState.addSufficientVerifier(verifierUid);
- }
-
- return sufficientVerifiers;
- }
-
- private int getUidForVerifier(VerifierInfo verifierInfo) {
- synchronized (mLock) {
- final AndroidPackage pkg = mPackages.get(verifierInfo.packageName);
- if (pkg == null) {
- return -1;
- } else if (pkg.getSigningDetails().signatures.length != 1) {
- Slog.i(TAG, "Verifier package " + verifierInfo.packageName
- + " has more than one signature; ignoring");
- return -1;
- }
-
- /*
- * If the public key of the package's signature does not match
- * our expected public key, then this is a different package and
- * we should skip.
- */
-
- final byte[] expectedPublicKey;
- try {
- final Signature verifierSig = pkg.getSigningDetails().signatures[0];
- final PublicKey publicKey = verifierSig.getPublicKey();
- expectedPublicKey = publicKey.getEncoded();
- } catch (CertificateException e) {
- return -1;
- }
-
- final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
-
- if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
- Slog.i(TAG, "Verifier package " + verifierInfo.packageName
- + " does not have the expected public key; ignoring");
- return -1;
- }
-
- return pkg.getUid();
- }
- }
-
private void setEnableRollbackCode(int token, int enableRollbackCode) {
final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS);
msg.arg1 = token;
@@ -16951,7 +16631,7 @@ public class PackageManagerService extends IPackageManager.Stub
*
* @return verification timeout in milliseconds
*/
- private long getVerificationTimeout() {
+ long getVerificationTimeout() {
long timeout = Global.getLong(mContext.getContentResolver(),
Global.PACKAGE_VERIFIER_TIMEOUT, DEFAULT_VERIFICATION_TIMEOUT);
// The setting can be used to increase the timeout but not decrease it, since that is
@@ -16959,18 +16639,6 @@ public class PackageManagerService extends IPackageManager.Stub
return Math.max(timeout, DEFAULT_VERIFICATION_TIMEOUT);
}
- /**
- * Get the integrity verification timeout.
- *
- * @return verification timeout in milliseconds
- */
- private long getIntegrityVerificationTimeout() {
- long timeout = Global.getLong(mContext.getContentResolver(),
- Global.APP_INTEGRITY_VERIFICATION_TIMEOUT, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
- // The setting can be used to increase the timeout but not decrease it, since that is
- // equivalent to disabling the integrity component.
- return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
- }
/**
* Get the default verification agent response code.
@@ -16995,65 +16663,6 @@ public class PackageManagerService extends IPackageManager.Stub
return PackageManager.VERIFICATION_REJECT;
}
- /**
- * Check whether or not package verification has been enabled.
- *
- * @return true if verification should be performed
- */
- private boolean isVerificationEnabled(
- PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
- if (!DEFAULT_VERIFY_ENABLE) {
- return false;
- }
-
- // Check if installing from ADB
- if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
- if (isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
- return true;
- }
- // Check if the developer wants to skip verification for ADB installs
- if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
- synchronized (mLock) {
- if (mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
- // Always verify fresh install
- return true;
- }
- }
- // Only skip when apk is debuggable
- return !pkgInfoLite.debuggable;
- }
- return Global.getInt(mContext.getContentResolver(),
- Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
- }
-
- // only when not installed from ADB, skip verification for instant apps when
- // the installer and verifier are the same.
- if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
- if (mInstantAppInstallerActivity != null
- && mInstantAppInstallerActivity.packageName.equals(
- mRequiredVerifierPackage)) {
- try {
- mInjector.getSystemService(AppOpsManager.class)
- .checkPackage(installerUid, mRequiredVerifierPackage);
- if (DEBUG_VERIFY) {
- Slog.i(TAG, "disable verification for instant app");
- }
- return false;
- } catch (SecurityException ignore) { }
- }
- }
- return true;
- }
-
- /**
- * Check whether or not integrity verification has been enabled.
- */
- private boolean isIntegrityVerificationEnabled() {
- // We are not exposing this as a user-configurable setting because we don't want to provide
- // an easy way to get around the integrity check.
- return DEFAULT_INTEGRITY_VERIFY_ENABLE;
- }
-
@Deprecated
@Override
public void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {
@@ -17170,9 +16779,10 @@ public class PackageManagerService extends IPackageManager.Stub
if (obj != null) {
if (obj instanceof SharedUserSetting) {
callerSignature =
- ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+ ((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
} else if (obj instanceof PackageSetting) {
- callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
+ callerSignature =
+ ((PackageSetting) obj).signatures.mSigningDetails.getSignatures();
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
@@ -17184,7 +16794,7 @@ public class PackageManagerService extends IPackageManager.Stub
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.signatures.mSigningDetails.signatures)
+ installerPackageSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -17201,7 +16811,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.signatures.mSigningDetails.signatures)
+ targetInstallerPkgSetting.signatures.mSigningDetails.getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
@@ -17265,124 +16875,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- // Queue up an async operation since the package installation may take a little while.
- private void processInstallRequestsAsync(boolean success,
- List<InstallRequest> installRequests) {
- mHandler.post(() -> {
- List<InstallRequest> apexInstallRequests = new ArrayList<>();
- List<InstallRequest> apkInstallRequests = new ArrayList<>();
- for (InstallRequest request : installRequests) {
- if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
- apexInstallRequests.add(request);
- } else {
- apkInstallRequests.add(request);
- }
- }
- // Note: supporting multi package install of both APEXes and APKs might requir some
- // thinking to ensure atomicity of the install.
- if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
- // This should've been caught at the validation step, but for some reason wasn't.
- throw new IllegalStateException(
- "Attempted to do a multi package install of both APEXes and APKs");
- }
- if (!apexInstallRequests.isEmpty()) {
- if (success) {
- // Since installApexPackages requires talking to external service (apexd), we
- // schedule to run it async. Once it finishes, it will resume the install.
- Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
- "installApexPackages");
- t.start();
- } else {
- // Non-staged APEX installation failed somewhere before
- // processInstallRequestAsync. In that case just notify the observer about the
- // failure.
- InstallRequest request = apexInstallRequests.get(0);
- notifyInstallObserver(request.installResult, request.args.observer);
- }
- return;
- }
- if (success) {
- for (InstallRequest request : apkInstallRequests) {
- request.args.doPreInstall(request.installResult.returnCode);
- }
- synchronized (mInstallLock) {
- installPackagesTracedLI(apkInstallRequests);
- }
- for (InstallRequest request : apkInstallRequests) {
- request.args.doPostInstall(
- request.installResult.returnCode, request.installResult.uid);
- }
- }
- for (InstallRequest request : apkInstallRequests) {
- restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
- new PostInstallData(request.args, request.installResult, null));
- }
- });
- }
-
- private void installApexPackagesTraced(List<InstallRequest> requests) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
- installApexPackages(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private void installApexPackages(List<InstallRequest> requests) {
- if (requests.isEmpty()) {
- return;
- }
- if (requests.size() != 1) {
- throw new IllegalStateException(
- "Only a non-staged install of a single APEX is supported");
- }
- InstallRequest request = requests.get(0);
- try {
- // Should directory scanning logic be moved to ApexManager for better test coverage?
- final File dir = request.args.origin.resolvedFile;
- final File[] apexes = dir.listFiles();
- if (apexes == null) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- dir.getAbsolutePath() + " is not a directory");
- }
- if (apexes.length != 1) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Expected exactly one .apex file under " + dir.getAbsolutePath()
- + " got: " + apexes.length);
- }
- try (PackageParser2 packageParser = mInjector.getScanningPackageParser()) {
- mApexManager.installPackage(apexes[0], packageParser);
- }
- } catch (PackageManagerException e) {
- request.installResult.setError("APEX installation failed", e);
- }
- invalidatePackageInfoCache();
- notifyInstallObserver(request.installResult, request.args.observer);
- }
-
- private PackageInstalledInfo createPackageInstalledInfo(
- int currentStatus) {
- PackageInstalledInfo res = new PackageInstalledInfo();
- res.setReturnCode(currentStatus);
- res.uid = -1;
- res.pkg = null;
- res.removedInfo = null;
- return res;
- }
-
/** @param data Post-install is performed only if this is non-null. */
- private void restoreAndPostInstall(
+ void restoreAndPostInstall(
int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
if (DEBUG_INSTALL) {
- Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.pkg);
+ Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg);
}
// A restore should be requested at this point if (a) the install
// succeeded, (b) the operation is not an update.
- final boolean update = res.removedInfo != null
- && res.removedInfo.removedPackage != null;
- boolean doRestore = !update && res.pkg != null;
+ final boolean update = res.mRemovedInfo != null
+ && res.mRemovedInfo.mRemovedPackage != null;
+ boolean doRestore = !update && res.mPkg != null;
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
@@ -17398,13 +16902,13 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
+ if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
- if (res.freezer != null) {
- res.freezer.close();
+ if (res.mFreezer != null) {
+ res.mFreezer.close();
}
doRestore = performBackupManagerRestore(userId, token, res);
}
@@ -17414,7 +16918,7 @@ public class PackageManagerService extends IPackageManager.Stub
// need to be snapshotted or restored for the package.
//
// TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
+ if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
doRestore = performRollbackManagerRestore(userId, token, res, data);
}
@@ -17450,7 +16954,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
if (bm.isUserReadyForBackup(userId)) {
bm.restoreAtInstallForUser(
- userId, res.pkg.getPackageName(), token);
+ userId, res.mPkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
+ "didn't take place.");
@@ -17477,7 +16981,7 @@ public class PackageManagerService extends IPackageManager.Stub
PostInstallData data) {
RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
- final String packageName = res.pkg.getPackageName();
+ final String packageName = res.mPkg.getPackageName();
final int[] allUsers = mUserManager.getUserIds();
final int[] installedUsers;
@@ -17497,11 +17001,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
boolean doSnapshotOrRestore = data != null && data.args != null
- && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
- || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+ && ((data.args.mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+ || (data.args.mInstallFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
if (ps != null && doSnapshotOrRestore) {
- final String seInfo = AndroidPackageUtils.getSeInfo(res.pkg, ps);
+ final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
appId, ceDataInode, seInfo, token);
return true;
@@ -17529,13 +17033,13 @@ public class PackageManagerService extends IPackageManager.Stub
mHandler.post(() -> {
for (int i = 0; i < mRunningInstalls.size(); i++) {
final PostInstallData data = mRunningInstalls.valueAt(i);
- if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ if (data.res.mReturnCode != PackageManager.INSTALL_SUCCEEDED) {
continue;
}
- if (packageName.equals(data.res.pkg.getPackageName())) {
+ if (packageName.equals(data.res.mPkg.getPackageName())) {
// right package; but is it for the right user?
- for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
- if (userId == data.res.newUsers[uIndex]) {
+ for (int uIndex = 0; uIndex < data.res.mNewUsers.length; uIndex++) {
+ if (userId == data.res.mNewUsers[uIndex]) {
if (DEBUG_BACKUP) {
Slog.i(TAG, "Package " + packageName
+ " being restored so deferring FIRST_LAUNCH");
@@ -17562,1391 +17066,12 @@ public class PackageManagerService extends IPackageManager.Stub
installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
}
- private abstract class HandlerParams {
- /** User handle for the user requesting the information or installation. */
- private final UserHandle mUser;
- String traceMethod;
- int traceCookie;
-
- HandlerParams(UserHandle user) {
- mUser = user;
- }
-
- UserHandle getUser() {
- return mUser;
- }
-
- HandlerParams setTraceMethod(String traceMethod) {
- this.traceMethod = traceMethod;
- return this;
- }
-
- HandlerParams setTraceCookie(int traceCookie) {
- this.traceCookie = traceCookie;
- return this;
- }
-
- final void startCopy() {
- if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
- handleStartCopy();
- handleReturnCode();
- }
-
- abstract void handleStartCopy();
- abstract void handleReturnCode();
- }
-
- static class OriginInfo {
- /**
- * Location where install is coming from, before it has been
- * copied/renamed into place. This could be a single monolithic APK
- * file, or a cluster directory. This location may be untrusted.
- */
- final File file;
-
- /**
- * Flag indicating that {@link #file} has already been staged, meaning downstream users
- * don't need to defensively copy the contents.
- */
- final boolean staged;
-
- /**
- * Flag indicating that {@link #file} is an already installed app that is being moved.
- */
- final boolean existing;
-
- final String resolvedPath;
- final File resolvedFile;
-
- static OriginInfo fromNothing() {
- return new OriginInfo(null, false, false);
- }
-
- static OriginInfo fromUntrustedFile(File file) {
- return new OriginInfo(file, false, false);
- }
-
- static OriginInfo fromExistingFile(File file) {
- return new OriginInfo(file, false, true);
- }
-
- static OriginInfo fromStagedFile(File file) {
- return new OriginInfo(file, true, false);
- }
-
- private OriginInfo(File file, boolean staged, boolean existing) {
- this.file = file;
- this.staged = staged;
- this.existing = existing;
-
- if (file != null) {
- resolvedPath = file.getAbsolutePath();
- resolvedFile = file;
- } else {
- resolvedPath = null;
- resolvedFile = null;
- }
- }
- }
-
- static class MoveInfo {
- final int moveId;
- final String fromUuid;
- final String toUuid;
- final String packageName;
- final int appId;
- final String seinfo;
- final int targetSdkVersion;
- final String fromCodePath;
-
- public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
- int appId, String seinfo, int targetSdkVersion,
- String fromCodePath) {
- this.moveId = moveId;
- this.fromUuid = fromUuid;
- this.toUuid = toUuid;
- this.packageName = packageName;
- this.appId = appId;
- this.seinfo = seinfo;
- this.targetSdkVersion = targetSdkVersion;
- this.fromCodePath = fromCodePath;
- }
- }
-
- static class VerificationInfo {
- /** A constant used to indicate that a uid value is not present. */
- public static final int NO_UID = -1;
-
- /** URI referencing where the package was downloaded from. */
- final Uri originatingUri;
-
- /** HTTP referrer URI associated with the originatingURI. */
- final Uri referrer;
-
- /** UID of the application that the install request originated from. */
- final int originatingUid;
-
- /** UID of application requesting the install */
- final int installerUid;
-
- VerificationInfo(Uri originatingUri, Uri referrer, int originatingUid, int installerUid) {
- this.originatingUri = originatingUri;
- this.referrer = referrer;
- this.originatingUid = originatingUid;
- this.installerUid = installerUid;
- }
- }
-
- /**
- * Container for a multi-package install which refers to all install sessions and args being
- * committed together.
- */
- class MultiPackageInstallParams extends HandlerParams {
- private final List<InstallParams> mChildParams;
- private final Map<InstallArgs, Integer> mCurrentState;
-
- MultiPackageInstallParams(InstallParams parent, List<InstallParams> childParams)
- throws PackageManagerException {
- super(parent.getUser());
- if (childParams.size() == 0) {
- throw new PackageManagerException("No child sessions found!");
- }
- mChildParams = childParams;
- for (int i = 0; i < childParams.size(); i++) {
- final InstallParams childParam = childParams.get(i);
- childParam.mParentInstallParams = this;
- }
- this.mCurrentState = new ArrayMap<>(mChildParams.size());
- }
-
- @Override
- void handleStartCopy() {
- for (InstallParams params : mChildParams) {
- params.handleStartCopy();
- }
- }
-
- @Override
- void handleReturnCode() {
- for (InstallParams params : mChildParams) {
- params.handleReturnCode();
- }
- }
-
- void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
- mCurrentState.put(args, currentStatus);
- if (mCurrentState.size() != mChildParams.size()) {
- return;
- }
- int completeStatus = PackageManager.INSTALL_SUCCEEDED;
- for (Integer status : mCurrentState.values()) {
- if (status == PackageManager.INSTALL_UNKNOWN) {
- return;
- } else if (status != PackageManager.INSTALL_SUCCEEDED) {
- completeStatus = status;
- break;
- }
- }
- final List<InstallRequest> installRequests = new ArrayList<>(mCurrentState.size());
- for (Map.Entry<InstallArgs, Integer> entry : mCurrentState.entrySet()) {
- installRequests.add(new InstallRequest(entry.getKey(),
- createPackageInstalledInfo(completeStatus)));
- }
- processInstallRequestsAsync(
- completeStatus == PackageManager.INSTALL_SUCCEEDED,
- installRequests);
- }
- }
-
- class InstallParams extends HandlerParams {
- final OriginInfo origin;
- final MoveInfo move;
- final IPackageInstallObserver2 observer;
- int installFlags;
- @NonNull final InstallSource installSource;
- final String volumeUuid;
- int mRet;
- final String packageAbiOverride;
- final String[] grantedRuntimePermissions;
- final List<String> whitelistedRestrictedPermissions;
- final int autoRevokePermissionsMode;
- final PackageParser.SigningDetails signingDetails;
- final int installReason;
- final int mInstallScenario;
- @Nullable MultiPackageInstallParams mParentInstallParams;
- final boolean forceQueryableOverride;
- final int mDataLoaderType;
- final long requiredInstalledVersionCode;
- final PackageLite mPackageLite;
-
- InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String packageAbiOverride, PackageLite packageLite) {
- super(user);
- this.origin = origin;
- this.move = move;
- this.observer = observer;
- this.installFlags = installFlags;
- this.installSource = Preconditions.checkNotNull(installSource);
- this.volumeUuid = volumeUuid;
- this.packageAbiOverride = packageAbiOverride;
-
- this.grantedRuntimePermissions = null;
- this.whitelistedRestrictedPermissions = null;
- this.autoRevokePermissionsMode = MODE_DEFAULT;
- this.signingDetails = PackageParser.SigningDetails.UNKNOWN;
- this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
- this.mInstallScenario = PackageManager.INSTALL_SCENARIO_DEFAULT;
- this.forceQueryableOverride = false;
- this.mDataLoaderType = DataLoaderType.NONE;
- this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
- this.mPackageLite = packageLite;
- }
-
- InstallParams(File stagedDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams sessionParams, InstallSource installSource,
- UserHandle user, SigningDetails signingDetails, int installerUid,
- PackageLite packageLite) {
- super(user);
- origin = OriginInfo.fromStagedFile(stagedDir);
- move = null;
- installReason = fixUpInstallReason(
- installSource.installerPackageName, installerUid, sessionParams.installReason);
- mInstallScenario = sessionParams.installScenario;
- this.observer = observer;
- installFlags = sessionParams.installFlags;
- this.installSource = installSource;
- volumeUuid = sessionParams.volumeUuid;
- packageAbiOverride = sessionParams.abiOverride;
- grantedRuntimePermissions = sessionParams.grantedRuntimePermissions;
- whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
- autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
- this.signingDetails = signingDetails;
- forceQueryableOverride = sessionParams.forceQueryableOverride;
- mDataLoaderType = (sessionParams.dataLoaderParams != null)
- ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
- requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
- mPackageLite = packageLite;
- }
-
- @Override
- public String toString() {
- return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
- + " file=" + origin.file + "}";
- }
-
- private int installLocationPolicy(PackageInfoLite pkgLite) {
- String packageName = pkgLite.packageName;
- int installLocation = pkgLite.installLocation;
- // reader
- synchronized (mLock) {
- // Currently installed package which the new package is attempting to replace or
- // null if no such package is installed.
- AndroidPackage installedPkg = mPackages.get(packageName);
-
- if (installedPkg != null) {
- if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- // Check for updated system application.
- if (installedPkg.isSystem()) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else {
- // If current upgrade specifies particular preference
- if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- // Application explicitly specified internal.
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- // App explictly prefers external. Let policy decide
- } else {
- // Prefer previous location
- if (installedPkg.isExternalStorage()) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- }
- } else {
- // Invalid install. Return error code
- return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
- }
- }
- }
- return pkgLite.recommendedInstallLocation;
- }
-
- /**
- * Override install location based on default policy if needed.
- *
- * Only {@link #installFlags} may mutate in this method.
- *
- * Only {@link PackageManager#INSTALL_INTERNAL} flag may mutate in
- * {@link #installFlags}
- */
- private int overrideInstallLocation(PackageInfoLite pkgLite) {
- final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- if (DEBUG_INSTANT && ephemeral) {
- Slog.v(TAG, "pkgLite for install: " + pkgLite);
- }
-
- if (origin.staged) {
- // If we're already staged, we've firmly committed to an install location
- if (origin.file != null) {
- installFlags |= PackageManager.INSTALL_INTERNAL;
- } else {
- throw new IllegalStateException("Invalid stage location");
- }
- } else if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- /*
- * If we are not staged and have too little free space, try to free cache
- * before giving up.
- */
- // TODO: focus freeing disk space on the target device
- final StorageManager storage = StorageManager.from(mContext);
- final long lowThreshold = storage.getStorageLowBytes(
- Environment.getDataDirectory());
-
- final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
- origin.resolvedPath, packageAbiOverride);
- if (sizeBytes >= 0) {
- try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
- pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- mPackageLite, origin.resolvedPath, installFlags,
- packageAbiOverride);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to free cache", e);
- }
- }
-
- /*
- * The cache free must have deleted the file we downloaded to install.
- *
- * TODO: fix the "freeCache" call to not delete the file we care about.
- */
- if (pkgLite.recommendedInstallLocation
- == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- pkgLite.recommendedInstallLocation
- = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
- }
- }
-
- int ret = INSTALL_SUCCEEDED;
- int loc = pkgLite.recommendedInstallLocation;
- if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
- ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
- ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
- ret = PackageManager.INSTALL_FAILED_INVALID_APK;
- } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
- ret = PackageManager.INSTALL_FAILED_INVALID_URI;
- } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
- ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
- } else {
- // Override with defaults if needed.
- loc = installLocationPolicy(pkgLite);
-
- final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
-
- if (!onInt) {
- // Override install location with flags
- if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
- // Set the flag to install on external media.
- installFlags &= ~PackageManager.INSTALL_INTERNAL;
- } else {
- // Make sure the flag for installing on external
- // media is unset
- installFlags |= PackageManager.INSTALL_INTERNAL;
- }
- }
- }
- return ret;
- }
-
- /*
- * Invoke remote method to get package information and install
- * location values. Override install location based on default
- * policy if needed and then create install arguments based
- * on the install location.
- */
- public void handleStartCopy() {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- mRet = INSTALL_SUCCEEDED;
- return;
- }
- PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
-
- // For staged session, there is a delay between its verification and install. Device
- // state can change within this delay and hence we need to re-verify certain conditions.
- boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
- if (isStaged) {
- mRet = verifyReplacingVersionCode(
- pkgLite, requiredInstalledVersionCode, installFlags);
- if (mRet != INSTALL_SUCCEEDED) {
- return;
- }
- }
-
- mRet = overrideInstallLocation(pkgLite);
- }
-
- @Override
- void handleReturnCode() {
- processPendingInstall();
- }
-
- private void processPendingInstall() {
- InstallArgs args = createInstallArgs(this);
- if (mRet == PackageManager.INSTALL_SUCCEEDED) {
- mRet = args.copyApk();
- }
- if (mRet == PackageManager.INSTALL_SUCCEEDED) {
- F2fsUtils.releaseCompressedBlocks(
- mContext.getContentResolver(), new File(args.getCodePath()));
- }
- if (mParentInstallParams != null) {
- mParentInstallParams.tryProcessInstallRequest(args, mRet);
- } else {
- PackageInstalledInfo res = createPackageInstalledInfo(mRet);
- processInstallRequestsAsync(
- res.returnCode == PackageManager.INSTALL_SUCCEEDED,
- Collections.singletonList(new InstallRequest(args, res)));
- }
- }
- }
-
- /**
- * Container for a multi-package install which refers to all install sessions and args being
- * committed together.
- */
- class MultiPackageVerificationParams extends HandlerParams {
- private final IPackageInstallObserver2 mObserver;
- private final List<VerificationParams> mChildParams;
- private final Map<VerificationParams, Integer> mVerificationState;
-
- MultiPackageVerificationParams(
- VerificationParams parent,
- List<VerificationParams> children)
- throws PackageManagerException {
- super(parent.getUser());
- if (children.size() == 0) {
- throw new PackageManagerException("No child sessions found!");
- }
- mChildParams = children;
- // Provide every child with reference to this object as parent
- for (int i = 0; i < children.size(); i++) {
- final VerificationParams childParams = children.get(i);
- childParams.mParentVerificationParams = this;
- }
- this.mVerificationState = new ArrayMap<>(mChildParams.size());
- mObserver = parent.observer;
- }
-
- @Override
- void handleStartCopy() {
- for (VerificationParams params : mChildParams) {
- params.handleStartCopy();
- }
- }
-
- @Override
- void handleReturnCode() {
- for (VerificationParams params : mChildParams) {
- params.handleReturnCode();
- }
- }
-
- void trySendVerificationCompleteNotification(VerificationParams child, int currentStatus) {
- mVerificationState.put(child, currentStatus);
- if (mVerificationState.size() != mChildParams.size()) {
- return;
- }
- int completeStatus = PackageManager.INSTALL_SUCCEEDED;
- for (Integer status : mVerificationState.values()) {
- if (status == PackageManager.INSTALL_UNKNOWN) {
- return;
- } else if (status != PackageManager.INSTALL_SUCCEEDED) {
- completeStatus = status;
- break;
- }
- }
- try {
- mObserver.onPackageInstalled(null, completeStatus,
- "Package Verification Result", new Bundle());
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
- }
- }
-
- class VerificationParams extends HandlerParams {
- final OriginInfo origin;
- final IPackageInstallObserver2 observer;
- final int installFlags;
- @NonNull final InstallSource installSource;
- final String packageAbiOverride;
- final VerificationInfo verificationInfo;
- final PackageParser.SigningDetails signingDetails;
- @Nullable MultiPackageVerificationParams mParentVerificationParams;
- final long requiredInstalledVersionCode;
- final int mDataLoaderType;
- final int mSessionId;
-
- private boolean mWaitForVerificationToComplete;
- private boolean mWaitForIntegrityVerificationToComplete;
- private boolean mWaitForEnableRollbackToComplete;
- private int mRet;
-
- final PackageLite mPackageLite;
-
- VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams sessionParams, InstallSource installSource,
- int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite) {
- super(user);
- origin = OriginInfo.fromStagedFile(stagedDir);
- this.observer = observer;
- installFlags = sessionParams.installFlags;
- this.installSource = installSource;
- packageAbiOverride = sessionParams.abiOverride;
- verificationInfo = new VerificationInfo(
- sessionParams.originatingUri,
- sessionParams.referrerUri,
- sessionParams.originatingUid,
- installerUid
- );
- this.signingDetails = signingDetails;
- requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
- mDataLoaderType = (sessionParams.dataLoaderParams != null)
- ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
- mSessionId = sessionId;
- mPackageLite = lite;
- }
-
- @Override
- public String toString() {
- return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
- + " file=" + origin.file + "}";
- }
-
- public void handleStartCopy() {
- if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
- // Apex packages get verified in StagingManager currently.
- // TODO(b/136257624): Move apex verification logic out of StagingManager
- mRet = INSTALL_SUCCEEDED;
- return;
- }
-
- PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
- mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
-
- mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
- if (mRet != INSTALL_SUCCEEDED) {
- return;
- }
-
- // Perform package verification and enable rollback (unless we are simply moving the
- // package).
- if (!origin.existing) {
- sendApkVerificationRequest(pkgLite);
- if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
- sendEnableRollbackRequest();
- }
- }
- }
-
- void sendApkVerificationRequest(PackageInfoLite pkgLite) {
- final int verificationId = mPendingVerificationToken++;
-
- PackageVerificationState verificationState =
- new PackageVerificationState(this);
- mPendingVerification.append(verificationId, verificationState);
-
- sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
- mRet = sendPackageVerificationRequest(
- verificationId, pkgLite, verificationState);
-
- // If both verifications are skipped, we should remove the state.
- if (verificationState.areAllVerificationsComplete()) {
- mPendingVerification.remove(verificationId);
- }
- }
-
- void sendEnableRollbackRequest() {
- final int enableRollbackToken = mPendingEnableRollbackToken++;
- Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
- mPendingEnableRollback.append(enableRollbackToken, this);
-
- Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
- enableRollbackIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
- enableRollbackToken);
- enableRollbackIntent.putExtra(
- PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
- mSessionId);
- enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
- enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Allow the broadcast to be sent before boot complete.
- // This is needed when committing the apk part of a staged
- // session in early boot. The rollback manager registers
- // its receiver early enough during the boot process that
- // it will not miss the broadcast.
- enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- mContext.sendOrderedBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
- android.Manifest.permission.PACKAGE_ROLLBACK_AGENT,
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // the duration to wait for rollback to be enabled, in millis
- long rollbackTimeout = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
- if (rollbackTimeout < 0) {
- rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
- }
- final Message msg = mHandler.obtainMessage(
- ENABLE_ROLLBACK_TIMEOUT);
- msg.arg1 = enableRollbackToken;
- msg.arg2 = mSessionId;
- mHandler.sendMessageDelayed(msg, rollbackTimeout);
- }
- }, null, 0, null, null);
-
- mWaitForEnableRollbackToComplete = true;
- }
-
- /**
- * Send a request to check the integrity of the package.
- */
- void sendIntegrityVerificationRequest(
- int verificationId,
- PackageInfoLite pkgLite,
- PackageVerificationState verificationState) {
- if (!isIntegrityVerificationEnabled()) {
- // Consider the integrity check as passed.
- verificationState.setIntegrityVerificationResult(
- PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- return;
- }
-
- final Intent integrityVerification =
- new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
-
- integrityVerification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
- PACKAGE_MIME_TYPE);
-
- final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND;
- integrityVerification.addFlags(flags);
-
- integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
- integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
- integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
- integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
- populateInstallerExtras(integrityVerification);
-
- // send to integrity component only.
- integrityVerification.setPackage("android");
-
- final BroadcastOptions options = BroadcastOptions.makeBasic();
-
- mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
- /* receiverPermission= */ null,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Message msg =
- mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
- msg.arg1 = verificationId;
- mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
- }
- }, /* scheduler= */ null,
- /* initialCode= */ 0,
- /* initialData= */ null,
- /* initialExtras= */ null);
-
- Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId);
-
- // stop the copy until verification succeeds.
- mWaitForIntegrityVerificationToComplete = true;
- }
-
- /**
- * Send a request to verifier(s) to verify the package if necessary, and return
- * {@link PackageManager#INSTALL_SUCCEEDED} if succeeded.
- */
- int sendPackageVerificationRequest(
- int verificationId,
- PackageInfoLite pkgLite,
- PackageVerificationState verificationState) {
- int ret = INSTALL_SUCCEEDED;
-
- // TODO: http://b/22976637
- // Apps installed for "all" users use the device owner to verify the app
- UserHandle verifierUser = getUser();
- if (verifierUser == UserHandle.ALL) {
- verifierUser = UserHandle.SYSTEM;
- }
-
- /*
- * Determine if we have any installed package verifiers. If we
- * do, then we'll defer to them to verify the packages.
- */
- final int requiredUid = mRequiredVerifierPackage == null ? -1
- : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
- verifierUser.getIdentifier());
- verificationState.setRequiredVerifierUid(requiredUid);
- final int installerUid =
- verificationInfo == null ? -1 : verificationInfo.installerUid;
- final boolean isVerificationEnabled = isVerificationEnabled(
- pkgLite, verifierUser.getIdentifier(), installFlags, installerUid);
- final boolean isV4Signed =
- (signingDetails.signatureSchemeVersion == SIGNING_BLOCK_V4);
- final boolean isIncrementalInstall =
- (mDataLoaderType == DataLoaderType.INCREMENTAL);
- // NOTE: We purposefully skip verification for only incremental installs when there's
- // a v4 signature block. Otherwise, proceed with verification as usual.
- if (!origin.existing
- && isVerificationEnabled
- && (!isIncrementalInstall || !isV4Signed)) {
- final Intent verification = new Intent(
- Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
- verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
- PACKAGE_MIME_TYPE);
- verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
- // Query all live verifiers based on current user state
- final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,
- PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier(),
- false /*allowDynamicSplits*/);
-
- if (DEBUG_VERIFY) {
- Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
- + verification.toString() + " with " + pkgLite.verifiers.length
- + " optional verifiers");
- }
-
- verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
-
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, installFlags);
-
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName);
-
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode);
-
- verification.putExtra(
- PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
- pkgLite.getLongVersionCode());
-
- populateInstallerExtras(verification);
-
- final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
- receivers, verificationState);
-
- DeviceIdleInternal idleController =
- mInjector.getLocalService(DeviceIdleInternal.class);
- final long idleDuration = getVerificationTimeout();
- final BroadcastOptions options = BroadcastOptions.makeBasic();
- options.setTemporaryAppAllowlist(idleDuration,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- REASON_PACKAGE_VERIFIER, "");
-
- /*
- * If any sufficient verifiers were listed in the package
- * manifest, attempt to ask them.
- */
- if (sufficientVerifiers != null) {
- final int n = sufficientVerifiers.size();
- if (n == 0) {
- Slog.i(TAG, "Additional verifiers required, but none installed.");
- ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
- } else {
- for (int i = 0; i < n; i++) {
- final ComponentName verifierComponent = sufficientVerifiers.get(i);
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- verifierComponent.getPackageName(), idleDuration,
- verifierUser.getIdentifier(), false,
- REASON_PACKAGE_VERIFIER,"package verifier");
-
- final Intent sufficientIntent = new Intent(verification);
- sufficientIntent.setComponent(verifierComponent);
- mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
- /* receiverPermission= */ null,
- options.toBundle());
- }
- }
- }
-
- if (mRequiredVerifierPackage != null) {
- final ComponentName requiredVerifierComponent = matchComponentForVerifier(
- mRequiredVerifierPackage, receivers);
- /*
- * Send the intent to the required verification agent,
- * but only start the verification timeout after the
- * target BroadcastReceivers have run.
- */
- verification.setComponent(requiredVerifierComponent);
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- mRequiredVerifierPackage, idleDuration,
- verifierUser.getIdentifier(), false,
- REASON_PACKAGE_VERIFIER, "package verifier");
- mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
- android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- /* appOp= */ AppOpsManager.OP_NONE,
- /* options= */ options.toBundle(),
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final Message msg = mHandler
- .obtainMessage(CHECK_PENDING_VERIFICATION);
- msg.arg1 = verificationId;
- mHandler.sendMessageDelayed(msg, getVerificationTimeout());
- }
- }, null, 0, null, null);
-
- Trace.asyncTraceBegin(
- TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
-
- /*
- * We don't want the copy to proceed until verification
- * succeeds.
- */
- mWaitForVerificationToComplete = true;
- }
- } else {
- verificationState.setVerifierResponse(
- requiredUid, PackageManager.VERIFICATION_ALLOW);
- }
- return ret;
- }
-
- void populateInstallerExtras(Intent intent) {
- intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
- installSource.initiatingPackageName);
-
- if (verificationInfo != null) {
- if (verificationInfo.originatingUri != null) {
- intent.putExtra(Intent.EXTRA_ORIGINATING_URI,
- verificationInfo.originatingUri);
- }
- if (verificationInfo.referrer != null) {
- intent.putExtra(Intent.EXTRA_REFERRER,
- verificationInfo.referrer);
- }
- if (verificationInfo.originatingUid >= 0) {
- intent.putExtra(Intent.EXTRA_ORIGINATING_UID,
- verificationInfo.originatingUid);
- }
- if (verificationInfo.installerUid >= 0) {
- intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
- verificationInfo.installerUid);
- }
- }
- }
-
- void setReturnCode(int ret) {
- if (mRet == PackageManager.INSTALL_SUCCEEDED) {
- // Only update mRet if it was previously INSTALL_SUCCEEDED to
- // ensure we do not overwrite any previous failure results.
- mRet = ret;
- }
- }
-
- void handleVerificationFinished() {
- mWaitForVerificationToComplete = false;
- handleReturnCode();
- }
-
- void handleIntegrityVerificationFinished() {
- mWaitForIntegrityVerificationToComplete = false;
- handleReturnCode();
- }
-
-
- void handleRollbackEnabled() {
- // TODO(ruhler) b/112431924: Consider halting the install if we
- // couldn't enable rollback.
- mWaitForEnableRollbackToComplete = false;
- handleReturnCode();
- }
-
- @Override
- void handleReturnCode() {
- if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
- || mWaitForEnableRollbackToComplete) {
- return;
- }
- sendVerificationCompleteNotification();
- }
-
- private void sendVerificationCompleteNotification() {
- if (mParentVerificationParams != null) {
- mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
- } else {
- try {
- observer.onPackageInstalled(null, mRet, "Package Verification Result",
- new Bundle());
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
- }
- }
- }
-
- private InstallArgs createInstallArgs(InstallParams params) {
- if (params.move != null) {
- return new MoveInstallArgs(params);
- } else {
- return new FileInstallArgs(params);
- }
- }
-
/**
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
*/
- private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
- return new FileInstallArgs(codePath, instructionSets);
- }
-
- static abstract class InstallArgs {
- /** @see InstallParams#origin */
- final OriginInfo origin;
- /** @see InstallParams#move */
- final MoveInfo move;
-
- final IPackageInstallObserver2 observer;
- // Always refers to PackageManager flags only
- final int installFlags;
- @NonNull final InstallSource installSource;
- final String volumeUuid;
- final UserHandle user;
- final String abiOverride;
- final String[] installGrantPermissions;
- final List<String> whitelistedRestrictedPermissions;
- final int autoRevokePermissionsMode;
- /** If non-null, drop an async trace when the install completes */
- final String traceMethod;
- final int traceCookie;
- final PackageParser.SigningDetails signingDetails;
- final int installReason;
- final int mInstallScenario;
- final boolean forceQueryableOverride;
- final int mDataLoaderType;
-
- // The list of instruction sets supported by this app. This is currently
- // only used during the rmdex() phase to clean up resources. We can get rid of this
- // if we move dex files under the common app path.
- /* nullable */ String[] instructionSets;
-
- InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
- int installFlags, InstallSource installSource, String volumeUuid,
- UserHandle user, String[] instructionSets,
- String abiOverride, String[] installGrantPermissions,
- List<String> whitelistedRestrictedPermissions,
- int autoRevokePermissionsMode,
- String traceMethod, int traceCookie, SigningDetails signingDetails,
- int installReason, int installScenario, boolean forceQueryableOverride,
- int dataLoaderType) {
- this.origin = origin;
- this.move = move;
- this.installFlags = installFlags;
- this.observer = observer;
- this.installSource = Preconditions.checkNotNull(installSource);
- this.volumeUuid = volumeUuid;
- this.user = user;
- this.instructionSets = instructionSets;
- this.abiOverride = abiOverride;
- this.installGrantPermissions = installGrantPermissions;
- this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
- this.autoRevokePermissionsMode = autoRevokePermissionsMode;
- this.traceMethod = traceMethod;
- this.traceCookie = traceCookie;
- this.signingDetails = signingDetails;
- this.installReason = installReason;
- this.mInstallScenario = installScenario;
- this.forceQueryableOverride = forceQueryableOverride;
- this.mDataLoaderType = dataLoaderType;
- }
-
- /** New install */
- InstallArgs(InstallParams params) {
- this(params.origin, params.move, params.observer, params.installFlags,
- params.installSource, params.volumeUuid,
- params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
- params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
- params.autoRevokePermissionsMode,
- params.traceMethod, params.traceCookie, params.signingDetails,
- params.installReason, params.mInstallScenario, params.forceQueryableOverride,
- params.mDataLoaderType);
- }
-
- abstract int copyApk();
- abstract int doPreInstall(int status);
-
- /**
- * Rename package into final resting place. All paths on the given
- * scanned package should be updated to reflect the rename.
- */
- abstract boolean doRename(int status, ParsedPackage parsedPackage);
- abstract int doPostInstall(int status, int uid);
-
- /** @see PackageSettingBase#getPath() */
- abstract String getCodePath();
-
- // Need installer lock especially for dex file removal.
- abstract void cleanUpResourcesLI();
- abstract boolean doPostDeleteLI(boolean delete);
-
- /**
- * Called before the source arguments are copied. This is used mostly
- * for MoveParams when it needs to read the source file to put it in the
- * destination.
- */
- int doPreCopy() {
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- /**
- * Called after the source arguments are copied. This is used mostly for
- * MoveParams when it needs to read the source file to put it in the
- * destination.
- */
- int doPostCopy(int uid) {
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- protected boolean isEphemeral() {
- return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- }
-
- UserHandle getUser() {
- return user;
- }
- }
-
- void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
- if (!allCodePaths.isEmpty()) {
- if (instructionSets == null) {
- throw new IllegalStateException("instructionSet == null");
- }
- String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
- for (String codePath : allCodePaths) {
- for (String dexCodeInstructionSet : dexCodeInstructionSets) {
- try {
- mInstaller.rmdex(codePath, dexCodeInstructionSet);
- } catch (InstallerException ignored) {
- }
- }
- }
- }
- }
-
- /**
- * Logic to handle installation of new applications, including copying
- * and renaming logic.
- */
- class FileInstallArgs extends InstallArgs {
- private File codeFile;
- private File resourceFile;
-
- // Example topology:
- // /data/app/com.example/base.apk
- // /data/app/com.example/split_foo.apk
- // /data/app/com.example/lib/arm/libfoo.so
- // /data/app/com.example/lib/arm64/libfoo.so
- // /data/app/com.example/dalvik/arm/base.apk@classes.dex
-
- /** New install */
- FileInstallArgs(InstallParams params) {
- super(params);
- }
-
- /** Existing install */
- FileInstallArgs(String codePath, String[] instructionSets) {
- super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
- null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
- PackageParser.SigningDetails.UNKNOWN,
- PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.INSTALL_SCENARIO_DEFAULT,
- false, DataLoaderType.NONE);
- this.codeFile = (codePath != null) ? new File(codePath) : null;
- }
-
- int copyApk() {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
- try {
- return doCopyApk();
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- private int doCopyApk() {
- if (origin.staged) {
- if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
- codeFile = origin.file;
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- try {
- final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- final File tempDir =
- mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
- codeFile = tempDir;
- } catch (IOException e) {
- Slog.w(TAG, "Failed to create copy file: " + e);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
-
- int ret = PackageManagerServiceUtils.copyPackage(
- origin.file.getAbsolutePath(), codeFile);
- if (ret != PackageManager.INSTALL_SUCCEEDED) {
- Slog.e(TAG, "Failed to copy package");
- return ret;
- }
-
- final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
- final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(codeFile);
- ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- abiOverride, isIncremental);
- } catch (IOException e) {
- Slog.e(TAG, "Copying native libraries failed", e);
- ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
-
- return ret;
- }
-
- int doPreInstall(int status) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp();
- }
- return status;
- }
-
- @Override
- boolean doRename(int status, ParsedPackage parsedPackage) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp();
- return false;
- }
-
- final File targetDir = resolveTargetDir();
- final File beforeCodeFile = codeFile;
- final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
-
- if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
- final boolean onIncremental = mIncrementalManager != null
- && isIncrementalPath(beforeCodeFile.getAbsolutePath());
- try {
- makeDirRecursive(afterCodeFile.getParentFile(), 0775);
- if (onIncremental) {
- // Just link files here. The stage dir will be removed when the installation
- // session is completed.
- mIncrementalManager.linkCodePath(beforeCodeFile, afterCodeFile);
- } else {
- Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
- }
- } catch (IOException | ErrnoException e) {
- Slog.w(TAG, "Failed to rename", e);
- return false;
- }
-
- if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
- Slog.w(TAG, "Failed to restorecon");
- return false;
- }
-
- // Reflect the rename internally
- codeFile = afterCodeFile;
-
- // Reflect the rename in scanned details
- try {
- parsedPackage.setCodePath(afterCodeFile.getCanonicalPath());
- } catch (IOException e) {
- Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
- return false;
- }
- parsedPackage.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, parsedPackage.getBaseApkPath()));
- parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
- afterCodeFile, parsedPackage.getSplitCodePaths()));
-
- return true;
- }
-
- // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
- // flow, we won't need this method anymore.
- private File resolveTargetDir() {
- boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0;
- if (isStagedInstall) {
- return Environment.getDataAppDirectory(null);
- } else {
- return codeFile.getParentFile();
- }
- }
-
- int doPostInstall(int status, int uid) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp();
- }
- return status;
- }
-
- @Override
- String getCodePath() {
- return (codeFile != null) ? codeFile.getAbsolutePath() : null;
- }
-
- private boolean cleanUp() {
- if (codeFile == null || !codeFile.exists()) {
- return false;
- }
- removeCodePathLI(codeFile);
- return true;
- }
-
- void cleanUpResourcesLI() {
- // Try enumerating all code paths before deleting
- List<String> allCodePaths = Collections.EMPTY_LIST;
- if (codeFile != null && codeFile.exists()) {
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
- input.reset(), codeFile, /* flags */ 0);
- if (result.isSuccess()) {
- // Ignore error; we tried our best
- allCodePaths = result.getResult().getAllApkPaths();
- }
- }
-
- cleanUp();
- removeDexFiles(allCodePaths, instructionSets);
- }
-
- boolean doPostDeleteLI(boolean delete) {
- // XXX err, shouldn't we respect the delete flag?
- cleanUpResourcesLI();
- return true;
- }
- }
-
- /**
- * Logic to handle movement of existing installed applications.
- */
- class MoveInstallArgs extends InstallArgs {
- private File codeFile;
-
- /** New install */
- MoveInstallArgs(InstallParams params) {
- super(params);
- }
-
- int copyApk() {
- if (DEBUG_INSTALL) Slog.d(TAG, "Moving " + move.packageName + " from "
- + move.fromUuid + " to " + move.toUuid);
- synchronized (mInstaller) {
- try {
- mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
- move.appId, move.seinfo, move.targetSdkVersion,
- move.fromCodePath);
- } catch (InstallerException e) {
- Slog.w(TAG, "Failed to move app", e);
- return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- }
- }
-
- final String toPathName = new File(move.fromCodePath).getName();
- codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
- if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
-
- return PackageManager.INSTALL_SUCCEEDED;
- }
-
- int doPreInstall(int status) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp(move.toUuid);
- }
- return status;
- }
-
- @Override
- boolean doRename(int status, ParsedPackage parsedPackage) {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- cleanUp(move.toUuid);
- return false;
- }
-
- return true;
- }
-
- int doPostInstall(int status, int uid) {
- if (status == PackageManager.INSTALL_SUCCEEDED) {
- cleanUp(move.fromUuid);
- } else {
- cleanUp(move.toUuid);
- }
- return status;
- }
-
- @Override
- String getCodePath() {
- return (codeFile != null) ? codeFile.getAbsolutePath() : null;
- }
-
- private boolean cleanUp(String volumeUuid) {
- final String toPathName = new File(move.fromCodePath).getName();
- final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
- toPathName);
- Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
- final int[] userIds = mUserManager.getUserIds();
- synchronized (mInstallLock) {
- // Clean up both app data and code
- // All package moves are frozen until finished
-
- // We purposefully exclude FLAG_STORAGE_EXTERNAL here, since
- // this task was only focused on moving data on internal storage.
- // We don't want ART profiles cleared, because they don't move,
- // so we would be deleting the only copy (b/149200535).
- final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE
- | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
- for (int userId : userIds) {
- try {
- mInstaller.destroyAppData(volumeUuid, move.packageName, userId, flags, 0);
- } catch (InstallerException e) {
- Slog.w(TAG, String.valueOf(e));
- }
- }
- removeCodePathLI(codeFile);
- }
- return true;
- }
-
- void cleanUpResourcesLI() {
- throw new UnsupportedOperationException();
- }
-
- boolean doPostDeleteLI(boolean delete) {
- throw new UnsupportedOperationException();
- }
+ InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
+ return new FileInstallArgs(codePath, instructionSets, this);
}
/**
@@ -18959,7 +17084,7 @@ public class PackageManagerService extends IPackageManager.Stub
* directory.
* @return File object for the directory that should hold the code files of {@code packageName}.
*/
- private File getNextCodePath(File targetDir, String packageName) {
+ static File getNextCodePath(File targetDir, String packageName) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
File firstLevelDir;
@@ -18974,60 +17099,6 @@ public class PackageManagerService extends IPackageManager.Stub
return new File(firstLevelDir, packageName + "-" + suffix);
}
- static class PackageInstalledInfo {
- String name;
- int uid;
- // The set of users that originally had this package installed.
- int[] origUsers;
- // The set of users that now have this package installed.
- int[] newUsers;
- AndroidPackage pkg;
- int returnCode;
- String returnMsg;
- String installerPackageName;
- PackageRemovedInfo removedInfo;
- // The set of packages consuming this shared library or null if no consumers exist.
- ArrayList<AndroidPackage> libraryConsumers;
- PackageFreezer freezer;
-
- public void setError(int code, String msg) {
- setReturnCode(code);
- setReturnMessage(msg);
- Slog.w(TAG, msg);
- }
-
- public void setError(String msg, PackageParserException e) {
- setReturnCode(e.error);
- setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
- Slog.w(TAG, msg, e);
- }
-
- public void setError(String msg, PackageManagerException e) {
- returnCode = e.error;
- setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
- Slog.w(TAG, msg, e);
- }
-
- public void setReturnCode(int returnCode) {
- this.returnCode = returnCode;
- }
-
- private void setReturnMessage(String returnMsg) {
- this.returnMsg = returnMsg;
- }
-
- // In some error cases we want to convey more info back to the observer
- String origPackage;
- String origPermission;
- }
-
- private static void updateDigest(MessageDigest digest, File file) throws IOException {
- try (DigestInputStream digestStream =
- new DigestInputStream(new FileInputStream(file), digest)) {
- while (digestStream.read() != -1) {} // nothing to do; just plow through the file
- }
- }
-
private void removeNativeBinariesLI(PackageSetting ps) {
if (ps != null) {
NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
@@ -19040,361 +17111,18 @@ public class PackageManagerService extends IPackageManager.Stub
}
@GuardedBy("mLock")
- private boolean disableSystemPackageLPw(AndroidPackage oldPkg) {
- return mSettings.disableSystemPackageLPw(oldPkg.getPackageName(), true);
- }
-
- private void updateSettingsLI(AndroidPackage newPackage, InstallArgs installArgs,
- int[] allUsers, PackageInstalledInfo res) {
- updateSettingsInternalLI(newPackage, installArgs, allUsers, res);
- }
-
- private void updateSettingsInternalLI(AndroidPackage pkg, InstallArgs installArgs,
- int[] allUsers, PackageInstalledInfo res) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
-
- final String pkgName = pkg.getPackageName();
- final int[] installedForUsers = res.origUsers;
- final int installReason = installArgs.installReason;
- InstallSource installSource = installArgs.installSource;
- final String installerPackageName = installSource.installerPackageName;
-
- if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
- synchronized (mLock) {
- // For system-bundled packages, we assume that installing an upgraded version
- // of the package implies that the user actually wants to run that new code,
- // so we enable the package.
- final PackageSetting ps = mSettings.getPackageLPr(pkgName);
- final int userId = installArgs.user.getIdentifier();
- if (ps != null) {
- if (pkg.isSystem()) {
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
- }
- // Enable system package for requested users
- if (res.origUsers != null) {
- for (int origUserId : res.origUsers) {
- if (userId == UserHandle.USER_ALL || userId == origUserId) {
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
- origUserId, installerPackageName);
- }
- }
- }
- // Also convey the prior install/uninstall state
- if (allUsers != null && installedForUsers != null) {
- for (int currentUserId : allUsers) {
- final boolean installed = ArrayUtils.contains(
- installedForUsers, currentUserId);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, " user " + currentUserId + " => " + installed);
- }
- ps.setInstalled(installed, currentUserId);
- }
- // these install state changes will be persisted in the
- // upcoming call to mSettings.writeLPr().
- }
-
- if (allUsers != null) {
- for (int currentUserId : allUsers) {
- ps.resetOverrideComponentLabelIcon(currentUserId);
- }
- }
- }
-
- // Retrieve the overlays for shared libraries of the package.
- if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
- for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
- for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
- if (!sharedLib.isDynamic()) {
- // TODO(146804378): Support overlaying static shared libraries
- continue;
- }
- final PackageSetting libPs = mSettings.getPackageLPr(
- sharedLib.getPackageName());
- if (libPs == null) {
- continue;
- }
- ps.setOverlayPathsForLibrary(sharedLib.getName(),
- libPs.getOverlayPaths(currentUserId), currentUserId);
- }
- }
- }
-
- // It's implied that when a user requests installation, they want the app to be
- // installed and enabled. (This does not apply to USER_ALL, which here means only
- // install on users for which the app is already installed).
- if (userId != UserHandle.USER_ALL) {
- ps.setInstalled(true, userId);
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
- }
-
- mSettings.addInstallerPackageNames(ps.installSource);
-
- // When replacing an existing package, preserve the original install reason for all
- // users that had the package installed before. Similarly for uninstall reasons.
- final Set<Integer> previousUserIds = new ArraySet<>();
- if (res.removedInfo != null && res.removedInfo.installReasons != null) {
- final int installReasonCount = res.removedInfo.installReasons.size();
- for (int i = 0; i < installReasonCount; i++) {
- final int previousUserId = res.removedInfo.installReasons.keyAt(i);
- final int previousInstallReason = res.removedInfo.installReasons.valueAt(i);
- ps.setInstallReason(previousInstallReason, previousUserId);
- previousUserIds.add(previousUserId);
- }
- }
- if (res.removedInfo != null && res.removedInfo.uninstallReasons != null) {
- for (int i = 0; i < res.removedInfo.uninstallReasons.size(); i++) {
- final int previousUserId = res.removedInfo.uninstallReasons.keyAt(i);
- final int previousReason = res.removedInfo.uninstallReasons.valueAt(i);
- ps.setUninstallReason(previousReason, previousUserId);
- }
- }
-
- // Set install reason for users that are having the package newly installed.
- final int[] allUsersList = mUserManager.getUserIds();
- if (userId == UserHandle.USER_ALL) {
- // TODO(b/152629990): It appears that the package doesn't actually get newly
- // installed in this case, so the installReason shouldn't get modified?
- for (int currentUserId : allUsersList) {
- if (!previousUserIds.contains(currentUserId)) {
- ps.setInstallReason(installReason, currentUserId);
- }
- }
- } else if (!previousUserIds.contains(userId)) {
- ps.setInstallReason(installReason, userId);
- }
-
- // TODO(b/169721400): generalize Incremental States and create a Callback object
- // that can be used for all the packages.
- final String codePath = ps.getPathString();
- if (IncrementalManager.isIncrementalPath(codePath) && mIncrementalManager != null) {
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(ps.name,
- UserHandle.getUid(userId, ps.appId),
- getInstalledUsers(ps, userId));
- ps.setIncrementalStatesCallback(incrementalStatesCallback);
- mIncrementalManager.registerLoadingProgressCallback(codePath,
- new IncrementalProgressListener(ps.name));
- }
-
- // Ensure that the uninstall reason is UNKNOWN for users with the package installed.
- for (int currentUserId : allUsersList) {
- if (ps.getInstalled(currentUserId)) {
- ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
- }
- }
-
- mSettings.writeKernelMappingLPr(ps);
-
- final PermissionManagerServiceInternal.PackageInstalledParams.Builder
- permissionParamsBuilder =
- new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
- final boolean grantPermissions = (installArgs.installFlags
- & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
- if (grantPermissions) {
- final List<String> grantedPermissions =
- installArgs.installGrantPermissions != null
- ? Arrays.asList(installArgs.installGrantPermissions)
- : pkg.getRequestedPermissions();
- permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
- }
- final boolean allowlistAllRestrictedPermissions =
- (installArgs.installFlags
- & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
- final List<String> allowlistedRestrictedPermissions =
- allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
- : installArgs.whitelistedRestrictedPermissions;
- if (allowlistedRestrictedPermissions != null) {
- permissionParamsBuilder.setAllowlistedRestrictedPermissions(
- allowlistedRestrictedPermissions);
- }
- final int autoRevokePermissionsMode = installArgs.autoRevokePermissionsMode;
- permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
- mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(), userId);
- }
- res.name = pkgName;
- res.uid = pkg.getUid();
- res.pkg = pkg;
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- //to update install status
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
- writeSettingsLPrTEMP();
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- private static class InstallRequest {
- public final InstallArgs args;
- public final PackageInstalledInfo installResult;
-
- private InstallRequest(InstallArgs args, PackageInstalledInfo res) {
- this.args = args;
- this.installResult = res;
- }
- }
-
- @GuardedBy("mInstallLock")
- private void installPackagesTracedLI(List<InstallRequest> requests) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
- installPackagesLI(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Package state to commit to memory and disk after reconciliation has completed.
- */
- private static class CommitRequest {
- final Map<String, ReconciledPackage> reconciledPackages;
- @NonNull
- final int[] mAllUsers;
-
- private CommitRequest(Map<String, ReconciledPackage> reconciledPackages,
- @NonNull int[] allUsers) {
- this.reconciledPackages = reconciledPackages;
- this.mAllUsers = allUsers;
- }
- }
-
- /**
- * Package scan results and related request details used to reconcile the potential addition of
- * one or more packages to the system.
- *
- * Reconcile will take a set of package details that need to be committed to the system and make
- * sure that they are valid in the context of the system and the other installing apps. Any
- * invalid state or app will result in a failed reconciliation and thus whatever operation (such
- * as install) led to the request.
- */
- private static class ReconcileRequest {
- public final Map<String, ScanResult> scannedPackages;
-
- public final Map<String, AndroidPackage> allPackages;
- public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource;
- public final Map<String, InstallArgs> installArgs;
- public final Map<String, PackageInstalledInfo> installResults;
- public final Map<String, PrepareResult> preparedPackages;
- public final Map<String, VersionInfo> versionInfos;
- public final Map<String, PackageSetting> lastStaticSharedLibSettings;
-
- private ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, InstallArgs> installArgs,
- Map<String, PackageInstalledInfo> installResults,
- Map<String, PrepareResult> preparedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
- Map<String, AndroidPackage> allPackages,
- Map<String, VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
- this.scannedPackages = scannedPackages;
- this.installArgs = installArgs;
- this.installResults = installResults;
- this.preparedPackages = preparedPackages;
- this.sharedLibrarySource = sharedLibrarySource;
- this.allPackages = allPackages;
- this.versionInfos = versionInfos;
- this.lastStaticSharedLibSettings = lastStaticSharedLibSettings;
- }
-
- private ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
- Map<String, AndroidPackage> allPackages,
- Map<String, VersionInfo> versionInfos,
- Map<String, PackageSetting> lastStaticSharedLibSettings) {
- this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
- Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
- lastStaticSharedLibSettings);
- }
- }
- private static class ReconcileFailure extends PackageManagerException {
- ReconcileFailure(String message) {
- super("Reconcile failed: " + message);
- }
- ReconcileFailure(int reason, String message) {
- super(reason, "Reconcile failed: " + message);
- }
- ReconcileFailure(PackageManagerException e) {
- this(e.error, e.getMessage());
- }
- }
-
- /**
- * A container of all data needed to commit a package to in-memory data structures and to disk.
- * TODO: move most of the data contained here into a PackageSetting for commit.
- */
- private static class ReconciledPackage {
- public final ReconcileRequest request;
- public final PackageSetting pkgSetting;
- public final ScanResult scanResult;
- // TODO: Remove install-specific details from the reconcile result
- public final PackageInstalledInfo installResult;
- @Nullable public final PrepareResult prepareResult;
- @Nullable public final InstallArgs installArgs;
- public final DeletePackageAction deletePackageAction;
- public final List<SharedLibraryInfo> allowedSharedLibraryInfos;
- public final SigningDetails signingDetails;
- public final boolean sharedUserSignaturesChanged;
- public ArrayList<SharedLibraryInfo> collectedSharedLibraryInfos;
- public final boolean removeAppKeySetData;
-
- private ReconciledPackage(ReconcileRequest request,
- InstallArgs installArgs,
- PackageSetting pkgSetting,
- PackageInstalledInfo installResult,
- PrepareResult prepareResult,
- ScanResult scanResult,
- DeletePackageAction deletePackageAction,
- List<SharedLibraryInfo> allowedSharedLibraryInfos,
- SigningDetails signingDetails,
- boolean sharedUserSignaturesChanged,
- boolean removeAppKeySetData) {
- this.request = request;
- this.installArgs = installArgs;
- this.pkgSetting = pkgSetting;
- this.installResult = installResult;
- this.prepareResult = prepareResult;
- this.scanResult = scanResult;
- this.deletePackageAction = deletePackageAction;
- this.allowedSharedLibraryInfos = allowedSharedLibraryInfos;
- this.signingDetails = signingDetails;
- this.sharedUserSignaturesChanged = sharedUserSignaturesChanged;
- this.removeAppKeySetData = removeAppKeySetData;
- }
-
- /**
- * Returns a combined set of packages containing the packages already installed combined
- * with the package(s) currently being installed. The to-be installed packages take
- * precedence and may shadow already installed packages.
- */
- private Map<String, AndroidPackage> getCombinedAvailablePackages() {
- final ArrayMap<String, AndroidPackage> combined =
- new ArrayMap<>(request.allPackages.size() + request.scannedPackages.size());
-
- combined.putAll(request.allPackages);
-
- for (ScanResult scanResult : request.scannedPackages.values()) {
- combined.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
- }
-
- return combined;
- }
- }
-
- @GuardedBy("mLock")
- private static Map<String, ReconciledPackage> reconcilePackagesLocked(
+ static Map<String, ReconciledPackage> reconcilePackagesLocked(
final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
throws ReconcileFailure {
- final Map<String, ScanResult> scannedPackages = request.scannedPackages;
+ final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
// make a copy of the existing set of packages so we can combine them with incoming packages
final ArrayMap<String, AndroidPackage> combinedPackages =
- new ArrayMap<>(request.allPackages.size() + scannedPackages.size());
+ new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
- combinedPackages.putAll(request.allPackages);
+ combinedPackages.putAll(request.mAllPackages);
final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
new ArrayMap<>();
@@ -19403,12 +17131,12 @@ public class PackageManagerService extends IPackageManager.Stub
final ScanResult scanResult = scannedPackages.get(installPackageName);
// add / replace existing with incoming packages
- combinedPackages.put(scanResult.pkgSetting.name, scanResult.request.parsedPackage);
+ combinedPackages.put(scanResult.mPkgSetting.name, scanResult.mRequest.mParsedPackage);
// in the first pass, we'll build up the set of incoming shared libraries
final List<SharedLibraryInfo> allowedSharedLibInfos =
- getAllowedSharedLibInfos(scanResult, request.sharedLibrarySource);
- final SharedLibraryInfo staticLib = scanResult.staticSharedLibraryInfo;
+ getAllowedSharedLibInfos(scanResult, request.mSharedLibrarySource);
+ final SharedLibraryInfo staticLib = scanResult.mStaticSharedLibraryInfo;
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
if (!addSharedLibraryToPackageVersionMap(incomingSharedLibraries, info)) {
@@ -19419,9 +17147,9 @@ public class PackageManagerService extends IPackageManager.Stub
}
// the following may be null if we're just reconciling on boot (and not during install)
- final InstallArgs installArgs = request.installArgs.get(installPackageName);
- final PackageInstalledInfo res = request.installResults.get(installPackageName);
- final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
+ final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+ final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
final boolean isInstall = installArgs != null;
if (isInstall && (res == null || prepareResult == null)) {
throw new ReconcileFailure("Reconcile arguments are not balanced for "
@@ -19430,12 +17158,12 @@ public class PackageManagerService extends IPackageManager.Stub
final DeletePackageAction deletePackageAction;
// we only want to try to delete for non system apps
- if (isInstall && prepareResult.replace && !prepareResult.system) {
- final boolean killApp = (scanResult.request.scanFlags & SCAN_DONT_KILL_APP) == 0;
+ if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
+ final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA
| (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- deletePackageAction = mayDeletePackageLocked(res.removedInfo,
- prepareResult.originalPs, prepareResult.disabledPs,
+ deletePackageAction = mayDeletePackageLocked(res.mRemovedInfo,
+ prepareResult.mOriginalPs, prepareResult.mDisabledPs,
deleteFlags, null /* all users */);
if (deletePackageAction == null) {
throw new ReconcileFailure(
@@ -19446,17 +17174,17 @@ public class PackageManagerService extends IPackageManager.Stub
deletePackageAction = null;
}
- final int scanFlags = scanResult.request.scanFlags;
- final int parseFlags = scanResult.request.parseFlags;
- final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
+ final int scanFlags = scanResult.mRequest.mScanFlags;
+ final int parseFlags = scanResult.mRequest.mParseFlags;
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
- final PackageSetting disabledPkgSetting = scanResult.request.disabledPkgSetting;
+ final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
final PackageSetting lastStaticSharedLibSetting =
- request.lastStaticSharedLibSettings.get(installPackageName);
+ request.mLastStaticSharedLibSettings.get(installPackageName);
final PackageSetting signatureCheckPs =
(prepareResult != null && lastStaticSharedLibSetting != null)
? lastStaticSharedLibSetting
- : scanResult.pkgSetting;
+ : scanResult.mPkgSetting;
boolean removeAppKeySetData = false;
boolean sharedUserSignaturesChanged = false;
SigningDetails signingDetails = null;
@@ -19479,11 +17207,11 @@ public class PackageManagerService extends IPackageManager.Stub
signingDetails = parsedPackage.getSigningDetails();
} else {
try {
- final VersionInfo versionInfo = request.versionInfos.get(installPackageName);
+ final VersionInfo versionInfo = request.mVersionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
final boolean isRollback = installArgs != null
- && installArgs.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
final boolean compatMatch = verifySignatures(signatureCheckPs,
disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
compareRecover, isRollback);
@@ -19531,11 +17259,11 @@ public class PackageManagerService extends IPackageManager.Stub
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
if (signatureCheckPs.sharedUser != null) {
- final Signature[] sharedUserSignatures =
- signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures;
+ final Signature[] sharedUserSignatures = signatureCheckPs.sharedUser
+ .signatures.mSigningDetails.getSignatures();
if (signatureCheckPs.sharedUser.signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().signatures)
+ parsedPackage.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
// Mismatched signatures is an error and silently skipping system
@@ -19547,7 +17275,7 @@ public class PackageManagerService extends IPackageManager.Stub
throw new ReconcileFailure(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
- + scanResult.pkgSetting.sharedUser);
+ + scanResult.mPkgSetting.sharedUser);
} else {
// Treat mismatched signatures on system packages using a shared
// UID as
@@ -19557,7 +17285,7 @@ public class PackageManagerService extends IPackageManager.Stub
"Signature mismatch on system package "
+ parsedPackage.getPackageName()
+ " for shared user "
- + scanResult.pkgSetting.sharedUser);
+ + scanResult.mPkgSetting.sharedUser);
}
}
@@ -19580,8 +17308,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
result.put(installPackageName,
- new ReconciledPackage(request, installArgs, scanResult.pkgSetting,
- res, request.preparedPackages.get(installPackageName), scanResult,
+ new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
+ res, request.mPreparedPackages.get(installPackageName), scanResult,
deletePackageAction, allowedSharedLibInfos, signingDetails,
sharedUserSignaturesChanged, removeAppKeySetData));
}
@@ -19595,15 +17323,15 @@ public class PackageManagerService extends IPackageManager.Stub
// scan don't update any libs as we do this wholesale after all
// apps are scanned to avoid dependency based scanning.
final ScanResult scanResult = scannedPackages.get(installPackageName);
- if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
- || (scanResult.request.parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+ if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
+ || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
!= 0) {
continue;
}
try {
- result.get(installPackageName).collectedSharedLibraryInfos =
- collectSharedLibraryInfos(scanResult.request.parsedPackage,
- combinedPackages, request.sharedLibrarySource,
+ result.get(installPackageName).mCollectedSharedLibraryInfos =
+ collectSharedLibraryInfos(scanResult.mRequest.mParsedPackage,
+ combinedPackages, request.mSharedLibrarySource,
incomingSharedLibraries, injector.getCompatibility());
} catch (PackageManagerException e) {
@@ -19622,29 +17350,29 @@ public class PackageManagerService extends IPackageManager.Stub
ScanResult scanResult,
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
// Let's used the parsed package as scanResult.pkgSetting may be null
- final ParsedPackage parsedPackage = scanResult.request.parsedPackage;
- if (scanResult.staticSharedLibraryInfo == null
- && scanResult.dynamicSharedLibraryInfos == null) {
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+ if (scanResult.mStaticSharedLibraryInfo == null
+ && scanResult.mDynamicSharedLibraryInfos == null) {
return null;
}
// Any app can add new static shared libraries
- if (scanResult.staticSharedLibraryInfo != null) {
- return Collections.singletonList(scanResult.staticSharedLibraryInfo);
+ if (scanResult.mStaticSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
}
final boolean hasDynamicLibraries = parsedPackage.isSystem()
- && scanResult.dynamicSharedLibraryInfos != null;
+ && scanResult.mDynamicSharedLibraryInfos != null;
if (!hasDynamicLibraries) {
return null;
}
- final boolean isUpdatedSystemApp = scanResult.pkgSetting.getPkgState()
+ final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState()
.isUpdatedSystemApp();
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
- ? scanResult.request.disabledPkgSetting == null
- ? scanResult.request.oldPkgSetting
- : scanResult.request.disabledPkgSetting
+ ? scanResult.mRequest.mDisabledPkgSetting == null
+ ? scanResult.mRequest.mOldPkgSetting
+ : scanResult.mRequest.mDisabledPkgSetting
: null;
if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
|| updatedSystemPs.pkg.getLibraryNames() == null)) {
@@ -19653,8 +17381,8 @@ public class PackageManagerService extends IPackageManager.Stub
return null;
}
final ArrayList<SharedLibraryInfo> infos =
- new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
- for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
+ new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size());
+ for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) {
final String name = info.getName();
if (isUpdatedSystemApp) {
// New library entries can only be added through the
@@ -19708,509 +17436,6 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- @GuardedBy("mLock")
- private void commitPackagesLocked(final CommitRequest request) {
- // TODO: remove any expected failures from this method; this should only be able to fail due
- // to unavoidable errors (I/O, etc.)
- for (ReconciledPackage reconciledPkg : request.reconciledPackages.values()) {
- final ScanResult scanResult = reconciledPkg.scanResult;
- final ScanRequest scanRequest = scanResult.request;
- final ParsedPackage parsedPackage = scanRequest.parsedPackage;
- final String packageName = parsedPackage.getPackageName();
- final PackageInstalledInfo res = reconciledPkg.installResult;
-
- if (reconciledPkg.prepareResult.replace) {
- AndroidPackage oldPackage = mPackages.get(packageName);
-
- // Set the update and install times
- PackageSetting deletedPkgSetting = getPackageSetting(oldPackage.getPackageName());
- reconciledPkg.pkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
- reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();
-
- res.removedInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(
- reconciledPkg.pkgSetting, request.mAllUsers, mSettings.getPackagesLocked());
- if (reconciledPkg.prepareResult.system) {
- // Remove existing system package
- removePackageLI(oldPackage, true);
- if (!disableSystemPackageLPw(oldPackage)) {
- // We didn't need to disable the .apk as a current system package,
- // which means we are replacing another update that is already
- // installed. We need to make sure to delete the older one's .apk.
- res.removedInfo.args = createInstallArgsForExisting(
- oldPackage.getPath(),
- getAppDexInstructionSets(
- AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
- deletedPkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
- deletedPkgSetting)));
- } else {
- res.removedInfo.args = null;
- }
- } else {
- try {
- // Settings will be written during the call to updateSettingsLI().
- executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
- true, request.mAllUsers, false, parsedPackage);
- } catch (SystemDeleteException e) {
- if (mIsEngBuild) {
- throw new RuntimeException("Unexpected failure", e);
- // ignore; not possible for non-system app
- }
- }
- // Successfully deleted the old package; proceed with replace.
-
- // If deleted package lived in a container, give users a chance to
- // relinquish resources before killing.
- if (oldPackage.isExternalStorage()) {
- if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + oldPackage
- + " is ASEC-hosted -> UNAVAILABLE");
- }
- final int[] uidArray = new int[]{oldPackage.getUid()};
- final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(oldPackage.getPackageName());
- sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
- }
-
- // Update the in-memory copy of the previous code paths.
- PackageSetting ps1 = mSettings.getPackageLPr(
- reconciledPkg.prepareResult.existingPackage.getPackageName());
- if ((reconciledPkg.installArgs.installFlags & PackageManager.DONT_KILL_APP)
- == 0) {
- if (ps1.mOldCodePaths == null) {
- ps1.mOldCodePaths = new ArraySet<>();
- }
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getBaseApkPath());
- if (oldPackage.getSplitCodePaths() != null) {
- Collections.addAll(ps1.mOldCodePaths, oldPackage.getSplitCodePaths());
- }
- } else {
- ps1.mOldCodePaths = null;
- }
-
- if (reconciledPkg.installResult.returnCode
- == PackageManager.INSTALL_SUCCEEDED) {
- PackageSetting ps2 = mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- if (ps2 != null) {
- res.removedInfo.removedForAllUsers = mPackages.get(ps2.name) == null;
- }
- }
- }
- }
-
- AndroidPackage pkg = commitReconciledScanResultLocked(reconciledPkg, request.mAllUsers);
- updateSettingsLI(pkg, reconciledPkg.installArgs, request.mAllUsers, res);
-
- final PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps != null) {
- res.newUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
- ps.setUpdateAvailable(false /*updateAvailable*/);
- }
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- updateSequenceNumberLP(ps, res.newUsers);
- updateInstantAppInstallerLocked(packageName);
- }
- }
- ApplicationPackageManager.invalidateGetPackagesForUidCache();
- }
-
- /**
- * Installs one or more packages atomically. This operation is broken up into four phases:
- * <ul>
- * <li><b>Prepare</b>
- * <br/>Analyzes any current install state, parses the package and does initial
- * validation on it.</li>
- * <li><b>Scan</b>
- * <br/>Interrogates the parsed packages given the context collected in prepare.</li>
- * <li><b>Reconcile</b>
- * <br/>Validates scanned packages in the context of each other and the current system
- * state to ensure that the install will be successful.
- * <li><b>Commit</b>
- * <br/>Commits all scanned packages and updates system state. This is the only place
- * that system state may be modified in the install flow and all predictable errors
- * must be determined before this phase.</li>
- * </ul>
- *
- * Failure at any phase will result in a full failure to install all packages.
- */
- @GuardedBy("mInstallLock")
- private void installPackagesLI(List<InstallRequest> requests) {
- final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
- final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
- final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
- final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
- final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
- final Map<String, PackageSetting> lastStaticSharedLibSettings =
- new ArrayMap<>(requests.size());
- final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
- boolean success = false;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
- for (InstallRequest request : requests) {
- // TODO(b/109941548): remove this once we've pulled everything from it and into
- // scan, reconcile or commit.
- final PrepareResult prepareResult;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
- prepareResult =
- preparePackageLI(request.args, request.installResult);
- } catch (PrepareFailure prepareFailure) {
- request.installResult.setError(prepareFailure.error,
- prepareFailure.getMessage());
- request.installResult.origPackage = prepareFailure.conflictingPackage;
- request.installResult.origPermission = prepareFailure.conflictingPermission;
- return;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- request.installResult.installerPackageName =
- request.args.installSource.installerPackageName;
-
- final String packageName = prepareResult.packageToScan.getPackageName();
- prepareResults.put(packageName, prepareResult);
- installResults.put(packageName, request.installResult);
- installArgs.put(packageName, request.args);
- try {
- final ScanResult result = scanPackageTracedLI(
- prepareResult.packageToScan, prepareResult.parseFlags,
- prepareResult.scanFlags, System.currentTimeMillis(),
- request.args.user, request.args.abiOverride);
- if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
- request.installResult.setError(
- PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Duplicate package " + result.pkgSetting.pkg.getPackageName()
- + " in multi-package install request.");
- return;
- }
- createdAppId.put(packageName, optimisticallyRegisterAppId(result));
- versionInfos.put(result.pkgSetting.pkg.getPackageName(),
- getSettingsVersionForPackage(result.pkgSetting.pkg));
- if (result.staticSharedLibraryInfo != null) {
- final PackageSetting sharedLibLatestVersionSetting =
- getSharedLibLatestVersionSetting(result);
- if (sharedLibLatestVersionSetting != null) {
- lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),
- sharedLibLatestVersionSetting);
- }
- }
- } catch (PackageManagerException e) {
- request.installResult.setError("Scanning Failed.", e);
- return;
- }
- }
- ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
- installResults,
- prepareResults,
- mSharedLibraries,
- Collections.unmodifiableMap(mPackages), versionInfos,
- lastStaticSharedLibSettings);
- CommitRequest commitRequest = null;
- synchronized (mLock) {
- Map<String, ReconciledPackage> reconciledPackages;
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = reconcilePackagesLocked(
- reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
- } catch (ReconcileFailure e) {
- for (InstallRequest request : requests) {
- request.installResult.setError("Reconciliation failed...", e);
- }
- return;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
- commitRequest = new CommitRequest(reconciledPackages,
- mUserManager.getUserIds());
- commitPackagesLocked(commitRequest);
- success = true;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
- executePostCommitSteps(commitRequest);
- } finally {
- if (success) {
- for (InstallRequest request : requests) {
- final InstallArgs args = request.args;
- if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
- continue;
- }
- if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
- continue;
- }
- // For incremental installs, we bypass the verifier prior to install. Now
- // that we know the package is valid, send a notice to the verifier with
- // the root hash of the base.apk.
- final String baseCodePath = request.installResult.pkg.getBaseApkPath();
- final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
- final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
- final int verificationId = mPendingVerificationToken++;
- final String rootHashString = PackageManagerServiceUtils
- .buildVerificationRootHashString(baseCodePath, splitCodePaths);
- broadcastPackageVerified(verificationId, originUri,
- PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.getUser());
- }
- } else {
- for (ScanResult result : preparedScans.values()) {
- if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
- false)) {
- cleanUpAppIdCreation(result);
- }
- }
- // TODO(patb): create a more descriptive reason than unknown in future release
- // mark all non-failure installs as UNKNOWN so we do not treat them as success
- for (InstallRequest request : requests) {
- if (request.installResult.freezer != null) {
- request.installResult.freezer.close();
- }
- if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
- }
- }
- }
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * On successful install, executes remaining steps after commit completes and the package lock
- * is released. These are typically more expensive or require calls to installd, which often
- * locks on {@link #mLock}.
- */
- private void executePostCommitSteps(CommitRequest commitRequest) {
- final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
- for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
- final boolean instantApp = ((reconciledPkg.scanResult.request.scanFlags
- & PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
- final AndroidPackage pkg = reconciledPkg.pkgSetting.pkg;
- final String packageName = pkg.getPackageName();
- final String codePath = pkg.getPath();
- final boolean onIncremental = mIncrementalManager != null
- && isIncrementalPath(codePath);
- if (onIncremental) {
- IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
- if (storage == null) {
- throw new IllegalArgumentException(
- "Install: null storage for incremental package " + packageName);
- }
- incrementalStorages.add(storage);
- }
- prepareAppDataAfterInstallLIF(pkg);
- if (reconciledPkg.prepareResult.clearCodeCache) {
- clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
- | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- }
- if (reconciledPkg.prepareResult.replace) {
- mDexManager.notifyPackageUpdated(pkg.getPackageName(),
- pkg.getBaseApkPath(), pkg.getSplitCodePaths());
- }
-
- // Prepare the application profiles for the new code paths.
- // This needs to be done before invoking dexopt so that any install-time profile
- // can be used for optimizations.
- mArtManagerService.prepareAppProfiles(
- pkg,
- resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
- /* updateReferenceProfileContent= */ true);
-
- // Compute the compilation reason from the installation scenario.
- final int compilationReason = mDexManager.getCompilationReasonForInstallScenario(
- reconciledPkg.installArgs.mInstallScenario);
-
- // Construct the DexoptOptions early to see if we should skip running dexopt.
- //
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- final boolean isBackupOrRestore =
- reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_RESTORE
- || reconciledPkg.installArgs.installReason == INSTALL_REASON_DEVICE_SETUP;
-
- final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
- | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE
- | (isBackupOrRestore ? DexoptOptions.DEXOPT_FOR_RESTORE : 0);
- DexoptOptions dexoptOptions =
- new DexoptOptions(packageName, compilationReason, dexoptFlags);
-
- // Check whether we need to dexopt the app.
- //
- // NOTE: it is IMPORTANT to call dexopt:
- // - after doRename which will sync the package data from AndroidPackage and
- // its corresponding ApplicationInfo.
- // - after installNewPackageLIF or replacePackageLIF which will update result with the
- // uid of the application (pkg.applicationInfo.uid).
- // This update happens in place!
- //
- // We only need to dexopt if the package meets ALL of the following conditions:
- // 1) it is not an instant app or if it is then dexopt is enabled via gservices.
- // 2) it is not debuggable.
- // 3) it is not on Incremental File System.
- //
- // Note that we do not dexopt instant apps by default. dexopt can take some time to
- // complete, so we skip this step during installation. Instead, we'll take extra time
- // the first time the instant app starts. It's preferred to do it this way to provide
- // continuous progress to the useur instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- //
- // Furthermore, dexopt may be skipped, depending on the install scenario and current
- // state of the device.
- //
- // TODO(b/174695087): instantApp and onIncremental should be removed and their install
- // path moved to SCENARIO_FAST.
- final boolean performDexopt =
- (!instantApp || Global.getInt(mContext.getContentResolver(),
- Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && !pkg.isDebuggable()
- && (!onIncremental)
- && dexoptOptions.isCompilationEnabled();
-
- if (performDexopt) {
- // Compile the layout resources.
- if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
- mViewCompiler.compileLayouts(pkg);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- ScanResult result = reconciledPkg.scanResult;
-
- // This mirrors logic from commitReconciledScanResultLocked, where the library files
- // needed for dexopt are assigned.
- // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
- // setting needs to be passed to have a comparison, hide it behind an immutable
- // interface. There's no good reason to have 3 different ways to access the real
- // PackageSetting object, only one of which is actually correct.
- PackageSetting realPkgSetting = result.existingSettingCopied
- ? result.request.pkgSetting : result.pkgSetting;
- if (realPkgSetting == null) {
- realPkgSetting = reconciledPkg.pkgSetting;
- }
-
- // Unfortunately, the updated system app flag is only tracked on this PackageSetting
- boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
- .isUpdatedSystemApp();
-
- realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
- mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
- null /* instructionSets */,
- getOrCreateCompilerPackageStats(pkg),
- mDexManager.getPackageUseInfoOrDefault(packageName),
- dexoptOptions);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BackgroundDexOptService will remove it from its denylist.
- // TODO: Layering violation
- BackgroundDexOptService.notifyPackageChanged(packageName);
-
- notifyPackageChangeObserversOnUpdate(reconciledPkg);
- }
- waitForNativeBinariesExtraction(incrementalStorages);
- }
-
- static void waitForNativeBinariesExtraction(
- ArraySet<IncrementalStorage> incrementalStorages) {
- if (incrementalStorages.isEmpty()) {
- return;
- }
- try {
- // Native library extraction may take very long time: each page could potentially
- // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
- // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
- // make much sense as blocking here doesn't lock up the framework, but only blocks
- // the installation session and the following ones.
- Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
- for (int i = 0; i < incrementalStorages.size(); ++i) {
- IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
- storage.waitForNativeBinariesExtraction();
- }
- } finally {
- Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
- }
- }
-
- private int[] getInstalledUsers(PackageSetting ps, int userId) {
- final int[] allUserIds = resolveUserIds(userId);
- final ArrayList<Integer> installedUserIdsList = new ArrayList<>();
- for (int i = 0; i < allUserIds.length; i++) {
- if (ps.getInstalled(allUserIds[i])) {
- installedUserIdsList.add(allUserIds[i]);
- }
- }
- final int numInstalledUserId = installedUserIdsList.size();
- final int[] installedUserIds = new int[numInstalledUserId];
- for (int i = 0; i < numInstalledUserId; i++) {
- installedUserIds[i] = installedUserIdsList.get(i);
- }
- return installedUserIds;
- }
-
- /**
- * Package states callback, used to listen for package state changes and send broadcasts
- */
- private final class IncrementalStatesCallback implements IncrementalStates.Callback {
- private final String mPackageName;
- private final int mUid;
- private final int[] mInstalledUserIds;
- IncrementalStatesCallback(String packageName, int uid, int[] installedUserIds) {
- mPackageName = packageName;
- mUid = uid;
- mInstalledUserIds = installedUserIds;
- }
-
- @Override
- public void onPackageFullyLoaded() {
- final SparseArray<int[]> newBroadcastAllowList;
- final String codePath;
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(mPackageName);
- if (ps == null) {
- return;
- }
- newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
- ps, mInstalledUserIds, mSettings.getPackagesLocked());
- codePath = ps.getPathString();
- }
- // Unregister progress listener
- mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
- // Make sure the information is preserved
- scheduleWriteSettingsLocked();
- }
- }
-
- /**
- * Loading progress callback, used to listen for progress changes and update package setting
- */
- private class IncrementalProgressListener extends IPackageLoadingProgressCallback.Stub {
- private final String mPackageName;
- IncrementalProgressListener(String packageName) {
- mPackageName = packageName;
- }
-
- @Override
- public void onPackageLoadingProgressChanged(float progress) {
- final PackageSetting ps;
- synchronized (mLock) {
- ps = mSettings.getPackageLPr(mPackageName);
- if (ps == null) {
- return;
- }
- ps.setLoadingProgress(progress);
- }
- }
- }
-
@Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
int userId) {
final PackageSetting ps;
@@ -20235,916 +17460,35 @@ public class PackageManagerService extends IPackageManager.Stub
return ps;
}
- private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
- final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
- final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
- final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
-
- PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
- pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
- pkgChangeEvent.version = pkgSetting.versionCode;
- pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
- pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
- pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
- pkgChangeEvent.isDeleted = false;
-
- notifyPackageChangeObservers(pkgChangeEvent);
- }
-
private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
- PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
- pkgChangeEvent.packageName = packageName;
- pkgChangeEvent.version = version;
- pkgChangeEvent.lastUpdateTimeMillis = 0L;
- pkgChangeEvent.newInstalled = false;
- pkgChangeEvent.dataRemoved = false;
- pkgChangeEvent.isDeleted = true;
-
- notifyPackageChangeObservers(pkgChangeEvent);
- }
-
- private void notifyPackageChangeObservers(PackageChangeEvent event) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
- synchronized (mPackageChangeObservers) {
- for(IPackageChangeObserver observer : mPackageChangeObservers) {
- try {
- observer.onPackageChanged(event);
- } catch(RemoteException e) {
- Log.wtf(TAG, e);
- }
- }
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * The set of data needed to successfully install the prepared package. This includes data that
- * will be used to scan and reconcile the package.
- */
- private static class PrepareResult {
- public final boolean replace;
- public final int scanFlags;
- public final int parseFlags;
- @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
- public final AndroidPackage existingPackage;
- public final ParsedPackage packageToScan;
- public final boolean clearCodeCache;
- public final boolean system;
- public final PackageSetting originalPs;
- public final PackageSetting disabledPs;
-
- private PrepareResult(boolean replace, int scanFlags,
- int parseFlags, AndroidPackage existingPackage,
- ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
- PackageSetting originalPs, PackageSetting disabledPs) {
- this.replace = replace;
- this.scanFlags = scanFlags;
- this.parseFlags = parseFlags;
- this.existingPackage = existingPackage;
- this.packageToScan = packageToScan;
- this.clearCodeCache = clearCodeCache;
- this.system = system;
- this.originalPs = originalPs;
- this.disabledPs = disabledPs;
- }
- }
-
- private static class PrepareFailure extends PackageManagerException {
-
- public String conflictingPackage;
- public String conflictingPermission;
-
- PrepareFailure(int error) {
- super(error, "Failed to prepare for install.");
- }
-
- PrepareFailure(int error, String detailMessage) {
- super(error, detailMessage);
- }
+ PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+ pkgChangeEvent.packageName = packageName;
+ pkgChangeEvent.version = version;
+ pkgChangeEvent.lastUpdateTimeMillis = 0L;
+ pkgChangeEvent.newInstalled = false;
+ pkgChangeEvent.dataRemoved = false;
+ pkgChangeEvent.isDeleted = true;
- PrepareFailure(String message, Exception e) {
- super(e instanceof PackageParserException
- ? ((PackageParserException) e).error
- : ((PackageManagerException) e).error,
- ExceptionUtils.getCompleteMessage(message, e));
- }
-
- PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
- String conflictingPackage) {
- this.conflictingPermission = conflictingPermission;
- this.conflictingPackage = conflictingPackage;
- return this;
- }
- }
-
- private boolean doesSignatureMatchForPermissions(@NonNull String sourcePackageName,
- @NonNull ParsedPackage parsedPackage, int scanFlags) {
- // If the defining package is signed with our cert, it's okay. This
- // also includes the "updating the same package" case, of course.
- // "updating same package" could also involve key-rotation.
-
- final PackageSetting sourcePackageSetting;
- synchronized (mLock) {
- sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
- }
-
- final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
- ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
- final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- if (sourcePackageName.equals(parsedPackage.getPackageName())
- && (ksms.shouldCheckUpgradeKeySetLocked(
- sourcePackageSetting, scanFlags))) {
- return ksms.checkUpgradeKeySetLocked(sourcePackageSetting, parsedPackage);
- } else {
-
- // in the event of signing certificate rotation, we need to see if the
- // package's certificate has rotated from the current one, or if it is an
- // older certificate with which the current is ok with sharing permissions
- if (sourceSigningDetails.checkCapability(
- parsedPackage.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- return true;
- } else if (parsedPackage.getSigningDetails().checkCapability(
- sourceSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
- // the scanned package checks out, has signing certificate rotation
- // history, and is newer; bring it over
- synchronized (mLock) {
- sourcePackageSetting.signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- }
- return true;
- } else {
- return false;
- }
- }
+ notifyPackageChangeObservers(pkgChangeEvent);
}
- /*
- * Cannot properly check CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS using CompatChanges
- * as this only works for packages that are installed
- *
- * TODO: Move logic for permission group compatibility into PermissionManagerService
- */
- @SuppressWarnings("AndroidFrameworkCompatChange")
- private static boolean cannotInstallWithBadPermissionGroups(ParsedPackage parsedPackage) {
- return parsedPackage.getTargetSdkVersion() >= Build.VERSION_CODES.S;
- }
-
- @GuardedBy("mInstallLock")
- private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
- throws PrepareFailure {
- final int installFlags = args.installFlags;
- final File tmpPackageFile = new File(args.getCodePath());
- final boolean onExternal = args.volumeUuid != null;
- final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
- final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
- final boolean virtualPreload =
- ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- final boolean isRollback = args.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
- @ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
- if (args.move != null) {
- // moving a complete application; perform an initial scan on the new install location
- scanFlags |= SCAN_INITIAL;
- }
- if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
- scanFlags |= SCAN_DONT_KILL_APP;
- }
- if (instantApp) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (fullApp) {
- scanFlags |= SCAN_AS_FULL_APP;
- }
- if (virtualPreload) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
-
- // Validity check
- if (instantApp && onExternal) {
- Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
-
- // Retrieve PackageSettings and parse package
- @ParseFlags final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
- | ParsingPackageUtils.PARSE_ENFORCE_CODE
- | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
- final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
- parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
- AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
- } catch (PackageParserException e) {
- throw new PrepareFailure("Failed parse during installPackageLI", e);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Instant apps have several additional install-time checks.
- if (instantApp) {
- if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " does not target at least O");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package must target at least O");
- }
- if (parsedPackage.getSharedUserId() != null) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " may not declare sharedUserId.");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package may not declare a sharedUserId");
- }
- }
-
- if (parsedPackage.isStaticSharedLibrary()) {
- // Static shared libraries have synthetic package names
- renameStaticSharedLibraryPackage(parsedPackage);
-
- // No static shared libs on external storage
- if (onExternal) {
- Slog.i(TAG, "Static shared libs can only be installed on internal storage.");
- throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Packages declaring static-shared libs cannot be updated");
- }
- }
-
- String pkgName = res.name = parsedPackage.getPackageName();
- if (parsedPackage.isTestOnly()) {
- if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
- throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
- }
- }
-
+ void notifyPackageChangeObservers(PackageChangeEvent event) {
try {
- // either use what we've been given or parse directly from the APK
- if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
- parsedPackage.setSigningDetails(args.signingDetails);
- } else {
- parsedPackage.setSigningDetails(ParsingPackageUtils.getSigningDetails(
- parsedPackage, false /* skipVerify */));
- }
- } catch (PackageParserException e) {
- throw new PrepareFailure("Failed collect during installPackageLI", e);
- }
-
- if (instantApp && parsedPackage.getSigningDetails().signatureSchemeVersion
- < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
- Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
- + " is not signed with at least APK Signature Scheme v2");
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Instant app package must be signed with APK Signature Scheme v2 or greater");
- }
-
- boolean systemApp = false;
- boolean replace = false;
- synchronized (mLock) {
- // Check if installing already existing package
- if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
- String oldName = mSettings.getRenamedPackageLPr(pkgName);
- if (parsedPackage.getOriginalPackages().contains(oldName)
- && mPackages.containsKey(oldName)) {
- // This package is derived from an original package,
- // and this device has been updating from that original
- // name. We must continue using the original name, so
- // rename the new package here.
- parsedPackage.setPackageName(oldName);
- pkgName = parsedPackage.getPackageName();
- replace = true;
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "Replacing existing renamed package: oldName="
- + oldName + " pkgName=" + pkgName);
- }
- } else if (mPackages.containsKey(pkgName)) {
- // This package, under its official name, already exists
- // on the device; we should replace it.
- replace = true;
- if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
- }
-
- if (replace) {
- // Prevent apps opting out from runtime permissions
- AndroidPackage oldPackage = mPackages.get(pkgName);
- final int oldTargetSdk = oldPackage.getTargetSdkVersion();
- final int newTargetSdk = parsedPackage.getTargetSdkVersion();
- if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
- && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
- "Package " + parsedPackage.getPackageName()
- + " new target SDK " + newTargetSdk
- + " doesn't support runtime permissions but the old"
- + " target SDK " + oldTargetSdk + " does.");
- }
- // Prevent persistent apps from being updated
- if (oldPackage.isPersistent()
- && ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
- "Package " + oldPackage.getPackageName() + " is a persistent app. "
- + "Persistent apps are not updateable.");
- }
- }
- }
-
- PackageSetting ps = mSettings.getPackageLPr(pkgName);
- if (ps != null) {
- if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
-
- // Static shared libs have same package with different versions where
- // we internally use a synthetic package name to allow multiple versions
- // of the same package, therefore we need to compare signatures against
- // the package setting for the latest library version.
- PackageSetting signatureCheckPs = ps;
- if (parsedPackage.isStaticSharedLibrary()) {
- SharedLibraryInfo libraryInfo = getLatestSharedLibraVersionLPr(parsedPackage);
- if (libraryInfo != null) {
- signatureCheckPs = mSettings.getPackageLPr(libraryInfo.getPackageName());
- }
- }
-
- // Quick validity check that we're signed correctly if updating;
- // we'll check this again later when scanning, but we want to
- // bail early here before tripping over redefined permissions.
- final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
- + parsedPackage.getPackageName() + " upgrade keys do not match the "
- + "previously installed version");
- }
- } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
+ synchronized (mPackageChangeObservers) {
+ for (IPackageChangeObserver observer : mPackageChangeObservers) {
try {
- final boolean compareCompat = isCompatSignatureUpdateNeeded(parsedPackage);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(
- parsedPackage);
- // We don't care about disabledPkgSetting on install for now.
- final boolean compatMatch = verifySignatures(signatureCheckPs, null,
- parsedPackage.getSigningDetails(), compareCompat, compareRecover,
- isRollback);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- synchronized (mLock) {
- ksms.removeAppKeySetDataLPw(parsedPackage.getPackageName());
- }
- }
- } catch (PackageManagerException e) {
- throw new PrepareFailure(e.error, e.getMessage());
- }
- }
-
- if (ps.pkg != null) {
- systemApp = ps.pkg.isSystem();
- }
- res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
- }
-
- final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
- for (int groupNum = 0; groupNum < numGroups; groupNum++) {
- final ParsedPermissionGroup group =
- parsedPackage.getPermissionGroups().get(groupNum);
- final PermissionGroupInfo sourceGroup = getPermissionGroupInfo(group.getName(), 0);
-
- if (sourceGroup != null
- && cannotInstallWithBadPermissionGroups(parsedPackage)) {
- final String sourcePackageName = sourceGroup.packageName;
-
- if ((replace || !parsedPackage.getPackageName().equals(sourcePackageName))
- && !doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
- scanFlags)) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to redeclare permission group "
- + group.getName() + " already owned by "
- + sourcePackageName);
- }
- }
- }
-
- // TODO: Move logic for checking permission compatibility into PermissionManagerService
- final int N = ArrayUtils.size(parsedPackage.getPermissions());
- for (int i = N - 1; i >= 0; i--) {
- final ParsedPermission perm = parsedPackage.getPermissions().get(i);
- final Permission bp = mPermissionManager.getPermissionTEMP(perm.getName());
-
- // Don't allow anyone but the system to define ephemeral permissions.
- if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
- && !systemApp) {
- Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
- + " attempting to delcare ephemeral permission "
- + perm.getName() + "; Removing ephemeral.");
- perm.setProtectionLevel(perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
- }
-
- // Check whether the newly-scanned package wants to define an already-defined perm
- if (bp != null) {
- final String sourcePackageName = bp.getPackageName();
-
- if (!doesSignatureMatchForPermissions(sourcePackageName, parsedPackage,
- scanFlags)) {
- // If the owning package is the system itself, we log but allow
- // install to proceed; we fail the install on all other permission
- // redefinitions.
- if (!sourcePackageName.equals("android")) {
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
- + parsedPackage.getPackageName()
- + " attempting to redeclare permission "
- + perm.getName() + " already owned by "
- + sourcePackageName)
- .conflictsWithExistingPermission(perm.getName(),
- sourcePackageName);
- } else {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " attempting to redeclare system permission "
- + perm.getName() + "; ignoring new declaration");
- parsedPackage.removePermission(i);
- }
- } else if (!PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())) {
- // Prevent apps to change protection level to dangerous from any other
- // type as this would allow a privilege escalation where an app adds a
- // normal/signature permission in other app's group and later redefines
- // it as dangerous leading to the group auto-grant.
- if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
- == PermissionInfo.PROTECTION_DANGEROUS) {
- if (bp != null && !bp.isRuntime()) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " trying to change a non-runtime permission "
- + perm.getName()
- + " to runtime; keeping old protection level");
- perm.setProtectionLevel(bp.getProtectionLevel());
- }
- }
- }
- }
-
- if (perm.getGroup() != null
- && cannotInstallWithBadPermissionGroups(parsedPackage)) {
- boolean isPermGroupDefinedByPackage = false;
- for (int groupNum = 0; groupNum < numGroups; groupNum++) {
- if (parsedPackage.getPermissionGroups().get(groupNum).getName()
- .equals(perm.getGroup())) {
- isPermGroupDefinedByPackage = true;
- break;
- }
- }
-
- if (!isPermGroupDefinedByPackage) {
- final PermissionGroupInfo sourceGroup =
- getPermissionGroupInfo(perm.getGroup(), 0);
-
- if (sourceGroup == null) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to declare permission "
- + perm.getName() + " in non-existing group "
- + perm.getGroup());
- } else {
- String groupSourcePackageName = sourceGroup.packageName;
-
- if (!PLATFORM_PACKAGE_NAME.equals(groupSourcePackageName)
- && !doesSignatureMatchForPermissions(groupSourcePackageName,
- parsedPackage, scanFlags)) {
- EventLog.writeEvent(0x534e4554, "146211400", -1,
- parsedPackage.getPackageName());
-
- throw new PrepareFailure(INSTALL_FAILED_BAD_PERMISSION_GROUP,
- "Package "
- + parsedPackage.getPackageName()
- + " attempting to declare permission "
- + perm.getName() + " in group "
- + perm.getGroup() + " owned by package "
- + groupSourcePackageName
- + " with incompatible certificate");
- }
- }
+ observer.onPackageChanged(event);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, e);
}
}
}
- }
-
- if (systemApp) {
- if (onExternal) {
- // Abort update; system app can't be replaced with app on sdcard
- throw new PrepareFailure(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Cannot install updates to system apps on sdcard");
- } else if (instantApp) {
- // Abort update; system app can't be replaced with an instant app
- throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
- "Cannot update a system app with an instant app");
- }
- }
-
- if (args.move != null) {
- // We did an in-place move, so dex is ready to roll
- scanFlags |= SCAN_NO_DEX;
- scanFlags |= SCAN_MOVE;
-
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(pkgName);
- if (ps == null) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR,
- "Missing settings for moved package " + pkgName);
- }
-
- // We moved the entire application as-is, so bring over the
- // previously derived ABI information.
- parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)
- .setSecondaryCpuAbi(ps.secondaryCpuAbiString);
- }
-
- } else {
- // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
- scanFlags |= SCAN_NO_DEX;
-
- try {
- PackageSetting pkgSetting;
- synchronized (mLock) {
- pkgSetting = mSettings.getPackageLPr(pkgName);
- }
- boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
- && pkgSetting.getPkgState().isUpdatedSystemApp();
- final String abiOverride = deriveAbiOverride(args.abiOverride);
- AndroidPackage oldPackage = mPackages.get(pkgName);
- boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
- isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, mAppLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- } catch (PackageManagerException pme) {
- Slog.e(TAG, "Error deriving application ABI", pme);
- throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Error deriving application ABI: " + pme.getMessage());
- }
- }
-
- if (!args.doRename(res.returnCode, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
- }
-
- try {
- setUpFsVerityIfPossible(parsedPackage);
- } catch (InstallerException | IOException | DigestException | NoSuchAlgorithmException e) {
- throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed to set up verity: " + e);
- }
-
- final PackageFreezer freezer =
- freezePackageForInstall(pkgName, installFlags, "installPackageLI");
- boolean shouldCloseFreezerBeforeReturn = true;
- try {
- final AndroidPackage existingPackage;
- String renamedPackage = null;
- boolean sysPkg = false;
- int targetScanFlags = scanFlags;
- int targetParseFlags = parseFlags;
- final PackageSetting ps;
- final PackageSetting disabledPs;
- if (replace) {
- if (parsedPackage.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // and cannot be updated as an update would get a new package name,
- // unless this is installed from adb which is useful for development.
- AndroidPackage existingPkg = mPackages.get(parsedPackage.getPackageName());
- if (existingPkg != null
- && (installFlags & PackageManager.INSTALL_FROM_ADB) == 0) {
- throw new PrepareFailure(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Packages declaring "
- + "static-shared libs cannot be updated");
- }
- }
-
- final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-
- final AndroidPackage oldPackage;
- final String pkgName11 = parsedPackage.getPackageName();
- final int[] allUsers;
- final int[] installedUsers;
- final int[] uninstalledUsers;
-
- synchronized (mLock) {
- oldPackage = mPackages.get(pkgName11);
- existingPackage = oldPackage;
- if (DEBUG_INSTALL) {
- Slog.d(TAG,
- "replacePackageLI: new=" + parsedPackage + ", old=" + oldPackage);
- }
-
- ps = mSettings.getPackageLPr(pkgName11);
- disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
-
- // verify signatures are valid
- final KeySetManagerService ksms = mSettings.getKeySetManagerService();
- if (ksms.shouldCheckUpgradeKeySetLocked(ps, scanFlags)) {
- if (!ksms.checkUpgradeKeySetLocked(ps, parsedPackage)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package not signed by keys specified by upgrade-keysets: "
- + pkgName11);
- }
- } else {
- SigningDetails parsedPkgSigningDetails = parsedPackage.getSigningDetails();
- SigningDetails oldPkgSigningDetails = oldPackage.getSigningDetails();
- // default to original signature matching
- if (!parsedPkgSigningDetails.checkCapability(oldPkgSigningDetails,
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !oldPkgSigningDetails.checkCapability(parsedPkgSigningDetails,
- SigningDetails.CertCapabilities.ROLLBACK)) {
- // Allow the update to proceed if this is a rollback and the parsed
- // package's current signing key is the current signer or in the lineage
- // of the old package; this allows a rollback to a previously installed
- // version after an app's signing key has been rotated without requiring
- // the rollback capability on the previous signing key.
- if (!isRollback || !oldPkgSigningDetails.hasAncestorOrSelf(
- parsedPkgSigningDetails)) {
- throw new PrepareFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "New package has a different signature: " + pkgName11);
- }
- }
- }
-
- // don't allow a system upgrade unless the upgrade hash matches
- if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
- final byte[] digestBytes;
- try {
- final MessageDigest digest = MessageDigest.getInstance("SHA-512");
- updateDigest(digest, new File(parsedPackage.getBaseApkPath()));
- if (!ArrayUtils.isEmpty(parsedPackage.getSplitCodePaths())) {
- for (String path : parsedPackage.getSplitCodePaths()) {
- updateDigest(digest, new File(path));
- }
- }
- digestBytes = digest.digest();
- } catch (NoSuchAlgorithmException | IOException e) {
- throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
- "Could not compute hash: " + pkgName11);
- }
- if (!Arrays.equals(oldPackage.getRestrictUpdateHash(), digestBytes)) {
- throw new PrepareFailure(INSTALL_FAILED_INVALID_APK,
- "New package fails restrict-update check: " + pkgName11);
- }
- // retain upgrade restriction
- parsedPackage.setRestrictUpdateHash(oldPackage.getRestrictUpdateHash());
- }
-
- // Check for shared user id changes
- String invalidPackageName = null;
- if (!Objects.equals(oldPackage.getSharedUserId(),
- parsedPackage.getSharedUserId())) {
- invalidPackageName = parsedPackage.getPackageName();
- }
-
- if (invalidPackageName != null) {
- throw new PrepareFailure(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
- "Package " + invalidPackageName + " tried to change user "
- + oldPackage.getSharedUserId());
- }
-
- // In case of rollback, remember per-user/profile install state
- allUsers = mUserManager.getUserIds();
- installedUsers = ps.queryInstalledUsers(allUsers, true);
- uninstalledUsers = ps.queryInstalledUsers(allUsers, false);
-
-
- // don't allow an upgrade from full to ephemeral
- if (isInstantApp) {
- if (args.user == null || args.user.getIdentifier() == UserHandle.USER_ALL) {
- for (int currentUser : allUsers) {
- if (!ps.getInstantApp(currentUser)) {
- // can't downgrade from full to instant
- Slog.w(TAG,
- "Can't replace full app with instant app: " + pkgName11
- + " for user: " + currentUser);
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
- }
- } else if (!ps.getInstantApp(args.user.getIdentifier())) {
- // can't downgrade from full to instant
- Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
- + " for user: " + args.user.getIdentifier());
- throw new PrepareFailure(
- PackageManager.INSTALL_FAILED_SESSION_INVALID);
- }
- }
- }
-
- // Update what is removed
- res.removedInfo = new PackageRemovedInfo(this);
- res.removedInfo.uid = oldPackage.getUid();
- res.removedInfo.removedPackage = oldPackage.getPackageName();
- res.removedInfo.installerPackageName = ps.installSource.installerPackageName;
- res.removedInfo.isStaticSharedLib = parsedPackage.getStaticSharedLibName() != null;
- res.removedInfo.isUpdate = true;
- res.removedInfo.origUsers = installedUsers;
- res.removedInfo.installReasons = new SparseArray<>(installedUsers.length);
- for (int i = 0; i < installedUsers.length; i++) {
- final int userId = installedUsers[i];
- res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
- }
- res.removedInfo.uninstallReasons = new SparseArray<>(uninstalledUsers.length);
- for (int i = 0; i < uninstalledUsers.length; i++) {
- final int userId = uninstalledUsers[i];
- res.removedInfo.uninstallReasons.put(userId, ps.getUninstallReason(userId));
- }
-
- sysPkg = oldPackage.isSystem();
- if (sysPkg) {
- // Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged = oldPackage.isPrivileged();
- final boolean oem = oldPackage.isOem();
- final boolean vendor = oldPackage.isVendor();
- final boolean product = oldPackage.isProduct();
- final boolean odm = oldPackage.isOdm();
- final boolean systemExt = oldPackage.isSystemExt();
- final @ParseFlags int systemParseFlags = parseFlags;
- final @ScanFlags int systemScanFlags = scanFlags
- | SCAN_AS_SYSTEM
- | (privileged ? SCAN_AS_PRIVILEGED : 0)
- | (oem ? SCAN_AS_OEM : 0)
- | (vendor ? SCAN_AS_VENDOR : 0)
- | (product ? SCAN_AS_PRODUCT : 0)
- | (odm ? SCAN_AS_ODM : 0)
- | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
-
- if (DEBUG_INSTALL) {
- Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
- + ", old=" + oldPackage);
- }
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- targetParseFlags = systemParseFlags;
- targetScanFlags = systemScanFlags;
- } else { // non system replace
- replace = true;
- if (DEBUG_INSTALL) {
- Slog.d(TAG,
- "replaceNonSystemPackageLI: new=" + parsedPackage + ", old="
- + oldPackage);
- }
- }
- } else { // new package install
- ps = null;
- disabledPs = null;
- replace = false;
- existingPackage = null;
- // Remember this for later, in case we need to rollback this install
- String pkgName1 = parsedPackage.getPackageName();
-
- if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + parsedPackage);
-
- // TODO(patb): MOVE TO RECONCILE
- synchronized (mLock) {
- renamedPackage = mSettings.getRenamedPackageLPr(pkgName1);
- if (renamedPackage != null) {
- // A package with the same name is already installed, though
- // it has been renamed to an older name. The package we
- // are trying to install should be installed as an update to
- // the existing one, but that has not been requested, so bail.
- throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
- "Attempt to re-install " + pkgName1
- + " without first uninstalling package running as "
- + renamedPackage);
- }
- if (mPackages.containsKey(pkgName1)) {
- // Don't allow installation over an existing package with the same name.
- throw new PrepareFailure(INSTALL_FAILED_ALREADY_EXISTS,
- "Attempt to re-install " + pkgName1
- + " without first uninstalling.");
- }
- }
- }
- // we're passing the freezer back to be closed in a later phase of install
- shouldCloseFreezerBeforeReturn = false;
-
- return new PrepareResult(replace, targetScanFlags, targetParseFlags,
- existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
- ps, disabledPs);
} finally {
- res.freezer = freezer;
- if (shouldCloseFreezerBeforeReturn) {
- freezer.close();
- }
- }
- }
-
- /**
- * Set up fs-verity for the given package if possible. This requires a feature flag of system
- * property to be enabled only if the kernel supports fs-verity.
- *
- * <p>When the feature flag is set to legacy mode, only APK is supported (with some experimental
- * kernel patches). In normal mode, all file format can be supported.
- */
- private void setUpFsVerityIfPossible(AndroidPackage pkg) throws InstallerException,
- PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
- final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
- final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
- if (!standardMode && !legacyMode) {
- return;
- }
-
- if (isIncrementalPath(pkg.getPath()) && IncrementalManager.getVersion()
- < IncrementalManager.MIN_VERSION_TO_SUPPORT_FSVERITY) {
- return;
- }
-
- // Collect files we care for fs-verity setup.
- ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
- if (legacyMode) {
- synchronized (mLock) {
- final PackageSetting ps = mSettings.getPackageLPr(pkg.getPackageName());
- if (ps != null && ps.isPrivileged()) {
- fsverityCandidates.put(pkg.getBaseApkPath(), null);
- if (pkg.getSplitCodePaths() != null) {
- for (String splitPath : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(splitPath, null);
- }
- }
- }
- }
- } else {
- // NB: These files will become only accessible if the signing key is loaded in kernel's
- // .fs-verity keyring.
- fsverityCandidates.put(pkg.getBaseApkPath(),
- VerityUtils.getFsveritySignatureFilePath(pkg.getBaseApkPath()));
-
- final String dmPath = DexMetadataHelper.buildDexMetadataPathForApk(
- pkg.getBaseApkPath());
- if (new File(dmPath).exists()) {
- fsverityCandidates.put(dmPath, VerityUtils.getFsveritySignatureFilePath(dmPath));
- }
-
- if (pkg.getSplitCodePaths() != null) {
- for (String path : pkg.getSplitCodePaths()) {
- fsverityCandidates.put(path, VerityUtils.getFsveritySignatureFilePath(path));
-
- final String splitDmPath = DexMetadataHelper.buildDexMetadataPathForApk(path);
- if (new File(splitDmPath).exists()) {
- fsverityCandidates.put(splitDmPath,
- VerityUtils.getFsveritySignatureFilePath(splitDmPath));
- }
- }
- }
- }
-
- for (Map.Entry<String, String> entry : fsverityCandidates.entrySet()) {
- final String filePath = entry.getKey();
- final String signaturePath = entry.getValue();
-
- if (!legacyMode) {
- // fs-verity is optional for now. Only set up if signature is provided.
- if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
- try {
- VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException e) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to enable fs-verity: " + e);
- }
- }
- continue;
- }
-
- // In legacy mode, fs-verity can only be enabled by process with CAP_SYS_ADMIN.
- final VerityUtils.SetupResult result = VerityUtils.generateApkVeritySetupData(filePath);
- if (result.isOk()) {
- if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling verity to " + filePath);
- final FileDescriptor fd = result.getUnownedFileDescriptor();
- try {
- final byte[] rootHash = VerityUtils.generateApkVerityRootHash(filePath);
- try {
- // A file may already have fs-verity, e.g. when reused during a split
- // install. If the measurement succeeds, no need to attempt to set up.
- mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- } catch (InstallerException e) {
- mInstaller.installApkVerity(filePath, fd, result.getContentSize());
- mInstaller.assertFsverityRootHashMatches(filePath, rootHash);
- }
- } finally {
- IoUtils.closeQuietly(fd);
- }
- } else if (result.isFailed()) {
- throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
- "Failed to generate verity");
- }
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- private static boolean isExternal(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
private static boolean isSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@@ -21153,7 +17497,7 @@ public class PackageManagerService extends IPackageManager.Stub
return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
}
- private VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
+ VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
if (pkg.isExternalStorage()) {
if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
return mSettings.getExternalVersion();
@@ -21393,12 +17737,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Allow caller having MANAGE_PROFILE_AND_DEVICE_OWNERS permission to silently
// uninstall for device owner provisioning.
- if (checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid)
- == PERMISSION_GRANTED) {
- return true;
- }
-
- return false;
+ return checkUidPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, callingUid)
+ == PERMISSION_GRANTED;
}
private int[] getBlockUninstallForUsers(String packageName, int[] userIds) {
@@ -21556,7 +17896,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- info.origUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
+ info.mOrigUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
if (isUpdatedSystemApp(uninstalledPs)
&& ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
@@ -21581,15 +17921,15 @@ public class PackageManagerService extends IPackageManager.Stub
try (PackageFreezer freezer = freezePackageForDelete(packageName, freezeUser,
deleteFlags, "deletePackageX")) {
res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers,
- deleteFlags | PackageManager.DELETE_CHATTY, info, true, null);
+ deleteFlags | PackageManager.DELETE_CHATTY, info, true);
}
synchronized (mLock) {
if (res) {
if (pkg != null) {
mInstantAppRegistry.onPackageUninstalledLPw(pkg, uninstalledPs,
- info.removedUsers);
+ info.mRemovedUsers);
}
- updateSequenceNumberLP(uninstalledPs, info.removedUsers);
+ updateSequenceNumberLP(uninstalledPs, info.mRemovedUsers);
updateInstantAppInstallerLocked(packageName);
}
}
@@ -21606,8 +17946,8 @@ public class PackageManagerService extends IPackageManager.Stub
// Delete the resources here after sending the broadcast to let
// other processes clean up before deleting resources.
synchronized (mInstallLock) {
- if (info.args != null) {
- info.args.doPostDeleteLI(true);
+ if (info.mArgs != null) {
+ info.mArgs.doPostDeleteLI(true);
}
boolean reEnableStub = false;
@@ -21659,135 +17999,6 @@ public class PackageManagerService extends IPackageManager.Stub
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
- static class PackageRemovedInfo {
- final PackageSender packageSender;
- String removedPackage;
- String installerPackageName;
- int uid = -1;
- int removedAppId = -1;
- int[] origUsers;
- int[] removedUsers = null;
- int[] broadcastUsers = null;
- int[] instantUserIds = null;
- SparseArray<Integer> installReasons;
- SparseArray<Integer> uninstallReasons;
- boolean isRemovedPackageSystemUpdate = false;
- boolean isUpdate;
- boolean dataRemoved;
- boolean removedForAllUsers;
- boolean isStaticSharedLib;
- // a two dimensional array mapping userId to the set of appIds that can receive notice
- // of package changes
- SparseArray<int[]> broadcastAllowList;
- // Clean up resources deleted packages.
- InstallArgs args = null;
-
- PackageRemovedInfo(PackageSender packageSender) {
- this.packageSender = packageSender;
- }
-
- void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem) {
- sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
- }
-
- void sendSystemPackageUpdatedBroadcasts() {
- if (isRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
- }
- }
-
- private void sendSystemPackageUpdatedBroadcastsInternal() {
- Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
- 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
- packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- removedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
- if (installerPackageName != null) {
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- removedPackage, extras, 0 /*flags*/,
- installerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- removedPackage, extras, 0 /*flags*/,
- installerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
- }
- }
-
- private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem) {
- // Don't send static shared library removal broadcasts as these
- // libs are visible only the the apps that depend on them an one
- // cannot remove the library if it has a dependency.
- if (isStaticSharedLib) {
- return;
- }
- Bundle extras = new Bundle(2);
- final int removedUid = removedAppId >= 0 ? removedAppId : uid;
- extras.putInt(Intent.EXTRA_UID, removedUid);
- extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
- extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- if (isUpdate || isRemovedPackageSystemUpdate) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
- if (removedPackage != null) {
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- removedPackage, extras, 0, null /*targetPackage*/, null,
- broadcastUsers, instantUserIds, broadcastAllowList, null);
- if (installerPackageName != null) {
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
- removedPackage, extras, 0 /*flags*/,
- installerPackageName, null, broadcastUsers, instantUserIds, null, null);
- }
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
- removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
- null /*finishedReceiver*/, broadcastUsers, instantUserIds,
- broadcastAllowList, null /*bOptions*/);
- if (dataRemoved && !isRemovedPackageSystemUpdate) {
- packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
- removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
- null, broadcastUsers, instantUserIds, broadcastAllowList, null);
- packageSender.notifyPackageRemoved(removedPackage, removedUid);
- }
- }
- if (removedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
- extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
- }
-
- packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
- null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- null, null, broadcastUsers, instantUserIds, broadcastAllowList, null);
- }
- }
-
- void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
- removedUsers = userIds;
- if (removedUsers == null) {
- broadcastUsers = null;
- return;
- }
-
- broadcastUsers = EMPTY_INT_ARRAY;
- instantUserIds = EMPTY_INT_ARRAY;
- for (int i = userIds.length - 1; i >= 0; --i) {
- final int userId = userIds[i];
- if (deletedPackageSetting.getInstantApp(userId)) {
- instantUserIds = ArrayUtils.appendInt(instantUserIds, userId);
- } else {
- broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
- }
- }
- }
- }
/*
* This method deletes the package from internal data structures. If the DELETE_KEEP_DATA
@@ -21802,9 +18013,9 @@ public class PackageManagerService extends IPackageManager.Stub
// Retrieve object to delete permissions for shared user later on
final AndroidPackage deletedPkg = deletedPs.pkg;
if (outInfo != null) {
- outInfo.removedPackage = packageName;
- outInfo.installerPackageName = deletedPs.installSource.installerPackageName;
- outInfo.isStaticSharedLib = deletedPkg != null
+ outInfo.mRemovedPackage = packageName;
+ outInfo.mInstallerPackageName = deletedPs.installSource.installerPackageName;
+ outInfo.mIsStaticSharedLib = deletedPkg != null
&& deletedPkg.getStaticSharedLibName() != null;
outInfo.populateUsers(deletedPs == null ? null
: deletedPs.queryInstalledUsers(mUserManager.getUserIds(), true), deletedPs);
@@ -21826,7 +18037,7 @@ public class PackageManagerService extends IPackageManager.Stub
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
destroyAppProfilesLIF(resolvedPkg);
if (outInfo != null) {
- outInfo.dataRemoved = true;
+ outInfo.mDataRemoved = true;
}
}
@@ -21843,7 +18054,7 @@ public class PackageManagerService extends IPackageManager.Stub
mAppsFilter.removePackage(getPackageSetting(packageName));
removedAppId = mSettings.removePackageLPw(packageName);
if (outInfo != null) {
- outInfo.removedAppId = removedAppId;
+ outInfo.mRemovedAppId = removedAppId;
}
if (!mSettings.isDisabledSystemPackageLPr(packageName)) {
// If we don't have a disabled system package to reinstall, the package is
@@ -21859,6 +18070,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
clearPackagePreferredActivitiesLPw(
deletedPs.name, changedUsers, UserHandle.USER_ALL);
+
+ mSettings.removeRenamedPackageLPw(deletedPs.realName);
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(changedUsers);
@@ -21867,12 +18080,12 @@ public class PackageManagerService extends IPackageManager.Stub
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
- if (outInfo != null && outInfo.origUsers != null) {
+ if (outInfo != null && outInfo.mOrigUsers != null) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
for (int userId : allUserHandles) {
- final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
+ final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId);
if (DEBUG_REMOVE) {
Slog.d(TAG, " user " + userId + " => " + installed);
}
@@ -21923,13 +18136,13 @@ public class PackageManagerService extends IPackageManager.Stub
@NonNull int[] allUserHandles, int flags, @Nullable PackageRemovedInfo outInfo,
boolean writeSettings)
throws SystemDeleteException {
- final boolean applyUserRestrictions = outInfo != null && (outInfo.origUsers != null);
+ final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
final AndroidPackage deletedPkg = deletedPs.pkg;
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
// reader
- final PackageSetting disabledPs = action.disabledPs;
+ final PackageSetting disabledPs = action.mDisabledPs;
if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.getPackageName()
+ " disabledPs=" + disabledPs);
Slog.d(TAG, "Deleting system pkg from data partition");
@@ -21938,7 +18151,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (applyUserRestrictions) {
Slog.d(TAG, "Remembering install states:");
for (int userId : allUserHandles) {
- final boolean finstalled = ArrayUtils.contains(outInfo.origUsers, userId);
+ final boolean finstalled = ArrayUtils.contains(outInfo.mOrigUsers, userId);
Slog.d(TAG, " u=" + userId + " inst=" + finstalled);
}
}
@@ -21946,7 +18159,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (outInfo != null) {
// Delete the updated package
- outInfo.isRemovedPackageSystemUpdate = true;
+ outInfo.mIsRemovedPackageSystemUpdate = true;
}
if (disabledPs.versionCode < deletedPs.versionCode) {
@@ -21976,7 +18189,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
try {
installPackageFromSystemLIF(disabledPs.getPathString(), allUserHandles,
- outInfo == null ? null : outInfo.origUsers, writeSettings);
+ outInfo == null ? null : outInfo.mOrigUsers, writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": "
+ e.getMessage());
@@ -21989,8 +18202,8 @@ public class PackageManagerService extends IPackageManager.Stub
// and re-enable it afterward.
final PackageSetting stubPs = mSettings.getPackageLPr(deletedPkg.getPackageName());
if (stubPs != null) {
- int userId = action.user == null
- ? UserHandle.USER_ALL : action.user.getIdentifier();
+ int userId = action.mUser == null
+ ? UserHandle.USER_ALL : action.mUser.getIdentifier();
if (userId == UserHandle.USER_ALL) {
for (int aUserId : allUserHandles) {
stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
@@ -22006,7 +18219,7 @@ public class PackageManagerService extends IPackageManager.Stub
/**
* Installs a package that's already on the system partition.
*/
- private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString,
+ private void installPackageFromSystemLIF(@NonNull String codePathString,
@NonNull int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings)
throws PackageManagerException {
final File codePath = new File(codePathString);
@@ -22088,7 +18301,6 @@ public class PackageManagerService extends IPackageManager.Stub
writeSettingsLPrTEMP();
}
}
- return pkg;
}
private void deleteInstalledPackageLIF(PackageSetting ps,
@@ -22096,8 +18308,8 @@ public class PackageManagerService extends IPackageManager.Stub
PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mLock) {
if (outInfo != null) {
- outInfo.uid = ps.appId;
- outInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
+ outInfo.mUid = ps.appId;
+ outInfo.mBroadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
allUserHandles, mSettings.getPackagesLocked());
}
}
@@ -22107,10 +18319,10 @@ public class PackageManagerService extends IPackageManager.Stub
// Delete application code and resources only for parent packages
if (deleteCodeAndResources && (outInfo != null)) {
- outInfo.args = createInstallArgsForExisting(
+ outInfo.mArgs = createInstallArgsForExisting(
ps.getPathString(), getAppDexInstructionSets(
ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
- if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
+ if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.mArgs);
}
}
@@ -22166,23 +18378,6 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- private static class DeletePackageAction {
- public final PackageSetting deletingPs;
- public final PackageSetting disabledPs;
- public final PackageRemovedInfo outInfo;
- public final int flags;
- public final UserHandle user;
-
- private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
- PackageRemovedInfo outInfo, int flags, UserHandle user) {
- this.deletingPs = deletingPs;
- this.disabledPs = disabledPs;
- this.outInfo = outInfo;
- this.flags = flags;
- this.user = user;
- }
- }
-
/**
* @return a {@link DeletePackageAction} if the provided package and related state may be
* deleted, {@code null} otherwise.
@@ -22215,8 +18410,7 @@ public class PackageManagerService extends IPackageManager.Stub
*/
private boolean deletePackageLIF(@NonNull String packageName, UserHandle user,
boolean deleteCodeAndResources, @NonNull int[] allUserHandles, int flags,
- PackageRemovedInfo outInfo, boolean writeSettings,
- ParsedPackage replacingPackage) {
+ PackageRemovedInfo outInfo, boolean writeSettings) {
final DeletePackageAction action;
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
@@ -22232,7 +18426,7 @@ public class PackageManagerService extends IPackageManager.Stub
try {
executeDeletePackageLIF(action, packageName, deleteCodeAndResources,
- allUserHandles, writeSettings, replacingPackage);
+ allUserHandles, writeSettings);
} catch (SystemDeleteException e) {
if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: system deletion failure", e);
return false;
@@ -22240,23 +18434,14 @@ public class PackageManagerService extends IPackageManager.Stub
return true;
}
- private static class SystemDeleteException extends Exception {
- public final PackageManagerException reason;
-
- private SystemDeleteException(PackageManagerException reason) {
- this.reason = reason;
- }
- }
-
/** Deletes a package. Only throws when install of a disabled package fails. */
- private void executeDeletePackageLIF(DeletePackageAction action,
+ void executeDeletePackageLIF(DeletePackageAction action,
String packageName, boolean deleteCodeAndResources,
- @NonNull int[] allUserHandles, boolean writeSettings,
- ParsedPackage replacingPackage) throws SystemDeleteException {
- final PackageSetting ps = action.deletingPs;
- final PackageRemovedInfo outInfo = action.outInfo;
- final UserHandle user = action.user;
- final int flags = action.flags;
+ @NonNull int[] allUserHandles, boolean writeSettings) throws SystemDeleteException {
+ final PackageSetting ps = action.mDeletingPs;
+ final PackageRemovedInfo outInfo = action.mRemovedInfo;
+ final UserHandle user = action.mUser;
+ final int flags = action.mFlags;
final boolean systemApp = isSystemApp(ps);
// We need to get the permission state before package state is (potentially) destroyed.
@@ -22327,7 +18512,7 @@ public class PackageManagerService extends IPackageManager.Stub
// If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
// place for all affected users.
- int[] affectedUserIds = (outInfo != null) ? outInfo.removedUsers : null;
+ int[] affectedUserIds = (outInfo != null) ? outInfo.mRemovedUsers : null;
if (affectedUserIds == null) {
affectedUserIds = resolveUserIds(userId);
}
@@ -22340,7 +18525,7 @@ public class PackageManagerService extends IPackageManager.Stub
// Take a note whether we deleted the package for all users
if (outInfo != null) {
- outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
+ outInfo.mRemovedForAllUsers = mPackages.get(ps.name) == null;
}
}
@@ -22406,14 +18591,14 @@ public class PackageManagerService extends IPackageManager.Stub
if (outInfo != null) {
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- outInfo.dataRemoved = true;
+ outInfo.mDataRemoved = true;
}
- outInfo.removedPackage = ps.name;
- outInfo.installerPackageName = ps.installSource.installerPackageName;
- outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
- outInfo.removedAppId = ps.appId;
- outInfo.removedUsers = userIds;
- outInfo.broadcastUsers = userIds;
+ outInfo.mRemovedPackage = ps.name;
+ outInfo.mInstallerPackageName = ps.installSource.installerPackageName;
+ outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
+ outInfo.mRemovedAppId = ps.appId;
+ outInfo.mRemovedUsers = userIds;
+ outInfo.mBroadcastUsers = userIds;
}
}
@@ -22428,7 +18613,7 @@ public class PackageManagerService extends IPackageManager.Stub
try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
- clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
+ clearAppProfilesLIF(pkg);
}
}
}
@@ -22927,7 +19112,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
private void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
- int installReason, @UserIdInt int userId) {
+ @UserIdInt int userId) {
// We may also need to apply pending (restored) runtime permission grants
// within these users.
mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId);
@@ -23468,7 +19653,7 @@ public class PackageManagerService extends IPackageManager.Stub
final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
- intent, null, 0, resolveInfos, 0, true, false, false, userId);
+ intent, null, 0, resolveInfos, true, false, false, userId);
final String packageName = preferredResolveInfo != null
&& preferredResolveInfo.activityInfo != null
? preferredResolveInfo.activityInfo.packageName : null;
@@ -23581,25 +19766,6 @@ public class PackageManagerService extends IPackageManager.Stub
getPackageFromComponentString(R.string.config_defaultRotationResolverService));
}
- private @Nullable String getDocumenterPackageName() {
- final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
-
- final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType,
- MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
- | MATCH_DISABLED_COMPONENTS,
- UserHandle.myUserId());
- if (matches.size() == 1) {
- return matches.get(0).getComponentInfo().packageName;
- } else {
- Slog.e(TAG, "There should probably be exactly one documenter; found "
- + matches.size() + ": matches=" + matches);
- return null;
- }
- }
-
@Nullable
private String getDeviceConfiguratorPackageName() {
return ensureSystemPackageName(mContext.getString(
@@ -23667,10 +19833,10 @@ public class PackageManagerService extends IPackageManager.Stub
final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
if (androidPkg != null) {
final SigningDetails signingDetail = androidPkg.getSigningDetails();
- if (signingDetail != null && signingDetail.signatures != null) {
+ if (signingDetail != null && signingDetail.getSignatures() != null) {
try {
final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
- for (Signature signature : signingDetail.signatures) {
+ for (Signature signature : signingDetail.getSignatures()) {
if (TextUtils.equals(predefinedSignature,
HexEncoding.encodeToString(msgDigest.digest(
signature.toByteArray()), false))) {
@@ -23730,18 +19896,6 @@ public class PackageManagerService extends IPackageManager.Stub
return packageName;
}
- @Nullable
- private String[] ensureSystemPackageNames(@Nullable String[] packageNames) {
- if (packageNames == null) {
- return null;
- }
- final int packageNamesLength = packageNames.length;
- for (int i = 0; i < packageNamesLength; i++) {
- packageNames[i] = ensureSystemPackageName(packageNames[i]);
- }
- return ArrayUtils.filterNotNull(packageNames, String[]::new);
- }
-
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -23749,7 +19903,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (callingPackage == null) {
callingPackage = Integer.toString(Binder.getCallingUid());
}
- setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
+
+ setEnabledSettings(List.of(new ComponentEnabledSetting(appPackageName, newState, flags)),
+ userId, callingPackage);
}
@Override
@@ -23846,229 +20002,270 @@ public class PackageManagerService extends IPackageManager.Stub
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
if (!mUserManager.exists(userId)) return;
- setEnabledSetting(componentName.getPackageName(),
- componentName.getClassName(), newState, flags, userId, null);
+
+ setEnabledSettings(List.of(new ComponentEnabledSetting(componentName, newState, flags)),
+ userId, null /* callingPackage */);
}
- private void setEnabledSetting(final String packageName, String className, int newState,
- final int flags, int userId, String callingPackage) {
- if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
- || newState == COMPONENT_ENABLED_STATE_ENABLED
- || newState == COMPONENT_ENABLED_STATE_DISABLED
- || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
- throw new IllegalArgumentException("Invalid new component state: "
- + newState);
+ @Override
+ public void setComponentEnabledSettings(List<ComponentEnabledSetting> settings, int userId) {
+ if (!mUserManager.exists(userId)) return;
+ if (settings == null || settings.isEmpty()) {
+ throw new IllegalArgumentException("The list of enabled settings is empty");
}
- PackageSetting pkgSetting;
+
+ setEnabledSettings(settings, userId, null /* callingPackage */);
+ }
+
+ private void setEnabledSettings(List<ComponentEnabledSetting> settings, int userId,
+ String callingPackage) {
final int callingUid = Binder.getCallingUid();
- final int permission;
- if (callingUid == Process.SYSTEM_UID) {
- permission = PackageManager.PERMISSION_GRANTED;
- } else {
- permission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
- }
enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
true /* checkShell */, "set enabled");
- final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
- boolean sendNow = false;
- boolean isApp = (className == null);
- final boolean isCallerInstantApp = (getInstantAppPackageName(callingUid) != null);
- String componentName = isApp ? packageName : className;
- ArrayList<String> components;
- // reader
- synchronized (mLock) {
- pkgSetting = mSettings.getPackageLPr(packageName);
- if (pkgSetting == null) {
- if (!isCallerInstantApp) {
- if (className == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
+ final int targetSize = settings.size();
+ for (int i = 0; i < targetSize; i++) {
+ final int newState = settings.get(i).getEnabledState();
+ if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == COMPONENT_ENABLED_STATE_ENABLED
+ || newState == COMPONENT_ENABLED_STATE_DISABLED
+ || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+ throw new IllegalArgumentException("Invalid new component state: " + newState);
+ }
+ }
+ if (targetSize > 1) {
+ final ArraySet<String> checkDuplicatedPackage = new ArraySet<>();
+ final ArraySet<ComponentName> checkDuplicatedComponent = new ArraySet<>();
+ final ArrayMap<String, Integer> checkConflictFlag = new ArrayMap<>();
+ for (int i = 0; i < targetSize; i++) {
+ final ComponentEnabledSetting setting = settings.get(i);
+ final String packageName = setting.getPackageName();
+ if (setting.isComponent()) {
+ final ComponentName componentName = setting.getComponentName();
+ if (checkDuplicatedComponent.contains(componentName)) {
+ throw new IllegalArgumentException("The component " + componentName
+ + " is duplicated");
+ }
+ checkDuplicatedComponent.add(componentName);
+
+ // check if there is a conflict of the DONT_KILL_APP flag between components
+ // in the package
+ final Integer enabledFlags = checkConflictFlag.get(packageName);
+ if (enabledFlags == null) {
+ checkConflictFlag.put(packageName, setting.getEnabledFlags());
+ } else if ((enabledFlags & PackageManager.DONT_KILL_APP)
+ != (setting.getEnabledFlags() & PackageManager.DONT_KILL_APP)) {
+ throw new IllegalArgumentException("A conflict of the DONT_KILL_APP flag "
+ + "between components in the package " + packageName);
}
- throw new IllegalArgumentException(
- "Unknown component: " + packageName + "/" + className);
} else {
- // throw SecurityException to prevent leaking package information
- throw new SecurityException(
- "Attempt to change component state; "
- + "pid=" + Binder.getCallingPid()
- + ", uid=" + callingUid
- + (className == null
- ? ", package=" + packageName
- : ", component=" + packageName + "/" + className));
+ if (checkDuplicatedPackage.contains(packageName)) {
+ throw new IllegalArgumentException("The package " + packageName
+ + " is duplicated");
+ }
+ checkDuplicatedPackage.add(packageName);
}
}
}
- // Limit who can change which apps
- if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
- // Don't allow apps that don't have permission to modify other apps
- final boolean filterApp;
- synchronized (mLock) {
- filterApp = (!allowedByPermission
- || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
- }
- if (filterApp) {
- throw new SecurityException(
- "Attempt to change component state; "
+ final boolean allowedByPermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE) == PERMISSION_GRANTED;
+ final boolean[] updateAllowed = new boolean[targetSize];
+ Arrays.fill(updateAllowed, true);
+
+ final Map<String, PackageSetting> pkgSettings = new ArrayMap<>(targetSize);
+ // reader
+ synchronized (mLock) {
+ // Checks for target packages
+ for (int i = 0; i < targetSize; i++) {
+ final ComponentEnabledSetting setting = settings.get(i);
+ final String packageName = setting.getPackageName();
+ if (pkgSettings.containsKey(packageName)) {
+ // this package has verified
+ continue;
+ }
+ final boolean isCallerTargetApp = ArrayUtils.contains(
+ getPackagesForUid(callingUid), packageName);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ // Limit who can change which apps
+ if (!isCallerTargetApp) {
+ // Don't allow apps that don't have permission to modify other apps
+ if (!allowedByPermission
+ || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
+ throw new SecurityException("Attempt to change component state; "
+ "pid=" + Binder.getCallingPid()
+ ", uid=" + callingUid
- + (className == null
- ? ", package=" + packageName
- : ", component=" + packageName + "/" + className));
- }
- // Don't allow changing protected packages.
- if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
- throw new SecurityException("Cannot disable a protected package: " + packageName);
+ + (!setting.isComponent() ? ", package=" + packageName
+ : ", component=" + setting.getComponentName()));
+ }
+ // Don't allow changing protected packages.
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ throw new SecurityException(
+ "Cannot disable a protected package: " + packageName);
+ }
+ }
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException(setting.isComponent()
+ ? "Unknown component: " + setting.getComponentName()
+ : "Unknown package: " + packageName);
+ }
+ if (callingUid == Process.SHELL_UID
+ && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+ // Shell can only change whole packages between ENABLED and DISABLED_USER states
+ // unless it is a test package.
+ final int oldState = pkgSetting.getEnabled(userId);
+ final int newState = setting.getEnabledState();
+ if (!setting.isComponent()
+ &&
+ (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || oldState == COMPONENT_ENABLED_STATE_DEFAULT
+ || oldState == COMPONENT_ENABLED_STATE_ENABLED)
+ &&
+ (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || newState == COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
+ // ok
+ } else {
+ throw new SecurityException(
+ "Shell cannot change component state for "
+ + setting.getComponentName() + " to " + newState);
+ }
+ }
+ pkgSettings.put(packageName, pkgSetting);
}
- }
- // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
- // app details activity
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)
- && !allowedByPermission) {
- throw new SecurityException("Cannot disable a system-generated component");
- }
+ // Checks for target components
+ for (int i = 0; i < targetSize; i++) {
+ final ComponentEnabledSetting setting = settings.get(i);
+ // skip if it's application
+ if (!setting.isComponent()) continue;
- synchronized (mLock) {
- if (callingUid == Process.SHELL_UID
- && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
- // Shell can only change whole packages between ENABLED and DISABLED_USER states
- // unless it is a test package.
- int oldState = pkgSetting.getEnabled(userId);
- if (className == null
- &&
- (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || oldState == COMPONENT_ENABLED_STATE_DEFAULT
- || oldState == COMPONENT_ENABLED_STATE_ENABLED)
- &&
- (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || newState == COMPONENT_ENABLED_STATE_DEFAULT
- || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
- // ok
- } else {
- throw new SecurityException(
- "Shell cannot change component state for " + packageName + "/"
- + className + " to " + newState);
+ // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
+ // app details activity
+ final String packageName = setting.getPackageName();
+ final String className = setting.getClassName();
+ if (!allowedByPermission
+ && PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+ throw new SecurityException("Cannot disable a system-generated component");
+ }
+ // Verify that this is a valid class name.
+ final AndroidPackage pkg = pkgSettings.get(packageName).getPkg();
+ if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
+ if (pkg != null
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.JELLY_BEAN) {
+ throw new IllegalArgumentException("Component class " + className
+ + " does not exist in " + packageName);
+ } else {
+ Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
+ + className + " does not exist in " + packageName);
+ updateAllowed[i] = false;
+ }
}
}
}
- if (className == null) {
- // We're dealing with an application/package level state change
+
+ // More work for application enabled setting updates
+ for (int i = 0; i < targetSize; i++) {
+ final ComponentEnabledSetting setting = settings.get(i);
+ // skip if it's component
+ if (setting.isComponent()) continue;
+
+ final PackageSetting pkgSetting = pkgSettings.get(setting.getPackageName());
+ final int newState = setting.getEnabledState();
synchronized (mLock) {
if (pkgSetting.getEnabled(userId) == newState) {
// Nothing to do
- return;
+ updateAllowed[i] = false;
+ continue;
}
}
// If we're enabling a system stub, there's a little more work to do.
// Prior to enabling the package, we need to decompress the APK(s) to the
// data partition and then replace the version on the system partition.
- final AndroidPackage deletedPkg = pkgSetting.pkg;
+ final AndroidPackage deletedPkg = pkgSetting.getPkg();
final boolean isSystemStub = (deletedPkg != null)
&& deletedPkg.isStub()
&& deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
- return;
+ Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ + "commpressed package " + setting.getPackageName());
+ updateAllowed[i] = false;
+ continue;
}
}
- if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- // Don't care about who enables an app.
- callingPackage = null;
- }
- synchronized (mLock) {
- pkgSetting.setEnabled(newState, userId, callingPackage);
- if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || newState == COMPONENT_ENABLED_STATE_DISABLED)
- && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
- == PERMISSION_GRANTED) {
- // This app should not generally be allowed to get disabled by the UI, but if it
- // ever does, we don't want to end up with some of the user's apps permanently
- // suspended.
- unsuspendForSuspendingPackage(packageName, userId);
- removeAllDistractingPackageRestrictions(userId);
+ }
+
+ // packageName -> list of components to send broadcasts now
+ final ArrayMap<String, ArrayList<String>> sendNowBroadcasts = new ArrayMap<>(targetSize);
+ synchronized (mLock) {
+ boolean scheduleBroadcastMessage = false;
+ boolean isSynchronous = false;
+ boolean anyChanged = false;
+
+ for (int i = 0; i < targetSize; i++) {
+ if (!updateAllowed[i]) {
+ continue;
}
- }
- } else {
- synchronized (mLock) {
- // We're dealing with a component level state change
- // First, verify that this is a valid class name.
- AndroidPackage pkg = pkgSetting.pkg;
- if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
- if (pkg != null &&
- pkg.getTargetSdkVersion() >=
- Build.VERSION_CODES.JELLY_BEAN) {
- throw new IllegalArgumentException("Component class " + className
- + " does not exist in " + packageName);
- } else {
- Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
- + className + " does not exist in " + packageName);
- }
+ // update enabled settings
+ final ComponentEnabledSetting setting = settings.get(i);
+ final String packageName = setting.getPackageName();
+ if (!setEnabledSettingInternalLocked(pkgSettings.get(packageName), setting,
+ userId, callingPackage)) {
+ continue;
}
- switch (newState) {
- case COMPONENT_ENABLED_STATE_ENABLED:
- if (!pkgSetting.enableComponentLPw(className, userId)) {
- return;
- }
- break;
- case COMPONENT_ENABLED_STATE_DISABLED:
- if (!pkgSetting.disableComponentLPw(className, userId)) {
- return;
- }
- break;
- case COMPONENT_ENABLED_STATE_DEFAULT:
- if (!pkgSetting.restoreComponentLPw(className, userId)) {
- return;
- }
- break;
- default:
- Slog.e(TAG, "Invalid new component state: " + newState);
- return;
+ anyChanged = true;
+
+ if ((setting.getEnabledFlags() & PackageManager.SYNCHRONOUS) != 0) {
+ isSynchronous = true;
+ }
+ // collect broadcast list for the package
+ final String componentName = setting.isComponent()
+ ? setting.getClassName() : packageName;
+ ArrayList<String> componentList = sendNowBroadcasts.get(packageName);
+ if (componentList == null) {
+ componentList = mPendingBroadcasts.get(userId, packageName);
+ }
+ final boolean newPackage = componentList == null;
+ if (newPackage) {
+ componentList = new ArrayList<>();
+ }
+ if (!componentList.contains(componentName)) {
+ componentList.add(componentName);
+ }
+ if ((setting.getEnabledFlags() & PackageManager.DONT_KILL_APP) == 0) {
+ sendNowBroadcasts.put(packageName, componentList);
+ // Purge entry from pending broadcast list if another one exists already
+ // since we are sending one right away.
+ mPendingBroadcasts.remove(userId, packageName);
+ } else {
+ if (newPackage) {
+ mPendingBroadcasts.put(userId, packageName, componentList);
+ }
+ scheduleBroadcastMessage = true;
}
}
- }
- synchronized (mLock) {
- if ((flags & PackageManager.SYNCHRONOUS) != 0) {
+ if (!anyChanged) {
+ // nothing changed, return immediately
+ return;
+ }
+
+ if (isSynchronous) {
flushPackageRestrictionsAsUserInternalLocked(userId);
} else {
scheduleWritePackageRestrictionsLocked(userId);
}
- updateSequenceNumberLP(pkgSetting, new int[] { userId });
- final long callingId = Binder.clearCallingIdentity();
- try {
- updateInstantAppInstallerLocked(packageName);
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- components = mPendingBroadcasts.get(userId, packageName);
- final boolean newPackage = components == null;
- if (newPackage) {
- components = new ArrayList<>();
- }
- if (!components.contains(componentName)) {
- components.add(componentName);
- }
- if ((flags&PackageManager.DONT_KILL_APP) == 0) {
- sendNow = true;
- // Purge entry from pending broadcast list if another one exists already
- // since we are sending one right away.
- mPendingBroadcasts.remove(userId, packageName);
- } else {
- if (newPackage) {
- mPendingBroadcasts.put(userId, packageName, components);
- }
+ if (scheduleBroadcastMessage) {
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
// Schedule a message - if it has been a "reasonably long time" since the
// service started, send the broadcast with a delay of one second to avoid
// delayed reactions from the receiver, else keep the default ten second delay
// to avoid extreme thrashing on service startup.
final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay
- ? BROADCAST_DELAY
- : BROADCAST_DELAY_DURING_STARTUP;
+ ? BROADCAST_DELAY
+ : BROADCAST_DELAY_DURING_STARTUP;
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, broadcastDelay);
}
}
@@ -24076,14 +20273,76 @@ public class PackageManagerService extends IPackageManager.Stub
final long callingId = Binder.clearCallingIdentity();
try {
- if (sendNow) {
- int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
- sendPackageChangedBroadcast(packageName,
- (flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
+ for (int i = 0; i < sendNowBroadcasts.size(); i++) {
+ final String packageName = sendNowBroadcasts.keyAt(i);
+ final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
+ final int packageUid = UserHandle.getUid(
+ userId, pkgSettings.get(packageName).appId);
+ sendPackageChangedBroadcast(packageName, false /* dontKillApp */,
+ components, packageUid, null /* reason */);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ private boolean setEnabledSettingInternalLocked(PackageSetting pkgSetting,
+ ComponentEnabledSetting setting, int userId, String callingPackage) {
+ final int newState = setting.getEnabledState();
+ final String packageName = setting.getPackageName();
+ boolean success = false;
+ if (!setting.isComponent()) {
+ // We're dealing with an application/package level state change
+ if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ // Don't care about who enables an app.
+ callingPackage = null;
+ }
+ pkgSetting.setEnabled(newState, userId, callingPackage);
+ if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || newState == COMPONENT_ENABLED_STATE_DISABLED)
+ && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
+ == PERMISSION_GRANTED) {
+ // This app should not generally be allowed to get disabled by the UI, but
+ // if it ever does, we don't want to end up with some of the user's apps
+ // permanently suspended.
+ unsuspendForSuspendingPackage(packageName, userId);
+ removeAllDistractingPackageRestrictions(userId);
+ }
+ success = true;
+ } else {
+ // We're dealing with a component level state change
+ final String className = setting.getClassName();
+ switch (newState) {
+ case COMPONENT_ENABLED_STATE_ENABLED:
+ success = pkgSetting.enableComponentLPw(className, userId);
+ break;
+ case COMPONENT_ENABLED_STATE_DISABLED:
+ success = pkgSetting.disableComponentLPw(className, userId);
+ break;
+ case COMPONENT_ENABLED_STATE_DEFAULT:
+ success = pkgSetting.restoreComponentLPw(className, userId);
+ break;
+ default:
+ Slog.e(TAG, "Failed setComponentEnabledSetting: component "
+ + packageName + "/" + className
+ + " requested an invalid new component state: " + newState);
+ break;
}
+ }
+ if (!success) {
+ return false;
+ }
+
+ updateSequenceNumberLP(pkgSetting, new int[] { userId });
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ updateInstantAppInstallerLocked(packageName);
} finally {
Binder.restoreCallingIdentity(callingId);
}
+
+ return true;
}
@WorkerThread
@@ -24120,7 +20379,7 @@ public class PackageManagerService extends IPackageManager.Stub
+ componentNames);
Bundle extras = new Bundle(4);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
- String nameList[] = new String[componentNames.size()];
+ String[] nameList = new String[componentNames.size()];
componentNames.toArray(nameList);
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
@@ -24394,11 +20653,7 @@ public class PackageManagerService extends IPackageManager.Stub
componentInfo.getComponentName(), userId);
if (componentEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
return componentInfo.isEnabled();
- } else if (componentEnabledSetting != COMPONENT_ENABLED_STATE_ENABLED) {
- return false;
- }
-
- return true;
+ } else return componentEnabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
} catch (PackageManager.NameNotFoundException ignored) {
return false;
}
@@ -24460,7 +20715,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
mContext.getContentResolver(),
android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
- PackageParser.setCompatibilityModeEnabled(compatibilityModeEnabled);
+ ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);
if (DEBUG_SETTINGS) {
Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
@@ -24855,7 +21110,7 @@ public class PackageManagerService extends IPackageManager.Stub
ipw.println("Known Packages:");
ipw.increaseIndent();
for (int i = 0; i <= LAST_KNOWN_PACKAGE; i++) {
- final String knownPackage = mPmInternal.knownPackageToString(i);
+ final String knownPackage = PackageManagerInternal.knownPackageToString(i);
ipw.print(knownPackage);
ipw.println(":");
final String[] pkgNames = mPmInternal.getKnownPackageNames(i,
@@ -25180,9 +21435,9 @@ public class PackageManagerService extends IPackageManager.Stub
//TODO: b/111402650
private void disableSkuSpecificApps() {
- String apkList[] = mContext.getResources().getStringArray(
+ String[] apkList = mContext.getResources().getStringArray(
R.array.config_disableApksUnlessMatchedSku_apk_list);
- String skuArray[] = mContext.getResources().getStringArray(
+ String[] skuArray = mContext.getResources().getStringArray(
R.array.config_disableApkUnlessMatchedSku_skus_list);
if (ArrayUtils.isEmpty(apkList)) {
return;
@@ -25281,32 +21536,6 @@ public class PackageManagerService extends IPackageManager.Stub
private static final String SD_ENCRYPTION_KEYSTORE_NAME = "AppsOnSD";
- private static final String SD_ENCRYPTION_ALGORITHM = "AES";
-
- private boolean mMediaMounted = false;
-
- static String getEncryptKey() {
- try {
- String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
- SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- sdEncKey = SystemKeyStore.getInstance().generateNewKeyHexString(128,
- SD_ENCRYPTION_ALGORITHM, SD_ENCRYPTION_KEYSTORE_NAME);
- if (sdEncKey == null) {
- Slog.e(TAG, "Failed to create encryption keys");
- return null;
- }
- }
- return sdEncKey;
- } catch (NoSuchAlgorithmException nsae) {
- Slog.e(TAG, "Failed to create encryption keys with exception: " + nsae);
- return null;
- } catch (IOException ioe) {
- Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
- return null;
- }
- }
-
private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
ArrayList<AndroidPackage> packages, IIntentReceiver finishedReceiver) {
final int size = packages.size();
@@ -25321,8 +21550,8 @@ public class PackageManagerService extends IPackageManager.Stub
finishedReceiver);
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+ void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
sendResourcesChangedBroadcast(mediaStatus, replacing,
pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
}
@@ -25466,7 +21695,7 @@ public class PackageManagerService extends IPackageManager.Stub
try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
"unloadPrivatePackagesInner")) {
if (deletePackageLIF(ps.name, null, false, userIds, deleteFlags, outInfo,
- false, null)) {
+ false)) {
unloaded.add(pkg);
} else {
Slog.w(TAG, "Failed to unload " + ps.getPath());
@@ -25723,7 +21952,7 @@ public class PackageManagerService extends IPackageManager.Stub
* <p>
* <em>Note: To avoid a deadlock, do not call this method with {@code mLock} lock held</em>
*/
- private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
+ void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.getPackageLPr(pkg.getPackageName());
@@ -25743,6 +21972,10 @@ public class PackageManagerService extends IPackageManager.Stub
continue;
}
+ // TODO@ashfall check ScanResult.mNeedsNewAppId, and if true instead
+ // of creating app data, migrate / change ownership of existing
+ // data.
+
if (ps.getInstalled(user.id)) {
// TODO: when user data is locked, mark that we're still dirty
prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
@@ -25778,9 +22011,9 @@ public class PackageManagerService extends IPackageManager.Stub
return prepareAppDataLeaf(batch, pkg, userId, flags);
}
- private @NonNull CompletableFuture<?> prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
+ private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
@NonNull AndroidPackage pkg, int userId, int flags, boolean maybeMigrateAppData) {
- return prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
+ prepareAppData(batch, pkg, userId, flags).thenRun(() -> {
// Note: this code block is executed with the Installer lock
// already held, since it's invoked as a side-effect of
// executeBatchLI()
@@ -25833,8 +22066,6 @@ public class PackageManagerService extends IPackageManager.Stub
} catch (InstallerException e2) {
logCriticalInfo(Log.DEBUG, "Recovery failed!");
}
- } else if (e != null) {
- Slog.e(TAG, "Failed to create app data for " + packageName + ": " + e);
}
// Prepare the application profiles only for upgrades and
@@ -25937,21 +22168,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
public PackageFreezer freezePackage(String packageName, int userId, String killReason) {
- return new PackageFreezer(packageName, userId, killReason);
- }
-
- public PackageFreezer freezePackageForInstall(String packageName, int installFlags,
- String killReason) {
- return freezePackageForInstall(packageName, UserHandle.USER_ALL, installFlags, killReason);
- }
-
- public PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
- String killReason) {
- if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
- return new PackageFreezer();
- } else {
- return freezePackage(packageName, userId, killReason);
- }
+ return new PackageFreezer(packageName, userId, killReason, this);
}
public PackageFreezer freezePackageForDelete(String packageName, int deleteFlags,
@@ -25962,74 +22179,13 @@ public class PackageManagerService extends IPackageManager.Stub
public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
String killReason) {
if ((deleteFlags & PackageManager.DELETE_DONT_KILL_APP) != 0) {
- return new PackageFreezer();
+ return new PackageFreezer(this);
} else {
return freezePackage(packageName, userId, killReason);
}
}
/**
- * Class that freezes and kills the given package upon creation, and
- * unfreezes it upon closing. This is typically used when doing surgery on
- * app code/data to prevent the app from running while you're working.
- */
- private class PackageFreezer implements AutoCloseable {
- private final String mPackageName;
-
- private final boolean mWeFroze;
-
- private final AtomicBoolean mClosed = new AtomicBoolean();
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- /**
- * Create and return a stub freezer that doesn't actually do anything,
- * typically used when someone requested
- * {@link PackageManager#INSTALL_DONT_KILL_APP} or
- * {@link PackageManager#DELETE_DONT_KILL_APP}.
- */
- public PackageFreezer() {
- mPackageName = null;
- mWeFroze = false;
- mCloseGuard.open("close");
- }
-
- public PackageFreezer(String packageName, int userId, String killReason) {
- synchronized (mLock) {
- mPackageName = packageName;
- mWeFroze = mFrozenPackages.add(mPackageName);
-
- final PackageSetting ps = mSettings.getPackageLPr(mPackageName);
- if (ps != null) {
- killApplication(ps.name, ps.appId, userId, killReason);
- }
- }
- mCloseGuard.open("close");
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- mCloseGuard.warnIfOpen();
- close();
- } finally {
- super.finalize();
- }
- }
-
- @Override
- public void close() {
- mCloseGuard.close();
- if (mClosed.compareAndSet(false, true)) {
- synchronized (mLock) {
- if (mWeFroze) {
- mFrozenPackages.remove(mPackageName);
- }
- }
- }
- }
- }
-
- /**
* Verify that given package is currently frozen.
*/
private void checkPackageFrozen(String packageName) {
@@ -26271,23 +22427,14 @@ public class PackageManagerService extends IPackageManager.Stub
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
- final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<PackageLite> ret = ApkLiteParseUtils.parsePackageLite(input,
- new File(origin.resolvedPath), /* flags */ 0);
+ new File(origin.mResolvedPath), /* flags */ 0);
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
- installSource, volumeUuid, user, packageAbiOverride, lite);
- params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
- msg.obj = params;
-
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "movePackage",
- System.identityHashCode(msg.obj));
- Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
- System.identityHashCode(msg.obj));
-
- mHandler.sendMessage(msg);
+ installSource, volumeUuid, user, packageAbiOverride, lite, this);
+ params.movePackage();
}
/**
@@ -26303,7 +22450,7 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
- final StorageManager storage = mInjector.getSystemService(StorageManager.class);;
+ final StorageManager storage = mInjector.getSystemService(StorageManager.class);
VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString());
int packageExternalStorageType = getPackageExternalStorageType(volume, pkg.isExternalStorage());
@@ -26553,11 +22700,7 @@ public class PackageManagerService extends IPackageManager.Stub
Binder.restoreCallingIdentity(token);
}
final boolean b;
- if (userInfo != null && userInfo.isManagedProfile()) {
- b = true;
- } else {
- b = false;
- }
+ b = userInfo != null && userInfo.isManagedProfile();
mUserNeedsBadging.put(userId, b);
return b;
}
@@ -26674,8 +22817,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
+ Pair<Integer, String> verifyReplacingVersionCode(PackageInfoLite pkgLite,
long requiredInstalledVersionCode, int installFlags) {
+ if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+ return verifyReplacingVersionCodeForApex(
+ pkgLite, requiredInstalledVersionCode, installFlags);
+ }
+
String packageName = pkgLite.packageName;
synchronized (mLock) {
// Package which currently owns the data that the new package will own if installed.
@@ -26692,18 +22840,22 @@ public class PackageManagerService extends IPackageManager.Stub
if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
if (dataOwnerPkg == null) {
- Slog.w(TAG, "Required installed version code was "
+ String errorMsg = "Required installed version code was "
+ requiredInstalledVersionCode
- + " but package is not installed");
- return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ + " but package is not installed";
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
}
if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
- Slog.w(TAG, "Required installed version code was "
+ String errorMsg = "Required installed version code was "
+ requiredInstalledVersionCode
+ " but actual installed version is "
- + dataOwnerPkg.getLongVersionCode());
- return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+ + dataOwnerPkg.getLongVersionCode();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
}
}
@@ -26713,13 +22865,52 @@ public class PackageManagerService extends IPackageManager.Stub
try {
checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
- Slog.w(TAG, "Downgrade detected: " + e.getMessage());
- return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+ String errorMsg = "Downgrade detected: " + e.getMessage();
+ Slog.w(TAG, errorMsg);
+ return Pair.create(
+ PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
}
}
}
}
- return PackageManager.INSTALL_SUCCEEDED;
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
+ }
+
+ private Pair<Integer, String> verifyReplacingVersionCodeForApex(PackageInfoLite pkgLite,
+ long requiredInstalledVersionCode, int installFlags) {
+ String packageName = pkgLite.packageName;
+
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+ if (activePackage == null) {
+ String errorMsg = "Attempting to install new APEX package " + packageName;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED, errorMsg);
+ }
+
+ final long activeVersion = activePackage.getLongVersionCode();
+ if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activeVersion != requiredInstalledVersionCode) {
+ String errorMsg = "Installed version of APEX package " + packageName
+ + " does not match required. Active version: " + activeVersion
+ + " required: " + requiredInstalledVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION, errorMsg);
+ }
+
+ final boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final long newVersionCode = pkgLite.getLongVersionCode();
+ if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, isAppDebuggable)
+ && newVersionCode < activeVersion) {
+ String errorMsg = "Downgrade of APEX package " + packageName
+ + " is not allowed. Active version: " + activeVersion
+ + " attempted: " + newVersionCode;
+ Slog.w(TAG, errorMsg);
+ return Pair.create(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE, errorMsg);
+ }
+
+ return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
}
/**
@@ -26731,12 +22922,12 @@ public class PackageManagerService extends IPackageManager.Stub
if (after.getLongVersionCode() < before.getLongVersionCode()) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
+ + before.getLongVersionCode());
} else if (after.getLongVersionCode() == before.getLongVersionCode()) {
if (after.baseRevisionCode < before.getBaseRevisionCode()) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
+ + " is older than current " + before.getBaseRevisionCode());
}
if (!ArrayUtils.isEmpty(after.splitNames)) {
@@ -26747,8 +22938,9 @@ public class PackageManagerService extends IPackageManager.Stub
if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
"Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i] + " is older than current "
- + before.getSplitRevisionCodes()[j]);
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
}
}
}
@@ -26967,7 +23159,7 @@ public class PackageManagerService extends IPackageManager.Stub
boolean[] results = new boolean[packageNames.length];
for (int i = results.length - 1; i >= 0; --i) {
ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser);
- results[i] = appInfo == null ? false : appInfo.isAudioPlaybackCaptureAllowed();
+ results[i] = appInfo != null && appInfo.isAudioPlaybackCaptureAllowed();
}
return results;
}
@@ -27003,6 +23195,29 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean hasSystemFeature(String featureName, int version) {
return PackageManagerService.this.hasSystemFeature(featureName, version);
}
+
+ @Override
+ public void registerStagedApexObserver(IStagedApexObserver observer) {
+ mInstallerService.getStagingManager().registerStagedApexObserver(observer);
+ }
+
+ @Override
+ public void unregisterStagedApexObserver(IStagedApexObserver observer) {
+ mInstallerService.getStagingManager().unregisterStagedApexObserver(observer);
+ }
+
+ @Override
+ public String[] getStagedApexModuleNames() {
+ return mInstallerService.getStagingManager()
+ .getStagedApexModuleNames().toArray(new String[0]);
+ }
+
+ @Override
+ @Nullable
+ public StagedApexInfo getStagedApexInfo(String moduleName) {
+ return mInstallerService.getStagingManager().getStagedApexInfo(moduleName);
+ }
+
}
private AndroidPackage getPackage(String packageName) {
@@ -27029,6 +23244,22 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.filterAppAccess(packageName, callingUid, userId);
}
+ private boolean filterAppAccess(int uid, int callingUid) {
+ return mComputer.filterAppAccess(uid, callingUid);
+ }
+
+ private int[] getVisibilityAllowList(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ final PackageSetting ps = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
+ if (ps == null) {
+ return null;
+ }
+ final SparseArray<int[]> visibilityAllowList = mAppsFilter.getVisibilityAllowList(ps,
+ new int[]{userId}, mSettings.getPackagesLocked());
+ return visibilityAllowList != null ? visibilityAllowList.get(userId) : null;
+ }
+ }
+
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
@@ -27050,7 +23281,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
return pkg.getSigningDetails().hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
|| mPlatformPackage.getSigningDetails().checkCapability(pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
@Override
@@ -27112,6 +23343,16 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public boolean filterAppAccess(int uid, int callingUid) {
+ return PackageManagerService.this.filterAppAccess(uid, callingUid);
+ }
+
+ @Nullable
+ public int[] getVisibilityAllowList(@NonNull String packageName, int userId) {
+ return PackageManagerService.this.getVisibilityAllowList(packageName, userId);
+ }
+
+ @Override
public AndroidPackage getPackage(String packageName) {
return PackageManagerService.this.getPackage(packageName);
}
@@ -27160,15 +23401,14 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public @Nullable
String getDisabledSystemPackageName(@NonNull String packageName) {
- PackageSetting disabledPkgSetting = (PackageSetting) getDisabledSystemPackage(
+ PackageSetting disabledPkgSetting = getDisabledSystemPackage(
packageName);
AndroidPackage disabledPkg = disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
return disabledPkg == null ? null : disabledPkg.getPackageName();
}
/**
- * Only keep package names that refer to {@link PackageParser.Package#isSystem system}
- * packages.
+ * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
*
* @param pkgNames The packages to filter
*
@@ -27227,8 +23467,6 @@ public class PackageManagerService extends IPackageManager.Stub
mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
- case PackageManagerInternal.PACKAGE_DOCUMENTER:
- return filterOnlySystemPackages(mDocumenterPackage);
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
return filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
@@ -27311,7 +23549,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isPackageSuspended(String packageName, int userId) {
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- return (ps != null) ? ps.getSuspended(userId) : false;
+ return ps != null && ps.getSuspended(userId);
}
}
@@ -27427,6 +23665,13 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public List<ResolveInfo> queryIntentReceivers(Intent intent,
+ String resolvedType, int flags, int filterCallingUid, int userId) {
+ return PackageManagerService.this.queryIntentReceiversInternal(intent, resolvedType,
+ flags, userId, filterCallingUid);
+ }
+
+ @Override
public List<ResolveInfo> queryIntentServices(
Intent intent, int flags, int callingUid, int userId) {
final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
@@ -27492,7 +23737,7 @@ public class PackageManagerService extends IPackageManager.Stub
public boolean isPackageEphemeral(int userId, String packageName) {
synchronized (mLock) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
- return ps != null ? ps.getInstantApp(userId) : false;
+ return ps != null && ps.getInstantApp(userId);
}
}
@@ -27720,12 +23965,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
- return PackageManagerService.this.resolveContentProviderInternal(
- name, flags, userId);
- }
-
- @Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId,
int callingUid) {
return PackageManagerService.this.resolveContentProviderInternal(
@@ -28307,7 +24546,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Nullable
- public PackageSetting getPackageSetting(String packageName) {
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ PackageSetting getPackageSetting(String packageName) {
return mComputer.getPackageSetting(packageName);
}
@@ -28346,16 +24586,6 @@ public class PackageManagerService extends IPackageManager.Stub
}
/**
- * Return a <b>copy</b> of the collection of packages known to the package manager.
- * @return A copy of the values of mPackages.
- */
- Collection<AndroidPackage> getPackages() {
- synchronized (mLock) {
- return new ArrayList<>(mPackages.values());
- }
- }
-
- /**
* Logs process start information (including base APK hash) to the security log.
* @hide
*/
@@ -28372,10 +24602,6 @@ public class PackageManagerService extends IPackageManager.Stub
processName, uid, seinfo, pid);
}
- public CompilerStats.PackageStats getCompilerPackageStats(String pkgName) {
- return mCompilerStats.getPackageStats(pkgName);
- }
-
public CompilerStats.PackageStats getOrCreateCompilerPackageStats(AndroidPackage pkg) {
return getOrCreateCompilerPackageStats(pkg.getPackageName());
}
@@ -28384,10 +24610,6 @@ public class PackageManagerService extends IPackageManager.Stub
return mCompilerStats.getOrCreatePackageStats(pkgName);
}
- public void deleteCompilerPackageStats(String pkgName) {
- mCompilerStats.deletePackageStats(pkgName);
- }
-
@Override
public boolean isAutoRevokeWhitelisted(String packageName) {
int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow(
@@ -28506,8 +24728,8 @@ public class PackageManagerService extends IPackageManager.Stub
public void grantImplicitAccess(int recipientUid, String visibleAuthority) {
// This API is exposed temporarily to only the contacts provider. (b/158688602)
final int callingUid = Binder.getCallingUid();
- ProviderInfo contactsProvider = resolveContentProviderInternal(
- ContactsContract.AUTHORITY, 0, UserHandle.getUserId(callingUid));
+ ProviderInfo contactsProvider = resolveContentProviderInternal(ContactsContract.AUTHORITY,
+ 0, UserHandle.getUserId(callingUid), callingUid);
if (contactsProvider == null || contactsProvider.applicationInfo == null
|| !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess");
@@ -28712,7 +24934,7 @@ public class PackageManagerService extends IPackageManager.Stub
*
* TODO(zhanghai): This should be removed once we finish migration of permission storage.
*/
- private void writeSettingsLPrTEMP() {
+ void writeSettingsLPrTEMP() {
mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
mSettings.writeLPr();
}
@@ -28870,11 +25092,6 @@ public class PackageManagerService extends IPackageManager.Stub
return bOptions;
}
- @NonNull
- public DomainVerificationService.Connection getDomainVerificationConnection() {
- return mDomainVerificationConnection;
- }
-
@Override
public void setKeepUninstalledPackages(List<String> packageList) {
mContext.enforceCallingPermission(
@@ -28912,6 +25129,56 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @Override
+ public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
+ String featureId, int userId) throws RemoteException {
+ Objects.requireNonNull(packageName);
+ final int callingUid = Binder.getCallingUid();
+ enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "get launch intent sender for package");
+ final int packageUid = getPackageUid(callingPackage, 0 /* flags */, userId);
+ if (!UserHandle.isSameApp(callingUid, packageUid)) {
+ throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
+ + callingUid + " does not own package: " + callingPackage);
+ }
+
+ // Using the same implementation with the #getLaunchIntentForPackage to get the ResolveInfo.
+ // Pass the resolveForStart as true in queryIntentActivities to skip the app filtering.
+ final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
+ intentToResolve.addCategory(Intent.CATEGORY_INFO);
+ intentToResolve.setPackage(packageName);
+ String resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ List<ResolveInfo> ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ if (ris == null || ris.size() <= 0) {
+ intentToResolve.removeCategory(Intent.CATEGORY_INFO);
+ intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
+ intentToResolve.setPackage(packageName);
+ resolvedType = intentToResolve.resolveTypeIfNeeded(mContext.getContentResolver());
+ ris = queryIntentActivitiesInternal(intentToResolve, resolvedType,
+ 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
+ true /* resolveForStart */, false /* allowDynamicSplits */);
+ }
+
+ final Intent intent = new Intent(intentToResolve);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // For the case of empty result, no component name is assigned into the intent. A
+ // non-launchable IntentSender which contains the failed intent is created. The
+ // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
+ if (ris != null && !ris.isEmpty()) {
+ intent.setClassName(ris.get(0).activityInfo.packageName,
+ ris.get(0).activityInfo.name);
+ }
+ final IIntentSender target = ActivityManager.getService().getIntentSenderWithFeature(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ featureId, null /* token */, null /* resultWho */,
+ 1 /* requestCode */, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null,
+ PendingIntent.FLAG_IMMUTABLE, null /* bOptions */, userId);
+ return new IntentSender(target);
+ }
+
private static class TempUserState {
public final int enabledState;
@Nullable
@@ -28926,20 +25193,3 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
-
-interface PackageSender {
- /**
- * @param userIds User IDs where the action occurred on a full application
- * @param instantUserIds User IDs where the action occurred on an instant application
- */
- void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final int flags, final String targetPkg,
- final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions);
- void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
- boolean includeStopped, int appId, int[] userIds, int[] instantUserIds,
- int dataLoaderType);
- void notifyPackageAdded(String packageName, int uid);
- void notifyPackageChanged(String packageName, int uid);
- void notifyPackageRemoved(String packageName, int uid);
-}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 8b5abf3afe51..5fdb57d5b873 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,9 +34,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
@@ -499,9 +499,9 @@ public class PackageManagerServiceUtils {
*/
public static boolean comparePackageSignatures(PackageSetting pkgSetting,
Signature[] signatures) {
- return pkgSetting.signatures.mSigningDetails
- == PackageParser.SigningDetails.UNKNOWN
- || compareSignatures(pkgSetting.signatures.mSigningDetails.signatures, signatures)
+ final SigningDetails signingDetails = pkgSetting.signatures.mSigningDetails;
+ return signingDetails == SigningDetails.UNKNOWN
+ || compareSignatures(signingDetails.getSignatures(), signatures)
== PackageManager.SIGNATURE_MATCH;
}
@@ -512,13 +512,13 @@ public class PackageManagerServiceUtils {
* system upgrade) and {@code scannedSigs} will be in the newer format.
*/
private static boolean matchSignaturesCompat(String packageName,
- PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
+ PackageSignatures packageSignatures, SigningDetails parsedSignatures) {
ArraySet<Signature> existingSet = new ArraySet<Signature>();
- for (Signature sig : packageSignatures.mSigningDetails.signatures) {
+ for (Signature sig : packageSignatures.mSigningDetails.getSignatures()) {
existingSet.add(sig);
}
ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
- for (Signature sig : parsedSignatures.signatures) {
+ for (Signature sig : parsedSignatures.getSignatures()) {
try {
Signature[] chainSignatures = sig.getChainSignatures();
for (Signature chainSig : chainSignatures) {
@@ -547,9 +547,9 @@ public class PackageManagerServiceUtils {
private static boolean matchSignaturesRecover(
String packageName,
- PackageParser.SigningDetails existingSignatures,
- PackageParser.SigningDetails parsedSignatures,
- @PackageParser.SigningDetails.CertCapabilities int flags) {
+ SigningDetails existingSignatures,
+ SigningDetails parsedSignatures,
+ @SigningDetails.CertCapabilities int flags) {
String msg = null;
try {
if (parsedSignatures.checkCapabilityRecover(existingSignatures, flags)) {
@@ -576,10 +576,10 @@ public class PackageManagerServiceUtils {
PackageSetting disabledPkgSetting) {
if (pkgSetting.signatures.mSigningDetails.checkCapability(
disabledPkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| disabledPkgSetting.signatures.mSigningDetails.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ SigningDetails.CertCapabilities.ROLLBACK)) {
return true;
} else {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
@@ -623,19 +623,19 @@ public class PackageManagerServiceUtils {
* @throws PackageManagerException if the signatures did not match.
*/
public static boolean verifySignatures(PackageSetting pkgSetting,
- PackageSetting disabledPkgSetting, PackageParser.SigningDetails parsedSignatures,
+ PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
final String packageName = pkgSetting.name;
boolean compatMatch = false;
- if (pkgSetting.signatures.mSigningDetails.signatures != null) {
+ if (pkgSetting.signatures.mSigningDetails.getSignatures() != null) {
// Already existing package. Make sure signatures match
boolean match = parsedSignatures.checkCapability(
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| pkgSetting.signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
if (!match && compareCompat) {
match = matchSignaturesCompat(packageName, pkgSetting.signatures,
parsedSignatures);
@@ -646,12 +646,12 @@ public class PackageManagerServiceUtils {
packageName,
pkgSetting.signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
|| matchSignaturesRecover(
packageName,
parsedSignatures,
pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK);
+ SigningDetails.CertCapabilities.ROLLBACK);
}
if (!match && isApkVerificationForced(disabledPkgSetting)) {
@@ -674,7 +674,7 @@ public class PackageManagerServiceUtils {
// Check for shared user signatures
if (pkgSetting.getSharedUser() != null
&& pkgSetting.getSharedUser().signatures.mSigningDetails
- != PackageParser.SigningDetails.UNKNOWN) {
+ != SigningDetails.UNKNOWN) {
// Already existing package. Make sure signatures match. In case of signing certificate
// rotation, the packages with newer certs need to be ok with being sharedUserId with
@@ -684,10 +684,10 @@ public class PackageManagerServiceUtils {
boolean match =
parsedSignatures.checkCapability(
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
// Special case: if the sharedUserId capability check failed it could be due to this
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
@@ -704,11 +704,11 @@ public class PackageManagerServiceUtils {
matchSignaturesRecover(packageName,
pkgSetting.getSharedUser().signatures.mSigningDetails,
parsedSignatures,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
+ SigningDetails.CertCapabilities.SHARED_USER_ID)
|| matchSignaturesRecover(packageName,
parsedSignatures,
pkgSetting.getSharedUser().signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
+ SigningDetails.CertCapabilities.SHARED_USER_ID);
compatMatch |= match;
}
if (!match) {
@@ -729,13 +729,13 @@ public class PackageManagerServiceUtils {
if (packageName.equals(shUidPkgSetting.name)) {
continue;
}
- PackageParser.SigningDetails shUidSigningDetails =
+ SigningDetails shUidSigningDetails =
shUidPkgSetting.getSigningDetails();
// The capability check only needs to be performed against the package if it is
// signed with a key that is in the lineage of the package being installed.
if (parsedSignatures.hasAncestor(shUidSigningDetails)) {
if (!parsedSignatures.checkCapability(shUidSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)) {
+ SigningDetails.CertCapabilities.SHARED_USER_ID)) {
throw new PackageManagerException(
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1aa80a953d6c..4862021ffe55 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2644,14 +2644,25 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
- // pm remove-user [--set-ephemeral-if-in-use] USER_ID
+ // pm remove-user [--set-ephemeral-if-in-use][--wait] USER_ID
public int runRemoveUser() throws RemoteException {
int userId;
String arg;
boolean setEphemeralIfInUse = false;
+ boolean wait = false;
+
while ((arg = getNextOption()) != null) {
- if (arg.equals("--set-ephemeral-if-in-use")) {
- setEphemeralIfInUse = true;
+ switch (arg) {
+ case "--set-ephemeral-if-in-use":
+ setEphemeralIfInUse = true;
+ break;
+ case "--wait": // fallthrough
+ case "-w":
+ wait = true;
+ break;
+ default:
+ getErrPrintWriter().println("Error: unknown option: " + arg);
+ return -1;
}
}
@@ -2666,18 +2677,64 @@ class PackageManagerShellCommand extends ShellCommand {
if (setEphemeralIfInUse) {
return removeUserOrSetEphemeral(um, userId);
} else {
- return removeUser(um, userId);
+ final boolean success = wait ? removeUserAndWait(um, userId) : removeUser(um, userId);
+ if (success) {
+ getOutPrintWriter().println("Success: removed user");
+ return 0;
+ } else {
+ // Error message should already have been printed.
+ return 1;
+ }
}
}
- private int removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException {
+ private boolean removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException {
Slog.i(TAG, "Removing user " + userId);
if (um.removeUser(userId)) {
- getOutPrintWriter().println("Success: removed user");
- return 0;
+ return true;
} else {
getErrPrintWriter().println("Error: couldn't remove user id " + userId);
- return 1;
+ return false;
+ }
+ }
+
+ private boolean removeUserAndWait(IUserManager um, @UserIdInt int userId)
+ throws RemoteException {
+ Slog.i(TAG, "Removing (and waiting for completion) user " + userId);
+
+ final CountDownLatch waitLatch = new CountDownLatch(1);
+ final UserManagerInternal.UserLifecycleListener listener =
+ new UserManagerInternal.UserLifecycleListener() {
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ if (userId == user.id) {
+ waitLatch.countDown();
+ }
+ }
+ };
+
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ umi.addUserLifecycleListener(listener);
+
+ try {
+ if (um.removeUser(userId)) {
+ final boolean awaitSuccess = waitLatch.await(10, TimeUnit.MINUTES);
+ if (!awaitSuccess) {
+ getErrPrintWriter().printf("Error: Remove user %d timed out\n", userId);
+ return false;
+ }
+ // Success!
+ return true;
+ } else {
+ getErrPrintWriter().println("Error: couldn't remove user id " + userId);
+ return false;
+ }
+ } catch (InterruptedException e) {
+ getErrPrintWriter().printf("Error: Remove user %d wait interrupted: %s\n", userId, e);
+ Thread.currentThread().interrupt();
+ return false;
+ } finally {
+ umi.removeUserLifecycleListener(listener);
}
}
@@ -3827,13 +3884,14 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'.");
pw.println(" --guest is shorthand for '--user-type android.os.usertype.full.GUEST'.");
pw.println("");
- pw.println(" remove-user [--set-ephemeral-if-in-use] USER_ID");
+ pw.println(" remove-user [--set-ephemeral-if-in-use | --wait] USER_ID");
pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data");
pw.println(" associated with that user.");
pw.println(" --set-ephemeral-if-in-use: If the user is currently running and");
pw.println(" therefore cannot be removed immediately, mark the user as ephemeral");
pw.println(" so that it will be automatically removed when possible (after user");
pw.println(" switch or reboot)");
+ pw.println(" --wait: Wait until user is removed. Ignored if set-ephemeral-if-in-use");
pw.println("");
pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
new file mode 100644
index 000000000000..e3581db14674
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.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.server.pm;
+
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
+import android.app.BroadcastOptions;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.PowerExemptionManager;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
+
+final class PackageRemovedInfo {
+ final PackageSender mPackageSender;
+ String mRemovedPackage;
+ String mInstallerPackageName;
+ int mUid = -1;
+ int mRemovedAppId = -1;
+ int[] mOrigUsers;
+ int[] mRemovedUsers = null;
+ int[] mBroadcastUsers = null;
+ int[] mInstantUserIds = null;
+ SparseArray<Integer> mInstallReasons;
+ SparseArray<Integer> mUninstallReasons;
+ boolean mIsRemovedPackageSystemUpdate = false;
+ boolean mIsUpdate;
+ boolean mDataRemoved;
+ boolean mRemovedForAllUsers;
+ boolean mIsStaticSharedLib;
+ // a two dimensional array mapping userId to the set of appIds that can receive notice
+ // of package changes
+ SparseArray<int[]> mBroadcastAllowList;
+ // Clean up resources deleted packages.
+ InstallArgs mArgs = null;
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
+
+ PackageRemovedInfo(PackageSender packageSender) {
+ mPackageSender = packageSender;
+ }
+
+ void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem) {
+ sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
+ }
+
+ void sendSystemPackageUpdatedBroadcasts() {
+ if (mIsRemovedPackageSystemUpdate) {
+ sendSystemPackageUpdatedBroadcastsInternal();
+ }
+ }
+
+ private void sendSystemPackageUpdatedBroadcastsInternal() {
+ Bundle extras = new Bundle(2);
+ extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
+ 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ mRemovedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
+ if (mInstallerPackageName != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ }
+
+ private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
+ @PowerExemptionManager.ReasonCode int reasonCode) {
+ long duration = 10_000;
+ final ActivityManagerInternal amInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+ if (amInternal != null) {
+ duration = amInternal.getBootTimeTempAllowListDuration();
+ }
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ reasonCode, "");
+ return bOptions;
+ }
+
+ private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem) {
+ // Don't send static shared library removal broadcasts as these
+ // libs are visible only the apps that depend on them an one
+ // cannot remove the library if it has a dependency.
+ if (mIsStaticSharedLib) {
+ return;
+ }
+ Bundle extras = new Bundle(2);
+ final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
+ extras.putInt(Intent.EXTRA_UID, removedUid);
+ extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
+ extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
+ if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
+ if (mRemovedPackage != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
+ mRemovedPackage, extras, 0, null /*targetPackage*/, null,
+ mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
+ if (mInstallerPackageName != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, mBroadcastUsers, mInstantUserIds, null, null);
+ }
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+ mRemovedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+ null /*finishedReceiver*/, mBroadcastUsers, mInstantUserIds,
+ mBroadcastAllowList, null /*bOptions*/);
+ if (mDataRemoved && !mIsRemovedPackageSystemUpdate) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ mRemovedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
+ null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
+ mPackageSender.notifyPackageRemoved(mRemovedPackage, removedUid);
+ }
+ }
+ if (mRemovedAppId >= 0) {
+ // If a system app's updates are uninstalled the UID is not actually removed. Some
+ // services need to know the package name affected.
+ if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
+ }
+
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
+ null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
+ }
+ }
+
+ void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
+ mRemovedUsers = userIds;
+ if (mRemovedUsers == null) {
+ mBroadcastUsers = null;
+ return;
+ }
+
+ mBroadcastUsers = EMPTY_INT_ARRAY;
+ mInstantUserIds = EMPTY_INT_ARRAY;
+ for (int i = userIds.length - 1; i >= 0; --i) {
+ final int userId = userIds[i];
+ if (deletedPackageSetting.getInstantApp(userId)) {
+ mInstantUserIds = ArrayUtils.appendInt(mInstantUserIds, userId);
+ } else {
+ mBroadcastUsers = ArrayUtils.appendInt(mBroadcastUsers, userId);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageSender.java b/services/core/java/com/android/server/pm/PackageSender.java
new file mode 100644
index 000000000000..d380098d44b3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageSender.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.IIntentReceiver;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+interface PackageSender {
+ /**
+ * @param userIds User IDs where the action occurred on a full application
+ * @param instantUserIds User IDs where the action occurred on an instant application
+ */
+ void sendPackageBroadcast(String action, String pkg,
+ Bundle extras, int flags, String targetPkg,
+ IIntentReceiver finishedReceiver, int[] userIds, int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions);
+ void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
+ boolean includeStopped, int appId, int[] userIds, int[] instantUserIds,
+ int dataLoaderType);
+ void notifyPackageAdded(String packageName, int uid);
+ void notifyPackageChanged(String packageName, int uid);
+ void notifyPackageRemoved(String packageName, int uid);
+}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 88dd03333262..717f3d57ec38 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -27,9 +27,9 @@ import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager.UninstallReason;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.overlay.OverlayPaths;
import android.os.PersistableBundle;
@@ -230,10 +230,10 @@ public abstract class PackageSettingBase extends SettingBase {
}
public Signature[] getSignatures() {
- return signatures.mSigningDetails.signatures;
+ return signatures.mSigningDetails.getSignatures();
}
- public PackageParser.SigningDetails getSigningDetails() {
+ public SigningDetails getSigningDetails() {
return signatures.mSigningDetails;
}
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 83f54f156750..76364fe6e703 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -17,9 +17,9 @@
package com.android.server.pm;
import android.annotation.NonNull;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.util.Log;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -35,40 +35,41 @@ import java.util.ArrayList;
class PackageSignatures {
- @NonNull PackageParser.SigningDetails mSigningDetails;
+ @NonNull SigningDetails mSigningDetails;
PackageSignatures(PackageSignatures orig) {
- if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
- mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+ if (orig != null && orig.mSigningDetails != SigningDetails.UNKNOWN) {
+ mSigningDetails = new SigningDetails(orig.mSigningDetails);
} else {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
- PackageSignatures(PackageParser.SigningDetails signingDetails) {
+ PackageSignatures(SigningDetails signingDetails) {
mSigningDetails = signingDetails;
}
PackageSignatures() {
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
void writeXml(TypedXmlSerializer serializer, String tagName,
ArrayList<Signature> writtenSignatures) throws IOException {
- if (mSigningDetails.signatures == null) {
+ if (mSigningDetails.getSignatures() == null) {
return;
}
serializer.startTag(null, tagName);
- serializer.attributeInt(null, "count", mSigningDetails.signatures.length);
- serializer.attributeInt(null, "schemeVersion", mSigningDetails.signatureSchemeVersion);
- writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
+ serializer.attributeInt(null, "count", mSigningDetails.getSignatures().length);
+ serializer.attributeInt(null, "schemeVersion", mSigningDetails.getSignatureSchemeVersion());
+ writeCertsListXml(serializer, writtenSignatures, mSigningDetails.getSignatures(), false);
// if we have past signer certificate information, write it out
- if (mSigningDetails.pastSigningCertificates != null) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
serializer.startTag(null, "pastSigs");
- serializer.attributeInt(null, "count", mSigningDetails.pastSigningCertificates.length);
+ serializer.attributeInt(null, "count",
+ mSigningDetails.getPastSigningCertificates().length);
writeCertsListXml(serializer, writtenSignatures,
- mSigningDetails.pastSigningCertificates, true);
+ mSigningDetails.getPastSigningCertificates(), true);
serializer.endTag(null, "pastSigs");
}
serializer.endTag(null, tagName);
@@ -106,8 +107,7 @@ class PackageSignatures {
void readXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures)
throws IOException, XmlPullParserException {
- PackageParser.SigningDetails.Builder builder =
- new PackageParser.SigningDetails.Builder();
+ SigningDetails.Builder builder = new SigningDetails.Builder();
final int count = parser.getAttributeInt(null, "count", -1);
if (count == -1) {
@@ -142,13 +142,13 @@ class PackageSignatures {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Error in package manager settings: <sigs> "
+ "unable to convert certificate(s) to public key(s).");
- mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+ mSigningDetails = SigningDetails.UNKNOWN;
}
}
private int readCertsListXml(TypedXmlPullParser parser, ArrayList<Signature> readSignatures,
ArrayList<Signature> signatures, int count, boolean isPastSigs,
- PackageParser.SigningDetails.Builder builder)
+ SigningDetails.Builder builder)
throws IOException, XmlPullParserException {
int pos = 0;
@@ -315,25 +315,25 @@ class PackageSignatures {
buf.append("PackageSignatures{");
buf.append(Integer.toHexString(System.identityHashCode(this)));
buf.append(" version:");
- buf.append(mSigningDetails.signatureSchemeVersion);
+ buf.append(mSigningDetails.getSignatureSchemeVersion());
buf.append(", signatures:[");
- if (mSigningDetails.signatures != null) {
- for (int i = 0; i < mSigningDetails.signatures.length; i++) {
+ if (mSigningDetails.getSignatures() != null) {
+ for (int i = 0; i < mSigningDetails.getSignatures().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.signatures[i].hashCode()));
+ mSigningDetails.getSignatures()[i].hashCode()));
}
}
buf.append("]");
buf.append(", past signatures:[");
- if (mSigningDetails.pastSigningCertificates != null) {
- for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+ if (mSigningDetails.getPastSigningCertificates() != null) {
+ for (int i = 0; i < mSigningDetails.getPastSigningCertificates().length; i++) {
if (i > 0) buf.append(", ");
buf.append(Integer.toHexString(
- mSigningDetails.pastSigningCertificates[i].hashCode()));
+ mSigningDetails.getPastSigningCertificates()[i].hashCode()));
buf.append(" flags: ");
- buf.append(
- Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
+ buf.append(Integer.toHexString(
+ mSigningDetails.getPastSigningCertificates()[i].getFlags()));
}
}
buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index cb9c2e997e22..a652d1c843eb 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -19,8 +19,6 @@ package com.android.server.pm;
import android.content.pm.PackageManager;
import android.util.SparseBooleanArray;
-import com.android.server.pm.PackageManagerService.VerificationParams;
-
/**
* Tracks the package verification state for a particular package. Each package verification has a
* required verifier and zero or more sufficient verifiers. Only one of the sufficient verifier list
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 21334c0f4d19..56258844a3fe 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import android.content.pm.PackageParser;
import android.os.Process;
import android.os.Trace;
@@ -33,7 +32,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
/**
- * Helper class for parallel parsing of packages using {@link PackageParser}.
+ * Helper class for parallel parsing of packages using {@link PackageParser2}.
* <p>Parsing requests are processed by a thread-pool of {@link #MAX_THREADS}.
* At any time, at most {@link #QUEUE_CAPACITY} results are kept in RAM</p>
*/
@@ -125,7 +124,7 @@ class ParallelPackageParser {
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
- throws PackageParser.PackageParserException {
+ throws PackageManagerException {
return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
}
diff --git a/services/core/java/com/android/server/pm/PrepareFailure.java b/services/core/java/com/android/server/pm/PrepareFailure.java
new file mode 100644
index 000000000000..a54ffa3219bc
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PrepareFailure.java
@@ -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.
+ */
+
+package com.android.server.pm;
+
+import android.util.ExceptionUtils;
+
+final class PrepareFailure extends PackageManagerException {
+
+ public String mConflictingPackage;
+ public String mConflictingPermission;
+
+ PrepareFailure(int error) {
+ super(error, "Failed to prepare for install.");
+ }
+
+ PrepareFailure(int error, String detailMessage) {
+ super(error, detailMessage);
+ }
+
+ PrepareFailure(String message, Exception e) {
+ super(((PackageManagerException) e).error,
+ ExceptionUtils.getCompleteMessage(message, e));
+ }
+
+ PrepareFailure conflictsWithExistingPermission(String conflictingPermission,
+ String conflictingPackage) {
+ mConflictingPermission = conflictingPermission;
+ mConflictingPackage = conflictingPackage;
+ return this;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PrepareResult.java b/services/core/java/com/android/server/pm/PrepareResult.java
new file mode 100644
index 000000000000..4e08e166ff0b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PrepareResult.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/**
+ * The set of data needed to successfully install the prepared package. This includes data that
+ * will be used to scan and reconcile the package.
+ */
+final class PrepareResult {
+ public final boolean mReplace;
+ public final int mScanFlags;
+ public final int mParseFlags;
+ @Nullable /* The original Package if it is being replaced, otherwise {@code null} */
+ public final AndroidPackage mExistingPackage;
+ public final ParsedPackage mPackageToScan;
+ public final boolean mClearCodeCache;
+ public final boolean mSystem;
+ public final PackageSetting mOriginalPs;
+ public final PackageSetting mDisabledPs;
+
+ PrepareResult(boolean replace, int scanFlags,
+ int parseFlags, AndroidPackage existingPackage,
+ ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
+ PackageSetting originalPs, PackageSetting disabledPs) {
+ mReplace = replace;
+ mScanFlags = scanFlags;
+ mParseFlags = parseFlags;
+ mExistingPackage = existingPackage;
+ mPackageToScan = packageToScan;
+ mClearCodeCache = clearCodeCache;
+ mSystem = system;
+ mOriginalPs = originalPs;
+ mDisabledPs = disabledPs;
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl b/services/core/java/com/android/server/pm/ReconcileFailure.java
index 2f782baeb1d9..c9615ffc1690 100644
--- a/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
+++ b/services/core/java/com/android/server/pm/ReconcileFailure.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 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,16 @@
* limitations under the License.
*/
-package com.android.internal.inputmethod;
+package com.android.server.pm;
-import android.os.IBinder;
-import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
-
-oneway interface IMultiClientInputMethod {
- void initialize(in IMultiClientInputMethodPrivilegedOperations privOps);
- void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
- void removeClient(int clientId);
+final class ReconcileFailure extends PackageManagerException {
+ ReconcileFailure(String message) {
+ super("Reconcile failed: " + message);
+ }
+ ReconcileFailure(int reason, String message) {
+ super(reason, "Reconcile failed: " + message);
+ }
+ ReconcileFailure(PackageManagerException e) {
+ this(e.error, e.getMessage());
+ }
}
diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java
new file mode 100644
index 000000000000..31881388e6ec
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ReconcileRequest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.pm.SharedLibraryInfo;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Package scan results and related request details used to reconcile the potential addition of
+ * one or more packages to the system.
+ *
+ * Reconcile will take a set of package details that need to be committed to the system and make
+ * sure that they are valid in the context of the system and the other installing apps. Any
+ * invalid state or app will result in a failed reconciliation and thus whatever operation (such
+ * as install) led to the request.
+ */
+final class ReconcileRequest {
+ public final Map<String, ScanResult> mScannedPackages;
+
+ public final Map<String, AndroidPackage> mAllPackages;
+ public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibrarySource;
+ public final Map<String, InstallArgs> mInstallArgs;
+ public final Map<String, PackageInstalledInfo> mInstallResults;
+ public final Map<String, PrepareResult> mPreparedPackages;
+ public final Map<String, Settings.VersionInfo> mVersionInfos;
+ public final Map<String, PackageSetting> mLastStaticSharedLibSettings;
+
+ ReconcileRequest(Map<String, ScanResult> scannedPackages,
+ Map<String, InstallArgs> installArgs,
+ Map<String, PackageInstalledInfo> installResults,
+ Map<String, PrepareResult> preparedPackages,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, AndroidPackage> allPackages,
+ Map<String, Settings.VersionInfo> versionInfos,
+ Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ mScannedPackages = scannedPackages;
+ mInstallArgs = installArgs;
+ mInstallResults = installResults;
+ mPreparedPackages = preparedPackages;
+ mSharedLibrarySource = sharedLibrarySource;
+ mAllPackages = allPackages;
+ mVersionInfos = versionInfos;
+ mLastStaticSharedLibSettings = lastStaticSharedLibSettings;
+ }
+
+ ReconcileRequest(Map<String, ScanResult> scannedPackages,
+ Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
+ Map<String, AndroidPackage> allPackages,
+ Map<String, Settings.VersionInfo> versionInfos,
+ Map<String, PackageSetting> lastStaticSharedLibSettings) {
+ this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
+ Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
+ lastStaticSharedLibSettings);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ReconciledPackage.java b/services/core/java/com/android/server/pm/ReconciledPackage.java
new file mode 100644
index 000000000000..f78249f0e757
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ReconciledPackage.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;
+
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.util.ArrayMap;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A container of all data needed to commit a package to in-memory data structures and to disk.
+ * TODO: move most of the data contained here into a PackageSetting for commit.
+ */
+final class ReconciledPackage {
+ public final ReconcileRequest mRequest;
+ public final PackageSetting mPkgSetting;
+ public final ScanResult mScanResult;
+ // TODO: Remove install-specific details from the reconcile result
+ public final PackageInstalledInfo mInstallResult;
+ @Nullable public final PrepareResult mPrepareResult;
+ @Nullable public final InstallArgs mInstallArgs;
+ public final DeletePackageAction mDeletePackageAction;
+ public final List<SharedLibraryInfo> mAllowedSharedLibraryInfos;
+ public final SigningDetails mSigningDetails;
+ public final boolean mSharedUserSignaturesChanged;
+ public ArrayList<SharedLibraryInfo> mCollectedSharedLibraryInfos;
+ public final boolean mRemoveAppKeySetData;
+
+ ReconciledPackage(ReconcileRequest request,
+ InstallArgs installArgs,
+ PackageSetting pkgSetting,
+ PackageInstalledInfo installResult,
+ PrepareResult prepareResult,
+ ScanResult scanResult,
+ DeletePackageAction deletePackageAction,
+ List<SharedLibraryInfo> allowedSharedLibraryInfos,
+ SigningDetails signingDetails,
+ boolean sharedUserSignaturesChanged,
+ boolean removeAppKeySetData) {
+ mRequest = request;
+ mInstallArgs = installArgs;
+ mPkgSetting = pkgSetting;
+ mInstallResult = installResult;
+ mPrepareResult = prepareResult;
+ mScanResult = scanResult;
+ mDeletePackageAction = deletePackageAction;
+ mAllowedSharedLibraryInfos = allowedSharedLibraryInfos;
+ mSigningDetails = signingDetails;
+ mSharedUserSignaturesChanged = sharedUserSignaturesChanged;
+ mRemoveAppKeySetData = removeAppKeySetData;
+ }
+
+ /**
+ * Returns a combined set of packages containing the packages already installed combined
+ * with the package(s) currently being installed. The to-be installed packages take
+ * precedence and may shadow already installed packages.
+ */
+ Map<String, AndroidPackage> getCombinedAvailablePackages() {
+ final ArrayMap<String, AndroidPackage> combined =
+ new ArrayMap<>(mRequest.mAllPackages.size() + mRequest.mScannedPackages.size());
+
+ combined.putAll(mRequest.mAllPackages);
+
+ for (ScanResult scanResult : mRequest.mScannedPackages.values()) {
+ combined.put(scanResult.mPkgSetting.name, scanResult.mRequest.mParsedPackage);
+ }
+
+ return combined;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index c5fbfba9b049..a7e1a62b817a 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,10 +17,11 @@
package com.android.server.pm;
import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
@@ -79,20 +80,20 @@ public final class SELinuxMMAC {
/**
* Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
- * Turning this change off for an app targeting the latest SDK is a no-op.
+ * Turning this change on for an app targeting the latest SDK or higher is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
* TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
*/
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @Disabled
@ChangeId
static final long SELINUX_LATEST_CHANGES = 143539591L;
/**
* This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting S is a no-op.
+ * off for an app targeting {@code >= android.os.Build.VERSION_CODES.R} is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -364,7 +365,8 @@ public final class SELinuxMMAC {
}
final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
- return android.os.Build.VERSION_CODES.S;
+ return Math.max(
+ android.os.Build.VERSION_CODES.CUR_DEVELOPMENT, pkg.getTargetSdkVersion());
} else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
@@ -569,7 +571,7 @@ final class Policy {
* In all cases, a return value of null should be interpreted as the apk failing
* to match this Policy instance; i.e. failing this policy stanza.
* </p>
- * @param pkg the apk to check given as a PackageParser.Package object
+ * @param pkg the apk to check given as a AndroidPackage object
* @return A string representing the seinfo matched during policy lookup.
* A value of null can also be returned if no match occured.
*/
@@ -577,7 +579,7 @@ final class Policy {
// Check for exact signature matches across all certs.
Signature[] certs = mCerts.toArray(new Signature[0]);
if (pkg.getSigningDetails() != SigningDetails.UNKNOWN
- && !Signature.areExactMatch(certs, pkg.getSigningDetails().signatures)) {
+ && !Signature.areExactMatch(certs, pkg.getSigningDetails().getSignatures())) {
// certs aren't exact match, but the package may have rotated from the known system cert
if (certs.length > 1 || !pkg.getSigningDetails().hasCertificate(certs[0])) {
diff --git a/services/core/java/com/android/server/pm/ScanRequest.java b/services/core/java/com/android/server/pm/ScanRequest.java
new file mode 100644
index 000000000000..482b79cf8378
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanRequest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/** A package to be scanned */
+@VisibleForTesting
+final class ScanRequest {
+ /** The parsed package */
+ @NonNull public final ParsedPackage mParsedPackage;
+ /** The package this package replaces */
+ @Nullable public final AndroidPackage mOldPkg;
+ /** Shared user settings, if the package has a shared user */
+ @Nullable public final SharedUserSetting mSharedUserSetting;
+ /**
+ * Package settings of the currently installed version.
+ * <p><em>IMPORTANT:</em> The contents of this object may be modified
+ * during scan.
+ */
+ @Nullable public final PackageSetting mPkgSetting;
+ /** A copy of the settings for the currently installed version */
+ @Nullable public final PackageSetting mOldPkgSetting;
+ /** Package settings for the disabled version on the /system partition */
+ @Nullable public final PackageSetting mDisabledPkgSetting;
+ /** Package settings for the installed version under its original package name */
+ @Nullable public final PackageSetting mOriginalPkgSetting;
+ /** The real package name of a renamed application */
+ @Nullable public final String mRealPkgName;
+ public final @ParsingPackageUtils.ParseFlags int mParseFlags;
+ public final @PackageManagerService.ScanFlags int mScanFlags;
+ /** The user for which the package is being scanned */
+ @Nullable public final UserHandle mUser;
+ /** Whether or not the platform package is being scanned */
+ public final boolean mIsPlatformPackage;
+ /** Override value for package ABI if set during install */
+ @Nullable public final String mCpuAbiOverride;
+
+ ScanRequest(
+ @NonNull ParsedPackage parsedPackage,
+ @Nullable SharedUserSetting sharedUserSetting,
+ @Nullable AndroidPackage oldPkg,
+ @Nullable PackageSetting pkgSetting,
+ @Nullable PackageSetting disabledPkgSetting,
+ @Nullable PackageSetting originalPkgSetting,
+ @Nullable String realPkgName,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags,
+ boolean isPlatformPackage,
+ @Nullable UserHandle user,
+ @Nullable String cpuAbiOverride) {
+ mParsedPackage = parsedPackage;
+ mOldPkg = oldPkg;
+ mPkgSetting = pkgSetting;
+ mSharedUserSetting = sharedUserSetting;
+ mOldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
+ mDisabledPkgSetting = disabledPkgSetting;
+ mOriginalPkgSetting = originalPkgSetting;
+ mRealPkgName = realPkgName;
+ mParseFlags = parseFlags;
+ mScanFlags = scanFlags;
+ mIsPlatformPackage = isPlatformPackage;
+ mUser = user;
+ mCpuAbiOverride = cpuAbiOverride;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
new file mode 100644
index 000000000000..34f86baf8cb8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanResult.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.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/** The result of a package scan. */
+@VisibleForTesting
+final class ScanResult {
+ /** The request that initiated the scan that produced this result. */
+ public final ScanRequest mRequest;
+ /** Whether or not the package scan was successful */
+ public final boolean mSuccess;
+ /**
+ * Whether or not the original PackageSetting needs to be updated with this result on
+ * commit.
+ */
+ public final boolean mExistingSettingCopied;
+ /**
+ * Whether or not the original PackageSetting needs to be updated with
+ * a new uid. Useful when leaving a sharedUserID.
+ */
+ public final boolean mNeedsNewAppId;
+ /**
+ * The final package settings. This may be the same object passed in
+ * the {@link ScanRequest}, but, with modified values.
+ */
+ @Nullable
+ public final PackageSetting mPkgSetting;
+ /** ABI code paths that have changed in the package scan */
+ @Nullable public final List<String> mChangedAbiCodePath;
+
+ public final SharedLibraryInfo mStaticSharedLibraryInfo;
+
+ public final List<SharedLibraryInfo> mDynamicSharedLibraryInfos;
+
+ ScanResult(
+ ScanRequest request, boolean success,
+ @Nullable PackageSetting pkgSetting,
+ @Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
+ boolean needsNewAppId,
+ SharedLibraryInfo staticSharedLibraryInfo,
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
+ mRequest = request;
+ mSuccess = success;
+ mPkgSetting = pkgSetting;
+ mChangedAbiCodePath = changedAbiCodePath;
+ mExistingSettingCopied = existingSettingCopied;
+ mNeedsNewAppId = needsNewAppId;
+ mStaticSharedLibraryInfo = staticSharedLibraryInfo;
+ mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 26aebbc1ea93..a854aa056238 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -747,6 +747,15 @@ public final class Settings implements Watchable, Snappable {
mRenamedPackages.remove(pkgName);
}
+ void pruneRenamedPackagesLPw() {
+ for (int i = mRenamedPackages.size() - 1; i >= 0; i--) {
+ PackageSetting ps = mPackages.get(mRenamedPackages.valueAt(i));
+ if (ps == null) {
+ mRenamedPackages.removeAt(i);
+ }
+ }
+ }
+
/** Gets and optionally creates a new shared user id. */
SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
boolean create) throws PackageManagerException {
@@ -1117,9 +1126,9 @@ public final class Settings implements Watchable, Snappable {
* already registered.
* @throws PackageManagerException If a user ID could not be allocated.
*/
- boolean registerAppIdLPw(PackageSetting p) throws PackageManagerException {
+ boolean registerAppIdLPw(PackageSetting p, boolean forceNew) throws PackageManagerException {
final boolean createdNew;
- if (p.appId == 0) {
+ if (p.appId == 0 || forceNew) {
// Assign new user ID
p.appId = acquireAndRegisterNewAppIdLPw(p);
createdNew = true;
@@ -1169,12 +1178,13 @@ public final class Settings implements Watchable, Snappable {
// by that time.
void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
// Update signatures if needed.
- if (p.signatures.mSigningDetails.signatures == null) {
+ if (p.signatures.mSigningDetails.getSignatures() == null) {
p.signatures.mSigningDetails = pkg.getSigningDetails();
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+ if (p.sharedUser != null
+ && p.sharedUser.signatures.mSigningDetails.getSignatures() == null) {
p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
}
addPackageSettingLPw(p, p.sharedUser);
@@ -2966,6 +2976,17 @@ public final class Settings implements Watchable, Snappable {
mReadMessages.append("Error reading: " + e.toString());
PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
+ } finally {
+ if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No internal VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+ }
+ if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) {
+ Slog.wtf(PackageManagerService.TAG,
+ "No external VersionInfo found in settings, using current.");
+ findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
+ }
}
// If the build is setup to drop runtime permissions
@@ -3267,7 +3288,7 @@ public final class Settings implements Watchable, Snappable {
IntentFilter.AuthorityEntry auth, PatternMatcher path, int userId) {
final List<ResolveInfo> ri =
pmInternal.queryIntentActivities(
- intent, intent.getType(), flags, Binder.getCallingUid(), 0);
+ intent, intent.getType(), flags, Binder.getCallingUid(), userId);
if (PackageManagerService.DEBUG_PREFERRED) {
Log.d(TAG, "Queried " + intent + " results: " + ri);
}
@@ -4490,7 +4511,7 @@ public final class Settings implements Watchable, Snappable {
pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName());
pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isUsesNonSdkApi());
pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, pkg); pw.println();
- final int apkSigningVersion = pkg.getSigningDetails().signatureSchemeVersion;
+ final int apkSigningVersion = pkg.getSigningDetails().getSignatureSchemeVersion();
pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
pw.print(prefix); pw.print(" applicationInfo=");
pw.println(pkg.toAppInfoToString());
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 8ddbe08e2572..8bfa7288892d 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -88,7 +88,7 @@ public final class SharedUserSetting extends SettingBase {
uidFlags = orig.uidFlags;
uidPrivateFlags = orig.uidPrivateFlags;
packages = new ArraySet(orig.packages);
- // A PackageParser.SigningDetails seems to consist solely of final attributes, so
+ // A SigningDetails seems to consist solely of final attributes, so
// it is safe to copy the reference.
signatures.mSigningDetails = orig.signatures.mSigningDetails;
signaturesChanged = orig.signaturesChanged;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 62d6717e847a..0951e0d01288 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -86,6 +86,7 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -598,6 +599,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG_PROCSTATE) {
Slog.d(TAG, "onUidStateChanged: uid=" + uid + " state=" + procState);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleOnUidStateChanged");
synchronized (mLock) {
mUidState.put(uid, procState);
@@ -608,6 +610,7 @@ public class ShortcutService extends IShortcutService.Stub {
mUidLastForegroundElapsedTime.put(uid, injectElapsedRealtime());
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
private boolean isProcessStateForeground(int processState) {
@@ -703,10 +706,12 @@ public class ShortcutService extends IShortcutService.Stub {
// or anything.
final long start = getStatStartTime();
injectRunOnNewThread(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleUnlockUser");
synchronized (mLock) {
logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
getUserShortcutsLocked(userId);
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
});
}
@@ -715,6 +720,7 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG || DEBUG_REBOOT) {
Slog.d(TAG, "handleStopUser: user=" + userId);
}
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
synchronized (mLock) {
unloadUserLocked(userId);
@@ -722,6 +728,7 @@ public class ShortcutService extends IShortcutService.Stub {
mUnlockedUsers.put(userId, false);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@GuardedBy("mLock")
@@ -1192,6 +1199,7 @@ public class ShortcutService extends IShortcutService.Stub {
return;
}
try {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutSaveDirtyInfo");
synchronized (mLock) {
for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) {
final int userId = mDirtyUserIds.get(i);
@@ -1205,6 +1213,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
} catch (Exception e) {
wtf("Exception in saveDirtyInfo", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index bdbcb277e8d6..4dfb6b8ea453 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.apex.ApexInfo;
import android.apex.ApexInfoList;
import android.apex.ApexSessionInfo;
@@ -28,17 +29,20 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ApplicationInfo;
+import android.content.pm.ApexStagedEvent;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.SigningDetails;
+import android.content.pm.SigningDetails.SignatureSchemeVersion;
+import android.content.pm.StagedApexInfo;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.Bundle;
@@ -52,6 +56,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -80,8 +85,11 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
@@ -98,7 +106,8 @@ public class StagingManager {
private final ApexManager mApexManager;
private final PowerManager mPowerManager;
private final Context mContext;
- private final PreRebootVerificationHandler mPreRebootVerificationHandler;
+ @VisibleForTesting
+ final PreRebootVerificationHandler mPreRebootVerificationHandler;
private final Supplier<PackageParser2> mPackageParserSupplier;
private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt");
@@ -114,6 +123,11 @@ public class StagingManager {
@GuardedBy("mSuccessfulStagedSessionIds")
private final List<Integer> mSuccessfulStagedSessionIds = new ArrayList<>();
+ @GuardedBy("mStagedApexObservers")
+ private final List<IStagedApexObserver> mStagedApexObservers = new ArrayList<>();
+
+ private final CompletableFuture<Void> mBootCompleted = new CompletableFuture<>();
+
interface StagedSession {
boolean isMultiPackage();
boolean isApexSession();
@@ -138,25 +152,28 @@ public class StagingManager {
boolean hasParentSessionId();
long getCommittedMillis();
void abandon();
- boolean notifyStartPreRebootVerification();
void notifyEndPreRebootVerification();
void verifySession();
}
- StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier) {
- this(context, packageParserSupplier, ApexManager.getInstance());
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier, Looper looper) {
+ this(context, packageParserSupplier, ApexManager.getInstance(), looper);
}
@VisibleForTesting
StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
ApexManager apexManager) {
+ this(context, packageParserSupplier, apexManager, BackgroundThread.get().getLooper());
+ }
+
+ StagingManager(Context context, Supplier<PackageParser2> packageParserSupplier,
+ ApexManager apexManager, Looper looper) {
mContext = context;
mPackageParserSupplier = packageParserSupplier;
mApexManager = apexManager;
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mPreRebootVerificationHandler = new PreRebootVerificationHandler(
- BackgroundThread.get().getLooper());
+ mPreRebootVerificationHandler = new PreRebootVerificationHandler(looper);
if (mFailureReasonFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) {
@@ -198,6 +215,35 @@ public class StagingManager {
mApexManager.markBootCompleted();
}
+ void registerStagedApexObserver(IStagedApexObserver observer) {
+ if (observer == null) {
+ return;
+ }
+ if (observer.asBinder() != null) {
+ try {
+ observer.asBinder().linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.remove(observer);
+ }
+ }
+ }, 0);
+ } catch (RemoteException re) {
+ Slog.w(TAG, re.getMessage());
+ }
+ }
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.add(observer);
+ }
+ }
+
+ void unregisterStagedApexObserver(IStagedApexObserver observer) {
+ synchronized (mStagedApexObservers) {
+ mStagedApexObservers.remove(observer);
+ }
+ }
+
/**
* Validates the signature used to sign the container of the new apex package
*
@@ -211,13 +257,15 @@ public class StagingManager {
int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
newApexPkg.applicationInfo.targetSdkVersion);
- final SigningDetails newSigningDetails;
- try {
- newSigningDetails = ApkSignatureVerifier.verify(apexPath, minSignatureScheme);
- } catch (PackageParserException e) {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify(
+ input.reset(), apexPath, minSignatureScheme);
+ if (newResult.isError()) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Failed to parse APEX package " + apexPath + " : " + e, e);
+ "Failed to parse APEX package " + apexPath + " : "
+ + newResult.getException(), newResult.getException());
}
+ final SigningDetails newSigningDetails = newResult.getResult();
// Get signing details of the existing package
final PackageInfo existingApexPkg = mApexManager.getPackageInfo(packageName,
@@ -228,15 +276,15 @@ public class StagingManager {
throw new IllegalStateException("Unknown apex package " + packageName);
}
- final SigningDetails existingSigningDetails;
- try {
- existingSigningDetails = ApkSignatureVerifier.verify(
- existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
- } catch (PackageParserException e) {
+ final ParseResult<SigningDetails> existingResult = ApkSignatureVerifier.verify(
+ input.reset(), existingApexPkg.applicationInfo.sourceDir,
+ SignatureSchemeVersion.JAR);
+ if (existingResult.isError()) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir
- + " : " + e, e);
+ + " : " + existingResult.getException(), existingResult.getException());
}
+ final SigningDetails existingSigningDetails = existingResult.getResult();
// Verify signing details for upgrade
if (newSigningDetails.checkCapability(existingSigningDetails,
@@ -292,19 +340,10 @@ public class StagingManager {
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Unable to generate package info: " + apexInfo.modulePath);
}
- } catch (PackageParserException e) {
+ } catch (PackageManagerException e) {
throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
"Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e);
}
- final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
- ApexManager.MATCH_ACTIVE_PACKAGE);
- if (activePackage == null) {
- Slog.w(TAG, "Attempting to install new APEX package " + packageInfo.packageName);
- throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "It is forbidden to install new APEX packages.");
- }
- checkRequiredVersionCode(session, activePackage);
- checkDowngrade(session, activePackage, packageInfo);
result.add(packageInfo);
apexPackageNames.add(packageInfo.packageName);
}
@@ -327,40 +366,6 @@ public class StagingManager {
"Could not find rollback id for commit session: " + sessionId);
}
- private void checkRequiredVersionCode(final StagedSession session,
- final PackageInfo activePackage) throws PackageManagerException {
- if (session.sessionParams().requiredInstalledVersionCode
- == PackageManager.VERSION_CODE_HIGHEST) {
- return;
- }
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- if (activeVersion != session.sessionParams().requiredInstalledVersionCode) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Installed version of APEX package " + activePackage.packageName
- + " does not match required. Active version: " + activeVersion
- + " required: " + session.sessionParams().requiredInstalledVersionCode);
- }
- }
-
- private void checkDowngrade(final StagedSession session,
- final PackageInfo activePackage, final PackageInfo newPackage)
- throws PackageManagerException {
- final long activeVersion = activePackage.applicationInfo.longVersionCode;
- final long newVersionCode = newPackage.applicationInfo.longVersionCode;
- final boolean isAppDebuggable = (activePackage.applicationInfo.flags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
- session.sessionParams().installFlags, isAppDebuggable);
- if (activeVersion > newVersionCode && !allowsDowngrade) {
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Downgrade of APEX package " + newPackage.packageName
- + " is not allowed. Active version: " + activeVersion
- + " attempted: " + newVersionCode);
- }
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(String failureReason, boolean supportsCheckpoint,
boolean needsCheckpoint) {
@@ -690,6 +695,7 @@ public class StagingManager {
* </ul>
* @throws PackageManagerException if session fails the check
*/
+ // TODO(b/192625695): Rename this method which checks rollbacks in addition to overlapping
@VisibleForTesting
void checkNonOverlappingWithStagedSessions(@NonNull StagedSession session)
throws PackageManagerException {
@@ -723,13 +729,6 @@ public class StagingManager {
continue;
}
- if (stagedSession.getCommittedMillis() > session.getCommittedMillis()) {
- // Ignore sessions that are committed after the provided session. When there are
- // overlaps between sessions, we will fail the one committed later instead of
- // the earlier one.
- continue;
- }
-
// From here on, stagedSession is a parent active staged session
// Check if session is one of the active sessions
@@ -754,15 +753,28 @@ public class StagingManager {
"Session was failed by rollback session: " + session.sessionId());
Slog.i(TAG, "Session " + root.sessionId() + " is marked failed due to "
+ "rollback session: " + session.sessionId());
+ } else if (!isRollback && isRollback(stagedSession)) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_CONFLICT,
+ "Session was failed by rollback session: " + stagedSession.sessionId());
} else if (stagedSession.sessionContains(
s -> s.getPackageName().equals(packageName))) {
- // New session cannot have same package name as one of the active sessions
- throw new PackageManagerException(
- SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId()
- + " has been staged already by session: "
- + stagedSession.sessionId(), null);
+ // Fail the session committed later when there are overlapping packages
+ if (stagedSession.getCommittedMillis() < session.getCommittedMillis()) {
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + session.getPackageName() + " in session: "
+ + session.sessionId()
+ + " has been staged already by session: "
+ + stagedSession.sessionId(), null);
+ } else {
+ stagedSession.setSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "Package: " + packageName + " in session: "
+ + stagedSession.sessionId()
+ + " has been staged already by session: "
+ + session.sessionId());
+ }
}
// Staging multiple root sessions is not allowed if device doesn't support
@@ -836,6 +848,9 @@ public class StagingManager {
// Also, cleaning up the stageDir prevents the apex from being activated.
Slog.e(TAG, "Failed to abort apex session " + session.sessionId());
}
+ if (session.containsApexSession()) {
+ notifyStagedApexObservers();
+ }
}
// Session was successfully aborted from apexd (if required) and pre-reboot verification
@@ -890,7 +905,8 @@ public class StagingManager {
} else if (!session.isSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ Slog.i(TAG, "Restart verification for session=" + session.sessionId());
+ mBootCompleted.thenRun(() -> session.verifySession());
StagedSession session2 = sessions.set(j - 1, session);
sessions.set(i, session2);
j--;
@@ -1079,7 +1095,7 @@ public class StagingManager {
@VisibleForTesting
void onBootCompletedBroadcastReceived() {
- mPreRebootVerificationHandler.readyToStart();
+ mBootCompleted.complete(null);
BackgroundThread.getExecutor().execute(() -> logFailedApexSessionsIfNecessary());
}
@@ -1120,24 +1136,107 @@ public class StagingManager {
return session;
}
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyVerificationComplete(StagedSession session) {
- mPreRebootVerificationHandler.onPreRebootVerificationComplete(session);
+ /**
+ * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>},
+ * where the key of the map is the module name of the ApexInfo.
+ *
+ * Returns an empty map if there is any error.
+ */
+ @VisibleForTesting
+ @NonNull
+ Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) {
+ Preconditions.checkArgument(session != null, "Session is null");
+ Preconditions.checkArgument(!session.hasParentSessionId(),
+ session.sessionId() + " session has parent session");
+ Preconditions.checkArgument(session.containsApexSession(),
+ session.sessionId() + " session does not contain apex");
+
+ // Even if caller calls this method on ready session, the session could be abandoned
+ // right after this method is called.
+ if (!session.isSessionReady() || session.isDestroyed()) {
+ return Collections.emptyMap();
+ }
+
+ ApexSessionParams params = new ApexSessionParams();
+ params.sessionId = session.sessionId();
+ final IntArray childSessionIds = new IntArray();
+ if (session.isMultiPackage()) {
+ for (StagedSession s : session.getChildSessions()) {
+ if (s.isApexSession()) {
+ childSessionIds.add(s.sessionId());
+ }
+ }
+ }
+ params.childSessionIds = childSessionIds.toArray();
+
+ ApexInfo[] infos = mApexManager.getStagedApexInfos(params);
+ Map<String, ApexInfo> result = new ArrayMap<>();
+ for (ApexInfo info : infos) {
+ result.put(info.moduleName, info);
+ }
+ return result;
+ }
+
+ /**
+ * Returns apex module names of all packages that are staged ready
+ */
+ List<String> getStagedApexModuleNames() {
+ List<String> result = new ArrayList<>();
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final StagedSession session = mStagedSessions.valueAt(i);
+ if (!session.isSessionReady() || session.isDestroyed()
+ || session.hasParentSessionId() || !session.containsApexSession()) {
+ continue;
+ }
+ result.addAll(getStagedApexInfos(session).keySet());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null.
+ */
+ @Nullable
+ StagedApexInfo getStagedApexInfo(String moduleName) {
+ synchronized (mStagedSessions) {
+ for (int i = 0; i < mStagedSessions.size(); i++) {
+ final StagedSession session = mStagedSessions.valueAt(i);
+ if (!session.isSessionReady() || session.isDestroyed()
+ || session.hasParentSessionId() || !session.containsApexSession()) {
+ continue;
+ }
+ ApexInfo ai = getStagedApexInfos(session).get(moduleName);
+ if (ai != null) {
+ StagedApexInfo info = new StagedApexInfo();
+ info.moduleName = ai.moduleName;
+ info.diskImagePath = ai.modulePath;
+ info.versionCode = ai.versionCode;
+ info.versionName = ai.versionName;
+ return info;
+ }
+ }
+ }
+ return null;
}
- // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
- // verification logic is extracted out of StagingManager into PMS, we can remove
- // this.
- void notifyPreRebootVerification_Apk_Complete(@NonNull StagedSession session) {
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(session);
+ private void notifyStagedApexObservers() {
+ synchronized (mStagedApexObservers) {
+ for (IStagedApexObserver observer : mStagedApexObservers) {
+ ApexStagedEvent event = new ApexStagedEvent();
+ event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]);
+ try {
+ observer.onApexStaged(event);
+ } catch (RemoteException re) {
+ Slog.w(TAG, "Failed to contact the observer " + re.getMessage());
+ }
+ }
+ }
}
- private final class PreRebootVerificationHandler extends Handler {
- // Hold sessions before handler gets ready to do the verification.
- private List<StagedSession> mPendingSessions;
- private boolean mIsReady;
+ @VisibleForTesting
+ final class PreRebootVerificationHandler extends Handler {
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1151,7 +1250,6 @@ public class StagingManager {
* <p><ul>
* <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
- * <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
* <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
* </ul></p>
*
@@ -1159,8 +1257,8 @@ public class StagingManager {
*/
private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
- private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
- private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+ @VisibleForTesting
+ static final int MSG_PRE_REBOOT_VERIFICATION_END = 3;
@Override
public void handleMessage(Message msg) {
@@ -1180,9 +1278,6 @@ public class StagingManager {
case MSG_PRE_REBOOT_VERIFICATION_APEX:
handlePreRebootVerification_Apex(session, rollbackId);
break;
- case MSG_PRE_REBOOT_VERIFICATION_APK:
- handlePreRebootVerification_Apk(session);
- break;
case MSG_PRE_REBOOT_VERIFICATION_END:
handlePreRebootVerification_End(session);
break;
@@ -1195,35 +1290,15 @@ public class StagingManager {
}
}
- // Notify the handler that system is ready, and reschedule the pre-reboot verifications.
- private synchronized void readyToStart() {
- mIsReady = true;
- if (mPendingSessions != null) {
- for (int i = 0; i < mPendingSessions.size(); i++) {
- StagedSession session = mPendingSessions.get(i);
- startPreRebootVerification(session);
- }
- mPendingSessions = null;
- }
- }
-
// Method for starting the pre-reboot verification
private synchronized void startPreRebootVerification(
@NonNull StagedSession session) {
- if (!mIsReady) {
- if (mPendingSessions == null) {
- mPendingSessions = new ArrayList<>();
- }
- mPendingSessions.add(session);
- return;
- }
-
- if (session.notifyStartPreRebootVerification()) {
+ mBootCompleted.thenRun(() -> {
int sessionId = session.sessionId();
Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, -1, session)
.sendToTarget();
- }
+ });
}
private void onPreRebootVerificationFailure(StagedSession session,
@@ -1252,12 +1327,6 @@ public class StagingManager {
private void notifyPreRebootVerification_Apex_Complete(
@NonNull StagedSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session.sessionId(), -1, session)
- .sendToTarget();
- }
-
- private void notifyPreRebootVerification_Apk_Complete(
- @NonNull StagedSession session) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session)
.sendToTarget();
}
@@ -1345,19 +1414,6 @@ public class StagingManager {
}
/**
- * Pre-reboot verification state for apk files. Session is sent to
- * {@link PackageManagerService} for verification and it notifies back the result via
- * {@link #notifyPreRebootVerification_Apk_Complete}
- */
- private void handlePreRebootVerification_Apk(@NonNull StagedSession session) {
- if (!session.containsApkSession()) {
- notifyPreRebootVerification_Apk_Complete(session);
- return;
- }
- session.verifySession();
- }
-
- /**
* Pre-reboot verification state for wrapping up:
* <p><ul>
* <li>enables rollback if required</li>
@@ -1402,6 +1458,7 @@ public class StagingManager {
if (hasApex) {
try {
mApexManager.markStagedSessionReady(session.sessionId());
+ notifyStagedApexObservers();
} catch (PackageManagerException e) {
session.setSessionFailed(e.error, e.getMessage());
return;
diff --git a/services/core/java/com/android/server/pm/SystemDeleteException.java b/services/core/java/com/android/server/pm/SystemDeleteException.java
new file mode 100644
index 000000000000..410876ff2a47
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SystemDeleteException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+final class SystemDeleteException extends Exception {
+ final PackageManagerException mReason;
+
+ SystemDeleteException(PackageManagerException reason) {
+ mReason = reason;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 9182d811d56d..cc5187ecab2f 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -1,6 +1,22 @@
{
"presubmit": [
{
+ "name": "CtsAtomicInstallTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ]
+ },
+ {
+ "name": "CtsPackageInstallTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ]
+ },
+ {
"name": "CtsUsesLibraryHostTestCases"
},
{
@@ -41,6 +57,53 @@
]
},
{
+ "name": "FrameworksServicesTests",
+ "file_patterns": ["(/|^)ShortcutService\\.java"],
+ "options": [
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest1"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest2"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest3"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest4"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest5"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest6"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest7"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest8"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest9"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest10"
+ },
+ {
+ "include-filter": "com.android.server.pm.ShortcutManagerTest11"
+ }
+ ]
+ },
+ {
+ "name": "CtsShortcutHostTestCases",
+ "file_patterns": ["(/|^)ShortcutService\\.java"]
+ },
+ {
+ "name": "CtsShortcutManagerTestCases",
+ "file_patterns": ["(/|^)ShortcutService\\.java"]
+ },
+ {
"name": "CtsContentTestCases",
"options": [
{
@@ -79,6 +142,19 @@
]
},
{
+ "name": "CtsAppSecurityHostTestCases",
+ "file_patterns": [
+ "core/java/.*Install.*",
+ "services/core/.*Install.*",
+ "services/core/java/com/android/server/pm/.*"
+ ],
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.SplitTests"
+ }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index d4feb3a728c8..e8182e07ba4d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -117,6 +117,7 @@ import com.android.server.am.UserState;
import com.android.server.pm.UserManagerInternal.UserLifecycleListener;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -727,7 +728,7 @@ public class UserManagerService extends IUserManager.Stub {
}
/* Prunes out any partially created or partially removed users. */
- void cleanupPartialUsers() {
+ private void cleanupPartialUsers() {
ArrayList<UserInfo> partials = new ArrayList<>();
synchronized (mUsersLock) {
final int userSize = mUsers.size();
@@ -755,7 +756,7 @@ public class UserManagerService extends IUserManager.Stub {
* Removes any pre-created users from the system. Should be invoked after OTAs, to ensure
* pre-created users are not stale. New pre-created pool can be re-created after the update.
*/
- void cleanupPreCreatedUsers() {
+ private void cleanupPreCreatedUsers() {
final ArrayList<UserInfo> preCreatedUsers;
synchronized (mUsersLock) {
final int userSize = mUsers.size();
@@ -1213,6 +1214,10 @@ public class UserManagerService extends IUserManager.Stub {
private void logQuietModeEnabled(@UserIdInt int userId, boolean enableQuietMode,
@Nullable String callingPackage) {
+ Slogf.i(LOG_TAG,
+ "requestQuietModeEnabled called by package %s, with enableQuietMode %b.",
+ callingPackage,
+ enableQuietMode);
UserData userData;
synchronized (mUsersLock) {
userData = getUserDataLU(userId);
@@ -2293,13 +2298,13 @@ public class UserManagerService extends IUserManager.Stub {
// Package private for the inner class.
@GuardedBy("mRestrictionsLock")
- void applyUserRestrictionsLR(@UserIdInt int userId) {
+ private void applyUserRestrictionsLR(@UserIdInt int userId) {
updateUserRestrictionsInternalLR(null, userId);
}
@GuardedBy("mRestrictionsLock")
// Package private for the inner class.
- void applyUserRestrictionsForAllUsersLR() {
+ private void applyUserRestrictionsForAllUsersLR() {
if (DBG) {
debug("applyUserRestrictionsForAllUsersLR");
}
@@ -2352,6 +2357,9 @@ public class UserManagerService extends IUserManager.Stub {
* {@link #canAddMoreProfilesToUser}.
*/
private boolean canAddMoreUsersOfType(UserTypeDetails userTypeDetails) {
+ if (!userTypeDetails.isEnabled()) {
+ return false;
+ }
final int max = userTypeDetails.getMaxAllowed();
if (max == UserTypeDetails.UNLIMITED_NUMBER_OF_USERS) {
return true; // Indicates that there is no max.
@@ -2392,7 +2400,7 @@ public class UserManagerService extends IUserManager.Stub {
boolean allowedToRemoveOne) {
checkManageUsersPermission("check if more profiles can be added.");
final UserTypeDetails type = mUserTypes.get(userType);
- if (type == null) {
+ if (type == null || !type.isEnabled()) {
return false;
}
// Managed profiles have their own specific rules.
@@ -2900,8 +2908,7 @@ public class UserManagerService extends IUserManager.Stub {
}
@GuardedBy("mUsersLock")
- @VisibleForTesting
- void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
+ private void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
@NonNull ArrayMap<String, UserTypeDetails> userTypes,
final int formerUserTypeVersion,
@NonNull Set<Integer> userIdsToWrite) {
@@ -3549,6 +3556,12 @@ public class UserManagerService extends IUserManager.Stub {
+ ") indicated SYSTEM user, which cannot be created.");
return null;
}
+ if (!userTypeDetails.isEnabled()) {
+ throwCheckedUserOperationException(
+ "Cannot add a user of disabled type " + userType + ".",
+ UserManager.USER_OPERATION_ERROR_MAX_USERS);
+ }
+
synchronized (mUsersLock) {
if (mForceEphemeralUsers) {
flags |= UserInfo.FLAG_EPHEMERAL;
@@ -3880,6 +3893,7 @@ public class UserManagerService extends IUserManager.Stub {
}
/** Checks that the flags do not contain mutually exclusive types/properties. */
+ @VisibleForTesting
static boolean checkUserTypeConsistency(@UserInfoFlag int flags) {
// Mask to check that flags don't refer to multiple user types.
final int userTypeFlagMask = UserInfo.FLAG_GUEST | UserInfo.FLAG_DEMO
@@ -4186,6 +4200,7 @@ public class UserManagerService extends IUserManager.Stub {
return false;
}
+ Slog.i(LOG_TAG, "Removing user " + userId);
addRemovingUserIdLocked(userId);
}
@@ -4308,8 +4323,8 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- void finishRemoveUser(final @UserIdInt int userId) {
- if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId);
+ private void finishRemoveUser(final @UserIdInt int userId) {
+ Slog.i(LOG_TAG, "finishRemoveUser " + userId);
UserInfo user;
synchronized (mUsersLock) {
@@ -4368,6 +4383,7 @@ public class UserManagerService extends IUserManager.Stub {
}
private void removeUserState(final @UserIdInt int userId) {
+ Slog.i(LOG_TAG, "Removing user state of user " + userId);
try {
mContext.getSystemService(StorageManager.class).destroyUserKey(userId);
} catch (IllegalStateException e) {
@@ -4814,8 +4830,15 @@ public class UserManagerService extends IUserManager.Stub {
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("prepareUserData-" + userId);
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+ t.traceEnd();
+
+ t.traceBegin("reconcileAppsData-" + userId);
mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
+ t.traceEnd();
}
/**
@@ -4984,7 +5007,7 @@ public class UserManagerService extends IUserManager.Stub {
(new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
}
- int onShellCommand(Shell shell, String cmd) {
+ private int onShellCommand(Shell shell, String cmd) {
if (cmd == null) {
return shell.handleDefaultCommands(cmd);
}
@@ -5064,8 +5087,11 @@ public class UserManagerService extends IUserManager.Stub {
Binder.restoreCallingIdentity(ident);
}
}
- pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s%s%s\n", i, user.id,
+ pw.printf("%d: id=%d, name=%s, type=%s, flags=%s%s%s%s%s%s%s%s%s\n",
+ i,
+ user.id,
user.name,
+ user.userType.replace("android.os.usertype.", ""),
UserInfo.flagsToString(user.flags),
hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
running ? " (running)" : "",
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 6e6d7c3f4fab..849d1a338fc7 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -487,7 +487,7 @@ class UserSystemPackageInstaller {
/**
* Gets the system package names that should be installed on users of the given user type, as
* determined by SystemConfig, the allowlist mode, and the apps actually on the device.
- * Names are the {@link PackageParser.Package#packageName}, not necessarily the manifest names.
+ * Names are the {@link AndroidPackage#getPackageName()}, not necessarily the manifest names.
*
* Returns null if all system packages should be installed (due to enforce-mode being off).
*/
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 92ec31b21e07..24dab9ed73b9 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -50,7 +50,7 @@ public final class UserTypeDetails {
/** Name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}. */
private final @NonNull String mName;
- // TODO(b/142482943): Currently unused. Hook this up.
+ /** Whether users of this type can be created. */
private final boolean mEnabled;
// TODO(b/142482943): Currently unused and not set. Hook this up.
@@ -195,7 +195,10 @@ public final class UserTypeDetails {
return mName;
}
- // TODO(b/142482943) Hook this up or delete it.
+ /**
+ * Returns whether this user type is enabled.
+ * If it is not enabled, all future attempts to create users of this type will be rejected.
+ */
public boolean isEnabled() {
return mEnabled;
}
@@ -390,7 +393,7 @@ public final class UserTypeDetails {
private @Nullable Bundle mDefaultSecureSettings = null;
private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
null;
- private boolean mEnabled = true;
+ private int mEnabled = 1;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
private @Nullable int[] mBadgeColors = null;
@@ -405,7 +408,7 @@ public final class UserTypeDetails {
return this;
}
- public Builder setEnabled(boolean enabled) {
+ public Builder setEnabled(int enabled) {
mEnabled = enabled;
return this;
}
@@ -522,12 +525,25 @@ public final class UserTypeDetails {
"UserTypeDetails %s has a non empty "
+ "defaultCrossProfileIntentFilters", mName);
}
- return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
- mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
- mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
+ return new UserTypeDetails(
+ mName,
+ mEnabled != 0,
+ mMaxAllowed,
+ mBaseType,
+ mDefaultUserInfoPropertyFlags,
+ mLabel,
+ mMaxAllowedPerParent,
+ mIconBadge,
+ mBadgePlain,
+ mBadgeNoBackground,
+ mBadgeLabels,
+ mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
- mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent);
+ mDefaultRestrictions,
+ mDefaultSystemSettings,
+ mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters,
+ mIsMediaSharedWithParent);
}
private boolean hasBadge() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 30cb40c34203..5fcb843a2699 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -376,6 +376,8 @@ public final class UserTypeFactory {
setResAttribute(parser, "badge-no-background", builder::setBadgeNoBackground);
}
+ setIntAttribute(parser, "enabled", builder::setEnabled);
+
// Process child elements.
final int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
diff --git a/services/core/java/com/android/server/pm/VerificationInfo.java b/services/core/java/com/android/server/pm/VerificationInfo.java
new file mode 100644
index 000000000000..3d4a42c16c1b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/VerificationInfo.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.server.pm;
+
+import android.net.Uri;
+
+final class VerificationInfo {
+ /** URI referencing where the package was downloaded from. */
+ final Uri mOriginatingUri;
+
+ /** HTTP referrer URI associated with the originatingURI. */
+ final Uri mReferrer;
+
+ /** UID of the application that the install request originated from. */
+ final int mOriginatingUid;
+
+ /** UID of application requesting the install */
+ final int mInstallerUid;
+
+ VerificationInfo(Uri originatingUri, Uri referrer, int originatingUid, int installerUid) {
+ mOriginatingUri = originatingUri;
+ mReferrer = referrer;
+ mOriginatingUid = originatingUid;
+ mInstallerUid = installerUid;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
new file mode 100644
index 000000000000..3c499de9ea46
--- /dev/null
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 static android.content.Intent.EXTRA_LONG_VERSION_CODE;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_VERSION_CODE;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
+import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.CHECK_PENDING_VERIFICATION;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
+import static com.android.server.pm.PackageManagerService.ENABLE_ROLLBACK_TIMEOUT;
+import static com.android.server.pm.PackageManagerService.PACKAGE_MIME_TYPE;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.DataLoaderType;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.VerifierInfo;
+import android.content.pm.parsing.PackageLite;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.server.DeviceIdleInternal;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.io.File;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+final class VerificationParams extends HandlerParams {
+ /**
+ * Whether integrity verification is enabled by default.
+ */
+ private static final boolean DEFAULT_INTEGRITY_VERIFY_ENABLE = true;
+ /**
+ * The default maximum time to wait for the integrity verification to return in
+ * milliseconds.
+ */
+ private static final long DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT = 30 * 1000;
+ /**
+ * Whether verification is enabled by default.
+ */
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
+ /**
+ * Timeout duration in milliseconds for enabling package rollback. If we fail to enable
+ * rollback within that period, the install will proceed without rollback enabled.
+ *
+ * <p>If flag value is negative, the default value will be assigned.
+ *
+ * Flag type: {@code long}
+ * Namespace: NAMESPACE_ROLLBACK
+ */
+ private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS = "enable_rollback_timeout";
+ /**
+ * The default duration to wait for rollback to be enabled in
+ * milliseconds.
+ */
+ private static final long DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS = 10 * 1000;
+
+ final OriginInfo mOriginInfo;
+ final IPackageInstallObserver2 mObserver;
+ final int mInstallFlags;
+ @NonNull
+ final InstallSource mInstallSource;
+ final String mPackageAbiOverride;
+ final VerificationInfo mVerificationInfo;
+ final SigningDetails mSigningDetails;
+ @Nullable
+ MultiPackageVerificationParams mParentVerificationParams;
+ final long mRequiredInstalledVersionCode;
+ final int mDataLoaderType;
+ final int mSessionId;
+
+ private boolean mWaitForVerificationToComplete;
+ private boolean mWaitForIntegrityVerificationToComplete;
+ private boolean mWaitForEnableRollbackToComplete;
+ private int mRet = PackageManager.INSTALL_SUCCEEDED;
+ private String mErrorMessage = null;
+
+ final PackageLite mPackageLite;
+ final PackageManagerService mPm;
+
+ VerificationParams(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
+ PackageInstaller.SessionParams sessionParams, InstallSource installSource,
+ int installerUid, SigningDetails signingDetails, int sessionId, PackageLite lite,
+ PackageManagerService pm) {
+ super(user);
+ mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
+ mObserver = observer;
+ mInstallFlags = sessionParams.installFlags;
+ mInstallSource = installSource;
+ mPackageAbiOverride = sessionParams.abiOverride;
+ mVerificationInfo = new VerificationInfo(
+ sessionParams.originatingUri,
+ sessionParams.referrerUri,
+ sessionParams.originatingUid,
+ installerUid
+ );
+ mSigningDetails = signingDetails;
+ mRequiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
+ mDataLoaderType = (sessionParams.dataLoaderParams != null)
+ ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+ mSessionId = sessionId;
+ mPackageLite = lite;
+ mPm = pm;
+ }
+
+ @Override
+ public String toString() {
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + mOriginInfo.mFile + "}";
+ }
+
+ public void handleStartCopy() {
+ PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
+ mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
+
+ Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
+ pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
+ setReturnCode(ret.first, ret.second);
+ if (mRet != INSTALL_SUCCEEDED) {
+ return;
+ }
+
+ // Perform package verification and enable rollback (unless we are simply moving the
+ // package).
+ if (!mOriginInfo.mExisting) {
+ if ((mInstallFlags & PackageManager.INSTALL_APEX) == 0) {
+ // TODO(b/182426975): treat APEX as APK when APK verification is concerned
+ sendApkVerificationRequest(pkgLite);
+ }
+ if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ sendEnableRollbackRequest();
+ }
+ }
+ }
+
+ void sendApkVerificationRequest(PackageInfoLite pkgLite) {
+ final int verificationId = mPm.mPendingVerificationToken++;
+
+ PackageVerificationState verificationState =
+ new PackageVerificationState(this);
+ mPm.mPendingVerification.append(verificationId, verificationState);
+
+ sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
+ sendPackageVerificationRequest(
+ verificationId, pkgLite, verificationState);
+
+ // If both verifications are skipped, we should remove the state.
+ if (verificationState.areAllVerificationsComplete()) {
+ mPm.mPendingVerification.remove(verificationId);
+ }
+ }
+
+ void sendEnableRollbackRequest() {
+ final int enableRollbackToken = mPm.mPendingEnableRollbackToken++;
+ Trace.asyncTraceBegin(
+ TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
+ mPm.mPendingEnableRollback.append(enableRollbackToken, this);
+
+ Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
+ enableRollbackIntent.putExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
+ enableRollbackToken);
+ enableRollbackIntent.putExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_SESSION_ID,
+ mSessionId);
+ enableRollbackIntent.setType(PACKAGE_MIME_TYPE);
+ enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Allow the broadcast to be sent before boot complete.
+ // This is needed when committing the apk part of a staged
+ // session in early boot. The rollback manager registers
+ // its receiver early enough during the boot process that
+ // it will not miss the broadcast.
+ enableRollbackIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ mPm.mContext.sendBroadcastAsUser(enableRollbackIntent, UserHandle.SYSTEM,
+ android.Manifest.permission.PACKAGE_ROLLBACK_AGENT);
+
+ mWaitForEnableRollbackToComplete = true;
+
+ // the duration to wait for rollback to be enabled, in millis
+ long rollbackTimeout = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
+ DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS);
+ if (rollbackTimeout < 0) {
+ rollbackTimeout = DEFAULT_ENABLE_ROLLBACK_TIMEOUT_MILLIS;
+ }
+ final Message msg = mPm.mHandler.obtainMessage(ENABLE_ROLLBACK_TIMEOUT);
+ msg.arg1 = enableRollbackToken;
+ msg.arg2 = mSessionId;
+ mPm.mHandler.sendMessageDelayed(msg, rollbackTimeout);
+ }
+
+ /**
+ * Send a request to check the integrity of the package.
+ */
+ void sendIntegrityVerificationRequest(
+ int verificationId,
+ PackageInfoLite pkgLite,
+ PackageVerificationState verificationState) {
+ if (!isIntegrityVerificationEnabled()) {
+ // Consider the integrity check as passed.
+ verificationState.setIntegrityVerificationResult(
+ PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ return;
+ }
+
+ final Intent integrityVerification =
+ new Intent(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+
+ integrityVerification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+ PACKAGE_MIME_TYPE);
+
+ final int flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND;
+ integrityVerification.addFlags(flags);
+
+ integrityVerification.putExtra(EXTRA_VERIFICATION_ID, verificationId);
+ integrityVerification.putExtra(EXTRA_PACKAGE_NAME, pkgLite.packageName);
+ integrityVerification.putExtra(EXTRA_VERSION_CODE, pkgLite.versionCode);
+ integrityVerification.putExtra(EXTRA_LONG_VERSION_CODE, pkgLite.getLongVersionCode());
+ populateInstallerExtras(integrityVerification);
+
+ // send to integrity component only.
+ integrityVerification.setPackage("android");
+
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+
+ mPm.mContext.sendOrderedBroadcastAsUser(integrityVerification, UserHandle.SYSTEM,
+ /* receiverPermission= */ null,
+ /* appOp= */ AppOpsManager.OP_NONE,
+ /* options= */ options.toBundle(),
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Message msg =
+ mPm.mHandler.obtainMessage(CHECK_PENDING_INTEGRITY_VERIFICATION);
+ msg.arg1 = verificationId;
+ mPm.mHandler.sendMessageDelayed(msg, getIntegrityVerificationTimeout());
+ }
+ }, /* scheduler= */ null,
+ /* initialCode= */ 0,
+ /* initialData= */ null,
+ /* initialExtras= */ null);
+
+ Trace.asyncTraceBegin(
+ TRACE_TAG_PACKAGE_MANAGER, "integrity_verification", verificationId);
+
+ // stop the copy until verification succeeds.
+ mWaitForIntegrityVerificationToComplete = true;
+ }
+
+
+ /**
+ * Get the integrity verification timeout.
+ *
+ * @return verification timeout in milliseconds
+ */
+ private long getIntegrityVerificationTimeout() {
+ long timeout = Settings.Global.getLong(mPm.mContext.getContentResolver(),
+ Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
+ DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
+ // The setting can be used to increase the timeout but not decrease it, since that is
+ // equivalent to disabling the integrity component.
+ return Math.max(timeout, DEFAULT_INTEGRITY_VERIFICATION_TIMEOUT);
+ }
+
+ /**
+ * Check whether or not integrity verification has been enabled.
+ */
+ private boolean isIntegrityVerificationEnabled() {
+ // We are not exposing this as a user-configurable setting because we don't want to provide
+ // an easy way to get around the integrity check.
+ return DEFAULT_INTEGRITY_VERIFY_ENABLE;
+ }
+
+ /**
+ * Send a request to verifier(s) to verify the package if necessary.
+ */
+ void sendPackageVerificationRequest(
+ int verificationId,
+ PackageInfoLite pkgLite,
+ PackageVerificationState verificationState) {
+
+ // TODO: http://b/22976637
+ // Apps installed for "all" users use the device owner to verify the app
+ UserHandle verifierUser = getUser();
+ if (verifierUser == UserHandle.ALL) {
+ verifierUser = UserHandle.SYSTEM;
+ }
+
+ /*
+ * Determine if we have any installed package verifiers. If we
+ * do, then we'll defer to them to verify the packages.
+ */
+ final int requiredUid = mPm.mRequiredVerifierPackage == null ? -1
+ : mPm.getPackageUid(mPm.mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
+ verifierUser.getIdentifier());
+ verificationState.setRequiredVerifierUid(requiredUid);
+ final int installerUid =
+ mVerificationInfo == null ? -1 : mVerificationInfo.mInstallerUid;
+ final boolean isVerificationEnabled = isVerificationEnabled(
+ pkgLite, verifierUser.getIdentifier(), mInstallFlags, installerUid);
+ final boolean isV4Signed =
+ (mSigningDetails.getSignatureSchemeVersion() == SIGNING_BLOCK_V4);
+ final boolean isIncrementalInstall =
+ (mDataLoaderType == DataLoaderType.INCREMENTAL);
+ // NOTE: We purposefully skip verification for only incremental installs when there's
+ // a v4 signature block. Otherwise, proceed with verification as usual.
+ if (!mOriginInfo.mExisting
+ && isVerificationEnabled
+ && (!isIncrementalInstall || !isV4Signed)) {
+ final Intent verification = new Intent(
+ Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ verification.setDataAndType(Uri.fromFile(new File(mOriginInfo.mResolvedPath)),
+ PACKAGE_MIME_TYPE);
+ verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Query all live verifiers based on current user state
+ final ParceledListSlice<ResolveInfo> receivers = mPm.queryIntentReceivers(verification,
+ PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());
+
+ if (DEBUG_VERIFY) {
+ Slog.d(TAG, "Found " + receivers.getList().size() + " verifiers for intent "
+ + verification.toString() + " with " + pkgLite.verifiers.length
+ + " optional verifiers");
+ }
+
+ verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
+
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS, mInstallFlags);
+
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME, pkgLite.packageName);
+
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_VERSION_CODE, pkgLite.versionCode);
+
+ verification.putExtra(
+ PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
+ pkgLite.getLongVersionCode());
+
+ populateInstallerExtras(verification);
+
+ final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
+ receivers.getList(), verificationState);
+
+ DeviceIdleInternal idleController =
+ mPm.mInjector.getLocalService(DeviceIdleInternal.class);
+ final long idleDuration = mPm.getVerificationTimeout();
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppAllowlist(idleDuration,
+ TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ REASON_PACKAGE_VERIFIER, "");
+
+ /*
+ * If any sufficient verifiers were listed in the package
+ * manifest, attempt to ask them.
+ */
+ if (sufficientVerifiers != null) {
+ final int n = sufficientVerifiers.size();
+ if (n == 0) {
+ String errorMsg = "Additional verifiers required, but none installed.";
+ Slog.i(TAG, errorMsg);
+ setReturnCode(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE, errorMsg);
+ } else {
+ for (int i = 0; i < n; i++) {
+ final ComponentName verifierComponent = sufficientVerifiers.get(i);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
+ verifierComponent.getPackageName(), idleDuration,
+ verifierUser.getIdentifier(), false,
+ REASON_PACKAGE_VERIFIER, "package verifier");
+
+ final Intent sufficientIntent = new Intent(verification);
+ sufficientIntent.setComponent(verifierComponent);
+ mPm.mContext.sendBroadcastAsUser(sufficientIntent, verifierUser,
+ /* receiverPermission= */ null,
+ options.toBundle());
+ }
+ }
+ }
+
+ if (mPm.mRequiredVerifierPackage != null) {
+ final ComponentName requiredVerifierComponent = matchComponentForVerifier(
+ mPm.mRequiredVerifierPackage, receivers.getList());
+ /*
+ * Send the intent to the required verification agent,
+ * but only start the verification timeout after the
+ * target BroadcastReceivers have run.
+ */
+ verification.setComponent(requiredVerifierComponent);
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
+ mPm.mRequiredVerifierPackage, idleDuration,
+ verifierUser.getIdentifier(), false,
+ REASON_PACKAGE_VERIFIER, "package verifier");
+ mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
+ android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ /* appOp= */ AppOpsManager.OP_NONE,
+ /* options= */ options.toBundle(),
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Message msg = mPm.mHandler
+ .obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ mPm.mHandler.sendMessageDelayed(msg, mPm.getVerificationTimeout());
+ }
+ }, null, 0, null, null);
+
+ Trace.asyncTraceBegin(
+ TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
+
+ /*
+ * We don't want the copy to proceed until verification
+ * succeeds.
+ */
+ mWaitForVerificationToComplete = true;
+ }
+ } else {
+ verificationState.setVerifierResponse(
+ requiredUid, PackageManager.VERIFICATION_ALLOW);
+ }
+ }
+
+
+ private List<ComponentName> matchVerifiers(PackageInfoLite pkgInfo,
+ List<ResolveInfo> receivers, final PackageVerificationState verificationState) {
+ if (pkgInfo.verifiers.length == 0) {
+ return null;
+ }
+
+ final int n = pkgInfo.verifiers.length;
+ final List<ComponentName> sufficientVerifiers = new ArrayList<>(n + 1);
+ for (int i = 0; i < n; i++) {
+ final VerifierInfo verifierInfo = pkgInfo.verifiers[i];
+
+ final ComponentName comp = matchComponentForVerifier(verifierInfo.packageName,
+ receivers);
+ if (comp == null) {
+ continue;
+ }
+
+ final int verifierUid = getUidForVerifier(verifierInfo);
+ if (verifierUid == -1) {
+ continue;
+ }
+
+ if (DEBUG_VERIFY) {
+ Slog.d(TAG, "Added sufficient verifier " + verifierInfo.packageName
+ + " with the correct signature");
+ }
+ sufficientVerifiers.add(comp);
+ verificationState.addSufficientVerifier(verifierUid);
+ }
+
+ return sufficientVerifiers;
+ }
+
+ private int getUidForVerifier(VerifierInfo verifierInfo) {
+ synchronized (mPm.mLock) {
+ final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
+ if (pkg == null) {
+ return -1;
+ } else if (pkg.getSigningDetails().getSignatures().length != 1) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " has more than one signature; ignoring");
+ return -1;
+ }
+
+ /*
+ * If the public key of the package's signature does not match
+ * our expected public key, then this is a different package and
+ * we should skip.
+ */
+
+ final byte[] expectedPublicKey;
+ try {
+ final Signature verifierSig = pkg.getSigningDetails().getSignatures()[0];
+ final PublicKey publicKey = verifierSig.getPublicKey();
+ expectedPublicKey = publicKey.getEncoded();
+ } catch (CertificateException e) {
+ return -1;
+ }
+
+ final byte[] actualPublicKey = verifierInfo.publicKey.getEncoded();
+
+ if (!Arrays.equals(actualPublicKey, expectedPublicKey)) {
+ Slog.i(TAG, "Verifier package " + verifierInfo.packageName
+ + " does not have the expected public key; ignoring");
+ return -1;
+ }
+
+ return pkg.getUid();
+ }
+ }
+
+ private static ComponentName matchComponentForVerifier(String packageName,
+ List<ResolveInfo> receivers) {
+ ActivityInfo targetReceiver = null;
+
+ final int nr = receivers.size();
+ for (int i = 0; i < nr; i++) {
+ final ResolveInfo info = receivers.get(i);
+ if (info.activityInfo == null) {
+ continue;
+ }
+
+ if (packageName.equals(info.activityInfo.packageName)) {
+ targetReceiver = info.activityInfo;
+ break;
+ }
+ }
+
+ if (targetReceiver == null) {
+ return null;
+ }
+
+ return new ComponentName(targetReceiver.packageName, targetReceiver.name);
+ }
+
+ /**
+ * Check whether or not package verification has been enabled.
+ *
+ * @return true if verification should be performed
+ */
+ private boolean isVerificationEnabled(
+ PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
+ if (!DEFAULT_VERIFY_ENABLE) {
+ return false;
+ }
+
+ // Check if installing from ADB
+ if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
+ if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+ return true;
+ }
+ // Check if the developer wants to skip verification for ADB installs
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ synchronized (mPm.mLock) {
+ if (mPm.mSettings.getPackageLPr(pkgInfoLite.packageName) == null) {
+ // Always verify fresh install
+ return true;
+ }
+ }
+ // Only skip when apk is debuggable
+ return !pkgInfoLite.debuggable;
+ }
+ return Settings.Global.getInt(mPm.mContext.getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+ }
+
+ // only when not installed from ADB, skip verification for instant apps when
+ // the installer and verifier are the same.
+ if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ if (mPm.mInstantAppInstallerActivity != null
+ && mPm.mInstantAppInstallerActivity.packageName.equals(
+ mPm.mRequiredVerifierPackage)) {
+ try {
+ mPm.mInjector.getSystemService(AppOpsManager.class)
+ .checkPackage(installerUid, mPm.mRequiredVerifierPackage);
+ if (DEBUG_VERIFY) {
+ Slog.i(TAG, "disable verification for instant app");
+ }
+ return false;
+ } catch (SecurityException ignore) { }
+ }
+ }
+ return true;
+ }
+
+ void populateInstallerExtras(Intent intent) {
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
+ mInstallSource.initiatingPackageName);
+
+ if (mVerificationInfo != null) {
+ if (mVerificationInfo.mOriginatingUri != null) {
+ intent.putExtra(Intent.EXTRA_ORIGINATING_URI,
+ mVerificationInfo.mOriginatingUri);
+ }
+ if (mVerificationInfo.mReferrer != null) {
+ intent.putExtra(Intent.EXTRA_REFERRER,
+ mVerificationInfo.mReferrer);
+ }
+ if (mVerificationInfo.mOriginatingUid >= 0) {
+ intent.putExtra(Intent.EXTRA_ORIGINATING_UID,
+ mVerificationInfo.mOriginatingUid);
+ }
+ if (mVerificationInfo.mInstallerUid >= 0) {
+ intent.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
+ mVerificationInfo.mInstallerUid);
+ }
+ }
+ }
+
+ void setReturnCode(int ret, String message) {
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ // Only update mRet if it was previously INSTALL_SUCCEEDED to
+ // ensure we do not overwrite any previous failure results.
+ mRet = ret;
+ mErrorMessage = message;
+ }
+ }
+
+ void handleVerificationFinished() {
+ mWaitForVerificationToComplete = false;
+ handleReturnCode();
+ }
+
+ void handleIntegrityVerificationFinished() {
+ mWaitForIntegrityVerificationToComplete = false;
+ handleReturnCode();
+ }
+
+
+ void handleRollbackEnabled() {
+ // TODO(b/112431924): Consider halting the install if we
+ // couldn't enable rollback.
+ mWaitForEnableRollbackToComplete = false;
+ handleReturnCode();
+ }
+
+ @Override
+ void handleReturnCode() {
+ if (mWaitForVerificationToComplete || mWaitForIntegrityVerificationToComplete
+ || mWaitForEnableRollbackToComplete) {
+ return;
+ }
+ sendVerificationCompleteNotification();
+ }
+
+ private void sendVerificationCompleteNotification() {
+ if (mParentVerificationParams != null) {
+ mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
+ } else {
+ try {
+ mObserver.onPackageInstalled(null, mRet, mErrorMessage,
+ new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+
+ public void verifyStage() {
+ mPm.mHandler.post(this::startCopy);
+ }
+
+ public void verifyStage(List<VerificationParams> children)
+ throws PackageManagerException {
+ final MultiPackageVerificationParams params =
+ new MultiPackageVerificationParams(this, children);
+ mPm.mHandler.post(params::startCopy);
+ }
+
+ /**
+ * Container for a multi-package install which refers to all install sessions and args being
+ * committed together.
+ */
+ static final class MultiPackageVerificationParams extends HandlerParams {
+ private final IPackageInstallObserver2 mObserver;
+ private final List<VerificationParams> mChildParams;
+ private final Map<VerificationParams, Integer> mVerificationState;
+
+ MultiPackageVerificationParams(VerificationParams parent, List<VerificationParams> children)
+ throws PackageManagerException {
+ super(parent.getUser());
+ if (children.size() == 0) {
+ throw new PackageManagerException("No child sessions found!");
+ }
+ mChildParams = children;
+ // Provide every child with reference to this object as parent
+ for (int i = 0; i < children.size(); i++) {
+ final VerificationParams childParams = children.get(i);
+ childParams.mParentVerificationParams = this;
+ }
+ mVerificationState = new ArrayMap<>(mChildParams.size());
+ mObserver = parent.mObserver;
+ }
+
+ @Override
+ void handleStartCopy() {
+ for (VerificationParams params : mChildParams) {
+ params.handleStartCopy();
+ }
+ }
+
+ @Override
+ void handleReturnCode() {
+ for (VerificationParams params : mChildParams) {
+ params.handleReturnCode();
+ }
+ }
+
+ void trySendVerificationCompleteNotification(VerificationParams child, int currentStatus) {
+ mVerificationState.put(child, currentStatus);
+ if (mVerificationState.size() != mChildParams.size()) {
+ return;
+ }
+ int completeStatus = PackageManager.INSTALL_SUCCEEDED;
+ String errorMsg = null;
+ for (VerificationParams params : mVerificationState.keySet()) {
+ int status = params.mRet;
+ if (status == PackageManager.INSTALL_UNKNOWN) {
+ return;
+ } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+ completeStatus = status;
+ errorMsg = params.mErrorMessage;
+ break;
+ }
+ }
+ try {
+ mObserver.onPackageInstalled(null, completeStatus,
+ errorMsg, new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+}
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 1859b4c83032..01bf63483829 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -608,6 +608,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
private static final int TRON_COMPILATION_REASON_CMDLINE = 22;
+ private static final int TRON_COMPILATION_REASON_PREBUILT = 23;
+ private static final int TRON_COMPILATION_REASON_VDEX = 24;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
@@ -628,6 +630,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
+ case "prebuilt" : return TRON_COMPILATION_REASON_PREBUILT;
+ case "vdex" : return TRON_COMPILATION_REASON_VDEX;
case "install-fast" :
return TRON_COMPILATION_REASON_INSTALL_FAST;
case "install-bulk" :
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 0f67be71f7d7..fa9ed66dd457 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -417,7 +417,7 @@ public class PackageInfoUtils {
* Returns true if the package is installed and not hidden, or if the caller
* explicitly wanted all uninstalled and hidden packages as well.
*/
- private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+ public static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
PackageSetting pkgSetting, PackageUserState state,
@PackageManager.PackageInfoFlags int flags) {
// Returns false if the package is hidden system app until installed.
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index e8be9b6fd82d..f467a7f2023f 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -22,8 +22,6 @@ import android.annotation.Nullable;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
@@ -39,6 +37,7 @@ import android.util.DisplayMetrics;
import android.util.Slog;
import com.android.internal.compat.IPlatformCompat;
+import com.android.server.pm.PackageManagerException;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -47,7 +46,7 @@ import java.io.File;
import java.util.List;
/**
- * The v2 of {@link PackageParser} for use when parsing is initiated in the server and must
+ * The v2 of package parsing for use when parsing is initiated in the server and must
* contain state contained by the server.
*
* The {@link AutoCloseable} helps signal that this class contains resources that must be freed.
@@ -147,7 +146,7 @@ public class PackageParser2 implements AutoCloseable {
*/
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
- throws PackageParserException {
+ throws PackageManagerException {
if (useCaches && mCacher != null) {
ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
if (parsed != null) {
@@ -159,7 +158,7 @@ public class PackageParser2 implements AutoCloseable {
ParseInput input = mSharedResult.get().reset();
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
- throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
+ throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
}
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 8a8a302734b1..4334bf6d32f2 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
@@ -21,7 +21,7 @@ import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_T
import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackage;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -82,7 +82,7 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater {
boolean hasClass = false;
String className = "android.content.pm.AndroidTestBaseUpdater";
try {
- Class clazz = (PackageParser.class.getClassLoader().loadClass(className));
+ Class clazz = ParsingPackage.class.getClassLoader().loadClass(className);
hasClass = clazz != null;
Log.i(TAG, "Loaded " + className);
} catch (ClassNotFoundException e) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index 471a4d3ada46..0d2bcec42667 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -21,8 +21,8 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
import android.content.pm.PermissionGroupInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackageRead;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -200,7 +200,7 @@ public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPacka
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
*/
- PackageParser.SigningDetails getSigningDetails();
+ SigningDetails getSigningDetails();
/**
* TODO(b/135203078): Move split stuff to an inner data class
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 5ee612b6d55f..d40aada1e9c1 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
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.DexMetadataHelper;
@@ -31,12 +30,15 @@ import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedProvider;
import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.incremental.IncrementalManager;
import android.text.TextUtils;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemConfig;
+import com.android.server.pm.PackageManagerException;
import com.android.server.pm.PackageSetting;
import java.io.IOException;
@@ -120,15 +122,21 @@ public class AndroidPackageUtils {
/**
* Validate the dex metadata files installed for the given package.
*
- * @throws PackageParserException in case of errors.
+ * @throws PackageManagerException in case of errors.
*/
public static void validatePackageDexMetadata(AndroidPackage pkg)
- throws PackageParserException {
+ throws PackageManagerException {
Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
String packageName = pkg.getPackageName();
long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (String dexMetadata : apkToDexMetadataList) {
- DexMetadataHelper.validateDexMetadataFile(dexMetadata, packageName, versionCode);
+ final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
+ input.reset(), dexMetadata, packageName, versionCode);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
}
}
@@ -196,6 +204,12 @@ public class AndroidPackageUtils {
}
}
+ if (pkg.getBackupAgentName() != null) {
+ if (Objects.equals(className, pkg.getBackupAgentName())) {
+ return true;
+ }
+ }
+
return false;
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
index 7fbe9533642d..2fcc4b2fae59 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -22,7 +22,7 @@ import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageImpl;
import android.content.pm.parsing.component.ParsedActivity;
@@ -55,6 +55,7 @@ import java.util.UUID;
*/
public final class PackageImpl extends ParsingPackageImpl implements ParsedPackage, AndroidPackage {
+ @NonNull
public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
@NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) {
return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp);
@@ -70,6 +71,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
* this case only cares about
* volumeUuid, just fake it rather than having separate method paths.
*/
+ @NonNull
public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) {
return ((ParsedPackage) PackageImpl.forTesting(packageName)
.setVolumeUuid(volumeUuid)
@@ -77,11 +79,13 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
.hideAsFinal();
}
+ @NonNull
@VisibleForTesting
public static ParsingPackage forTesting(String packageName) {
return forTesting(packageName, "");
}
+ @NonNull
@VisibleForTesting
public static ParsingPackage forTesting(String packageName, String baseCodePath) {
return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false);
@@ -252,7 +256,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
}
@Override
- public PackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ public PackageImpl setSigningDetails(@Nullable SigningDetails value) {
super.setSigningDetails(value);
return this;
}
@@ -306,8 +310,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
}
@Override
- public PackageImpl setCodePath(@NonNull String value) {
- this.mPath = value;
+ public PackageImpl setPath(@NonNull String path) {
+ this.mPath = path;
return this;
}
@@ -381,8 +385,8 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
}
@Override
- public PackageImpl setBaseCodePath(@NonNull String baseCodePath) {
- this.mBaseApkPath = TextUtils.safeIntern(baseCodePath);
+ public PackageImpl setBaseApkPath(@NonNull String baseApkPath) {
+ this.mBaseApkPath = TextUtils.safeIntern(baseApkPath);
return this;
}
@@ -568,6 +572,7 @@ public final class PackageImpl extends ParsingPackageImpl implements ParsedPacka
assignDerivedFields();
}
+ @NonNull
public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
@Override
public PackageImpl createFromParcel(Parcel source) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 657f32c25a89..0051bab5e059 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -17,7 +17,7 @@
package com.android.server.pm.parsing.pkg;
import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.SigningDetails;
/**
* Methods used for mutation after direct package parsing, mostly done inside
@@ -43,9 +43,9 @@ public interface ParsedPackage extends AndroidPackage {
ParsedPackage clearProtectedBroadcasts();
- ParsedPackage setBaseCodePath(String baseCodePath);
+ ParsedPackage setBaseApkPath(String baseApkPath);
- ParsedPackage setCodePath(String codePath);
+ ParsedPackage setPath(String path);
ParsedPackage setNativeLibraryDir(String nativeLibraryDir);
@@ -59,7 +59,7 @@ public interface ParsedPackage extends AndroidPackage {
ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
- ParsedPackage setSigningDetails(PackageParser.SigningDetails signingDetails);
+ ParsedPackage setSigningDetails(SigningDetails signingDetails);
ParsedPackage setSplitCodePaths(String[] splitCodePaths);
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 cda48063e914..b1b46af8a6e7 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -304,10 +304,6 @@ public final class Permission {
& PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0;
}
- public boolean isDocumenter() {
- return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0;
- }
-
public boolean isConfigurator() {
return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0;
}
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 c11ffb492a93..74497f72ff21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -87,12 +87,13 @@ import android.content.pm.PackageManager.PermissionGroupInfoFlags;
import android.content.pm.PackageManager.PermissionInfoFlags;
import android.content.pm.PackageManager.PermissionWhitelistFlags;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
@@ -2858,7 +2859,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ if (!isCompatPlatformPermissionForPackage(perm, pkg)) {
shouldGrantNormalPermission = false;
}
}
@@ -3411,14 +3412,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return result;
}
- private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
+ private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- for (int ip=0; ip<NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (npi.name.equals(perm)
- && pkg.getTargetSdkVersion() < npi.sdkVersion) {
+ for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
+ final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
+ if (info.getName().equals(perm)
+ && pkg.getTargetSdkVersion() < info.sdkVersion) {
allowed = true;
Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ pkg.getPackageName());
@@ -3538,15 +3537,15 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// - or it shares a common signing certificate in its lineage with the defining package,
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
- final PackageParser.SigningDetails sourceSigningDetails =
+ final SigningDetails sourceSigningDetails =
getSourcePackageSigningDetails(bp);
return sourceSigningDetails.hasCommonSignerWithCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION)
+ SigningDetails.CertCapabilities.PERMISSION)
|| pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
|| systemPackage.getSigningDetails().checkCapability(
pkg.getSigningDetails(),
- PackageParser.SigningDetails.CertCapabilities.PERMISSION);
+ SigningDetails.CertCapabilities.PERMISSION);
}
private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg,
@@ -3656,14 +3655,6 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Special permissions for the device configurator.
allowed = true;
}
- if (!allowed && bp.isDocumenter()
- && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
- PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
- pkg.getPackageName())) {
- // If this permission is to be granted to the documenter and
- // this app is the documenter, then it gets the permission.
- allowed = true;
- }
if (!allowed && bp.isIncidentReportApprover()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
@@ -3704,11 +3695,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@NonNull
- private PackageParser.SigningDetails getSourcePackageSigningDetails(
+ private SigningDetails getSourcePackageSigningDetails(
@NonNull Permission bp) {
final PackageSetting ps = getSourcePackageSetting(bp);
if (ps == null) {
- return PackageParser.SigningDetails.UNKNOWN;
+ return SigningDetails.UNKNOWN;
}
return ps.getSigningDetails();
}
@@ -4264,33 +4255,35 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// If the target package is being uninstalled, we need to revoke this permission
// From all other packages
if (pkg == null || !hasPermission(pkg, bp.getName())) {
- Slog.i(TAG, "Removing permission " + bp.getName()
- + " that used to be declared by " + bp.getPackageName());
- if (bp.isRuntime()) {
- final int[] userIds = mUserManagerInt.getUserIds();
- final int numUserIds = userIds.length;
- for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
- final int userId = userIds[userIdNum];
- mPackageManagerInt.forEachPackage((AndroidPackage p) ->
- revokePermissionFromPackageForUser(p.getPackageName(),
- bp.getName(), true, userId, callback));
- }
- } else {
- mPackageManagerInt.forEachPackage(p -> {
+ if (!isPermissionDeclaredByDisabledSystemPkg(bp)) {
+ Slog.i(TAG, "Removing permission " + bp.getName()
+ + " that used to be declared by " + bp.getPackageName());
+ if (bp.isRuntime()) {
final int[] userIds = mUserManagerInt.getUserIds();
- synchronized (mLock) {
- for (final int userId : userIds) {
- final UidPermissionState uidState = getUidStateLocked(p,
- userId);
- if (uidState == null) {
- Slog.e(TAG, "Missing permissions state for "
- + p.getPackageName() + " and user " + userId);
- continue;
+ final int numUserIds = userIds.length;
+ for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+ final int userId = userIds[userIdNum];
+ mPackageManagerInt.forEachPackage((AndroidPackage p) ->
+ revokePermissionFromPackageForUser(p.getPackageName(),
+ bp.getName(), true, userId, callback));
+ }
+ } else {
+ mPackageManagerInt.forEachPackage(p -> {
+ final int[] userIds = mUserManagerInt.getUserIds();
+ synchronized (mLock) {
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidStateLocked(p,
+ userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for "
+ + p.getPackageName() + " and user " + userId);
+ continue;
+ }
+ uidState.removePermissionState(bp.getName());
}
- uidState.removePermissionState(bp.getName());
}
- }
- });
+ });
+ }
}
synchronized (mLock) {
mRegistry.removePermission(bp.getName());
@@ -4315,6 +4308,22 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return changed;
}
+ private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
+ final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+ permission.getPackageName());
+ if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
+ final String permissionName = permission.getName();
+ final List<ParsedPermission> sourcePerms = disabledSourcePs.getPkg().getPermissions();
+ for (ParsedPermission sourcePerm : sourcePerms) {
+ if (TextUtils.equals(permissionName, sourcePerm.getName())
+ && permission.getProtectionLevel() == sourcePerm.getProtectionLevel()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Revoke a runtime permission from a package for a given user ID.
*/
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index fad0aefd3c0a..ef2154392d46 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -31,8 +31,7 @@ import java.util.List;
import java.util.stream.Collectors;
/**
- * For use by {@link PackageSetting} to maintain functionality that used to exist in
- * {@link PackageParser.Package}.
+ * For use by {@link PackageSetting} to maintain functionality that used to exist in PackageParser.
*
* It is assumed that anything inside the package was not cached or written to disk, so none of
* these fields are either. They must be set on every boot from other state on the device.
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 adf8f0d3e486..844111a8fab1 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
@@ -182,10 +182,10 @@ public class DomainVerificationDebug {
if (!reusedMap.isEmpty()) {
if (!wasHeaderPrinted) {
- Signature[] signatures = pkg.getSigningDetails().signatures;
+ Signature[] signatures = pkg.getSigningDetails().getSignatures();
String signaturesDigest = signatures == null ? null : Arrays.toString(
PackageUtils.computeSignaturesSha256Digests(
- pkg.getSigningDetails().signatures, ":"));
+ pkg.getSigningDetails().getSignatures(), ":"));
writer.println(pkgState.getPackageName() + ":");
writer.increaseIndent();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 8b4690629ec5..18c45e494c9b 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -196,8 +196,9 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat
}
private static boolean isHotwordDetectionServiceRequired(PackageManager pm) {
- // Usage of the HotwordDetectionService won't be enforced until a later release.
- return false;
+ // The HotwordDetectionService APIs aren't ready yet for Auto or TV.
+ return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
}
@Override
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index 3c9b1063ba93..04b5005aa283 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -16,10 +16,8 @@
package com.android.server.policy;
-import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
-import android.hardware.ICameraService;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.hardware.display.DisplayManagerInternal;
@@ -27,13 +25,11 @@ import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDisplayFoldListener;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
-import com.android.server.camera.CameraServiceProxy;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -41,13 +37,8 @@ import com.android.server.wm.WindowManagerInternal;
* TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
*/
class DisplayFoldController {
- private static final String TAG = "DisplayFoldController";
-
private final WindowManagerInternal mWindowManagerInternal;
private final DisplayManagerInternal mDisplayManagerInternal;
- // Camera service proxy can be disabled through a config.
- @Nullable
- private final CameraServiceProxy mCameraServiceProxy;
private final int mDisplayId;
private final Handler mHandler;
@@ -64,12 +55,10 @@ class DisplayFoldController {
DisplayFoldController(
Context context, WindowManagerInternal windowManagerInternal,
- DisplayManagerInternal displayManagerInternal,
- @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
+ DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
Handler handler) {
mWindowManagerInternal = windowManagerInternal;
mDisplayManagerInternal = displayManagerInternal;
- mCameraServiceProxy = cameraServiceProxy;
mDisplayId = displayId;
mFoldedArea = new Rect(foldedArea);
mHandler = handler;
@@ -124,16 +113,6 @@ class DisplayFoldController {
}
}
- if (mCameraServiceProxy != null) {
- if (folded) {
- mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
- } else {
- mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
- }
- } else {
- Slog.w(TAG, "Camera service unavailable to toggle folded state.");
- }
-
mDurationLogger.setDeviceFolded(folded);
mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
mFolded = folded;
@@ -188,8 +167,6 @@ class DisplayFoldController {
LocalServices.getService(WindowManagerInternal.class);
final DisplayManagerInternal displayService =
LocalServices.getService(DisplayManagerInternal.class);
- final CameraServiceProxy cameraServiceProxy =
- LocalServices.getService(CameraServiceProxy.class);
final String configFoldedArea = context.getResources().getString(
com.android.internal.R.string.config_foldedArea);
@@ -201,6 +178,6 @@ class DisplayFoldController {
}
return new DisplayFoldController(context, windowManagerService, displayService,
- cameraServiceProxy, displayId, foldedArea, DisplayThread.getHandler());
+ displayId, foldedArea, DisplayThread.getHandler());
}
}
diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
index bdcd2cde2e4e..3524d7f51090 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java
@@ -44,8 +44,8 @@ class DisplayFoldDurationLogger {
@Retention(RetentionPolicy.SOURCE)
public @interface ScreenState {}
- private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
- private Long mLastChanged = null;
+ private volatile @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN;
+ private volatile Long mLastChanged = null;
private static final int LOG_SUBTYPE_UNFOLDED = 0;
private static final int LOG_SUBTYPE_FOLDED = 1;
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 89d54158b006..ad435146f579 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -71,6 +71,7 @@ import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
+import com.android.server.utils.TimingsTraceAndSlog;
import java.util.ArrayList;
import java.util.Collections;
@@ -365,7 +366,11 @@ public final class PermissionPolicyService extends SystemService {
return;
}
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("Permission_grant_default_permissions-" + userId);
grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId);
+ t.traceEnd();
final OnInitializedCallback callback;
@@ -375,11 +380,15 @@ public final class PermissionPolicyService extends SystemService {
}
// Force synchronization as permissions might have changed
+ t.traceBegin("Permission_synchronize_permissions-" + userId);
synchronizePermissionsAndAppOpsForUser(userId);
+ t.traceEnd();
// Tell observers we are initialized for this user.
if (callback != null) {
+ t.traceBegin("Permission_onInitialized-" + userId);
callback.onInitialized(userId);
+ t.traceEnd();
}
}
@@ -394,6 +403,7 @@ public final class PermissionPolicyService extends SystemService {
private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) {
if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")");
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final PackageManagerInternal packageManagerInternal =
LocalServices.getService(PackageManagerInternal.class);
@@ -426,9 +436,12 @@ public final class PermissionPolicyService extends SystemService {
}
});
try {
+ t.traceBegin("Permission_callback_waiting-" + userId);
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
+ } finally {
+ t.traceEnd();
}
permissionControllerManager.updateUserSensitive();
@@ -494,14 +507,19 @@ public final class PermissionPolicyService extends SystemService {
*/
private void synchronizePermissionsAndAppOpsForUser(@UserIdInt int userId) {
if (DEBUG) Slog.i(LOG_TAG, "synchronizePermissionsAndAppOpsForUser(" + userId + ")");
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
PackageManagerInternal.class);
final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
getUserContext(getContext(), UserHandle.of(userId)));
+ t.traceBegin("Permission_synchronize_addPackages-" + userId);
packageManagerInternal.forEachPackage(
(pkg) -> synchronizer.addPackage(pkg.getPackageName()));
+ t.traceEnd();
+ t.traceBegin("Permission_syncPackages-" + userId);
synchronizer.syncPackages();
+ t.traceEnd();
}
private void resetAppOpPermissionsIfNotRequestedForUidAsync(int uid) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e40882268e67..1a513946dbfb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -261,6 +261,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
static final int SHORT_PRESS_POWER_GO_HOME = 4;
static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
+ static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
// must match: config_LongPressOnPowerBehavior in config.xml
static final int LONG_PRESS_POWER_NOTHING = 0;
@@ -484,6 +485,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mLidNavigationAccessibility;
int mShortPressOnPowerBehavior;
int mLongPressOnPowerBehavior;
+ long mLongPressOnPowerAssistantTimeoutMs;
int mVeryLongPressOnPowerBehavior;
int mDoublePressOnPowerBehavior;
int mTriplePressOnPowerBehavior;
@@ -732,6 +734,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Global.POWER_BUTTON_LONG_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -976,6 +981,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
break;
}
+ case SHORT_PRESS_POWER_LOCK_OR_SLEEP: {
+ if (keyguardOn()) {
+ sleepDefaultDisplayFromPowerButton(eventTime, 0);
+ } else {
+ lockNow(null /*options*/);
+ }
+ break;
+ }
}
}
}
@@ -1732,6 +1745,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
+ mLongPressOnPowerAssistantTimeoutMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerDurationMs);
mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_veryLongPressOnPowerBehavior);
mDoublePressOnPowerBehavior = mContext.getResources().getInteger(
@@ -1955,7 +1970,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
PowerKeyRule(int gestures) {
- super(KEYCODE_POWER, gestures);
+ super(mContext, KEYCODE_POWER, gestures);
}
@Override
@@ -1970,6 +1985,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
+ long getLongPressTimeoutMs() {
+ if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
+ return mLongPressOnPowerAssistantTimeoutMs;
+ } else {
+ return super.getLongPressTimeoutMs();
+ }
+ }
+
+ @Override
void onLongPress(long eventTime) {
if (mSingleKeyGestureDetector.beganFromNonInteractive()
&& !mSupportLongPressPowerWhenNonInteractive) {
@@ -1997,7 +2021,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
BackKeyRule(int gestures) {
- super(KEYCODE_BACK, gestures);
+ super(mContext, KEYCODE_BACK, gestures);
}
@Override
@@ -2017,7 +2041,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void initSingleKeyGestureRules() {
- mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext);
+ mSingleKeyGestureDetector = new SingleKeyGestureDetector();
int powerKeyGestures = 0;
if (hasVeryLongPressOnPowerBehavior()) {
@@ -2115,6 +2139,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Global.POWER_BUTTON_LONG_PRESS,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior));
+ mLongPressOnPowerAssistantTimeoutMs = Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
+ mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerDurationMs));
mVeryLongPressOnPowerBehavior = Settings.Global.getInt(resolver,
Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
mContext.getResources().getInteger(
@@ -2789,9 +2818,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mInputManagerInternal.toggleCapsLock(event.getDeviceId());
mPendingCapsLockToggle = false;
} else if (mPendingMetaAction) {
- launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
- event.getDeviceId(),
- event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
+ if (!canceled) {
+ launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
+ event.getDeviceId(),
+ event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
+ }
mPendingMetaAction = false;
}
}
@@ -3020,7 +3051,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void onKeyguardOccludedChangedLw(boolean occluded) {
- if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
+ if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()
+ && !WindowManagerService.sEnableShellTransitions) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
} else {
@@ -4250,6 +4282,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pmWakeReason)) + ")");
}
+ mActivityTaskManagerInternal.notifyWakingUp();
mDefaultDisplayPolicy.setAwake(true);
// Since goToSleep performs these functions synchronously, we must
@@ -5329,6 +5362,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print("mLongPressOnPowerBehavior=");
pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
pw.print(prefix);
+ pw.print("mLongPressOnPowerAssistantTimeoutMs=");
+ pw.println(mLongPressOnPowerAssistantTimeoutMs);
+ pw.print(prefix);
pw.print("mVeryLongPressOnPowerBehavior=");
pw.println(veryLongPressOnPowerBehaviorToString(mVeryLongPressOnPowerBehavior));
pw.print(prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 1ef2bf9151e0..6fee69bf0472 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -44,9 +44,6 @@ public final class SingleKeyGestureDetector {
private static final int MSG_KEY_VERY_LONG_PRESS = 1;
private static final int MSG_KEY_DELAYED_PRESS = 2;
- private final long mLongPressTimeout;
- private final long mVeryLongPressTimeout;
-
private volatile int mKeyPressCounter;
private boolean mBeganFromNonInteractive = false;
@@ -86,12 +83,19 @@ public final class SingleKeyGestureDetector {
* </pre>
*/
abstract static class SingleKeyRule {
+
private final int mKeyCode;
private final int mSupportedGestures;
+ private final long mDefaultLongPressTimeout;
+ private final long mDefaultVeryLongPressTimeout;
- SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) {
+ SingleKeyRule(Context context, int keyCode, @KeyGestureFlag int supportedGestures) {
mKeyCode = keyCode;
mSupportedGestures = supportedGestures;
+ mDefaultLongPressTimeout =
+ ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
+ mDefaultVeryLongPressTimeout = context.getResources().getInteger(
+ com.android.internal.R.integer.config_veryLongPressTimeout);
}
/**
@@ -134,10 +138,28 @@ public final class SingleKeyGestureDetector {
*/
void onMultiPress(long downTime, int count) {}
/**
+ * Returns the timeout in milliseconds for a long press.
+ *
+ * If multipress is also supported, this should always be greater than the multipress
+ * timeout. If very long press is supported, this should always be less than the very long
+ * press timeout.
+ */
+ long getLongPressTimeoutMs() {
+ return mDefaultLongPressTimeout;
+ }
+ /**
* Callback when long press has been detected.
*/
void onLongPress(long eventTime) {}
/**
+ * Returns the timeout in milliseconds for a very long press.
+ *
+ * If long press is supported, this should always be longer than the long press timeout.
+ */
+ long getVeryLongPressTimeoutMs() {
+ return mDefaultVeryLongPressTimeout;
+ }
+ /**
* Callback when very long press has been detected.
*/
void onVeryLongPress(long eventTime) {}
@@ -151,10 +173,7 @@ public final class SingleKeyGestureDetector {
}
}
- public SingleKeyGestureDetector(Context context) {
- mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
- mVeryLongPressTimeout = context.getResources().getInteger(
- com.android.internal.R.integer.config_veryLongPressTimeout);
+ public SingleKeyGestureDetector() {
mHandler = new KeyHandler();
}
@@ -225,14 +244,14 @@ public final class SingleKeyGestureDetector {
final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
eventTime);
msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, mLongPressTimeout);
+ mHandler.sendMessageDelayed(msg, mActiveRule.getLongPressTimeoutMs());
}
if (mActiveRule.supportVeryLongPress()) {
final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0,
eventTime);
msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout);
+ mHandler.sendMessageDelayed(msg, mActiveRule.getVeryLongPressTimeoutMs());
}
} else {
mHandler.removeMessages(MSG_KEY_LONG_PRESS);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 855a1ccc172d..051f555fab95 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -276,4 +276,4 @@ public class KeyguardServiceWrapper implements IKeyguardService {
public void dump(String prefix, PrintWriter pw) {
mKeyguardStateMonitor.dump(prefix, pw);
}
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
index c2596c74146a..3d47e92a0003 100644
--- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
+++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
@@ -53,6 +53,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -308,6 +309,13 @@ public class RoleServicePlatformHelperImpl implements RoleServicePlatformHelper
dataOutputStream.writeInt(packageManagerInternal.getApplicationEnabledState(
pkg.getPackageName(), userId));
+ final List<String> requestedPermissions = pkg.getRequestedPermissions();
+ final int requestedPermissionsSize = requestedPermissions.size();
+ dataOutputStream.writeInt(requestedPermissionsSize);
+ for (int i = 0; i < requestedPermissionsSize; i++) {
+ dataOutputStream.writeUTF(requestedPermissions.get(i));
+ }
+
final ArraySet<String> enabledComponents =
packageManagerInternal.getEnabledComponents(pkg.getPackageName(), userId);
final int enabledComponentsSize = CollectionUtils.size(enabledComponents);
@@ -323,7 +331,7 @@ public class RoleServicePlatformHelperImpl implements RoleServicePlatformHelper
dataOutputStream.writeUTF(disabledComponents.valueAt(i));
}
- for (final Signature signature : pkg.getSigningDetails().signatures) {
+ for (final Signature signature : pkg.getSigningDetails().getSignatures()) {
dataOutputStream.write(signature.toByteArray());
}
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7555a7f2920b..c91d8dedc41b 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -96,6 +96,7 @@ public class Notifier {
private static final int MSG_BROADCAST_ENHANCED_PREDICTION = 4;
private static final int MSG_PROFILE_TIMED_OUT = 5;
private static final int MSG_WIRED_CHARGING_STARTED = 6;
+ private static final int MSG_SCREEN_POLICY = 7;
private static final long[] CHARGING_VIBRATION_TIME = {
40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms
@@ -120,6 +121,7 @@ public class Notifier {
private final SuspendBlocker mSuspendBlocker;
private final WindowManagerPolicy mPolicy;
private final FaceDownDetector mFaceDownDetector;
+ private final ScreenUndimDetector mScreenUndimDetector;
private final ActivityManagerInternal mActivityManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final InputMethodManagerInternal mInputMethodManagerInternal;
@@ -167,13 +169,14 @@ public class Notifier {
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector) {
+ FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
mContext = context;
mBatteryStats = batteryStats;
mAppOps = mContext.getSystemService(AppOpsManager.class);
mSuspendBlocker = suspendBlocker;
mPolicy = policy;
mFaceDownDetector = faceDownDetector;
+ mScreenUndimDetector = screenUndimDetector;
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
@@ -620,6 +623,22 @@ public class Notifier {
}
/**
+ * Called when the screen policy changes.
+ */
+ public void onScreenPolicyUpdate(int newPolicy) {
+ if (DEBUG) {
+ Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy);
+ }
+
+ synchronized (mLock) {
+ Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY);
+ msg.arg1 = newPolicy;
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
* Dumps data for bugreports.
*
* @param pw The stream to print to.
@@ -659,6 +678,7 @@ public class Notifier {
tm.notifyUserActivity();
mPolicy.userActivity();
mFaceDownDetector.userActivity(event);
+ mScreenUndimDetector.userActivity();
}
void postEnhancedDischargePredictionBroadcast(long delayMs) {
@@ -812,6 +832,10 @@ public class Notifier {
mSuspendBlocker.release();
}
+ private void screenPolicyChanging(int screenPolicy) {
+ mScreenUndimDetector.recordScreenPolicy(screenPolicy);
+ }
+
private void lockProfile(@UserIdInt int userId) {
mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
}
@@ -852,6 +876,9 @@ public class Notifier {
case MSG_WIRED_CHARGING_STARTED:
showWiredChargingStarted(msg.arg1);
break;
+ case MSG_SCREEN_POLICY:
+ screenPolicyChanging(msg.arg1);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3d47dcf4eadb..9e61dcedf3a8 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -274,6 +274,7 @@ public final class PowerManagerService extends SystemService
private final BatterySavingStats mBatterySavingStats;
private final AttentionDetector mAttentionDetector;
private final FaceDownDetector mFaceDownDetector;
+ private final ScreenUndimDetector mScreenUndimDetector;
private final BinderService mBinderService;
private final LocalService mLocalService;
private final NativeWrapper mNativeWrapper;
@@ -353,6 +354,12 @@ public final class PowerManagerService extends SystemService
// requested because it is updated asynchronously by the display power controller.
private DisplayGroupPowerStateMapper mDisplayGroupPowerStateMapper;
+ // The suspend blocker used to keep the CPU alive while the device is booting.
+ private final SuspendBlocker mBootingSuspendBlocker;
+
+ // True if the wake lock suspend blocker has been acquired.
+ private boolean mHoldingBootingSuspendBlocker;
+
// The suspend blocker used to keep the CPU alive when an application has acquired
// a wake lock.
private final SuspendBlocker mWakeLockSuspendBlocker;
@@ -837,9 +844,10 @@ public final class PowerManagerService extends SystemService
static class Injector {
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector) {
+ FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
return new Notifier(
- looper, context, batteryStats, suspendBlocker, policy, faceDownDetector);
+ looper, context, batteryStats, suspendBlocker, policy, faceDownDetector,
+ screenUndimDetector);
}
SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -960,6 +968,7 @@ public final class PowerManagerService extends SystemService
mInjector.createAmbientDisplaySuppressionController(context);
mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
mFaceDownDetector = new FaceDownDetector(this::onFlip);
+ mScreenUndimDetector = new ScreenUndimDetector();
mBatterySavingStats = new BatterySavingStats(mLock);
mBatterySaverPolicy =
@@ -1042,10 +1051,16 @@ public final class PowerManagerService extends SystemService
}
synchronized (mLock) {
+ mBootingSuspendBlocker =
+ mInjector.createSuspendBlocker(this, "PowerManagerService.Booting");
mWakeLockSuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.WakeLocks");
mDisplaySuspendBlocker =
mInjector.createSuspendBlocker(this, "PowerManagerService.Display");
+ if (mBootingSuspendBlocker != null) {
+ mBootingSuspendBlocker.acquire();
+ mHoldingBootingSuspendBlocker = true;
+ }
if (mDisplaySuspendBlocker != null) {
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
@@ -1146,7 +1161,7 @@ public final class PowerManagerService extends SystemService
mBatteryStats = BatteryStatsService.getService();
mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
- mPolicy, mFaceDownDetector);
+ mPolicy, mFaceDownDetector, mScreenUndimDetector);
mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager,
mInjector.createSuspendBlocker(
@@ -1181,6 +1196,7 @@ public final class PowerManagerService extends SystemService
mBatterySaverController.systemReady();
mBatterySaverPolicy.systemReady();
mFaceDownDetector.systemReady(mContext);
+ mScreenUndimDetector.systemReady(mContext);
// Register for settings changes.
resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -3190,6 +3206,7 @@ public final class PowerManagerService extends SystemService
final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
displayPowerRequest, mRequestWaitForNegativeProximity);
+ mNotifier.onScreenPolicyUpdate(displayPowerRequest.policy);
if (DEBUG_SPEW) {
Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
@@ -3396,6 +3413,10 @@ public final class PowerManagerService extends SystemService
}
// First acquire suspend blockers if needed.
+ if (!mBootCompleted && !mHoldingBootingSuspendBlocker) {
+ mBootingSuspendBlocker.acquire();
+ mHoldingBootingSuspendBlocker = true;
+ }
if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.acquire();
mHoldingWakeLockSuspendBlocker = true;
@@ -3422,6 +3443,10 @@ public final class PowerManagerService extends SystemService
}
// Then release suspend blockers if needed.
+ if (mBootCompleted && mHoldingBootingSuspendBlocker) {
+ mBootingSuspendBlocker.release();
+ mHoldingBootingSuspendBlocker = false;
+ }
if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
mWakeLockSuspendBlocker.release();
mHoldingWakeLockSuspendBlocker = false;
@@ -4093,7 +4118,10 @@ public final class PowerManagerService extends SystemService
if (sQuiescent) {
// Pass the optional "quiescent" argument to the bootloader to let it know
// that it should not turn the screen/lights on.
- reason = reason + ",quiescent";
+ if (reason != ""){
+ reason += ",";
+ }
+ reason = reason + "quiescent";
}
SystemProperties.set("sys.powerctl", "reboot," + reason);
diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java
new file mode 100644
index 000000000000..951bc1f76e6d
--- /dev/null
+++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java
@@ -0,0 +1,297 @@
+/*
+ * 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.power;
+
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Detects when user manually undims the screen (x times) and acquires a wakelock to keep the screen
+ * on temporarily (without changing the screen timeout setting).
+ */
+public class ScreenUndimDetector {
+ private static final String TAG = "ScreenUndimDetector";
+ private static final boolean DEBUG = false;
+
+ private static final String UNDIM_DETECTOR_WAKE_LOCK = "UndimDetectorWakeLock";
+
+ /** DeviceConfig flag: is keep screen on feature enabled. */
+ static final String KEY_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled";
+ private static final boolean DEFAULT_KEEP_SCREEN_ON_ENABLED = true;
+ private static final int OUTCOME_POWER_BUTTON =
+ FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__POWER_BUTTON;
+ private static final int OUTCOME_TIMEOUT =
+ FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__TIMEOUT;
+ private boolean mKeepScreenOnEnabled;
+
+ /** DeviceConfig flag: how long should we keep the screen on. */
+ @VisibleForTesting
+ static final String KEY_KEEP_SCREEN_ON_FOR_MILLIS = "keep_screen_on_for_millis";
+ @VisibleForTesting
+ static final long DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS = TimeUnit.MINUTES.toMillis(10);
+ private long mKeepScreenOnForMillis;
+
+ /** DeviceConfig flag: how many user undims required to trigger keeping the screen on. */
+ @VisibleForTesting
+ static final String KEY_UNDIMS_REQUIRED = "undims_required";
+ @VisibleForTesting
+ static final int DEFAULT_UNDIMS_REQUIRED = 2;
+ private int mUndimsRequired;
+
+ /**
+ * DeviceConfig flag: what is the maximum duration between undims to still consider them
+ * consecutive.
+ */
+ @VisibleForTesting
+ static final String KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
+ "max_duration_between_undims_millis";
+ @VisibleForTesting
+ static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = TimeUnit.MINUTES.toMillis(5);
+ private long mMaxDurationBetweenUndimsMillis;
+
+ @VisibleForTesting
+ PowerManager.WakeLock mWakeLock;
+
+ @VisibleForTesting
+ int mCurrentScreenPolicy;
+ @VisibleForTesting
+ int mUndimCounter = 0;
+ @VisibleForTesting
+ long mUndimCounterStartedMillis;
+ private long mUndimOccurredTime = -1;
+ private long mInteractionAfterUndimTime = -1;
+ private InternalClock mClock;
+
+ public ScreenUndimDetector() {
+ mClock = new InternalClock();
+ }
+
+ ScreenUndimDetector(InternalClock clock) {
+ mClock = clock;
+ }
+
+ static class InternalClock {
+ public long getCurrentTime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ /** Should be called in parent's systemReady() */
+ public void systemReady(Context context) {
+ readValuesFromDeviceConfig();
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ context.getMainExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+ | PowerManager.ON_AFTER_RELEASE,
+ UNDIM_DETECTOR_WAKE_LOCK);
+ }
+
+ /**
+ * Launches a message that figures out the screen transitions and detects user undims. Must be
+ * called by the parent that is trying to update the screen policy.
+ */
+ public void recordScreenPolicy(int newPolicy) {
+ if (newPolicy == mCurrentScreenPolicy) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Screen policy transition: " + mCurrentScreenPolicy + " -> " + newPolicy);
+ }
+
+ // update the current policy with the new one immediately so we don't accidentally get
+ // into a loop (which is possible if the switch below triggers a new policy).
+ final int currentPolicy = mCurrentScreenPolicy;
+ mCurrentScreenPolicy = newPolicy;
+
+ if (!mKeepScreenOnEnabled) {
+ return;
+ }
+
+ switch (currentPolicy) {
+ case POLICY_DIM:
+ if (newPolicy == POLICY_BRIGHT) {
+ final long now = mClock.getCurrentTime();
+ final long timeElapsedSinceFirstUndim = now - mUndimCounterStartedMillis;
+ if (timeElapsedSinceFirstUndim >= mMaxDurationBetweenUndimsMillis) {
+ reset();
+ }
+ if (mUndimCounter == 0) {
+ mUndimCounterStartedMillis = now;
+ }
+
+ mUndimCounter++;
+
+ if (DEBUG) {
+ Slog.d(TAG, "User undim, counter=" + mUndimCounter
+ + " (required=" + mUndimsRequired + ")"
+ + ", timeElapsedSinceFirstUndim=" + timeElapsedSinceFirstUndim
+ + " (max=" + mMaxDurationBetweenUndimsMillis + ")");
+ }
+ if (mUndimCounter >= mUndimsRequired) {
+ reset();
+ if (DEBUG) {
+ Slog.d(TAG, "Acquiring a wake lock for " + mKeepScreenOnForMillis);
+ }
+ if (mWakeLock != null) {
+ mUndimOccurredTime = mClock.getCurrentTime();
+ mWakeLock.acquire(mKeepScreenOnForMillis);
+ }
+ }
+ } else {
+ if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) {
+ checkAndLogUndim(OUTCOME_TIMEOUT);
+ }
+ reset();
+ }
+ break;
+ case POLICY_BRIGHT:
+ if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) {
+ checkAndLogUndim(OUTCOME_POWER_BUTTON);
+ }
+ if (newPolicy != POLICY_DIM) {
+ reset();
+ }
+ break;
+ }
+ }
+
+ @VisibleForTesting
+ void reset() {
+ if (DEBUG) {
+ Slog.d(TAG, "Resetting the undim detector");
+ }
+ mUndimCounter = 0;
+ mUndimCounterStartedMillis = 0;
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ private boolean readKeepScreenOnNotificationEnabled() {
+ return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_KEEP_SCREEN_ON_ENABLED,
+ DEFAULT_KEEP_SCREEN_ON_ENABLED);
+ }
+
+ private long readKeepScreenOnForMillis() {
+ return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_KEEP_SCREEN_ON_FOR_MILLIS,
+ DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS);
+ }
+
+ private int readUndimsRequired() {
+ int undimsRequired = DeviceConfig.getInt(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ DEFAULT_UNDIMS_REQUIRED);
+
+ if (undimsRequired < 1 || undimsRequired > 5) {
+ Slog.e(TAG, "Provided undimsRequired=" + undimsRequired
+ + " is not allowed [1, 5]; using the default=" + DEFAULT_UNDIMS_REQUIRED);
+ return DEFAULT_UNDIMS_REQUIRED;
+ }
+
+ return undimsRequired;
+ }
+
+ private long readMaxDurationBetweenUndimsMillis() {
+ return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
+ DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS);
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ for (String key : keys) {
+ Slog.i(TAG, "onDeviceConfigChange; key=" + key);
+ switch (key) {
+ case KEY_KEEP_SCREEN_ON_ENABLED:
+ case KEY_KEEP_SCREEN_ON_FOR_MILLIS:
+ case KEY_UNDIMS_REQUIRED:
+ case KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS:
+ readValuesFromDeviceConfig();
+ return;
+ default:
+ Slog.i(TAG, "Ignoring change on " + key);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void readValuesFromDeviceConfig() {
+ mKeepScreenOnEnabled = readKeepScreenOnNotificationEnabled();
+ mKeepScreenOnForMillis = readKeepScreenOnForMillis();
+ mUndimsRequired = readUndimsRequired();
+ mMaxDurationBetweenUndimsMillis = readMaxDurationBetweenUndimsMillis();
+
+ Slog.i(TAG, "readValuesFromDeviceConfig():"
+ + "\nmKeepScreenOnForMillis=" + mKeepScreenOnForMillis
+ + "\nmKeepScreenOnNotificationEnabled=" + mKeepScreenOnEnabled
+ + "\nmUndimsRequired=" + mUndimsRequired);
+
+ }
+
+ /**
+ * The user interacted with the screen after an undim, indicating the phone is in use.
+ * We use this event for logging.
+ */
+ public void userActivity() {
+ if (mUndimOccurredTime != 1 && mInteractionAfterUndimTime == -1) {
+ mInteractionAfterUndimTime = mClock.getCurrentTime();
+ }
+ }
+
+ /**
+ * Checks and logs if an undim occurred.
+ *
+ * A log will occur if an undim seems to have resulted in a timeout or a direct screen off such
+ * as from a power button. Some outcomes may not be correctly assigned to a
+ * TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME value.
+ */
+ private void checkAndLogUndim(int outcome) {
+ if (mUndimOccurredTime != -1) {
+ long now = mClock.getCurrentTime();
+ FrameworkStatsLog.write(FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED,
+ outcome,
+ /* time_to_outcome_millis=*/ now - mUndimOccurredTime,
+ /* time_to_first_interaction_millis= */
+ mInteractionAfterUndimTime != -1 ? now - mInteractionAfterUndimTime : -1
+ );
+ mUndimOccurredTime = -1;
+ mInteractionAfterUndimTime = -1;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index 770779d16102..57d69c2c93d1 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -17,7 +17,6 @@ package com.android.server.power.batterysaver;
import android.Manifest;
import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -34,13 +33,11 @@ import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal.LowPowerModeListener;
import android.os.PowerSaveState;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.power.PowerManagerService;
@@ -70,7 +67,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
private final Object mLock;
private final Context mContext;
private final MyHandler mHandler;
- private final FileUpdater mFileUpdater;
private PowerManager mPowerManager;
@@ -212,7 +208,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
mHandler = new MyHandler(looper);
mBatterySaverPolicy = policy;
mBatterySaverPolicy.addListener(this);
- mFileUpdater = new FileUpdater(context);
mBatterySavingStats = batterySavingStats;
PowerManager.invalidatePowerSaveModeCaches();
@@ -238,8 +233,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
- mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
- .isRuntimeRestarted());
mHandler.postSystemReady();
}
@@ -436,7 +429,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
final boolean enabled;
final boolean isInteractive = getPowerManager().isInteractive();
- final ArrayMap<String, String> fileValues;
synchronized (mLock) {
enabled = getFullEnabledLocked() || getAdaptiveEnabledLocked();
@@ -456,12 +448,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
listeners = mListeners.toArray(new LowPowerModeListener[0]);
mIsInteractive = isInteractive;
-
- if (enabled) {
- fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
- } else {
- fileValues = null;
- }
}
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
@@ -471,12 +457,6 @@ public class BatterySaverController implements BatterySaverPolicyListener {
updateBatterySavingStats();
- if (ArrayUtils.isEmpty(fileValues)) {
- mFileUpdater.restoreDefault();
- } else {
- mFileUpdater.writeFiles(fileValues);
- }
-
if (sendBroadcast) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 09f8941eb2c2..ffcb2bdbffab 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -129,7 +129,11 @@ public class BatterySaverPolicy extends ContentObserver implements
@VisibleForTesting
static final String KEY_ENABLE_NIGHT_MODE = "enable_night_mode";
+ /** @deprecated Old key used to set CPU frequency caps directly in sys files. */
+ @Deprecated
private static final String KEY_CPU_FREQ_INTERACTIVE = "cpufreq-i";
+ /** @deprecated Old key used to set CPU frequency caps directly in sys files. */
+ @Deprecated
private static final String KEY_CPU_FREQ_NONINTERACTIVE = "cpufreq-n";
private static final String KEY_SUFFIX_ADAPTIVE = "_adaptive";
@@ -138,8 +142,6 @@ public class BatterySaverPolicy extends ContentObserver implements
static final Policy OFF_POLICY = new Policy(
1f, /* adjustBrightnessFactor */
false, /* advertiseIsEnabled */
- new CpuFrequencies(), /* cpuFrequenciesForInteractive */
- new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
false, /* deferFullBackup */
false, /* deferKeyValueBackup */
false, /* disableAnimation */
@@ -163,8 +165,6 @@ public class BatterySaverPolicy extends ContentObserver implements
private static final Policy DEFAULT_FULL_POLICY = new Policy(
0.5f, /* adjustBrightnessFactor */
true, /* advertiseIsEnabled */
- new CpuFrequencies(), /* cpuFrequenciesForInteractive */
- new CpuFrequencies(), /* cpuFrequenciesForNoninteractive */
true, /* deferFullBackup */
true, /* deferKeyValueBackup */
false, /* disableAnimation */
@@ -487,8 +487,6 @@ public class BatterySaverPolicy extends ContentObserver implements
mEffectivePolicyRaw = new Policy(
rawPolicy.adjustBrightnessFactor,
rawPolicy.advertiseIsEnabled,
- rawPolicy.cpuFrequenciesForInteractive,
- rawPolicy.cpuFrequenciesForNoninteractive,
rawPolicy.deferFullBackup,
rawPolicy.deferKeyValueBackup,
rawPolicy.disableAnimation,
@@ -648,22 +646,6 @@ public class BatterySaverPolicy extends ContentObserver implements
public final boolean enableQuickDoze;
/**
- * List of CPU frequencies that should be written when battery saver is activated
- * and the device is interactive.
- *
- * We use this to change the max CPU frequencies.
- */
- public final CpuFrequencies cpuFrequenciesForInteractive;
-
- /**
- * List of CPU frequencies that should be written when battery saver is activated
- * and the device is non-interactive.
- *
- * We use this to change the max CPU frequencies.
- */
- public final CpuFrequencies cpuFrequenciesForNoninteractive;
-
- /**
* Whether to put all apps in the stand-by mode.
*/
public final boolean forceAllAppsStandby;
@@ -687,8 +669,6 @@ public class BatterySaverPolicy extends ContentObserver implements
Policy(
float adjustBrightnessFactor,
boolean advertiseIsEnabled,
- CpuFrequencies cpuFrequenciesForInteractive,
- CpuFrequencies cpuFrequenciesForNoninteractive,
boolean deferFullBackup,
boolean deferKeyValueBackup,
boolean disableAnimation,
@@ -708,8 +688,6 @@ public class BatterySaverPolicy extends ContentObserver implements
this.adjustBrightnessFactor = Math.min(1, Math.max(0, adjustBrightnessFactor));
this.advertiseIsEnabled = advertiseIsEnabled;
- this.cpuFrequenciesForInteractive = cpuFrequenciesForInteractive;
- this.cpuFrequenciesForNoninteractive = cpuFrequenciesForNoninteractive;
this.deferFullBackup = deferFullBackup;
this.deferKeyValueBackup = deferKeyValueBackup;
this.disableAnimation = disableAnimation;
@@ -744,8 +722,6 @@ public class BatterySaverPolicy extends ContentObserver implements
mHashCode = Objects.hash(
adjustBrightnessFactor,
advertiseIsEnabled,
- cpuFrequenciesForInteractive,
- cpuFrequenciesForNoninteractive,
deferFullBackup,
deferKeyValueBackup,
disableAnimation,
@@ -772,16 +748,10 @@ public class BatterySaverPolicy extends ContentObserver implements
// Device-specific parameters.
Map<String, String> deviceSpecificSettings = config.getDeviceSpecificSettings();
- final String cpuFreqInteractive =
- deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_INTERACTIVE, "");
- final String cpuFreqNoninteractive =
- deviceSpecificSettings.getOrDefault(KEY_CPU_FREQ_NONINTERACTIVE, "");
return new Policy(
config.getAdjustBrightnessFactor(),
config.getAdvertiseIsEnabled(),
- (new CpuFrequencies()).parseString(cpuFreqInteractive),
- (new CpuFrequencies()).parseString(cpuFreqNoninteractive),
config.getDeferFullBackup(),
config.getDeferKeyValueBackup(),
config.getDisableAnimation(),
@@ -803,10 +773,6 @@ public class BatterySaverPolicy extends ContentObserver implements
BatterySaverPolicyConfig toConfig() {
return new BatterySaverPolicyConfig.Builder()
- .addDeviceSpecificSetting(KEY_CPU_FREQ_INTERACTIVE,
- cpuFrequenciesForInteractive.toString())
- .addDeviceSpecificSetting(KEY_CPU_FREQ_NONINTERACTIVE,
- cpuFrequenciesForNoninteractive.toString())
.setAdjustBrightnessFactor(adjustBrightnessFactor)
.setAdvertiseIsEnabled(advertiseIsEnabled)
.setDeferFullBackup(deferFullBackup)
@@ -848,9 +814,6 @@ public class BatterySaverPolicy extends ContentObserver implements
+ deviceSpecificSettings);
}
- final String cpuFreqInteractive = parser.getString(KEY_CPU_FREQ_INTERACTIVE, "");
- final String cpuFreqNoninteractive = parser.getString(KEY_CPU_FREQ_NONINTERACTIVE, "");
-
// Non-device-specific parameters.
try {
parser.setString(settings == null ? "" : settings);
@@ -918,8 +881,6 @@ public class BatterySaverPolicy extends ContentObserver implements
return new Policy(
adjustBrightnessFactor,
advertiseIsEnabled,
- (new CpuFrequencies()).parseString(cpuFreqInteractive),
- (new CpuFrequencies()).parseString(cpuFreqNoninteractive),
deferFullBackup,
deferKeyValueBackup,
disableAnimation,
@@ -962,10 +923,7 @@ public class BatterySaverPolicy extends ContentObserver implements
&& forceAllAppsStandby == other.forceAllAppsStandby
&& forceBackgroundCheck == other.forceBackgroundCheck
&& locationMode == other.locationMode
- && soundTriggerMode == other.soundTriggerMode
- && cpuFrequenciesForInteractive.equals(other.cpuFrequenciesForInteractive)
- && cpuFrequenciesForNoninteractive.equals(
- other.cpuFrequenciesForNoninteractive);
+ && soundTriggerMode == other.soundTriggerMode;
}
@Override
@@ -1179,14 +1137,6 @@ public class BatterySaverPolicy extends ContentObserver implements
}
}
- public ArrayMap<String, String> getFileValues(boolean interactive) {
- synchronized (mLock) {
- return interactive
- ? getCurrentPolicyLocked().cpuFrequenciesForInteractive.toSysFileMap()
- : getCurrentPolicyLocked().cpuFrequenciesForNoninteractive.toSysFileMap();
- }
- }
-
public boolean isLaunchBoostDisabled() {
synchronized (mLock) {
return getCurrentPolicyLocked().disableLaunchBoost;
@@ -1274,17 +1224,6 @@ public class BatterySaverPolicy extends ContentObserver implements
pw.println(KEY_ENABLE_QUICK_DOZE + "=" + p.enableQuickDoze);
pw.println(KEY_ENABLE_NIGHT_MODE + "=" + p.enableNightMode);
- pw.println("Interactive File values:");
- pw.increaseIndent();
- dumpMap(pw, p.cpuFrequenciesForInteractive.toSysFileMap());
- pw.decreaseIndent();
- pw.println();
-
- pw.println("Noninteractive File values:");
- pw.increaseIndent();
- dumpMap(pw, p.cpuFrequenciesForNoninteractive.toSysFileMap());
- pw.decreaseIndent();
-
// Decrease from indent right after "Policy" line
pw.decreaseIndent();
}
diff --git a/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java b/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java
deleted file mode 100644
index 5b38cf48e537..000000000000
--- a/services/core/java/com/android/server/power/batterysaver/CpuFrequencies.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.power.batterysaver;
-
-import android.util.ArrayMap;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Map;
-
-
-/**
- * Helper to parse a list of "core-number:frequency" pairs concatenated with / as a separator,
- * and convert them into a map of "filename -> value" that should be written to
- * /sys/.../scaling_max_freq.
- *
- * Example input: "0:1900800/4:2500000", which will be converted into:
- * "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" "1900800"
- * "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq" "2500000"
- *
- * Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
- */
-public class CpuFrequencies {
- private static final String TAG = "CpuFrequencies";
-
- private static final String CPU_DELIM = "/";
- private static final String FREQUENCY_DELIM = ":";
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private final ArrayMap<Integer, Long> mCoreAndFrequencies = new ArrayMap<>();
-
- public CpuFrequencies() {
- }
-
- /**
- * Parse a string.
- */
- public CpuFrequencies parseString(String cpuNumberAndFrequencies) {
- synchronized (mLock) {
- mCoreAndFrequencies.clear();
- try {
- for (String pair : cpuNumberAndFrequencies.split(CPU_DELIM)) {
- pair = pair.trim();
- if (pair.length() == 0) {
- continue;
- }
- final String[] coreAndFreq = pair.split(FREQUENCY_DELIM, 2);
-
- if (coreAndFreq.length != 2) {
- throw new IllegalArgumentException("Wrong format");
- }
- final int core = Integer.parseInt(coreAndFreq[0]);
- final long freq = Long.parseLong(coreAndFreq[1]);
-
- mCoreAndFrequencies.put(core, freq);
- }
- } catch (IllegalArgumentException e) {
- Slog.wtf(TAG, "Invalid configuration: '" + cpuNumberAndFrequencies + "'");
- }
- }
- return this;
- }
-
- /**
- * Return a new map containing the filename-value pairs.
- */
- public ArrayMap<String, String> toSysFileMap() {
- final ArrayMap<String, String> map = new ArrayMap<>();
- addToSysFileMap(map);
- return map;
- }
-
- /**
- * Add the filename-value pairs to an existing map.
- */
- public void addToSysFileMap(Map<String, String> map) {
- synchronized (mLock) {
- final int size = mCoreAndFrequencies.size();
-
- for (int i = 0; i < size; i++) {
- final int core = mCoreAndFrequencies.keyAt(i);
- final long freq = mCoreAndFrequencies.valueAt(i);
-
- final String file = "/sys/devices/system/cpu/cpu" + Integer.toString(core) +
- "/cpufreq/scaling_max_freq";
-
- map.put(file, Long.toString(freq));
- }
- }
- }
-
- /**
- * Returns String describing the frequency settings used.
- * The returned String can be parsed again by {@link #parseString(String)}.
- */
- public String toString() {
- synchronized (mLock) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < mCoreAndFrequencies.size(); i++) {
- if (i > 0) {
- sb.append(CPU_DELIM);
- }
- sb.append(mCoreAndFrequencies.keyAt(i));
- sb.append(FREQUENCY_DELIM);
- sb.append(mCoreAndFrequencies.valueAt(i));
- }
-
- return sb.toString();
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- synchronized (mLock) {
- if (this == obj) return true;
- if (!(obj instanceof CpuFrequencies)) return false;
- CpuFrequencies other = (CpuFrequencies) obj;
- return mCoreAndFrequencies.equals(other.mCoreAndFrequencies);
- }
- }
-
- @Override
- public int hashCode() {
- return mCoreAndFrequencies.hashCode();
- }
-}
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
deleted file mode 100644
index 1387617ab39c..000000000000
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.power.batterysaver;
-
-import android.content.Context;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Slog;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
-import android.util.Xml;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-import com.android.server.IoThread;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Map;
-
-/**
- * Used by {@link BatterySaverController} to write values to /sys/ (and possibly /proc/ too) files
- * with retries. It also support restoring to the file original values.
- *
- * Retries are needed because writing to "/sys/.../scaling_max_freq" returns EIO when the current
- * frequency happens to be above the new max frequency.
- *
- * Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
- */
-public class FileUpdater {
- private static final String TAG = BatterySaverController.TAG;
-
- private static final boolean DEBUG = BatterySaverController.DEBUG;
-
- /**
- * If this system property is set to 1, it'll skip all file writes. This can be used when
- * one needs to change max CPU frequency for benchmarking, for example.
- */
- private static final String PROP_SKIP_WRITE = "debug.batterysaver.no_write_files";
-
- private static final String TAG_DEFAULT_ROOT = "defaults";
-
- // Don't do disk access with this lock held.
- private final Object mLock = new Object();
-
- private final Context mContext;
-
- private final Handler mHandler;
-
- /**
- * Filename -> value map that holds pending writes.
- */
- @GuardedBy("mLock")
- private final ArrayMap<String, String> mPendingWrites = new ArrayMap<>();
-
- /**
- * Filename -> value that holds the original value of each file.
- */
- @GuardedBy("mLock")
- private final ArrayMap<String, String> mDefaultValues = new ArrayMap<>();
-
- /** Number of retries. We give up on writing after {@link #MAX_RETRIES} retries. */
- @GuardedBy("mLock")
- private int mRetries = 0;
-
- private final int MAX_RETRIES;
-
- private final long RETRY_INTERVAL_MS;
-
- /**
- * "Official" constructor. Don't use the other constructor in the production code.
- */
- public FileUpdater(Context context) {
- this(context, IoThread.get().getLooper(), 10, 5000);
- }
-
- /**
- * Constructor for test.
- */
- @VisibleForTesting
- FileUpdater(Context context, Looper looper, int maxRetries, int retryIntervalMs) {
- mContext = context;
- mHandler = new Handler(looper);
-
- MAX_RETRIES = maxRetries;
- RETRY_INTERVAL_MS = retryIntervalMs;
- }
-
- public void systemReady(boolean runtimeRestarted) {
- synchronized (mLock) {
- if (runtimeRestarted) {
- // If it runtime restarted, read the original values from the disk and apply.
- if (loadDefaultValuesLocked()) {
- Slog.d(TAG, "Default values loaded after runtime restart; writing them...");
- restoreDefault();
- }
- } else {
- // Delete it, without checking the result. (file-not-exist is not an exception.)
- injectDefaultValuesFilename().delete();
- }
- }
- }
-
- /**
- * Write values to files. (Note the actual writes happen ASAP but asynchronously.)
- */
- public void writeFiles(ArrayMap<String, String> fileValues) {
- synchronized (mLock) {
- for (int i = fileValues.size() - 1; i >= 0; i--) {
- final String file = fileValues.keyAt(i);
- final String value = fileValues.valueAt(i);
-
- if (DEBUG) {
- Slog.d(TAG, "Scheduling write: '" + value + "' to '" + file + "'");
- }
-
- mPendingWrites.put(file, value);
-
- }
- mRetries = 0;
-
- mHandler.removeCallbacks(mHandleWriteOnHandlerRunnable);
- mHandler.post(mHandleWriteOnHandlerRunnable);
- }
- }
-
- /**
- * Restore the default values.
- */
- public void restoreDefault() {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Resetting file default values.");
- }
- mPendingWrites.clear();
-
- writeFiles(mDefaultValues);
- }
- }
-
- private Runnable mHandleWriteOnHandlerRunnable = () -> handleWriteOnHandler();
-
- /** Convert map keys into a single string for debug messages. */
- private String getKeysString(Map<String, String> source) {
- return new ArrayList<>(source.keySet()).toString();
- }
-
- /** Clone an ArrayMap. */
- private ArrayMap<String, String> cloneMap(ArrayMap<String, String> source) {
- return new ArrayMap<>(source);
- }
-
- /**
- * Called on the handler and writes {@link #mPendingWrites} to the disk.
- *
- * When it about to write to each file for the first time, it'll read the file and store
- * the original value in {@link #mDefaultValues}.
- */
- private void handleWriteOnHandler() {
- // We don't want to access the disk with the lock held, so copy the pending writes to
- // a local map.
- final ArrayMap<String, String> writes;
- synchronized (mLock) {
- if (mPendingWrites.size() == 0) {
- return;
- }
-
- if (DEBUG) {
- Slog.d(TAG, "Writing files: (# retries=" + mRetries + ") " +
- getKeysString(mPendingWrites));
- }
-
- writes = cloneMap(mPendingWrites);
- }
-
- // Then write.
-
- boolean needRetry = false;
-
- final int size = writes.size();
- for (int i = 0; i < size; i++) {
- final String file = writes.keyAt(i);
- final String value = writes.valueAt(i);
-
- // Make sure the default value is loaded.
- if (!ensureDefaultLoaded(file)) {
- continue;
- }
-
- // Write to the file. When succeeded, remove it from the pending list.
- // Otherwise, schedule a retry.
- try {
- injectWriteToFile(file, value);
-
- removePendingWrite(file);
- } catch (IOException e) {
- needRetry = true;
- }
- }
- if (needRetry) {
- scheduleRetry();
- }
- }
-
- private void removePendingWrite(String file) {
- synchronized (mLock) {
- mPendingWrites.remove(file);
- }
- }
-
- private void scheduleRetry() {
- synchronized (mLock) {
- if (mPendingWrites.size() == 0) {
- return; // Shouldn't happen but just in case.
- }
-
- mRetries++;
- if (mRetries > MAX_RETRIES) {
- doWtf("Gave up writing files: " + getKeysString(mPendingWrites));
- return;
- }
-
- mHandler.removeCallbacks(mHandleWriteOnHandlerRunnable);
- mHandler.postDelayed(mHandleWriteOnHandlerRunnable, RETRY_INTERVAL_MS);
- }
- }
-
- /**
- * Make sure {@link #mDefaultValues} has the default value loaded for {@code file}.
- *
- * @return true if the default value is loaded. false if the file cannot be read.
- */
- private boolean ensureDefaultLoaded(String file) {
- // Has the default already?
- synchronized (mLock) {
- if (mDefaultValues.containsKey(file)) {
- return true;
- }
- }
- final String originalValue;
- try {
- originalValue = injectReadFromFileTrimmed(file);
- } catch (IOException e) {
- // If the file is not readable, assume can't write too.
- injectWtf("Unable to read from file", e);
-
- removePendingWrite(file);
- return false;
- }
- synchronized (mLock) {
- mDefaultValues.put(file, originalValue);
- saveDefaultValuesLocked();
- }
- return true;
- }
-
- @VisibleForTesting
- String injectReadFromFileTrimmed(String file) throws IOException {
- return IoUtils.readFileAsString(file).trim();
- }
-
- @VisibleForTesting
- void injectWriteToFile(String file, String value) throws IOException {
- if (injectShouldSkipWrite()) {
- Slog.i(TAG, "Skipped writing to '" + file + "'");
- return;
- }
- if (DEBUG) {
- Slog.d(TAG, "Writing: '" + value + "' to '" + file + "'");
- }
- try (FileWriter out = new FileWriter(file)) {
- out.write(value);
- } catch (IOException | RuntimeException e) {
- Slog.w(TAG, "Failed writing '" + value + "' to '" + file + "': " + e.getMessage());
- throw e;
- }
- }
-
- @GuardedBy("mLock")
- private void saveDefaultValuesLocked() {
- final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
-
- FileOutputStream outs = null;
- try {
- file.getBaseFile().getParentFile().mkdirs();
- outs = file.startWrite();
-
- // Write to XML
- TypedXmlSerializer out = Xml.resolveSerializer(outs);
- out.startDocument(null, true);
- out.startTag(null, TAG_DEFAULT_ROOT);
-
- XmlUtils.writeMapXml(mDefaultValues, out, null);
-
- // Epilogue.
- out.endTag(null, TAG_DEFAULT_ROOT);
- out.endDocument();
-
- // Close.
- file.finishWrite(outs);
- } catch (IOException | XmlPullParserException | RuntimeException e) {
- Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
- file.failWrite(outs);
- }
- }
-
- @GuardedBy("mLock")
- @VisibleForTesting
- boolean loadDefaultValuesLocked() {
- final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
- if (DEBUG) {
- Slog.d(TAG, "Loading from " + file.getBaseFile());
- }
- Map<String, String> read = null;
- try (FileInputStream in = file.openRead()) {
- TypedXmlPullParser parser = Xml.resolvePullParser(in);
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
- // Check the root tag
- final String tag = parser.getName();
- if (depth == 1) {
- if (!TAG_DEFAULT_ROOT.equals(tag)) {
- Slog.e(TAG, "Invalid root tag: " + tag);
- return false;
- }
- continue;
- }
- final String[] tagName = new String[1];
- read = (ArrayMap<String, String>) XmlUtils.readThisArrayMapXml(parser,
- TAG_DEFAULT_ROOT, tagName, null);
- }
- } catch (FileNotFoundException e) {
- read = null;
- } catch (IOException | XmlPullParserException | RuntimeException e) {
- Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
- }
- if (read != null) {
- mDefaultValues.clear();
- mDefaultValues.putAll(read);
- return true;
- }
- return false;
- }
-
- private void doWtf(String message) {
- injectWtf(message, null);
- }
-
- @VisibleForTesting
- void injectWtf(String message, Throwable e) {
- Slog.wtf(TAG, message, e);
- }
-
- File injectDefaultValuesFilename() {
- final File dir = new File(Environment.getDataSystemDirectory(), "battery-saver");
- dir.mkdirs();
- return new File(dir, "default-values.xml");
- }
-
- @VisibleForTesting
- boolean injectShouldSkipWrite() {
- return SystemProperties.getBoolean(PROP_SKIP_WRITE, false);
- }
-
- @VisibleForTesting
- ArrayMap<String, String> getDefaultValuesForTest() {
- return mDefaultValues;
- }
-}
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index 3d78828888da..141d4dcf77d0 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -44,6 +44,8 @@ public class RecoverySystemShellCommand extends ShellCommand {
return requestLskf();
case "clear-lskf":
return clearLskf();
+ case "is-lskf-captured":
+ return isLskfCaptured();
case "reboot-and-apply":
return rebootAndApply();
default:
@@ -74,6 +76,14 @@ public class RecoverySystemShellCommand extends ShellCommand {
return 0;
}
+ private int isLskfCaptured() throws RemoteException {
+ String packageName = getNextArgRequired();
+ boolean captured = mService.isLskfCaptured(packageName);
+ PrintWriter pw = getOutPrintWriter();
+ pw.printf("%s LSKF capture status: %s\n", packageName, captured ? "true" : "false");
+ return 0;
+ }
+
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
@@ -90,8 +100,9 @@ public class RecoverySystemShellCommand extends ShellCommand {
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Recovery system commands:");
- pw.println(" request-lskf <token>");
+ pw.println(" request-lskf <package_name>");
pw.println(" clear-lskf");
- pw.println(" reboot-and-apply <token> <reason>");
+ pw.println(" is-lskf-captured <package_name>");
+ pw.println(" reboot-and-apply <package_name> <reason>");
}
}
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index c8e36481c738..c7e7784715cd 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -143,10 +143,10 @@ public class AppDataRollbackHelper {
int rollbackId, int appId, String seInfo, int flags) {
if (packageRollbackInfo.isApex()) {
switch (packageRollbackInfo.getRollbackDataPolicy()) {
- case PackageManager.RollbackDataPolicy.WIPE:
+ case PackageManager.ROLLBACK_DATA_POLICY_WIPE:
// TODO: Implement WIPE for apex CE data
break;
- case PackageManager.RollbackDataPolicy.RESTORE:
+ case PackageManager.ROLLBACK_DATA_POLICY_RESTORE:
// For APEX, only restore of CE may be done here.
if ((flags & Installer.FLAG_STORAGE_CE) != 0) {
mApexManager.restoreCeData(
@@ -160,11 +160,11 @@ public class AppDataRollbackHelper {
// APK
try {
switch (packageRollbackInfo.getRollbackDataPolicy()) {
- case PackageManager.RollbackDataPolicy.WIPE:
+ case PackageManager.ROLLBACK_DATA_POLICY_WIPE:
mInstaller.clearAppData(null, packageRollbackInfo.getPackageName(),
userId, flags, 0);
break;
- case PackageManager.RollbackDataPolicy.RESTORE:
+ case PackageManager.ROLLBACK_DATA_POLICY_RESTORE:
mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(),
appId, seInfo, userId, rollbackId, flags);
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 9560f59924de..38e6b2820ad8 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -116,10 +116,10 @@ class Rollback {
static final int ROLLBACK_STATE_DELETED = 4;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
+ * The session ID associate with this rollback. This is the parent session ID in the case of
+ * a multi-package session.
*/
- private final int mStagedSessionId;
+ private final int mOriginalSessionId;
/**
* The rollback info for this rollback.
@@ -181,13 +181,6 @@ class Rollback {
private final int[] mPackageSessionIds;
/**
- * The number of sessions in the install which are notified with success by
- * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
- * This rollback will be enabled only after all child sessions finished with success.
- */
- private int mNumPackageSessionsWithSuccess;
-
- /**
* The extension versions supported at the time of rollback creation.
*/
@NonNull private final SparseIntArray mExtensionVersions;
@@ -203,24 +196,25 @@ class Rollback {
*
* @param rollbackId the id of the rollback.
* @param backupDir the directory where the rollback data is stored.
- * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
+ * @param originalSessionId the session id associated with this rollback.
+ * @param isStaged true if this is a staged rollback.
* @param userId the user that performed the install with rollback enabled.
* @param installerPackageName the installer package name from the original install session.
* @param packageSessionIds the session ids for all packages in the install.
* @param extensionVersions the extension versions supported at the time of rollback creation
*/
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
+ Rollback(int rollbackId, File backupDir, int originalSessionId, boolean isStaged, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
- /* isStaged */ stagedSessionId != -1,
+ /* isStaged */ isStaged,
/* causePackages */ new ArrayList<>(),
/* committedSessionId */ -1);
mUserId = userId;
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = ROLLBACK_STATE_ENABLING;
mTimestamp = Instant.now();
mPackageSessionIds = packageSessionIds != null ? packageSessionIds : new int[0];
@@ -228,16 +222,10 @@ class Rollback {
mHandler = Looper.myLooper() != null ? new Handler(Looper.myLooper()) : null;
}
- Rollback(int rollbackId, File backupDir, int stagedSessionId, int userId,
- String installerPackageName) {
- this(rollbackId, backupDir, stagedSessionId, userId, installerPackageName, null,
- new SparseIntArray(0));
- }
-
/**
* Constructs a pre-populated Rollback instance.
*/
- Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int originalSessionId,
@RollbackState int state, String stateDescription, boolean restoreUserDataInProgress,
int userId, String installerPackageName, SparseIntArray extensionVersions) {
this.info = info;
@@ -245,7 +233,7 @@ class Rollback {
mInstallerPackageName = installerPackageName;
mBackupDir = backupDir;
mTimestamp = timestamp;
- mStagedSessionId = stagedSessionId;
+ mOriginalSessionId = originalSessionId;
mState = state;
mStateDescription = stateDescription;
mRestoreUserDataInProgress = restoreUserDataInProgress;
@@ -298,12 +286,11 @@ class Rollback {
}
/**
- * Returns the session ID for the staged session if this rollback data represents a staged
- * session, or {@code -1} otherwise.
+ * Returns the session ID associated with this rollback, or {@code -1} if unknown.
*/
@AnyThread
- int getStagedSessionId() {
- return mStagedSessionId;
+ int getOriginalSessionId() {
+ return mOriginalSessionId;
}
/**
@@ -451,7 +438,7 @@ class Rollback {
for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
if (pkgRollbackInfo.getPackageName().equals(packageName)) {
if (pkgRollbackInfo.getRollbackDataPolicy()
- == PackageManager.RollbackDataPolicy.RESTORE) {
+ == PackageManager.ROLLBACK_DATA_POLICY_RESTORE) {
dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
addAll(pkgRollbackInfo.getSnapshottedUsers(), userIds);
RollbackStore.saveRollback(this);
@@ -838,17 +825,6 @@ class Rollback {
}
/**
- * Called when a child session finished with success.
- * Returns true when all child sessions are notified with success. This rollback will be
- * enabled only after all child sessions finished with success.
- */
- @WorkerThread
- boolean notifySessionWithSuccess() {
- assertInWorkerThread();
- return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
- }
-
- /**
* Returns true if all packages in this rollback are enabled. We won't enable this rollback
* until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
* automatically when the embedding apex is enabled.
@@ -954,9 +930,8 @@ class Rollback {
ipw.println("-state: " + getStateAsString());
ipw.println("-stateDescription: " + mStateDescription);
ipw.println("-timestamp: " + getTimestamp());
- if (getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + getStagedSessionId());
- }
+ ipw.println("-isStaged: " + isStaged());
+ ipw.println("-originalSessionId: " + getOriginalSessionId());
ipw.println("-packages:");
ipw.increaseIndent();
for (PackageRollbackInfo pkg : info.getPackages()) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9e19f57ed0e2..a5646247f766 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -247,11 +247,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
PackageManagerInternal.class);
pm.setEnableRollbackCode(token, ret);
});
-
- // We're handling the ordered broadcast. Abort the
- // broadcast because there is no need for it to go to
- // anyone else.
- abortBroadcast();
}
}
}, enableRollbackFilter, null, getHandler());
@@ -611,11 +606,11 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
}
PackageInstaller.SessionInfo session = mContext.getPackageManager()
- .getPackageInstaller().getSessionInfo(rollback.getStagedSessionId());
+ .getPackageInstaller().getSessionInfo(rollback.getOriginalSessionId());
if (session == null || session.isStagedSessionFailed()) {
if (rollback.isEnabling()) {
iter.remove();
- deleteRollback(rollback, "Session " + rollback.getStagedSessionId()
+ deleteRollback(rollback, "Session " + rollback.getOriginalSessionId()
+ " not existed or failed");
}
continue;
@@ -789,7 +784,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
newRollback = createNewRollback(parentSession);
}
- return enableRollbackForPackageSession(newRollback, packageSession);
+ if (enableRollbackForPackageSession(newRollback, packageSession)) {
+ // Persist the rollback if all packages are enabled. We will make the rollback
+ // available once the whole session is installed successfully.
+ return newRollback.allPackagesEnabled() ? completeEnableRollback(newRollback) : true;
+ } else {
+ return false;
+ }
}
@WorkerThread
@@ -799,7 +800,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
// precedence only when it is not the default (i.e. RESTORE). We will remove
// SessionParams#setEnableRollback(boolean, int) and related code when Play has migrated to
// using the manifest to specify the policy.
- if (manifestPolicy != PackageManager.RollbackDataPolicy.RESTORE) {
+ if (manifestPolicy != PackageManager.ROLLBACK_DATA_POLICY_RESTORE) {
return manifestPolicy;
}
return sessionPolicy;
@@ -978,43 +979,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
throw new SecurityException("notifyStagedSession may only be called by the system.");
}
- // NOTE: We post this runnable on the RollbackManager's binder thread because we'd prefer
- // to preserve the invariant that all operations that modify state happen there.
return awaitResult(() -> {
assertInWorkerThread();
- PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
-
- final PackageInstaller.SessionInfo session = installer.getSessionInfo(sessionId);
- if (session == null) {
- Slog.e(TAG, "No matching install session for: " + sessionId);
- return -1;
- }
-
- Rollback newRollback = createNewRollback(session);
- if (!session.isMultiPackage()) {
- if (!enableRollbackForPackageSession(newRollback, session)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- }
- } else {
- for (int childSessionId : session.getChildSessionIds()) {
- final PackageInstaller.SessionInfo childSession =
- installer.getSessionInfo(childSessionId);
- if (childSession == null) {
- Slog.e(TAG, "No matching child install session for: " + childSessionId);
- break;
- }
- if (!enableRollbackForPackageSession(newRollback, childSession)) {
- Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
- break;
- }
- }
- }
-
- if (!completeEnableRollback(newRollback)) {
- return -1;
- } else {
- return newRollback.info.getRollbackId();
- }
+ Rollback rollback = getRollbackForSession(sessionId);
+ return rollback != null ? rollback.info.getRollbackId() : -1;
});
}
@@ -1124,21 +1092,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
}
+ Rollback rollback = getRollbackForSession(sessionId);
+ if (rollback == null || !rollback.isEnabling()
+ || sessionId != rollback.getOriginalSessionId()) {
+ // We only care about the parent session id which will tell us whether the
+ // whole session is successful or not.
+ return;
+ }
if (success) {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && !rollback.isStaged() && rollback.isEnabling()
- && rollback.notifySessionWithSuccess()
- && completeEnableRollback(rollback)) {
+ if (!rollback.isStaged() && completeEnableRollback(rollback)) {
+ // completeEnableRollback() ensures the rollback is deleted if not all packages
+ // are enabled. For staged rollbacks, we will make them available in
+ // onBootCompleted().
makeRollbackAvailable(rollback);
}
} else {
- Rollback rollback = getRollbackForSession(sessionId);
- if (rollback != null && rollback.isEnabling()) {
- Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
- + " for failed session id=" + sessionId);
- mRollbacks.remove(rollback);
- deleteRollback(rollback, "Session " + sessionId + " failed");
- }
+ Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+ + " for failed session id=" + sessionId);
+ mRollbacks.remove(rollback);
+ deleteRollback(rollback, "Session " + sessionId + " failed");
}
}
}
@@ -1307,7 +1279,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
} else {
- rollback = mRollbackStore.createNonStagedRollback(rollbackId, userId,
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName, packageSessionIds, getExtensionVersions());
}
@@ -1339,7 +1311,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
// We expect mRollbacks to be a very small list; linear search should be plenty fast.
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.getStagedSessionId() == sessionId
+ if (rollback.getOriginalSessionId() == sessionId
|| rollback.containsSessionId(sessionId)) {
return rollback;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index fc62f5be801c..2cfc7856c197 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -209,23 +209,24 @@ class RollbackStore {
* Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
- int[] packageSessionIds, SparseIntArray extensionVersions) {
+ Rollback createNonStagedRollback(int rollbackId, int originalSessionId, int userId,
+ String installerPackageName, int[] packageSessionIds,
+ SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ false, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
/**
* Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
*/
- Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
+ Rollback createStagedRollback(int rollbackId, int originalSessionId, int userId,
String installerPackageName, int[] packageSessionIds,
SparseIntArray extensionVersions) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
- packageSessionIds, extensionVersions);
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, userId,
+ installerPackageName, packageSessionIds, extensionVersions);
}
private static boolean isLinkPossible(File oldFile, File newFile) {
@@ -312,7 +313,7 @@ class RollbackStore {
JSONObject dataJson = new JSONObject();
dataJson.put("info", rollbackInfoToJson(rollback.info));
dataJson.put("timestamp", rollback.getTimestamp().toString());
- dataJson.put("stagedSessionId", rollback.getStagedSessionId());
+ dataJson.put("originalSessionId", rollback.getOriginalSessionId());
dataJson.put("state", rollback.getStateAsString());
dataJson.put("stateDescription", rollback.getStateDescription());
dataJson.put("restoreUserDataInProgress", rollback.isRestoreUserDataInProgress());
@@ -380,7 +381,9 @@ class RollbackStore {
rollbackInfoFromJson(dataJson.getJSONObject("info")),
backupDir,
Instant.parse(dataJson.getString("timestamp")),
- dataJson.getInt("stagedSessionId"),
+ // Backward compatibility: Historical rollbacks are not erased upon OTA update.
+ // Need to load the old field 'stagedSessionId' as fallback.
+ dataJson.optInt("originalSessionId", dataJson.optInt("stagedSessionId", -1)),
rollbackStateFromString(dataJson.getString("state")),
dataJson.optString("stateDescription"),
dataJson.getBoolean("restoreUserDataInProgress"),
@@ -444,7 +447,7 @@ class RollbackStore {
// Backward compatibility: no such field for old versions.
final int rollbackDataPolicy = json.optInt("rollbackDataPolicy",
- PackageManager.RollbackDataPolicy.RESTORE);
+ PackageManager.ROLLBACK_DATA_POLICY_RESTORE);
return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
pendingBackups, pendingRestores, isApex, isApkInApex, snapshottedUsers,
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index a58291425d4c..82ba60fa3ce5 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -53,17 +53,15 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
private static final String TAG = RemoteRotationResolverService.class.getSimpleName();
private final long mIdleUnbindTimeoutMs;
- private final Object mLock;
RemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
+ int userId, long idleUnbindTimeoutMs) {
super(context,
new Intent(RotationResolverService.SERVICE_INTERFACE).setComponent(serviceName),
BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
IRotationResolverService.Stub::asInterface);
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
- mLock = lock;
// Bind right away.
connect();
@@ -75,8 +73,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
return -1;
}
- @GuardedBy("mLock")
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
final RotationResolutionRequest remoteRequest = request.mRemoteRequest;
post(service -> service.resolveRotation(request.mIRotationResolverCallback, remoteRequest));
@@ -97,13 +94,15 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
@NonNull
private final IRotationResolverCallback mIRotationResolverCallback;
@NonNull
- private ICancellationSignal mCancellation;
- @NonNull
private final CancellationSignal mCancellationSignalInternal;
@NonNull
final RotationResolverInternal.RotationResolverCallbackInternal
mCallbackInternal;
+ @NonNull
+ @GuardedBy("mLock")
+ private ICancellationSignal mCancellation;
+
@GuardedBy("mLock")
boolean mIsFulfilled;
@@ -111,17 +110,19 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
final RotationResolutionRequest mRemoteRequest;
boolean mIsDispatched;
- private final Object mLock = new Object();
+ private final Object mLock;
private final long mRequestStartTimeMillis;
RotationRequest(
@NonNull RotationResolverInternal.RotationResolverCallbackInternal callbackInternal,
- RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal) {
+ RotationResolutionRequest request, @NonNull CancellationSignal cancellationSignal,
+ Object lock) {
mCallbackInternal = callbackInternal;
mRemoteRequest = request;
mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
+ mLock = lock;
}
@@ -153,7 +154,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
}
private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
- private WeakReference<RotationRequest> mRequestWeakReference;
+ private final WeakReference<RotationRequest> mRequestWeakReference;
RotationResolverCallback(RotationRequest request) {
this.mRequestWeakReference = new WeakReference<>(request);
@@ -215,7 +216,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol
}
}
}
-
}
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 165a1d617429..48d8fed32aca 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -121,26 +121,26 @@ final class RotationResolverManagerPerUserService extends
final RotationResolverInternal.RotationResolverCallbackInternal wrapper =
new RotationResolverInternal.RotationResolverCallbackInternal() {
- @Override
- public void onSuccess(int result) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onSuccess(result);
- }
-
- @Override
- public void onFailure(int error) {
- synchronized (mLock) {
- mLatencyTracker
- .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
- }
- callbackInternal.onFailure(error);
- }
- };
+ @Override
+ public void onSuccess(int result) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ synchronized (mLock) {
+ mLatencyTracker
+ .onActionEnd(ACTION_ROTATE_SCREEN_CAMERA_CHECK);
+ }
+ callbackInternal.onFailure(error);
+ }
+ };
mCurrentRequest = new RemoteRotationResolverService.RotationRequest(wrapper,
- request, cancellationSignalInternal);
+ request, cancellationSignalInternal, mLock);
cancellationSignalInternal.setOnCancelListener(() -> {
synchronized (mLock) {
@@ -152,7 +152,7 @@ final class RotationResolverManagerPerUserService extends
});
- mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mRemoteService.resolveRotation(mCurrentRequest);
mCurrentRequest.mIsDispatched = true;
}
@@ -160,7 +160,7 @@ final class RotationResolverManagerPerUserService extends
private void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new RemoteRotationResolverService(getContext(), mComponentName,
- getUserId(), CONNECTION_TTL_MILLIS, mLock);
+ getUserId(), CONNECTION_TTL_MILLIS);
}
}
diff --git a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java b/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
index 3ca8a5a1f554..87b28dbc905e 100644
--- a/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
+++ b/services/core/java/com/android/server/servicewatcher/CurrentUserServiceSupplier.java
@@ -62,7 +62,7 @@ import java.util.Objects;
* not require callers to hold this permission is rejected (2) a service permission - any service
* whose package does not hold this permission is rejected.
*/
-public class CurrentUserServiceSupplier extends BroadcastReceiver implements
+public final class CurrentUserServiceSupplier extends BroadcastReceiver implements
ServiceSupplier<CurrentUserServiceSupplier.BoundServiceInfo> {
private static final String TAG = "CurrentUserServiceSupplier";
@@ -144,6 +144,53 @@ public class CurrentUserServiceSupplier extends BroadcastReceiver implements
}
}
+ /**
+ * Creates an instance using package details retrieved from config.
+ *
+ * @see #create(Context, String, String, String, String)
+ */
+ public static CurrentUserServiceSupplier createFromConfig(Context context, String action,
+ @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
+ String explicitPackage = retrieveExplicitPackage(context, enableOverlayResId,
+ nonOverlayPackageResId);
+ return CurrentUserServiceSupplier.create(context, action, explicitPackage,
+ /*callerPermission=*/null, /*servicePermission=*/null);
+ }
+
+ /**
+ * Creates an instance with the specific service details and permission requirements.
+ *
+ * @param context the context the supplier is to use
+ * @param action the action the service must declare in its intent-filter
+ * @param explicitPackage the package of the service, or {@code null} if the package of the
+ * service is not constrained
+ * @param callerPermission a permission that the service forces callers (i.e.
+ * ServiceWatcher/system server) to hold, or {@code null} if there isn't one
+ * @param servicePermission a permission that the service package should hold, or {@code null}
+ * if there isn't one
+ */
+ public static CurrentUserServiceSupplier create(Context context, String action,
+ @Nullable String explicitPackage, @Nullable String callerPermission,
+ @Nullable String servicePermission) {
+ boolean matchSystemAppsOnly = true;
+ return new CurrentUserServiceSupplier(context, action,
+ explicitPackage, callerPermission, servicePermission, matchSystemAppsOnly);
+ }
+
+ /**
+ * Creates an instance like {@link #create} except it allows connection to services that are not
+ * supplied by system packages. Only intended for use during tests.
+ *
+ * @see #create(Context, String, String, String, String)
+ */
+ public static CurrentUserServiceSupplier createUnsafeForTestsOnly(Context context,
+ String action, @Nullable String explicitPackage, @Nullable String callerPermission,
+ @Nullable String servicePermission) {
+ boolean matchSystemAppsOnly = false;
+ return new CurrentUserServiceSupplier(context, action,
+ explicitPackage, callerPermission, servicePermission, matchSystemAppsOnly);
+ }
+
private static @Nullable String retrieveExplicitPackage(Context context,
@BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
Resources resources = context.getResources();
@@ -162,31 +209,14 @@ public class CurrentUserServiceSupplier extends BroadcastReceiver implements
private final @Nullable String mCallerPermission;
// a permission that the service package should hold
private final @Nullable String mServicePermission;
+ // whether to use MATCH_SYSTEM_ONLY in queries
+ private final boolean mMatchSystemAppsOnly;
private volatile ServiceChangedListener mListener;
- public CurrentUserServiceSupplier(Context context, String action) {
- this(context, action, null, null, null);
- }
-
- public CurrentUserServiceSupplier(Context context, String action,
- @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
- this(context, action,
- retrieveExplicitPackage(context, enableOverlayResId, nonOverlayPackageResId), null,
- null);
- }
-
- public CurrentUserServiceSupplier(Context context, String action,
- @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId,
- @Nullable String callerPermission, @Nullable String servicePermission) {
- this(context, action,
- retrieveExplicitPackage(context, enableOverlayResId, nonOverlayPackageResId),
- callerPermission, servicePermission);
- }
-
- public CurrentUserServiceSupplier(Context context, String action,
+ private CurrentUserServiceSupplier(Context context, String action,
@Nullable String explicitPackage, @Nullable String callerPermission,
- @Nullable String servicePermission) {
+ @Nullable String servicePermission, boolean matchSystemAppsOnly) {
mContext = context;
mActivityManager = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
@@ -198,13 +228,18 @@ public class CurrentUserServiceSupplier extends BroadcastReceiver implements
mCallerPermission = callerPermission;
mServicePermission = servicePermission;
+ mMatchSystemAppsOnly = matchSystemAppsOnly;
}
@Override
public boolean hasMatchingService() {
+ int intentQueryFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
+ if (mMatchSystemAppsOnly) {
+ intentQueryFlags |= MATCH_SYSTEM_ONLY;
+ }
List<ResolveInfo> resolveInfos = mContext.getPackageManager()
.queryIntentServicesAsUser(mIntent,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
+ intentQueryFlags,
UserHandle.USER_SYSTEM);
return !resolveInfos.isEmpty();
}
@@ -234,11 +269,15 @@ public class CurrentUserServiceSupplier extends BroadcastReceiver implements
public BoundServiceInfo getServiceInfo() {
BoundServiceInfo bestServiceInfo = null;
- // only allow privileged services in the correct direct boot state to match
+ // only allow services in the correct direct boot state to match
+ int intentQueryFlags = MATCH_DIRECT_BOOT_AUTO | GET_META_DATA;
+ if (mMatchSystemAppsOnly) {
+ intentQueryFlags |= MATCH_SYSTEM_ONLY;
+ }
int currentUserId = mActivityManager.getCurrentUserId();
List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
mIntent,
- GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
+ intentQueryFlags,
currentUserId);
for (ResolveInfo resolveInfo : resolveInfos) {
ServiceInfo service = Objects.requireNonNull(resolveInfo.serviceInfo);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 6366280e1762..e006b6525526 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -21,24 +21,23 @@ import android.annotation.Nullable;
import android.hardware.audio.common.V2_0.Uuid;
import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
import android.hardware.soundtrigger.V2_3.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.Properties;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import android.media.audio.common.AudioConfig;
import android.media.audio.common.AudioOffloadInfo;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.HidlMemory;
import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
@@ -55,9 +54,9 @@ import java.util.regex.Matcher;
*/
class ConversionUtil {
static @NonNull
- SoundTriggerModuleProperties hidl2aidlProperties(
+ Properties hidl2aidlProperties(
@NonNull ISoundTriggerHw.Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = new SoundTriggerModuleProperties();
+ Properties aidlProperties = new Properties();
aidlProperties.implementor = hidlProperties.implementor;
aidlProperties.description = hidlProperties.description;
aidlProperties.version = hidlProperties.version;
@@ -75,9 +74,9 @@ class ConversionUtil {
return aidlProperties;
}
- static @NonNull SoundTriggerModuleProperties hidl2aidlProperties(
- @NonNull Properties hidlProperties) {
- SoundTriggerModuleProperties aidlProperties = hidl2aidlProperties(hidlProperties.base);
+ static @NonNull Properties hidl2aidlProperties(
+ @NonNull android.hardware.soundtrigger.V2_3.Properties hidlProperties) {
+ Properties aidlProperties = hidl2aidlProperties(hidlProperties.base);
aidlProperties.supportedModelArch = hidlProperties.supportedModelArch;
aidlProperties.audioCapabilities =
hidl2aidlAudioCapabilities(hidlProperties.audioCapabilities);
@@ -216,9 +215,11 @@ class ConversionUtil {
}
static @NonNull android.hardware.soundtrigger.V2_3.RecognitionConfig aidl2hidlRecognitionConfig(
- @NonNull RecognitionConfig aidlConfig) {
+ @NonNull RecognitionConfig aidlConfig, int deviceHandle, int ioHandle) {
android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
new android.hardware.soundtrigger.V2_3.RecognitionConfig();
+ hidlConfig.base.header.captureDevice = deviceHandle;
+ hidlConfig.base.header.captureHandle = ioHandle;
hidlConfig.base.header.captureRequested = aidlConfig.captureRequested;
for (PhraseRecognitionExtra aidlPhraseExtra : aidlConfig.phraseRecognitionExtras) {
hidlConfig.base.header.phrases.add(aidl2hidlPhraseRecognitionExtra(aidlPhraseExtra));
@@ -299,8 +300,6 @@ class ConversionUtil {
aidlEvent.status = hidl2aidlRecognitionStatus(hidlEvent.status);
aidlEvent.type = hidl2aidlSoundModelType(hidlEvent.type);
aidlEvent.captureAvailable = hidlEvent.captureAvailable;
- // hidlEvent.captureSession is never a valid field.
- aidlEvent.captureSession = -1;
aidlEvent.captureDelayMs = hidlEvent.captureDelayMs;
aidlEvent.capturePreambleMs = hidlEvent.capturePreambleMs;
aidlEvent.triggerInData = hidlEvent.triggerInData;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
new file mode 100644
index 000000000000..2f2cb594ff3a
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.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 com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.os.HwBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This is the basic implementation of HalFactory, which uses either the default STHAL or a mock.
+ *
+ * The choice of which HAL to use is as follows:
+ * - Get the (int) value of "debug.soundtrigger_middleware.use_mock_hal" sysprop, if it doesn't
+ * exist, assume 0.
+ * - If the value is 0, use the default HAL on the device. Connect to the latest-version "default"
+ * instance declared in the device manifest (either AIDL or HIDL).
+ * - If the value is 2, connect to a "mock" instance of the latest v2.x (HIDL).
+ * - If the value is 3, connect to a "mock" instance of soundtrigger3 (AIDL).
+ * - Otherwise, throw.
+ */
+class DefaultHalFactory implements HalFactory {
+ private static final String TAG = "SoundTriggerMiddlewareDefaultHalFactory";
+
+ private static final @NonNull ICaptureStateNotifier mCaptureStateNotifier =
+ new ExternalCaptureStateTracker();
+
+ private static final int USE_DEFAULT_HAL = 0;
+ private static final int USE_MOCK_HAL_V2 = 2;
+ private static final int USE_MOCK_HAL_V3 = 3;
+
+ @Override
+ public ISoundTriggerHal create() {
+ try {
+ int mockHal = SystemProperties.getInt("debug.soundtrigger_middleware.use_mock_hal",
+ USE_DEFAULT_HAL);
+ if (mockHal == USE_DEFAULT_HAL) {
+ // Use production HAL.
+
+ // Try soundtrigger3 (AIDL) first.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/default";
+ if (ServiceManager.isDeclared(aidlServiceName)) {
+ Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ });
+ }
+
+ // Fallback to soundtrigger-V2.x (HIDL).
+ Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+ ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ // This property needs to be defined in an init.rc script and
+ // trigger a HAL reboot.
+ SystemProperties.set("sys.audio.restart.hal", "1");
+ }, mCaptureStateNotifier);
+ } else if (mockHal == USE_MOCK_HAL_V2) {
+ // Use V2 mock.
+ Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+ HwBinder.setTrebleTestingOverride(true);
+ try {
+ ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
+ return SoundTriggerHw2Compat.create(driver, () -> {
+ try {
+ driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ }, mCaptureStateNotifier);
+ } finally {
+ HwBinder.setTrebleTestingOverride(false);
+ }
+ } else if (mockHal == USE_MOCK_HAL_V3) {
+ // Use V3 mock.
+ final String aidlServiceName =
+ android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ + "/mock";
+ Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+ return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
+ () -> {
+ try {
+ ServiceManager.waitForService(aidlServiceName).shellCommand(null,
+ null, null, new String[]{"reboot"}, null, null);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to reboot mock HAL", e);
+ }
+ });
+ } else {
+ throw new RuntimeException("Unknown HAL mock version: " + mockHal);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index 940490411b49..d195fbedcf2f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -16,42 +16,55 @@
package com.android.server.soundtrigger_middleware;
+import android.annotation.NonNull;
import android.util.Log;
+import java.util.LinkedList;
+import java.util.List;
import java.util.concurrent.Semaphore;
-import java.util.function.Consumer;
/**
* This is a never-give-up listener for sound trigger external capture state notifications, as
* published by the audio policy service.
*
* This class will constantly try to connect to the service over a background thread and tolerate
- * its death. The client will be notified by a single provided function that is called in a
- * synchronized manner.
- * For simplicity, there is currently no way to stop the tracker. This is possible to add if the
- * need ever arises.
+ * its death.
*/
-class ExternalCaptureStateTracker {
+class ExternalCaptureStateTracker implements ICaptureStateNotifier {
private static final String TAG = "CaptureStateTracker";
- /** Our client's listener. */
- private final Consumer<Boolean> mListener;
+
+ /** Our client's listeners. Also used as lock. */
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ /** Conservatively, until notified otherwise, we assume capture is active. */
+ private boolean mCaptureActive = true;
+
/** This semaphore will get a permit every time we need to reconnect. */
private final Semaphore mNeedToConnect = new Semaphore(1);
/**
* Constructor. Will start a background thread to do the work.
- *
- * @param listener A client provided listener that will be called on state
- * changes. May be
- * called multiple consecutive times with the same value. Never
- * called
- * concurrently.
*/
- ExternalCaptureStateTracker(Consumer<Boolean> listener) {
- mListener = listener;
+ ExternalCaptureStateTracker() {
new Thread(this::run).start();
}
+
+ @Override
+ public boolean registerListener(@NonNull Listener listener) {
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ return mCaptureActive;
+ }
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
/**
* Routine for the background thread. Keeps trying to reconnect.
*/
@@ -74,7 +87,12 @@ class ExternalCaptureStateTracker {
*/
private void setCaptureState(boolean active) {
try {
- mListener.accept(active);
+ synchronized (mListeners) {
+ mCaptureActive = active;
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(active);
+ }
+ }
} catch (Exception e) {
Log.e(TAG, "Exception caught while setting capture state", e);
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
index b19e2ed1a91b..6da8a795991f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/HalFactory.java
@@ -16,16 +16,14 @@
package com.android.server.soundtrigger_middleware;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
-
/**
- * A factory for creating instances of {@link ISoundTriggerHw}.
+ * A factory for creating instances of {@link ISoundTriggerHal}.
*
* @hide
*/
public interface HalFactory {
/**
- * @return An instance of {@link ISoundTriggerHw}.
+ * @return An instance of {@link ISoundTriggerHal}.
*/
- ISoundTriggerHw create();
+ ISoundTriggerHal create();
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
new file mode 100644
index 000000000000..07d83cabcf83
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ICaptureStateNotifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+
+/**
+ * Allow registering listeners for tracking changes in audio capture state (when recording starts /
+ * stops). The client will be notified in a synchronized manner.
+ */
+interface ICaptureStateNotifier {
+ interface Listener {
+ void onCaptureStateChange(boolean state);
+ }
+
+ /**
+ * Register a listener for state change notifications. Returns the current capture state and
+ * any subsequent changes will be sent to the listener.
+ * @param listener The listener.
+ * @return The state at the time of registration.
+ */
+ boolean registerListener(@NonNull Listener listener);
+
+ /**
+ * Unregister a listener, previously registered with {@link #registerListener(Listener)}.
+ * Once this call returns, no more invocations of the listener will be made.
+ */
+ void unregisterListener(@NonNull Listener listener);
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
new file mode 100644
index 000000000000..aa85dd01405e
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHal.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
+
+/**
+ * This interface mimics the soundtrigger HAL interface, with a few key differences:
+ * <ul>
+ * <li>Generally, methods should not throw, except for the following cases:
+ * <ul>
+ * <li>Any unexpected HAL behavior is considered an internal HAL malfunction and should be thrown
+ * as a {@link HalException}, from any method.
+ * <li>A {@link RuntimeException} with a {@link android.os.DeadObjectException} cause represents
+ * a dead HAL process and may be thrown by any method.
+ * <li>Implementations of earlier versions of the interface may throw a
+ * {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED} for methods that
+ * have been introduced in later versions of the interface.
+ * <li>Certain methods are allowed to throw a {@link RecoverableException} with a
+ * {@link android.media.soundtrigger.Status#RESOURCE_CONTENTION} to indicate transient
+ * failures.
+ * </ul>
+ * <li>Some binder-specific details are hidden.
+ * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
+ * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
+ * them.
+ * </ul>
+ * For cases where the client wants to explicitly handle specific versions of the underlying driver
+ * interface, they may call {@link #interfaceDescriptor()}.
+ * <p>
+ * <b>Note to maintainers</b>: This class must always be kept in sync with the latest version,
+ * so that clients have access to the entire functionality without having to burden themselves with
+ * compatibility, as much as possible.
+ */
+interface ISoundTriggerHal {
+ /**
+ * @see ISoundTriggerHw#getProperties()
+ */
+ Properties getProperties();
+
+ /**
+ * @see ISoundTriggerHw#registerGlobalCallback(ISoundTriggerHwGlobalCallback)
+ */
+ void registerCallback(GlobalCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadSoundModel(android.media.soundtrigger.SoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadSoundModel(SoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#loadPhraseSoundModel(android.media.soundtrigger.PhraseSoundModel,
+ * ISoundTriggerHwCallback)
+ */
+ int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback);
+
+ /**
+ * @see ISoundTriggerHw#unloadSoundModel(int)
+ */
+ void unloadSoundModel(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#startRecognition(int, int, int, RecognitionConfig)
+ */
+ void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config);
+
+ /**
+ * @see ISoundTriggerHw#stopRecognition(int)
+ */
+ void stopRecognition(int modelHandle);
+
+ /**
+ * @see ISoundTriggerHw#forceRecognitionEvent(int)
+ */
+ void forceRecognitionEvent(int modelHandle);
+
+ /**
+ * @return null if not supported.
+ * @see ISoundTriggerHw#queryParameter(int, int)
+ */
+ ModelParameterRange queryParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#getParameter(int, int)
+ */
+ int getModelParameter(int modelHandle, int param);
+
+ /**
+ * @see ISoundTriggerHw#setParameter(int, int, int)
+ */
+ void setModelParameter(int modelHandle, int param, int value);
+
+ /**
+ * @see IBinder#getInterfaceDescriptor()
+ */
+ String interfaceDescriptor();
+
+ /**
+ * @see IBinder#linkToDeath(IBinder.DeathRecipient, int)
+ */
+ void linkToDeath(IBinder.DeathRecipient recipient);
+
+ /**
+ * @see IBinder#unlinkToDeath(IBinder.DeathRecipient, int)
+ */
+ void unlinkToDeath(IBinder.DeathRecipient recipient);
+
+ /*
+ * This is only useful for testing decorators and doesn't actually do anything with the real
+ * HAL. This method would block until all callbacks that were previously generated have been
+ * invoked. For most decorators, this merely flushes the delegate, but for delegates that may
+ * have additional buffers for callbacks this should flush them.
+ */
+ void flushCallbacks();
+
+ /**
+ * Kill and restart the HAL instance. This is typically a last resort for error recovery and may
+ * result in other related services being killed.
+ */
+ void reboot();
+
+ /**
+ * Called when this interface is guaranteed to no longer be used and can free up any resources
+ * used.
+ */
+ void detach();
+
+ /**
+ * Callback interface for model-related events.
+ */
+ interface ModelCallback {
+ /**
+ * @see ISoundTriggerHwCallback#recognitionCallback(int, RecognitionEvent)
+ */
+ void recognitionCallback(int modelHandle, RecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#phraseRecognitionCallback(int, PhraseRecognitionEvent)
+ */
+ void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event);
+
+ /**
+ * @see ISoundTriggerHwCallback#modelUnloaded(int)
+ */
+ void modelUnloaded(int modelHandle);
+ }
+
+ /**
+ * Callback interface for global events.
+ */
+ interface GlobalCallback {
+ /**
+ * @see ISoundTriggerHwGlobalCallback#onResourcesAvailable()
+ */
+ void onResourcesAvailable();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
deleted file mode 100644
index 8b434bd84363..000000000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerHw2.java
+++ /dev/null
@@ -1,158 +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.server.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hidl.base.V1_0.IBase;
-import android.os.IHwBinder;
-
-/**
- * This interface mimics android.hardware.soundtrigger.V2_x.ISoundTriggerHw and
- * android.hardware.soundtrigger.V2_x.ISoundTriggerHwCallback, with a few key differences:
- * <ul>
- * <li>Methods in the original interface generally have a status return value and potentially a
- * second return value which is the actual return value. This is reflected via a synchronous
- * callback, which is not very pleasant to work with. This interface replaces that pattern with
- * the convention that a HalException is thrown for non-OK status, and then we can use the
- * return value for the actual return value.
- * <li>This interface will always include all the methods from the latest 2.x version (and thus
- * from every 2.x version) interface, with the convention that unsupported methods throw a
- * {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
- * code.
- * <li>Cases where the original interface had multiple versions of a method representing the exact
- * thing, or there exists a trivial conversion between the new and old version, this interface
- * represents only the latest version, without any _version suffixes.
- * <li>Removes some of the obscure IBinder methods.
- * <li>No RemoteExceptions are specified. Some implementations of this interface may rethrow
- * RemoteExceptions as RuntimeExceptions, some can guarantee handling them somehow and never throw
- * them.
- * <li>soundModelCallback has been removed, since nobody cares about it. Implementations are free
- * to silently discard it.
- * </ul>
- * For cases where the client wants to explicitly handle specific versions of the underlying driver
- * interface, they may call {@link #interfaceDescriptor()}.
- * <p>
- * <b>Note to maintainers</b>: This class must always be kept in sync with the latest 2.x version,
- * so that clients have access to the entire functionality without having to burden themselves with
- * compatibility, as much as possible.
- */
-public interface ISoundTriggerHw2 {
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getPropertiesEx(
- * android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getPropertiesExCallback)
- */
- android.hardware.soundtrigger.V2_3.Properties getProperties();
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback)
- */
- int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#loadPhraseSoundModel_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback)
- */
- int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#unloadSoundModel(int)
- */
- void unloadSoundModel(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopRecognition(int)
- */
- void stopRecognition(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#stopAllRecognitions()
- */
- void stopAllRecognitions();
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#startRecognition_2_3(int,
- * android.hardware.soundtrigger.V2_3.RecognitionConfig,
- * android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback, int)
- */
- void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- SoundTriggerHw2Compat.Callback callback, int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_2.ISoundTriggerHw#getModelState(int)
- */
- void getModelState(int modelHandle);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#getParameter(int, int,
- * ISoundTriggerHw.getParameterCallback)
- */
- int getModelParameter(int modelHandle, int param);
-
- /**
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#setParameter(int, int, int)
- */
- void setModelParameter(int modelHandle, int param, int value);
-
- /**
- * @return null if not supported.
- * @see android.hardware.soundtrigger.V2_3.ISoundTriggerHw#queryParameter(int, int,
- * ISoundTriggerHw.queryParameterCallback)
- */
- ModelParameterRange queryParameter(int modelHandle, int param);
-
- /**
- * @see IHwBinder#linkToDeath(IHwBinder.DeathRecipient, long)
- */
- boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie);
-
- /**
- * @see IHwBinder#unlinkToDeath(IHwBinder.DeathRecipient)
- */
- boolean unlinkToDeath(IHwBinder.DeathRecipient recipient);
-
- /**
- * @see IBase#interfaceDescriptor()
- */
- String interfaceDescriptor() throws android.os.RemoteException;
-
- interface Callback {
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#recognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent,
- * int)
- */
- void recognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie);
-
- /**
- * @see android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback#phraseRecognitionCallback_2_1(android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent,
- * int)
- */
- void phraseRecognitionCallback(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie);
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
index a90053a23dea..60f89da8e4d3 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java
@@ -16,18 +16,18 @@
package com.android.server.soundtrigger_middleware;
-import android.media.ICaptureStateListener;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
/**
- * This interface unifies methods from ISoundTriggerMiddlewareService and ICaptureStateListener.
+ * This interface closely follows ISoundTriggerMiddlewareService with some subtle changes for
+ * convenience.
*
* The ISoundTriggerMiddlewareService have been modified to exclude identity information and the
* RemoteException signature, both of which are only relevant at the service boundary layer.
*/
-public interface ISoundTriggerMiddlewareInternal extends ICaptureStateListener {
+public interface ISoundTriggerMiddlewareInternal {
/**
* Query the available modules and their capabilities.
*/
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
index 416548d9bc5e..016e5c95a851 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/README.md
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -1,19 +1,145 @@
# Sound Trigger Middleware
-TODO: Add component description.
-## Notes about thread synchronization
+## Overview
+Sound Trigger Middleware is a system service that exposes sound trigger functionality (low-power
+detection of acoustic events) to applications and higher-level system service.
+
+It has the following roles:
+- Isolating the soundtrigger HAL from potentially untrusted clients.
+- Enforcing correct behavior of the clients.
+- Enforcing correct behavior of the HAL and attempting to recover from failures.
+- Enforcing permissions for using soundtrigger functionality.
+- Serializing access to the HAL.
+- Logging soundtrigger usage in a comprehensive and consistent manner.
+- Generating a dumpsys report including current state and history of operations.
+- Providing a standard interface regardless which version of the HAL is implemented and gracefully
+ degrading operation whenever necessary.
+
+## Structure
+
+The service implementation can be divided into three main layers:
+
+- The "bottom layer" is concerned with HAL compatibility - making all HAL versions look and behave
+ the same.
+- The "middle layer" is concerned with the business logic of the service.
+- The "top layer" is concerned with exposing this functionality as a System Service and integrating
+ with other parts of the system.
+
+### HAL Compatibility Layer
+
+This layer implements the `ISoundTriggerHal` interface, which is the version-agnostic representation
+of the sound trigger HAL driver. It has two main implementations, `SoundTriggerHw2Compat` and
+`SoundTriggerHw3Compat` responsible for adapting to V2.x and V3 HAL drivers, respectively, including
+supporting their respective minor-version differences.
+
+This layer also includes several `ISoundTriggerHal` decorators, such as `SoundTriggerHalWatchdog`
+that enforces deadlines on calls into the HAL, and `SoundTriggerHalEnforcer` which enforces that
+the HAL respects the expected protocol.
+
+The decorator-based design is an effective tool for separation of aspects and modularity, thus
+keeping classes relatively small and focused on one concern. It is also very effective for
+testability by following dependency injection principles.
+
+### Business Logic Layer
+
+This layer also uses a decorator-based design for separation of concerns. The main interface being
+decorated is `ISoundTriggerMiddlwareInternal`, which closely follows the external-facing AIDL
+interface, `ISoundTriggerMiddlewareService`.
+
+Each of the decorators serves a focused purpose: for example, `SoundTriggerMiddlwarePermission`
+deals with enforcing permissions required for the various methods, `SoundTriggerMiddlewareLogging`
+logs all API usage, `SoundTriggerMiddlewareValidation` enforces correct usage of the protocol and
+isolates client errors from internal server errors.
+
+At the bottom of this decorator stack is `SoundTriggerMiddlewareImpl` / `SoundTriggerModule`, which
+are the adapter between `ISoundTriggerHal` and `ISoundTriggerMiddlwareInternal`, introducing the
+notion of having separate client sessions sharing the same HAL.
+
+### Service Layer
+
+This layer ties everything together. It instantiates the actual system service and the decorator
+stack. It also provides concrete connections to the Audio service (for negotiating sessions shared
+between Audio and Sound Trigger and for notifications about audio recording) and to the various HAL
+factories.
+
+This is the only layer that makes strong assumptions about the environment instead of relying on
+abstractions.
+
+## Error Handling and Exception Conventions
+
+We follow conventions for usage of exceptions in the service, in order to correctly and consistently
+distinguish the following cases:
+
+1. The client has done something wrong.
+2. The service implementation has done something wrong.
+3. The HAL has done something wrong.
+4. Nobody has done anything wrong, but runtime conditions prevent an operation from being fulfilled
+ as intended.
+
+The `SoundTriggerMiddlewarePermission` class would reject any calls from unauthorized clients,
+responding with the appropriate exception.
+
+The `SoundTriggerMiddlewareValidation` class does much of this separation. By validating the
+client's data and state, it would throw a relevant `RuntimeException` exception to the client
+without passing the requests down to the lower layers. Once that is done, any exception thrown from
+the underlying implementation can be assumed to be not the client's fault. If caught, they will be
+classified according to the following rule:
+
+- If they are `RecoverableException`s, they represent category #4 above, and will be presented to
+ the client as `ServiceSpecificException`s with the same error code.
+- Otherwise, they are considered an internal error (including HAL malfunction) and will be
+ presented to the client as `ServiceSpecificException(Status.INTERNAL_ERROR)`.
+
+Internally, we would throw `RecoverableException` whenever appropriate. Whenever a HAL malfunctions,
+`SoundTriggerHalEnforcer` is responsible for rebooting it and throwing an exception. A HAL death is
+considered a valid failure mode, and thus result in `RecoverableException(Status.DEAD_OBJECT)`,
+which ends up as a `ServiceSpecificException(Status.DEAD_OBJECT)` on the client side.
+
+## Notes About Thread Synchronization
This component has some tricky thread synchronization considerations due to its layered design and
due to the fact that it is involved in both in-bound and out-bound calls from / to
-external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
-nesting locks. The order is:
-- `SoundTriggerMiddlewareValidation` lock.
-- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
+external components.
+
+The following mutexes need to be considered:
+- Typically, a one or more mutexes that exist in every layer of the sound trigger middleware stack
+ to serialize access to its internal state or to external components.
+- Audio Policy Service lock. This one is external - it should be assumed to be held whenever we're
inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
- within our calls into `AudioSessionProvider.acquireSession()`.
-- `SoundTriggerModule` lock.
-
-This dictates careful consideration of callbacks going from `SoundTriggerModule` to
-`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
-We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
-`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
-in `SoundTriggerMiddlewareValidation` and avoid the lock.
+ within our calls into `AudioSessionProvider.acquireSession()` /
+ `AudioSessionProvider.releaseSession()`.
+
+To avoid potential deadlocks, a strict locking order must be ensured whenever nesting locks. The
+order is:
+- Upper layers of the stack, starting from the top (i.e. may not attempt to acquire a higher-layer
+ mutex while a lower-layer mutex is being held) until `ISoundTriggerHw2`.
+- Audio Policy Service lock.
+- Lower layers of the stack, starting from `ISoundTriggerHw2` all the way down to the HAL.
+
+In order to enforce this order, some conventions are established around when it is safe for a module
+to call another module, while having its local mutex(es) held:
+- Most calls (see exceptions below) originating from SoundTriggerMiddlewareService simply propagate
+ down the decorator stack. It is legal to call into the next layer down while holding a local
+ mutex. It is illegal to invoke a callback with a local mutex held.
+- Callbacks propagate from the lower layers up to the upper layers. It is legal to hold a local
+ mutex within a callback, but **not** while call to an upper layer.
+- In order to be able to synchronize, despite the asynchronous nature of callbacks,
+ `stopRecognition()` and `unloadModel()` work differently. They guarantee that once they return,
+ the callbacks associated with them will no longer be called. This implies that they have to block
+ until any pending callbacks are done processing and since these callbacks are potentially holding
+ locks of higher-order mutexes, we must not be holding a local mutex while calling down. The proper
+ sequence for these calls is:
+ - Obtain the local lock if needed. Update/check local state as necessary.
+ - Call the respective method of the delegate ("downwards"). Once it returns, not more callbacks
+ related to this operation will be called.
+ - Obtain the local lock if needed. Update local state as necessary. Assume that state might have
+ changed while the lock has been released.
+ - Release the local lock.
+ - Invoke any synchronous callbacks if needed.
+- Calling from `SoundTriggerMiddlewareImpl` / `SoundTriggerModule` into the audio policy service via
+ `acquireSession()` / `releaseSession()` while holding the local lock is legal.
+- `setCaptureState()` calls, originating from Audio Policy Service, into the lower layers of the
+ stack may call into the HAL (specifically, they must invoke `stopRecognition()`, but must not
+ block on callbacks. For this reason, `SoundTriggerHw2ConcurrentCaptureHandler`, which is the
+ recipient of these calls, features a buffer and an additional thread, which allows the actual
+ stopping to be synchronous, as required, without having to block the call upon higher layers
+ processing the callbacks.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
new file mode 100644
index 000000000000..e3ce71962794
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of concurrent capture
+ * constraints, for HAL implementations older than V2.4 (later versions support this feature at the
+ * HAL level).
+ * <p>
+ * Decorating an instance with this class would result in all active recognitions being aborted as
+ * soon as capture state becomes active. This class ensures consistent handling of abortions coming
+ * from that HAL and abortions coming from concurrent capture, in that only one abort event will be
+ * delivered, irrespective of the relative timing of the two events.
+ * <p>
+ * There are some delicate thread-safety issues handled here:
+ * <ul>
+ * <li>When a model is stopped via stopRecognition(), we guarantee that by the time the call
+ * returns, there will be no more recognition events (including abort) delivered for this model.
+ * This implies synchronous stopping and blocking until all pending events have been delivered.
+ * <li>When a model is stopped via onCaptureStateChange(true), the stopping of the recognition at
+ * the HAL level must be synchronous, but the call must not block on the delivery of the
+ * callbacks, due to the risk of a deadlock: the onCaptureStateChange() calls are typically
+ * invoked with the audio policy mutex held, so must not call method which may attempt to lock
+ * higher-level mutexes. See README.md in this directory for further details.
+ * </ul>
+ * The way this behavior is achieved is by having an additional thread with an event queue, which
+ * joins together model events coming from the delegate module with abort events originating from
+ * this layer (as result of external capture).
+ */
+public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal,
+ ICaptureStateNotifier.Listener {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private GlobalCallback mGlobalCallback;
+
+ /**
+ * Information about a model that is currently loaded. This is needed in order to be able to
+ * send abort events to its designated callback.
+ */
+ private static class LoadedModel {
+ final int type;
+ final @NonNull ModelCallback callback;
+
+ private LoadedModel(int type, @NonNull ModelCallback callback) {
+ this.type = type;
+ this.callback = callback;
+ }
+ }
+
+ /**
+ * This map holds the model type for every model that is loaded.
+ */
+ private final @NonNull Map<Integer, LoadedModel> mLoadedModels = new ConcurrentHashMap<>();
+
+ /**
+ * A set of all models that are currently active.
+ * We use this in order to know which models to stop in case of external capture.
+ * Used as a lock to synchronize operations that effect activity.
+ */
+ private final @NonNull Set<Integer> mActiveModels = new HashSet<>();
+
+ /**
+ * Notifier for changes in capture state.
+ */
+ private final @NonNull ICaptureStateNotifier mNotifier;
+
+ /**
+ * Whether capture is active.
+ */
+ private boolean mCaptureState;
+
+ /**
+ * Since we're wrapping the death recipient, we need to keep a translation map for unlinking.
+ * Key is the client recipient, value is the wrapper.
+ */
+ private final @NonNull Map<IBinder.DeathRecipient, IBinder.DeathRecipient>
+ mDeathRecipientMap = new ConcurrentHashMap<>();
+
+ private final @NonNull CallbackThread mCallbackThread = new CallbackThread();
+
+ public SoundTriggerHalConcurrentCaptureHandler(
+ @NonNull ISoundTriggerHal delegate,
+ @NonNull ICaptureStateNotifier notifier) {
+ mDelegate = delegate;
+ mNotifier = notifier;
+ mCaptureState = mNotifier.registerListener(this);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ synchronized (mActiveModels) {
+ if (mCaptureState) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mActiveModels.add(modelHandle);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ synchronized (mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ mActiveModels.remove(modelHandle);
+ }
+ // Block until all previous events are delivered. Since this is potentially blocking on
+ // upward calls, it must be done outside the lock.
+ mCallbackThread.flush();
+ }
+
+ @Override
+ public void onCaptureStateChange(boolean active) {
+ synchronized (mActiveModels) {
+ if (active) {
+ // Abort all active models. This must be done as one transaction to the event
+ // thread, in order to be able to dedupe events before they are delivered.
+ try (SafeCloseable ignored = mCallbackThread.stallReader()) {
+ for (int modelHandle : mActiveModels) {
+ mDelegate.stopRecognition(modelHandle);
+ LoadedModel model = mLoadedModels.get(modelHandle);
+ // An abort event must be the last one for its model.
+ mCallbackThread.pushWithDedupe(modelHandle, true,
+ () -> notifyAbort(modelHandle, model));
+ }
+ }
+ } else {
+ mGlobalCallback.onResourcesAvailable();
+ }
+
+ mCaptureState = active;
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ int handle = mDelegate.loadSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.GENERIC, callback));
+ return handle;
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ int handle = mDelegate.loadPhraseSoundModel(soundModel, new CallbackWrapper(callback));
+ mLoadedModels.put(handle, new LoadedModel(SoundModelType.KEYPHRASE, callback));
+ return handle;
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ mLoadedModels.remove(modelHandle);
+ mDelegate.unloadSoundModel(modelHandle);
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = new GlobalCallback() {
+ @Override
+ public void onResourcesAvailable() {
+ mCallbackThread.push(callback::onResourcesAvailable);
+ }
+ };
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IBinder.DeathRecipient wrapper = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallbackThread.push(() -> recipient.binderDied());
+ }
+ };
+ mDelegate.linkToDeath(wrapper);
+ mDeathRecipientMap.put(recipient, wrapper);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(mDeathRecipientMap.remove(recipient));
+ }
+
+ private class CallbackWrapper implements ISoundTriggerHal.ModelCallback {
+ private final @NonNull ISoundTriggerHal.ModelCallback mDelegateCallback;
+
+ private CallbackWrapper(@NonNull ModelCallback delegateCallback) {
+ mDelegateCallback = delegateCallback;
+ }
+
+ @Override
+ public void recognitionCallback(int modelHandle, RecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.recognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int modelHandle, PhraseRecognitionEvent event) {
+ // A recognition event must be the last one for its model, unless it is a forced one
+ // (those leave the model active).
+ mCallbackThread.pushWithDedupe(modelHandle,
+ event.common.status != RecognitionStatus.FORCED,
+ () -> mDelegateCallback.phraseRecognitionCallback(modelHandle, event));
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ mCallbackThread.push(() -> mDelegateCallback.modelUnloaded(modelHandle));
+ }
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ mCallbackThread.flush();
+ }
+
+ /**
+ * This is a thread for asynchronous delivery of callback events, having the following features:
+ * <ul>
+ * <li>Events are processed on a separate thread than the thread that pushed them, in the order
+ * they were pushed.
+ * <li>Events can be deduped upon entry to the queue. This is achieved as follows:
+ * <ul>
+ * <li>Temporarily stall the reader via {@link #stallReader()}.
+ * <li>Within this scope, push as many events as needed via
+ * {@link #pushWithDedupe(int, boolean, Runnable)}.
+ * If an event with the same model handle as the one being pushed is already in the queue
+ * and has been marked as "lastForModel", the new event will be discarded before entering
+ * the queue.
+ * <li>Finally, un-stall the reader by existing the scope.
+ * <li>Events that do not require deduping can be pushed via {@link #push(Runnable)}.
+ * </ul>
+ * <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior
+ * to this call have been fully processed.
+ * </ul>
+ */
+ private static class CallbackThread {
+ private static class Entry {
+ final boolean lastForModel;
+ final int modelHandle;
+ final Runnable runnable;
+
+ private Entry(boolean lastForModel, int modelHandle, Runnable runnable) {
+ this.lastForModel = lastForModel;
+ this.modelHandle = modelHandle;
+ this.runnable = runnable;
+ }
+ }
+
+ private boolean mStallReader = false;
+ private final Queue<Entry> mList = new LinkedList<>();
+ private int mPushCount = 0;
+ private int mProcessedCount = 0;
+
+ /**
+ * Ctor. Starts the thread.
+ */
+ CallbackThread() {
+ new Thread(() -> {
+ try {
+ while (true) {
+ pop().run();
+ synchronized (mList) {
+ mProcessedCount++;
+ mList.notifyAll();
+ }
+ }
+ } catch (InterruptedException e) {
+ // If interrupted, exit.
+ }
+ }).start();
+ }
+
+ /**
+ * Push a new runnable to the queue, with no deduping.
+ *
+ * @param runnable The runnable to push.
+ */
+ void push(Runnable runnable) {
+ pushEntry(new Entry(false, 0, runnable), false);
+ }
+
+
+ /**
+ * Push a new runnable to the queue, with deduping.
+ * If an entry with the same model handle is already in the queue and was designated as
+ * last for model, this one will be discarded.
+ *
+ * @param modelHandle The model handle, used for deduping purposes.
+ * @param lastForModel If true, this entry will be considered the last one for this model
+ * and any subsequence calls for this handle (whether lastForModel or
+ * not) will be discarded while this entry is in the queue.
+ * @param runnable The runnable to push.
+ */
+ void pushWithDedupe(int modelHandle, boolean lastForModel, Runnable runnable) {
+ pushEntry(new Entry(lastForModel, modelHandle, runnable), true);
+ }
+
+ /**
+ * Block until every entry pushed prior to this call has been processed.
+ */
+ void flush() {
+ try {
+ synchronized (mList) {
+ int pushCount = mPushCount;
+ while (mProcessedCount != pushCount) {
+ mList.wait();
+ }
+ }
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ /**
+ * Creates a scope (using a try-with-resources block), within which events that are pushed
+ * remain queued and processed. This is useful in order to utilize deduping.
+ */
+ SafeCloseable stallReader() {
+ synchronized (mList) {
+ mStallReader = true;
+ return () -> {
+ synchronized (mList) {
+ mStallReader = false;
+ mList.notifyAll();
+ }
+ };
+ }
+ }
+
+ private void pushEntry(Entry entry, boolean dedupe) {
+ synchronized (mList) {
+ if (dedupe) {
+ for (Entry existing : mList) {
+ if (existing.lastForModel && existing.modelHandle == entry.modelHandle) {
+ return;
+ }
+ }
+ }
+ mList.add(entry);
+ mPushCount++;
+ mList.notifyAll();
+ }
+ }
+
+ private Runnable pop() throws InterruptedException {
+ synchronized (mList) {
+ while (mStallReader || mList.isEmpty()) {
+ mList.wait();
+ }
+ return mList.remove().runnable;
+ }
+ }
+ }
+
+ /** Notify the client that recognition has been aborted. */
+ private static void notifyAbort(int modelHandle, LoadedModel model) {
+ switch (model.type) {
+ case SoundModelType.GENERIC: {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = RecognitionStatus.ABORTED;
+ event.type = SoundModelType.GENERIC;
+ model.callback.recognitionCallback(modelHandle, event);
+ }
+ break;
+
+ case SoundModelType.KEYPHRASE: {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common.status = RecognitionStatus.ABORTED;
+ event.common.type = SoundModelType.KEYPHRASE;
+ model.callback.phraseRecognitionCallback(modelHandle, event);
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ mNotifier.unregisterListener(this);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ // All methods below do trivial delegation - no interesting logic.
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
new file mode 100644
index 000000000000..6870f4feb3de
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -0,0 +1,305 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.DeadObjectException;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
+ * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
+ * common HAL malfunctions, to help track them and assist in debugging.
+ *
+ * The class is thread-safe.
+ */
+public class SoundTriggerHalEnforcer implements ISoundTriggerHal {
+ private static final String TAG = "SoundTriggerHalEnforcer";
+
+ /** The state of a model. */
+ private enum ModelState {
+ /** Model is loaded, but inactive. */
+ INACTIVE,
+ /** Model is active. */
+ ACTIVE,
+ /** A request to stop is being made, which may or may not have been processed yet. */
+ PENDING_STOP,
+ }
+
+ private final ISoundTriggerHal mUnderlying;
+ private final Map<Integer, ModelState> mModelStates = new HashMap<>();
+
+ public SoundTriggerHalEnforcer(
+ ISoundTriggerHal underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mUnderlying.getProperties();
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mUnderlying.registerCallback(callback);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ synchronized (mModelStates) {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel,
+ new ModelCallbackEnforcer(callback));
+ mModelStates.put(handle, ModelState.INACTIVE);
+ return handle;
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting unload events for that model.
+ mUnderlying.unloadSoundModel(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model may have already been removed by a HAL callback, but the
+ // remove() method is a no-op in this case, so thus safe.
+ mModelStates.remove(modelHandle);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ // This call into the HAL may block on callback processing, thus must be done outside
+ // of the critical section. After this call returns we are guaranteed to no longer be
+ // getting stop events for that model.
+ synchronized (mModelStates) {
+ mModelStates.replace(modelHandle, ModelState.PENDING_STOP);
+ }
+ mUnderlying.stopRecognition(modelHandle);
+ synchronized (mModelStates) {
+ // At this point, the model might have been preemptively unloaded, but replace()
+ // do nothing when the entry does not exist, so all good.
+ mModelStates.replace(modelHandle, ModelState.INACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ synchronized (mModelStates) {
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ mModelStates.replace(modelHandle, ModelState.ACTIVE);
+ }
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mUnderlying.forceRecognitionEvent(modelHandle);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mUnderlying.queryParameter(modelHandle, param);
+ } catch (RuntimeException e) {
+ throw handleException(e);
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mUnderlying.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ private RuntimeException handleException(RuntimeException e) {
+ if (e instanceof RecoverableException) {
+ throw e;
+ }
+ if (e.getCause() instanceof DeadObjectException) {
+ // Server is dead, no need to reboot.
+ Log.e(TAG, "HAL died");
+ throw new RecoverableException(Status.DEAD_OBJECT);
+ }
+ Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ reboot();
+ throw e;
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
+ }
+
+ private class ModelCallbackEnforcer implements ModelCallback {
+ private final ModelCallback mUnderlying;
+
+ private ModelCallbackEnforcer(
+ ModelCallback underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ int status = event.status;
+
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.recognitionCallback(model, event);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ int status = event.common.status;
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(model);
+ if (state == null || state == ModelState.INACTIVE) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ reboot();
+ return;
+ }
+ if (status != RecognitionStatus.FORCED) {
+ mModelStates.replace(model, ModelState.INACTIVE);
+ }
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void modelUnloaded(int modelHandle) {
+ synchronized (mModelStates) {
+ ModelState state = mModelStates.get(modelHandle);
+ if (state == null) {
+ Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+ reboot();
+ return;
+ }
+
+ if (state == ModelState.ACTIVE) {
+ Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+ reboot();
+ return;
+ }
+ mModelStates.remove(modelHandle);
+ }
+ // Always invoke the delegate from outside the critical section.
+ mUnderlying.modelUnloaded(modelHandle);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.java
new file mode 100644
index 000000000000..7dd28e0b88f9
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalMaxModelLimiter.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.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+/**
+ * This is a decorator around ISoundTriggerHal, which implements enforcement of the maximum number
+ * of models supported by the HAL, for HAL implementations older than V2.4 that do not support
+ * rejection of model loading at the HAL layer.
+ * Since preemptive model unloading has been introduced in V2.4, it should never be used in
+ * conjunction with this class, hence we don't bother considering preemtive unloading when counting
+ * the number of currently loaded models.
+ */
+public class SoundTriggerHalMaxModelLimiter implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHal mDelegate;
+ private final int mMaxModels;
+
+ // This counter is used to enforce the maximum number of loaded models.
+ private int mNumLoadedModels = 0;
+
+ private GlobalCallback mGlobalCallback;
+
+ public SoundTriggerHalMaxModelLimiter(
+ ISoundTriggerHal delegate, int maxModels) {
+ mDelegate = delegate;
+ this.mMaxModels = maxModels;
+ }
+
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = callback;
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
+ synchronized (this) {
+ if (mNumLoadedModels == mMaxModels) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ int result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+ ++mNumLoadedModels;
+ return result;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ boolean wasAtMaxCapacity;
+ synchronized (this) {
+ wasAtMaxCapacity = mNumLoadedModels-- == mMaxModels;
+ }
+ try {
+ mDelegate.unloadSoundModel(modelHandle);
+ } catch (Exception e) {
+ synchronized (this) {
+ ++mNumLoadedModels;
+ }
+ throw e;
+ }
+ if (wasAtMaxCapacity) {
+ // It is legal to invoke callbacks from within unloadSoundModel().
+ // See README.md for details.
+ mGlobalCallback.onResourcesAvailable();
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 212f81f72b24..5fe06eef146d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Watchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -17,13 +17,12 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.os.IBinder;
import android.util.Log;
import java.util.Objects;
@@ -31,21 +30,19 @@ import java.util.Timer;
import java.util.TimerTask;
/**
- * An {@link ISoundTriggerHw2} decorator that would enforce deadlines on all calls and reboot the
+ * An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
* HAL whenever they expire.
*/
-public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
+public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
private static final long TIMEOUT_MS = 3000;
- private static final String TAG = "SoundTriggerHw2Watchdog";
+ private static final String TAG = "SoundTriggerHalWatchdog";
- private final @NonNull
- ISoundTriggerHw2 mUnderlying;
- private final @NonNull
- Timer mTimer;
+ private final @NonNull ISoundTriggerHal mUnderlying;
+ private final @NonNull Timer mTimer;
- public SoundTriggerHw2Watchdog(@NonNull ISoundTriggerHw2 underlying) {
+ public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
mUnderlying = Objects.requireNonNull(underlying);
- mTimer = new Timer("SoundTriggerHw2Watchdog");
+ mTimer = new Timer("SoundTriggerHalWatchdog");
}
@Override
@@ -56,54 +53,53 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
}
@Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
+ public void registerCallback(GlobalCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadSoundModel(soundModel, callback, cookie);
+ mUnderlying.registerCallback(callback);
}
}
@Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- return mUnderlying.loadPhraseSoundModel(soundModel, callback, cookie);
+ return mUnderlying.loadSoundModel(soundModel, callback);
}
}
@Override
- public void unloadSoundModel(int modelHandle) {
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel,
+ ModelCallback callback) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.unloadSoundModel(modelHandle);
+ return mUnderlying.loadPhraseSoundModel(soundModel, callback);
}
}
@Override
- public void stopRecognition(int modelHandle) {
+ public void unloadSoundModel(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.stopRecognition(modelHandle);
+ mUnderlying.unloadSoundModel(modelHandle);
}
}
@Override
- public void stopAllRecognitions() {
+ public void stopRecognition(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.stopAllRecognitions();
+ mUnderlying.stopRecognition(modelHandle);
}
}
@Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.startRecognition(modelHandle, config, callback, cookie);
+ mUnderlying.startRecognition(modelHandle, deviceHandle, ioHandle, config);
}
}
@Override
- public void getModelState(int modelHandle) {
+ public void forceRecognitionEvent(int modelHandle) {
try (Watchdog ignore = new Watchdog()) {
- mUnderlying.getModelState(modelHandle);
+ mUnderlying.forceRecognitionEvent(modelHandle);
}
}
@@ -129,23 +125,33 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.linkToDeath(recipient);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mUnderlying.unlinkToDeath(recipient);
}
@Override
- public String interfaceDescriptor() throws RemoteException {
+ public String interfaceDescriptor() {
return mUnderlying.interfaceDescriptor();
}
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
+ @Override
+ public void flushCallbacks() {
+ mUnderlying.flushCallbacks();
+ }
+
+ @Override
+ public void reboot() {
+ mUnderlying.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mUnderlying.detach();
}
private class Watchdog implements AutoCloseable {
@@ -160,7 +166,7 @@ public class SoundTriggerHw2Watchdog implements ISoundTriggerHw2 {
@Override
public void run() {
Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
- rebootHal();
+ reboot();
}
};
mTimer.schedule(mTask, TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index 2f087f46da86..7a1f775d6036 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -18,60 +18,116 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.Status;
+import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
+import android.system.OsConstants;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
- * An implementation of {@link ISoundTriggerHw2}, on top of any
+ * An implementation of {@link ISoundTriggerHal}, on top of any
* android.hardware.soundtrigger.V2_x.ISoundTriggerHw implementation. This class hides away some of
* the details involved with retaining backward compatibility and adapts to the more pleasant syntax
- * exposed by {@link ISoundTriggerHw2}, compared to the bare driver interface.
+ * exposed by {@link ISoundTriggerHal}, compared to the bare driver interface.
* <p>
* Exception handling:
* <ul>
* <li>All {@link RemoteException}s get rethrown as {@link RuntimeException}.
* <li>All HAL malfunctions get thrown as {@link HalException}.
* <li>All unsupported operations get thrown as {@link RecoverableException} with a
- * {@link android.media.soundtrigger_middleware.Status#OPERATION_NOT_SUPPORTED}
+ * {@link android.media.soundtrigger.Status#OPERATION_NOT_SUPPORTED}
* code.
* </ul>
*/
-final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
- private final @NonNull
- IHwBinder mBinder;
- private final @NonNull
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
- private final @Nullable
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
- private final @Nullable
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
- private final @Nullable
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
-
- public SoundTriggerHw2Compat(
- @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw underlying) {
- this(underlying.asBinder());
+final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+ private final @NonNull Runnable mRebootRunnable;
+ private final @NonNull IHwBinder mBinder;
+ private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
+ private @Nullable android.hardware.soundtrigger.V2_1.ISoundTriggerHw mUnderlying_2_1;
+ private @Nullable android.hardware.soundtrigger.V2_2.ISoundTriggerHw mUnderlying_2_2;
+ private @Nullable android.hardware.soundtrigger.V2_3.ISoundTriggerHw mUnderlying_2_3;
+ private @Nullable android.hardware.soundtrigger.V2_4.ISoundTriggerHw mUnderlying_2_4;
+
+ // HAL <=2.1 requires us to pass a callback argument to startRecognition. We will store the one
+ // passed on load and then pass it on start. We don't bother storing the callback on newer
+ // versions.
+ private final @NonNull ConcurrentMap<Integer, ModelCallback> mModelCallbacks =
+ new ConcurrentHashMap<>();
+
+ // A map from IBinder.DeathRecipient to IHwBinder.DeathRecipient for doing the mapping upon
+ // unlinking.
+ private final @NonNull Map<IBinder.DeathRecipient, IHwBinder.DeathRecipient>
+ mDeathRecipientMap = new HashMap<>();
+
+ // The properties are read at construction time and cached, since we need to use some of them
+ // to enforce constraints.
+ private final @NonNull Properties mProperties;
+
+ static ISoundTriggerHal create(
+ @NonNull ISoundTriggerHw underlying,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ return create(underlying.asBinder(), rebootRunnable, notifier);
}
- public SoundTriggerHw2Compat(IHwBinder binder) {
- Objects.requireNonNull(binder);
+ static ISoundTriggerHal create(@NonNull IHwBinder binder,
+ @NonNull Runnable rebootRunnable,
+ ICaptureStateNotifier notifier) {
+ SoundTriggerHw2Compat compat = new SoundTriggerHw2Compat(binder, rebootRunnable);
+ ISoundTriggerHal result = compat;
+ // Add max model limiter for versions <2.4.
+ if (compat.mUnderlying_2_4 == null) {
+ result = new SoundTriggerHalMaxModelLimiter(result,
+ compat.mProperties.maxSoundModels);
+ }
+ // Add concurrent capture handler for versions <2.4 which do not support concurrent capture.
+ if (compat.mUnderlying_2_4 == null && !compat.mProperties.concurrentCapture) {
+ result = new SoundTriggerHalConcurrentCaptureHandler(result, notifier);
+ }
+ return result;
+ }
- mBinder = binder;
+ private SoundTriggerHw2Compat(@NonNull IHwBinder binder, @NonNull Runnable rebootRunnable) {
+ mRebootRunnable = Objects.requireNonNull(rebootRunnable);
+ mBinder = Objects.requireNonNull(binder);
+ initUnderlying(binder);
+ mProperties = Objects.requireNonNull(getPropertiesInternal());
+ }
+ private void initUnderlying(IHwBinder binder) {
// We want to share the proxy instances rather than create a separate proxy for every
// version, so we go down the versions in descending order to find the latest one supported,
// and then simply up-cast it to obtain all the versions that are earlier.
+ // Attempt 2.4
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4 =
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.asInterface(binder);
+ if (as2_4 != null) {
+ mUnderlying_2_0 =
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = as2_4;
+ return;
+ }
+
// Attempt 2.3
android.hardware.soundtrigger.V2_3.ISoundTriggerHw as2_3 =
android.hardware.soundtrigger.V2_3.ISoundTriggerHw.asInterface(binder);
if (as2_3 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = as2_3;
+ mUnderlying_2_4 = null;
return;
}
@@ -80,7 +136,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_2.ISoundTriggerHw.asInterface(binder);
if (as2_2 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = mUnderlying_2_2 = as2_2;
- mUnderlying_2_3 = null;
+ mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -89,7 +145,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.asInterface(binder);
if (as2_1 != null) {
mUnderlying_2_0 = mUnderlying_2_1 = as2_1;
- mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -98,7 +154,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.asInterface(binder);
if (as2_0 != null) {
mUnderlying_2_0 = as2_0;
- mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = null;
+ mUnderlying_2_1 = mUnderlying_2_2 = mUnderlying_2_3 = mUnderlying_2_4 = null;
return;
}
@@ -111,12 +167,32 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
}
+ private static void handleHalStatusAllowBusy(int status, String methodName) {
+ if (status == -OsConstants.EBUSY) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ handleHalStatus(status, methodName);
+ }
+
+ @Override
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
@Override
- public android.hardware.soundtrigger.V2_3.Properties getProperties() {
+ public Properties getProperties() {
+ return mProperties;
+ }
+
+ private Properties getPropertiesInternal() {
try {
AtomicInteger retval = new AtomicInteger(-1);
- AtomicReference<android.hardware.soundtrigger.V2_3.Properties>
- properties =
+ AtomicReference<android.hardware.soundtrigger.V2_3.Properties> properties =
new AtomicReference<>();
try {
as2_3().getProperties_2_3(
@@ -129,30 +205,57 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return getProperties_2_0();
}
handleHalStatus(retval.get(), "getProperties_2_3");
- return properties.get();
+ return ConversionUtil.hidl2aidlProperties(properties.get());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public int loadSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie) {
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ try {
+ as2_4().registerGlobalCallback(new GlobalCallbackWrapper(callback));
+ } catch (NotSupported e) {
+ // In versions < 2.4 the events represented by this callback don't exist, we can
+ // safely ignore this.
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.SoundModel hidlModel =
+ ConversionUtil.aidl2hidlSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
+
try {
- as2_1().loadSoundModel_2_1(soundModel, new SoundTriggerCallback(callback), cookie,
+ as2_4().loadSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -160,24 +263,35 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public int loadPhraseSoundModel(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie) {
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
+ ConversionUtil.aidl2hidlPhraseSoundModel(soundModel);
try {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
try {
- as2_1().loadPhraseSoundModel_2_1(soundModel, new SoundTriggerCallback(callback),
- cookie,
+ as2_4().loadPhraseSoundModel_2_4(hidlModel, new ModelCallbackWrapper(callback),
(r, h) -> {
retval.set(r);
handle.set(h);
});
+ handleHalStatusAllowBusy(retval.get(), "loadPhraseSoundModel_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- return loadPhraseSoundModel_2_0(soundModel, callback, cookie);
+ // Fall-back to the 2.1 version:
+ try {
+ as2_1().loadPhraseSoundModel_2_1(hidlModel, new ModelCallbackWrapper(callback),
+ 0,
+ (r, h) -> {
+ retval.set(r);
+ handle.set(h);
+ });
+ handleHalStatus(retval.get(), "loadPhraseSoundModel_2_1");
+ mModelCallbacks.put(handle.get(), callback);
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ return loadPhraseSoundModel_2_0(hidlModel, callback);
+ }
}
- handleHalStatus(retval.get(), "loadSoundModel_2_1");
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -187,6 +301,8 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
@Override
public void unloadSoundModel(int modelHandle) {
try {
+ // Safe if key doesn't exist.
+ mModelCallbacks.remove(modelHandle);
int retval = as2_0().unloadSoundModel(modelHandle);
handleHalStatus(retval, "unloadSoundModel");
} catch (RemoteException e) {
@@ -206,26 +322,23 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public void stopAllRecognitions() {
- try {
- int retval = as2_0().stopAllRecognitions();
- handleHalStatus(retval, "stopAllRecognitions");
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- @Override
- public void startRecognition(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
+ ConversionUtil.aidl2hidlRecognitionConfig(config, deviceHandle, ioHandle);
try {
try {
- int retval = as2_3().startRecognition_2_3(modelHandle, config);
- handleHalStatus(retval, "startRecognition_2_3");
+ int retval = as2_4().startRecognition_2_4(modelHandle, hidlConfig);
+ handleHalStatusAllowBusy(retval, "startRecognition_2_4");
} catch (NotSupported e) {
- // Fall-back to the 2.0 version:
- startRecognition_2_1(modelHandle, config, callback, cookie);
+ // Fall-back to the 2.3 version:
+ try {
+ int retval = as2_3().startRecognition_2_3(modelHandle, hidlConfig);
+ handleHalStatus(retval, "startRecognition_2_3");
+ } catch (NotSupported ee) {
+ // Fall-back to the 2.0 version:
+ startRecognition_2_1(modelHandle, hidlConfig);
+ }
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -233,7 +346,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public void getModelState(int modelHandle) {
+ public void forceRecognitionEvent(int modelHandle) {
try {
int retval = as2_2().getModelState(modelHandle);
handleHalStatus(retval, "getModelState");
@@ -276,8 +389,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
@Override
- public android.hardware.soundtrigger.V2_3.ModelParameterRange queryParameter(int modelHandle,
- int param) {
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
AtomicInteger status = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_3.OptionalModelParameterRange>
optionalRange =
@@ -298,25 +410,36 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return (optionalRange.get().getDiscriminator()
== android.hardware.soundtrigger.V2_3.OptionalModelParameterRange.hidl_discriminator.range)
?
- optionalRange.get().range() : null;
+ ConversionUtil.hidl2aidlModelParameterRange(optionalRange.get().range()) : null;
}
@Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mBinder.linkToDeath(recipient, cookie);
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ IHwBinder.DeathRecipient wrapper = cookie -> recipient.binderDied();
+ mDeathRecipientMap.put(recipient, wrapper);
+ mBinder.linkToDeath(wrapper, 0);
}
@Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mBinder.unlinkToDeath(recipient);
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mBinder.unlinkToDeath(mDeathRecipientMap.remove(recipient));
}
@Override
- public String interfaceDescriptor() throws RemoteException {
- return as2_0().interfaceDescriptor();
+ public String interfaceDescriptor() {
+ try {
+ return as2_0().interfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
- private android.hardware.soundtrigger.V2_3.Properties getProperties_2_0()
+ @Override
+ public void flushCallbacks() {
+ // This is a no-op. Only implemented for decorators.
+ }
+
+ private Properties getProperties_2_0()
throws RemoteException {
AtomicInteger retval = new AtomicInteger(-1);
AtomicReference<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties>
@@ -328,12 +451,13 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
properties.set(p);
});
handleHalStatus(retval.get(), "getProperties");
- return Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get());
+ return ConversionUtil.hidl2aidlProperties(
+ Hw2CompatUtil.convertProperties_2_0_to_2_3(properties.get()));
}
private int loadSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model_2_0 =
@@ -341,17 +465,18 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie, (r, h) -> {
+ as2_0().loadSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0, (r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private int loadPhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel soundModel,
- Callback callback, int cookie)
+ ModelCallback callback)
throws RemoteException {
// Convert the soundModel to V2.0.
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model_2_0 =
@@ -359,28 +484,28 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
AtomicInteger retval = new AtomicInteger(-1);
AtomicInteger handle = new AtomicInteger(0);
- as2_0().loadPhraseSoundModel(model_2_0, new SoundTriggerCallback(callback), cookie,
+ as2_0().loadPhraseSoundModel(model_2_0, new ModelCallbackWrapper(callback), 0,
(r, h) -> {
retval.set(r);
handle.set(h);
});
handleHalStatus(retval.get(), "loadSoundModel");
+ mModelCallbacks.put(handle.get(), callback);
return handle.get();
}
private void startRecognition_2_1(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie) {
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config) {
try {
try {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config_2_1 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_1(config);
int retval = as2_1().startRecognition_2_1(modelHandle, config_2_1,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition_2_1");
} catch (NotSupported e) {
// Fall-back to the 2.0 version:
- startRecognition_2_0(modelHandle, config, callback, cookie);
+ startRecognition_2_0(modelHandle, config);
}
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -388,13 +513,12 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
private void startRecognition_2_0(int modelHandle,
- android.hardware.soundtrigger.V2_3.RecognitionConfig config,
- Callback callback, int cookie)
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config)
throws RemoteException {
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config_2_0 =
Hw2CompatUtil.convertRecognitionConfig_2_3_to_2_0(config);
int retval = as2_0().startRecognition(modelHandle, config_2_0,
- new SoundTriggerCallback(callback), cookie);
+ new ModelCallbackWrapper(mModelCallbacks.get(modelHandle)), 0);
handleHalStatus(retval, "startRecognition");
}
@@ -427,6 +551,14 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
return mUnderlying_2_3;
}
+ private @NonNull
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw as2_4() throws NotSupported {
+ if (mUnderlying_2_4 == null) {
+ throw new NotSupported("Underlying driver version < 2.4");
+ }
+ return mUnderlying_2_4;
+ }
+
/**
* A checked exception representing the requested interface version not being supported.
* At the public interface layer, use {@link #throwAsRecoverableException()} to propagate it to
@@ -448,28 +580,48 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
}
}
- private static class SoundTriggerCallback extends
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.Stub {
- private final @NonNull
- Callback mDelegate;
+ private static class GlobalCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
+
+ private GlobalCallbackWrapper(@NonNull GlobalCallback delegate) {
+ mDelegate = delegate;
+ }
- private SoundTriggerCallback(
- @NonNull Callback delegate) {
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackWrapper extends
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ private ModelCallbackWrapper(
+ @NonNull ModelCallback delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
@Override
+ public void modelUnloaded(int modelHandle) {
+ mDelegate.modelUnloaded(modelHandle);
+ }
+
+ @Override
public void recognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event,
int cookie) {
- mDelegate.recognitionCallback(event, cookie);
+ mDelegate.recognitionCallback(event.header.model,
+ ConversionUtil.hidl2aidlRecognitionEvent(event));
}
@Override
public void phraseRecognitionCallback_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent event,
int cookie) {
- mDelegate.phraseRecognitionCallback(event, cookie);
+ mDelegate.phraseRecognitionCallback(event.common.header.model,
+ ConversionUtil.hidl2aidlPhraseRecognitionEvent(event));
}
@Override
@@ -485,7 +637,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent event_2_1 =
Hw2CompatUtil.convertRecognitionEvent_2_0_to_2_1(event);
- mDelegate.recognitionCallback(event_2_1, cookie);
+ recognitionCallback_2_1(event_2_1, cookie);
}
@Override
@@ -494,7 +646,7 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHw2 {
int cookie) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent
event_2_1 = Hw2CompatUtil.convertPhraseRecognitionEvent_2_0_to_2_1(event);
- mDelegate.phraseRecognitionCallback(event_2_1, cookie);
+ phraseRecognitionCallback_2_1(event_2_1, cookie);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
deleted file mode 100644
index cf7460b306cd..000000000000
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ /dev/null
@@ -1,257 +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.soundtrigger_middleware;
-
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_3.ModelParameterRange;
-import android.hardware.soundtrigger.V2_3.Properties;
-import android.hardware.soundtrigger.V2_3.RecognitionConfig;
-import android.media.soundtrigger_middleware.Status;
-import android.os.DeadObjectException;
-import android.os.IHwBinder;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
- * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
- * common HAL malfunctions, to help track them and assist in debugging.
- *
- * The class is thread-safe.
- */
-public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
- static final String TAG = "SoundTriggerHw2Enforcer";
-
- final ISoundTriggerHw2 mUnderlying;
- Map<Integer, Boolean> mModelStates = new HashMap<>();
-
- public SoundTriggerHw2Enforcer(
- ISoundTriggerHw2 underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public Properties getProperties() {
- try {
- return mUnderlying.getProperties();
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
- int cookie) {
- try {
- int handle = mUnderlying.loadPhraseSoundModel(soundModel,
- new CallbackEnforcer(callback),
- cookie);
- synchronized (mModelStates) {
- mModelStates.put(handle, false);
- }
- return handle;
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void unloadSoundModel(int modelHandle) {
- try {
- mUnderlying.unloadSoundModel(modelHandle);
- synchronized (mModelStates) {
- mModelStates.remove(modelHandle);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopRecognition(int modelHandle) {
- try {
- mUnderlying.stopRecognition(modelHandle);
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, false);
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void stopAllRecognitions() {
- try {
- mUnderlying.stopAllRecognitions();
- synchronized (mModelStates) {
- for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
- entry.setValue(false);
- }
- }
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
- int cookie) {
- // It is possible that an event will be sent before the HAL returns from the
- // startRecognition call, thus it is important to set the state to active before the call.
- synchronized (mModelStates) {
- mModelStates.replace(modelHandle, true);
- }
- try {
- mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback),
- cookie);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void getModelState(int modelHandle) {
- try {
- mUnderlying.getModelState(modelHandle);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public int getModelParameter(int modelHandle, int param) {
- try {
- return mUnderlying.getModelParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public void setModelParameter(int modelHandle, int param, int value) {
- try {
- mUnderlying.setModelParameter(modelHandle, param, value);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public ModelParameterRange queryParameter(int modelHandle, int param) {
- try {
- return mUnderlying.queryParameter(modelHandle, param);
- } catch (RuntimeException e) {
- throw handleException(e);
- }
- }
-
- @Override
- public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
- return mUnderlying.linkToDeath(recipient, cookie);
- }
-
- @Override
- public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
- return mUnderlying.unlinkToDeath(recipient);
- }
-
- @Override
- public String interfaceDescriptor() throws RemoteException {
- return mUnderlying.interfaceDescriptor();
- }
-
- private static RuntimeException handleException(RuntimeException e) {
- if (e.getCause() instanceof DeadObjectException) {
- // Server is dead, no need to reboot.
- Log.e(TAG, "HAL died");
- throw new RecoverableException(Status.DEAD_OBJECT);
- }
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
- rebootHal();
- throw e;
- }
-
- private static void rebootHal() {
- // This property needs to be defined in an init.rc script and trigger a HAL reboot.
- SystemProperties.set("sys.audio.restart.hal", "1");
- }
-
- private class CallbackEnforcer implements Callback {
- private final Callback mUnderlying;
-
- private CallbackEnforcer(
- Callback underlying) {
- mUnderlying = underlying;
- }
-
- @Override
- public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
- int cookie) {
- int model = event.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.recognitionCallback(event, cookie);
- }
-
- @Override
- public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
- int cookie) {
- int model = event.common.header.model;
- synchronized (mModelStates) {
- if (!mModelStates.getOrDefault(model, false)) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
- rebootHal();
- return;
- }
- if (event.common.header.status
- != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
- mModelStates.replace(model, false);
- }
- }
- mUnderlying.phraseRecognitionCallback(event, cookie);
- }
- }
-}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
new file mode 100644
index 000000000000..f56475682be0
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger3.ISoundTriggerHw;
+import android.hardware.soundtrigger3.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+public class SoundTriggerHw3Compat implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHw mDriver;
+ private final @NonNull Runnable mRebootRunnable;
+
+ public SoundTriggerHw3Compat(@NonNull IBinder binder, @NonNull Runnable rebootRunnable) {
+ mDriver = android.hardware.soundtrigger3.ISoundTriggerHw.Stub.asInterface(binder);
+ mRebootRunnable = rebootRunnable;
+ }
+
+ @Override
+ public Properties getProperties() {
+ try {
+ return mDriver.getProperties();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ try {
+ mDriver.registerGlobalCallback(new GlobalCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ try {
+ return mDriver.loadPhraseSoundModel(soundModel, new ModelCallbackAdaper(callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ try {
+ mDriver.unloadSoundModel(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, int deviceHandle, int ioHandle,
+ RecognitionConfig config) {
+ try {
+ mDriver.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == Status.RESOURCE_CONTENTION) {
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ try {
+ mDriver.stopRecognition(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ try {
+ mDriver.forceRecognitionEvent(modelHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ try {
+ return mDriver.queryParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ try {
+ return mDriver.getParameter(modelHandle, param);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ try {
+ mDriver.setParameter(modelHandle, param, value);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ try {
+ return mDriver.asBinder().getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ try {
+ mDriver.asBinder().linkToDeath(recipient, 0);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDriver.asBinder().unlinkToDeath(recipient, 0);
+ }
+
+ @Override
+ public void flushCallbacks() {
+ // No-op.
+ }
+
+ @Override
+ public void reboot() {
+ mRebootRunnable.run();
+ }
+
+ @Override
+ public void detach() {
+ // No-op.
+ }
+
+ private static class GlobalCallbackAdaper extends ISoundTriggerHwGlobalCallback.Stub {
+ private final @NonNull GlobalCallback mDelegate;
+
+ public GlobalCallbackAdaper(@NonNull GlobalCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ mDelegate.onResourcesAvailable();
+ }
+ }
+
+ private static class ModelCallbackAdaper extends ISoundTriggerHwCallback.Stub {
+ private final @NonNull ModelCallback mDelegate;
+
+ public ModelCallbackAdaper(ModelCallback callback) {
+ mDelegate = callback;
+ }
+
+ @Override
+ public void modelUnloaded(int model) {
+ mDelegate.modelUnloaded(model);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(int model, PhraseRecognitionEvent event) {
+ mDelegate.phraseRecognitionCallback(model, event);
+ }
+
+ @Override
+ public void recognitionCallback(int model, RecognitionEvent event) {
+ mDelegate.recognitionCallback(model, event);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index d76b1bf71a1c..c8c0f3d00cbf 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -17,12 +17,9 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
-import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.os.IBinder;
import android.util.Log;
import java.util.ArrayList;
@@ -39,7 +36,7 @@ import java.util.List;
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status}
+ * {@link android.media.soundtrigger.Status}
* constants. Any other exception thrown should be regarded as a bug in the implementation or one
* of its dependencies (assuming correct usage).
* <li>The implementation is designed for testibility by featuring dependency injection (the
@@ -84,15 +81,15 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareIntern
@NonNull AudioSessionProvider audioSessionProvider) {
List<SoundTriggerModule> modules = new ArrayList<>(halFactories.length);
- for (int i = 0; i < halFactories.length; ++i) {
+ for (HalFactory halFactory : halFactories) {
try {
- modules.add(new SoundTriggerModule(halFactories[i], audioSessionProvider));
+ modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
} catch (Exception e) {
Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
- mModules = modules.toArray(new SoundTriggerModule[modules.size()]);
+ mModules = modules.toArray(new SoundTriggerModule[0]);
}
/**
@@ -122,18 +119,4 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareIntern
ISoundTriggerModule attach(int handle, @NonNull ISoundTriggerCallback callback) {
return mModules[handle].attach(callback);
}
-
- @Override
- public void setCaptureState(boolean active) {
- for (SoundTriggerModule module : mModules) {
- module.setExternalCaptureState(active);
- }
- }
-
- @Override
- public @NonNull
- IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2ef0759719fc..559e777a8c0e 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,16 +18,16 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.IBinder;
import android.os.RemoteException;
@@ -57,9 +57,10 @@ import java.util.Objects;
* }
* }
* </code></pre>
- * The actual handling of these events is then done inside of {@link #logReturnWithObject(Object,
- * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link
- * #logExceptionWithObject(Object, String, Exception, Object[])}.
+ * The actual handling of these events is then done inside of
+ * {@link #logReturnWithObject(Object, Identity, String, Object, Object[])},
+ * {@link #logVoidReturnWithObject(Object, Identity, String, Object[])} and {@link
+ * #logExceptionWithObject(Object, Identity, String, Exception, Object[])}.
*/
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareLogging";
@@ -96,23 +97,6 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- try {
- mDelegate.setCaptureState(active);
- logVoidReturn("setCaptureState", active);
- } catch (Exception e) {
- logException("setCaptureState", e, active);
- throw e;
- }
- }
-
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -298,10 +282,10 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onRecognition(modelHandle, event);
+ mCallbackDelegate.onRecognition(modelHandle, event, captureSession);
logVoidReturn("onRecognition", modelHandle, event);
} catch (Exception e) {
logException("onRecognition", e, modelHandle, event);
@@ -310,10 +294,11 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
try {
- mCallbackDelegate.onPhraseRecognition(modelHandle, event);
+ mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
logVoidReturn("onPhraseRecognition", modelHandle, event);
} catch (Exception e) {
logException("onPhraseRecognition", e, modelHandle, event);
@@ -322,12 +307,23 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ try {
+ mCallbackDelegate.onModelUnloaded(modelHandle);
+ logVoidReturn("onModelUnloaded", modelHandle);
+ } catch (Exception e) {
+ logException("onModelUnloaded", e, modelHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() throws RemoteException {
try {
- mCallbackDelegate.onRecognitionAvailabilityChange(available);
- logVoidReturn("onRecognitionAvailabilityChange", available);
+ mCallbackDelegate.onResourcesAvailable();
+ logVoidReturn("onResourcesAvailable");
} catch (Exception e) {
- logException("onRecognitionAvailabilityChange", e, available);
+ logException("onResourcesAvailable", e);
throw e;
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 2b03fe88a1ec..76927e15a0f7 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -24,20 +24,20 @@ import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.PermissionChecker;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.permission.PermissionUtil;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -90,24 +90,12 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
return wrapper.attach(mDelegate.attach(handle, wrapper.getCallbackWrapper()));
}
- @Override
- public void setCaptureState(boolean active) throws RemoteException {
- // This is an internal call. No permissions needed.
- mDelegate.setCaptureState(active);
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
return Objects.toString(mDelegate);
}
- @Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
/**
* Get the identity context, or throws an InternalServerError if it has not been established.
*
@@ -125,25 +113,16 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
* originator temporarily doesn't have the right permissions to use this service.
*/
private void enforcePermissionsForPreflight(@NonNull Identity identity) {
- enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO,
- /* allowSoftDenial= */ true);
- enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD,
- /* allowSoftDenial= */ true);
+ enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO);
+ enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD);
}
/**
* Throws a {@link SecurityException} iff the originator has permission to receive data.
*/
void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) {
- // SoundTrigger data is treated the same as Hotword-source audio. This should incur the
- // HOTWORD op instead of the RECORD_AUDIO op. The RECORD_AUDIO permission is still required,
- // and since this is a data delivery check, soft denials aren't accepted.
- enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO,
- /* allowSoftDenial= */ false);
- int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
- mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, identity.uid,
- identity.packageName, identity.attributionTag, reason);
-
+ enforcePermissionForDataDelivery(mContext, identity, RECORD_AUDIO,
+ reason);
enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD,
reason);
}
@@ -172,25 +151,20 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
/**
* Throws a {@link SecurityException} if originator permanently doesn't have the given
* permission.
+ * Soft (temporary) denials are considered OK for preflight purposes.
*
- * @param context A {@link Context}, used for permission checks.
- * @param identity The identity to check.
- * @param permission The identifier of the permission we want to check.
- * @param allowSoftDenial If true, the operation succeeds even for soft (temporary) denials.
+ * @param context A {@link Context}, used for permission checks.
+ * @param identity The identity to check.
+ * @param permission The identifier of the permission we want to check.
*/
- // TODO: Consider splitting up this method instead of using `allowSoftDenial`, to make it
- // clearer when soft denials are not allowed.
private static void enforcePermissionForPreflight(@NonNull Context context,
- @NonNull Identity identity, @NonNull String permission, boolean allowSoftDenial) {
+ @NonNull Identity identity, @NonNull String permission) {
final int status = PermissionUtil.checkPermissionForPreflight(context, identity,
permission);
switch (status) {
case PermissionChecker.PERMISSION_GRANTED:
- return;
case PermissionChecker.PERMISSION_SOFT_DENIED:
- if (allowSoftDenial) {
- return;
- } // else fall through
+ return;
case PermissionChecker.PERMISSION_HARD_DENIED:
throw new SecurityException(
String.format("Failed to obtain permission %s for identity %s", permission,
@@ -319,22 +293,28 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware
}
@Override
- public void onRecognition(int modelHandle, RecognitionEvent event)
+ public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger recognition.");
- mDelegate.onRecognition(modelHandle, event);
+ mDelegate.onRecognition(modelHandle, event, captureSession);
}
@Override
- public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event)
+ public void onPhraseRecognition(int modelHandle, PhraseRecognitionEvent event,
+ int captureSession)
throws RemoteException {
enforcePermissions("Sound trigger phrase recognition.");
- mDelegate.onPhraseRecognition(modelHandle, event);
+ mDelegate.onPhraseRecognition(modelHandle, event, captureSession);
+ }
+
+ @Override
+ public void onResourcesAvailable() throws RemoteException {
+ mDelegate.onResourcesAvailable();
}
@Override
- public void onRecognitionAvailabilityChange(boolean available) throws RemoteException {
- mDelegate.onRecognitionAvailabilityChange(available);
+ public void onModelUnloaded(int modelHandle) throws RemoteException {
+ mDelegate.onModelUnloaded(modelHandle);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index db7a575b08e2..1995e5497e55 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,7 +20,10 @@ import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.soundtrigger.V2_0.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.Identity;
import android.media.permission.PermissionUtil;
@@ -28,13 +31,8 @@ import android.media.permission.SafeCloseable;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.os.RemoteException;
-import android.util.Log;
import com.android.server.SystemService;
@@ -79,13 +77,6 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
@NonNull Context context) {
mDelegate = Objects.requireNonNull(delegate);
mContext = context;
- new ExternalCaptureStateTracker(active -> {
- try {
- mDelegate.setCaptureState(active);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- });
}
@Override
@@ -232,23 +223,15 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
@Override
public void onStart() {
- HalFactory[] factories = new HalFactory[]{() -> {
- try {
- Log.d(TAG, "Connecting to default ISoundTriggerHw");
- return ISoundTriggerHw.getService(true);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }};
+ HalFactory[] factories = new HalFactory[]{new DefaultHalFactory()};
publishBinderService(Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE,
- new SoundTriggerMiddlewareService(
- new SoundTriggerMiddlewareLogging(
- new SoundTriggerMiddlewarePermission(
- new SoundTriggerMiddlewareValidation(
- new SoundTriggerMiddlewareImpl(factories,
- new AudioSessionProviderImpl())),
- getContext())), getContext()));
+ new SoundTriggerMiddlewareService(new SoundTriggerMiddlewareLogging(
+ new SoundTriggerMiddlewarePermission(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl())),
+ getContext())), getContext()));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 458eae916d20..7b31946e0c9d 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -18,21 +18,21 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -46,10 +46,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
/**
* This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces correct usage by
@@ -89,7 +85,8 @@ import java.util.concurrent.atomic.AtomicReference;
* }
* </pre></code>
* Following this patterns ensures a consistent and rigorous handling of all aspects associated
- * with client-server separation.
+ * with client-server separation. Notable exceptions are stopRecognition() and unloadModel(), which
+ * follow slightly more complicated rules for synchronization (see README.md for details).
* <p>
* <b>Exception handling approach:</b><br>
* We make sure all client faults (argument and state validation) happen first, and
@@ -115,21 +112,18 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
private class ModuleState {
- final @NonNull SoundTriggerModuleProperties properties;
- Set<Session> sessions = new HashSet<>();
+ public @NonNull Properties properties;
+ public Set<Session> sessions = new HashSet<>();
- private ModuleState(@NonNull SoundTriggerModuleProperties properties) {
+ private ModuleState(@NonNull Properties properties) {
this.properties = properties;
}
}
- private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
-
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
private Map<Integer, ModuleState> mModules;
- public SoundTriggerMiddlewareValidation(
- @NonNull ISoundTriggerMiddlewareInternal delegate) {
+ public SoundTriggerMiddlewareValidation(@NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = delegate;
}
@@ -137,8 +131,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* Generic exception handling for exceptions thrown by the underlying implementation.
*
* Would throw any {@link RecoverableException} as a {@link ServiceSpecificException} (passed
- * by Binder to the caller) and <i>any other</i> exception as {@link InternalServerError}
- * (<b>not</b> passed by Binder to the caller).
+ * by Binder to the caller) and <i>any other</i> exception as a {@link ServiceSpecificException}
+ * with a {@link Status#INTERNAL_ERROR} code.
* <p>
* Typical usage:
* <code><pre>
@@ -149,8 +143,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* }
* </pre></code>
*/
- static @NonNull
- RuntimeException handleException(@NonNull Exception e) {
+ static @NonNull RuntimeException handleException(@NonNull Exception e) {
if (e instanceof RecoverableException) {
throw new ServiceSpecificException(((RecoverableException) e).errorCode,
e.getMessage());
@@ -161,8 +154,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public @NonNull
- SoundTriggerModuleDescriptor[] listModules() {
+ public @NonNull SoundTriggerModuleDescriptor[] listModules() {
// Input validation (always valid).
synchronized (this) {
@@ -186,6 +178,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
throw new RuntimeException(
"listModules must always return the same result.");
}
+ mModules.get(desc.handle).properties = desc.properties;
}
}
return result;
@@ -223,23 +216,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
}
- @Override
- public void setCaptureState(boolean active) {
- // This is an internal call. No permissions needed.
- //
- // Normally, we would acquire a lock here. However, we do not access any state here so it
- // is safe to not lock. This call is typically done from a different context than all the
- // other calls and may result in a deadlock if we lock here (between the audio server and
- // the system server).
- try {
- mDelegate.setCaptureState(active);
- } catch (Exception e) {
- throw handleException(e);
- } finally {
- mCaptureState.set(active);
- }
- }
-
// Override toString() in order to have the delegate's ID in it.
@Override
public String toString() {
@@ -247,17 +223,8 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public IBinder asBinder() {
- throw new UnsupportedOperationException(
- "This implementation is not inteded to be used directly with Binder.");
- }
-
- @Override
public void dump(PrintWriter pw) {
synchronized (this) {
- Boolean captureState = mCaptureState.get();
- pw.printf("Capture state is %s\n\n", captureState == null ? "uninitialized"
- : (captureState ? "active" : "inactive"));
if (mModules != null) {
for (int handle : mModules.keySet()) {
final ModuleState module = mModules.get(handle);
@@ -303,10 +270,17 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* delivered to the caller (most commonly, for permission reasons).
*/
INTERCEPTED,
+ /**
+ * Model has been preemptively unloaded by the HAL.
+ */
+ PREEMPTED,
}
/** Activity state. */
- private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
+ Activity activityState = Activity.LOADED;
+
+ /** Recognition config, used to start the model. */
+ RecognitionConfig config;
/** Human-readable description of the model. */
final String description;
@@ -316,9 +290,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
* parameter is supported. A null value means it is known to not be supported. A non-null
* value indicates the valid value range.
*/
- private Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
-
- private RecognitionConfig mConfig;
+ private final Map<Integer, ModelParameterRange> parameterSupport = new HashMap<>();
/**
* Check that the given parameter is known to be supported for this model.
@@ -353,32 +325,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
Preconditions.checkArgumentInRange(value, range.minInclusive, range.maxInclusive,
"value");
}
-
- /**
- * Update support state for the given parameter for this model.
- *
- * @param modelParam The parameter key.
- * @param range The parameter value range, or null if not supported.
- */
- void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
- parameterSupport.put(modelParam, range);
- }
-
- Activity getActivityState() {
- return Activity.values()[mActivityState.get()];
- }
-
- void setActivityState(Activity activity) {
- mActivityState.set(activity.ordinal());
- }
-
- void setRecognitionConfig(@NonNull RecognitionConfig config) {
- mConfig = config;
- }
-
- RecognitionConfig getRecognitionConfig() {
- return mConfig;
- }
}
/**
@@ -387,13 +333,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
*/
private class Session extends ISoundTriggerModule.Stub {
private ISoundTriggerModule mDelegate;
- // While generally all the fields of this class must be changed under a lock, an exception
- // is made for the specific case of changing a model state from ACTIVE to LOADED, which
- // may happen as result of a recognition callback. This would happen atomically and is
- // necessary in order to avoid deadlocks associated with locking from within callbacks
- // possibly originating from the audio server.
- private @NonNull
- ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
+ private final @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
private final CallbackWrapper mCallbackWrapper;
@@ -461,7 +401,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
@Override
public void unloadModel(int modelHandle) {
// Input validation (always valid).
-
synchronized (SoundTriggerMiddlewareValidation.this) {
// State validation.
if (mState == ModuleStatus.DETACHED) {
@@ -472,18 +411,24 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (modelState.activityState != ModelState.Activity.LOADED
+ && modelState.activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for unloading");
}
+ }
- // From here on, every exception isn't client's fault.
- try {
- mDelegate.unloadModel(modelHandle);
- mLoadedModels.remove(modelHandle);
- } catch (Exception e) {
- throw handleException(e);
- }
+ // From here on, every exception isn't client's fault.
+ try {
+ // Calling the delegate must be done outside the lock.
+ mDelegate.unloadModel(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ mLoadedModels.remove(modelHandle);
}
}
@@ -502,21 +447,20 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.getActivityState() != ModelState.Activity.LOADED) {
+ ModelState.Activity activityState = modelState.activityState;
+ // To avoid race conditions, we treat LOADED and PREEMPTED exactly the same.
+ if (activityState != ModelState.Activity.LOADED
+ && activityState != ModelState.Activity.PREEMPTED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for starting recognition");
}
// From here on, every exception isn't client's fault.
try {
- // Normally, we would set the state after the operation succeeds. However, since
- // the activity state may be reset outside of the lock, we set it here first,
- // and reset it in case of exception.
- modelState.setRecognitionConfig(config);
- modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
+ modelState.config = config;
+ modelState.activityState = ModelState.Activity.ACTIVE;
} catch (Exception e) {
- modelState.setActivityState(ModelState.Activity.LOADED);
throw handleException(e);
}
}
@@ -540,17 +484,36 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// From here on, every exception isn't client's fault.
try {
- // If the activity state is LOADED or INTERCEPTED, we skip delegating the
- // command, but still consider the call valid. In either case, the resulting
- // state is LOADED.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(modelHandle);
+ // If the activity state is INTERCEPTED, we skip delegating the command, but
+ // still consider the call valid.
+ if (modelState.activityState == ModelState.Activity.INTERCEPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ return;
}
- modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
}
}
+
+ // Calling the delegate's stop must be done without the lock.
+ try {
+ mDelegate.stopRecognition(modelHandle);
+ } catch (Exception e) {
+ throw handleException(e);
+ }
+
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState == null) {
+ // The model was unloaded while we let go of the lock.
+ return;
+ }
+
+ // After the call, the state is LOADED, unless it has been first preempted.
+ if (modelState.activityState != ModelState.Activity.PREEMPTED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
+ }
}
private void restartIfIntercepted(int modelHandle) {
@@ -561,12 +524,12 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
ModelState modelState = mLoadedModels.get(modelHandle);
if (modelState == null
- || modelState.getActivityState() != ModelState.Activity.INTERCEPTED) {
+ || modelState.activityState != ModelState.Activity.INTERCEPTED) {
return;
}
try {
- mDelegate.startRecognition(modelHandle, modelState.getRecognitionConfig());
- modelState.setActivityState(ModelState.Activity.ACTIVE);
+ mDelegate.startRecognition(modelHandle, modelState.config);
+ modelState.activityState = ModelState.Activity.ACTIVE;
Log.i(TAG, "Restarted intercepted model " + modelHandle);
} catch (Exception e) {
Log.i(TAG, "Failed to restart intercepted model " + modelHandle, e);
@@ -594,7 +557,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
// If the activity state is LOADED or INTERCEPTED, we skip delegating the
// command, but still consider the call valid.
- if (modelState.getActivityState() == ModelState.Activity.ACTIVE) {
+ if (modelState.activityState == ModelState.Activity.ACTIVE) {
mDelegate.forceRecognitionEvent(modelHandle);
}
} catch (Exception e) {
@@ -676,7 +639,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
try {
ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle,
modelParam);
- modelState.updateParameterSupport(modelParam, result);
+ modelState.parameterSupport.put(modelParam, result);
return result;
} catch (Exception e) {
throw handleException(e);
@@ -734,7 +697,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
- pw.print(entry.getValue().getActivityState().name());
+ pw.print(entry.getValue().activityState.name());
pw.print('\t');
pw.print(entry.getValue().description);
pw.println();
@@ -764,78 +727,89 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
}
@Override
- public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ public void onRecognition(int modelHandle, @NonNull RecognitionEvent event,
+ int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
}
+
+ // Calling the delegate callback must be done outside the lock.
try {
- mCallback.onRecognition(modelHandle, event);
+ mCallback.onRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- if (event.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
- // If we failed to deliver an actual event to the client, they would never
- // know to restart it whenever circumstances change. Thus, we restart it
- // here. We do this from a separate thread to avoid any race conditions.
- new Thread(() -> restartIfIntercepted(modelHandle)).start();
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ // If we failed to deliver an actual event to the client, they would
+ // never know to restart it whenever circumstances change. Thus, we
+ // restart it here. We do this from a separate thread to avoid any
+ // race conditions.
+ new Thread(() -> restartIfIntercepted(modelHandle)).start();
+ }
}
}
}
- }
- @Override
- public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
- // might be coming from the audio server (via setCaptureState()) while it is holding
- // a lock that is also acquired while loading / unloading models. Thus, we require a
- // strict locking order here, where obtaining our lock must always come first.
- // To avoid this problem, we use an atomic model activity state. There is a risk of the
- // model not being in the mLoadedModels map here, since it might have been stopped /
- // unloaded while the event was in flight.
- ModelState modelState = mLoadedModels.get(modelHandle);
- if (modelState != null) {
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.LOADED);
+ @Override
+ public void onPhraseRecognition(int modelHandle,
+ @NonNull PhraseRecognitionEvent event, int captureSession) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.LOADED;
+ }
}
+
+ // Calling the delegate callback must be done outside the lock.
try {
- mCallback.onPhraseRecognition(modelHandle, event);
+ mCallback.onPhraseRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- if (event.common.status != RecognitionStatus.FORCED) {
- modelState.setActivityState(ModelState.Activity.INTERCEPTED);
- // If we failed to deliver an actual event to the client, they would never
- // know to restart it whenever circumstances change. Thus, we restart it
- // here. We do this from a separate thread to avoid any race conditions.
- new Thread(() -> restartIfIntercepted(modelHandle)).start();
+ Log.w(TAG, "Client callback exception.", e);
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (event.common.status != RecognitionStatus.FORCED) {
+ modelState.activityState = ModelState.Activity.INTERCEPTED;
+ // If we failed to deliver an actual event to the client, they would
+ // never know to restart it whenever circumstances change. Thus, we
+ // restart it here. We do this from a separate thread to avoid any
+ // race conditions.
+ new Thread(() -> restartIfIntercepted(modelHandle)).start();
+ }
}
}
}
- }
- @Override
- public void onRecognitionAvailabilityChange(boolean available) {
- // Not locking to avoid deadlocks (not affecting any state).
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ @Override
+ public void onModelUnloaded(int modelHandle) {
+ synchronized (SoundTriggerMiddlewareValidation.this) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ modelState.activityState = ModelState.Activity.PREEMPTED;
+ }
+
+ // Calling the delegate callback must be done outside the lock.
+ try {
+ mCallback.onModelUnloaded(modelHandle);
+ } catch (Exception e) {
+ Log.w(TAG, "Client callback exception.", e);
+ }
+ }
+
+ @Override
+ public void onResourcesAvailable() {
+ // Not locking to avoid deadlocks (not affecting any state).
+ try {
+ mCallback.onResourcesAvailable();
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
+ }
}
- }
@Override
public void onModuleDied() {
@@ -860,8 +834,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware
// Gracefully stop all active recognitions and unload the models.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().getActivityState()
- == ModelState.Activity.ACTIVE) {
+ if (entry.getValue().activityState == ModelState.Activity.ACTIVE) {
mDelegate.stopRecognition(entry.getKey());
}
mDelegate.unloadModel(entry.getKey());
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 02d978dfdf99..f2111581d340 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -18,30 +18,24 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
-import android.hardware.soundtrigger.V2_2.ISoundTriggerHw;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.media.soundtrigger_middleware.Status;
import android.os.IBinder;
-import android.os.IHwBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -71,9 +65,8 @@ import java.util.Set;
* <li>There is no binder instance associated with this implementation. Do not call asBinder().
* <li>The implementation may throw a {@link RecoverableException} to indicate non-fatal,
* recoverable faults. The error code would one of the
- * {@link android.media.soundtrigger_middleware.Status} constants. Any other exception
- * thrown should be regarded as a bug in the implementation or one of its dependencies
- * (assuming correct usage).
+ * {@link android.media.soundtrigger.Status} constants. Any other exception thrown should be
+ * regarded as a bug in the implementation or one of its dependencies (assuming correct usage).
* <li>The implementation is designed for testability by featuring dependency injection (the
* underlying HAL driver instances are passed to the ctor) and by minimizing dependencies
* on Android runtime.
@@ -88,15 +81,13 @@ import java.util.Set;
*
* @hide
*/
-class SoundTriggerModule implements IHwBinder.DeathRecipient {
+class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.GlobalCallback {
static private final String TAG = "SoundTriggerModule";
- @NonNull private HalFactory mHalFactory;
- @NonNull private ISoundTriggerHw2 mHalService;
+ @NonNull private final HalFactory mHalFactory;
+ @NonNull private ISoundTriggerHal mHalService;
@NonNull private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider;
private final Set<Session> mActiveSessions = new HashSet<>();
- private int mNumLoadedModels = 0;
- private SoundTriggerModuleProperties mProperties;
- private boolean mRecognitionAvailable;
+ private Properties mProperties;
/**
* Ctor.
@@ -110,9 +101,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
mAudioSessionProvider = audioSessionProvider;
attachToHal();
- mProperties = ConversionUtil.hidl2aidlProperties(mHalService.getProperties());
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
}
/**
@@ -140,50 +128,13 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
* @return The properties structure.
*/
synchronized @NonNull
- SoundTriggerModuleProperties getProperties() {
+ Properties getProperties() {
return mProperties;
}
- /**
- * Notify the module that external capture has started / finished, using the same input device
- * used for recognition.
- * If the underlying driver does not support recognition while capturing, capture will be
- * aborted, and the recognition callback will receive and abort event. In addition, all active
- * clients will be notified of the change in state.
- *
- * @param active true iff external capture is active.
- */
- void setExternalCaptureState(boolean active) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
- synchronized (this) {
- if (mProperties.concurrentCapture) {
- // If we support concurrent capture, we don't care about any of this.
- return;
- }
- mRecognitionAvailable = !active;
- if (!mRecognitionAvailable) {
- // Our module does not support recognition while a capture is active -
- // need to abort all active recognitions.
- for (Session session : mActiveSessions) {
- session.abortActiveRecognitions(callbacks);
- }
- }
- }
- for (Runnable callback : callbacks) {
- callback.run();
- }
- for (Session session : mActiveSessions) {
- session.notifyRecognitionAvailability();
- }
- }
-
@Override
- public void serviceDied(long cookie) {
- Log.w(TAG, String.format("Underlying HAL driver died."));
+ public void binderDied() {
+ Log.w(TAG, "Underlying HAL driver died.");
List<ISoundTriggerCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<>(mActiveSessions.size());
@@ -207,20 +158,19 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
* Resets the transient state of this object.
*/
private void reset() {
+ mHalService.detach();
attachToHal();
- // We conservatively assume that external capture is active until explicitly told otherwise.
- mRecognitionAvailable = mProperties.concurrentCapture;
- mNumLoadedModels = 0;
}
/**
* Attached to the HAL service via factory.
*/
private void attachToHal() {
- mHalService = new SoundTriggerHw2Enforcer(
- new SoundTriggerHw2Watchdog(
- new SoundTriggerHw2Compat(mHalFactory.create())));
- mHalService.linkToDeath(this, 0);
+ mHalService = new SoundTriggerHalEnforcer(
+ new SoundTriggerHalWatchdog(mHalFactory.create()));
+ mHalService.linkToDeath(this);
+ mHalService.registerCallback(this);
+ mProperties = mHalService.getProperties();
}
/**
@@ -232,6 +182,25 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
mActiveSessions.remove(session);
}
+ @Override
+ public void onResourcesAvailable() {
+ List<ISoundTriggerCallback> callbacks;
+ synchronized (this) {
+ callbacks = new ArrayList<>(mActiveSessions.size());
+ for (Session session : mActiveSessions) {
+ callbacks.add(session.mCallback);
+ }
+ }
+ // Trigger the callbacks outside of the lock to avoid deadlocks.
+ for (ISoundTriggerCallback callback : callbacks) {
+ try {
+ callback.onResourcesAvailable();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
+
/** State of a single sound model. */
private enum ModelState {
/** Initial state, until load() is called. */
@@ -249,7 +218,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*/
private class Session implements ISoundTriggerModule {
private ISoundTriggerCallback mCallback;
- private Map<Integer, Model> mLoadedModels = new HashMap<>();
+ private final Map<Integer, Model> mLoadedModels = new HashMap<>();
/**
* Ctor.
@@ -258,7 +227,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*/
private Session(@NonNull ISoundTriggerCallback callback) {
mCallback = callback;
- notifyRecognitionAvailability();
}
@Override
@@ -274,95 +242,65 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Override
public int loadModel(@NonNull SoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+ try {
checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
Model loadedModel = new Model();
- int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
- return result;
- }
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
- try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ return loadedModel.load(model, audioSession);
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- throw e;
}
}
@Override
public int loadPhraseModel(@NonNull PhraseSoundModel model) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
- mAudioSessionProvider.acquireSession();
-
- try {
- synchronized (SoundTriggerModule.this) {
+ synchronized (SoundTriggerModule.this) {
+ SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+ mAudioSessionProvider.acquireSession();
+ try {
checkValid();
- if (mNumLoadedModels == mProperties.maxSoundModels) {
- throw new RecoverableException(Status.RESOURCE_CONTENTION,
- "Maximum number of models loaded.");
- }
Model loadedModel = new Model();
int result = loadedModel.load(model, audioSession);
- ++mNumLoadedModels;
Log.d(TAG, String.format("loadPhraseModel()->%d", result));
return result;
+ } catch (Exception e) {
+ // We must do this outside the lock, to avoid possible deadlocks with the remote
+ // process that provides the audio sessions, which may also be calling into us.
+ try {
+ mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+ } catch (Exception ee) {
+ Log.e(TAG, "Failed to release session.", ee);
+ }
+ throw e;
}
- } catch (Exception e) {
- // We must do this outside the lock, to avoid possible deadlocks with the remote
- // process that provides the audio sessions, which may also be calling into us.
- try {
- mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
- } catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
- }
- throw e;
}
}
@Override
public void unloadModel(int modelHandle) {
- int sessionId;
synchronized (SoundTriggerModule.this) {
+ int sessionId;
checkValid();
sessionId = mLoadedModels.get(modelHandle).unload();
- --mNumLoadedModels;
+ mAudioSessionProvider.releaseSession(sessionId);
}
-
- // We must do this outside the lock, to avoid possible deadlocks with the remote process
- // that provides the audio sessions, which may also be calling into us.
- mAudioSessionProvider.releaseSession(sessionId);
}
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
- // We should never invoke callbacks while holding the lock, since this may deadlock with
- // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
- // the lock, but invoke them after releasing it.
- List<Runnable> callbacks = new LinkedList<>();
-
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
- }
-
- for (Runnable callback : callbacks) {
- callback.run();
+ mLoadedModels.get(modelHandle).startRecognition(config);
}
}
@@ -407,27 +345,6 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
/**
- * Abort all currently active recognitions.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
- for (Model model : mLoadedModels.values()) {
- model.abortActiveRecognition(callbacks);
- }
- }
-
- private void notifyRecognitionAvailability() {
- try {
- mCallback.onRecognitionAvailabilityChange(mRecognitionAvailable);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
- }
-
- /**
* The underlying module HAL is dead.
* @return The client callback that needs to be invoked to notify the client.
*/
@@ -455,10 +372,9 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
*
* All model-based operations are delegated to this class and implemented here.
*/
- private class Model implements ISoundTriggerHw2.Callback {
+ private class Model implements ISoundTriggerHal.ModelCallback {
public int mHandle;
private ModelState mState = ModelState.INIT;
- private int mModelType = SoundModelType.UNKNOWN;
private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession;
private @NonNull
@@ -473,11 +389,8 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
private int load(@NonNull SoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.type;
mSession = audioSession;
- ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
-
- mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
return mHandle;
@@ -485,12 +398,8 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
private int load(@NonNull PhraseSoundModel model,
SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
- mModelType = model.common.type;
mSession = audioSession;
- ISoundTriggerHw.PhraseSoundModel hidlModel =
- ConversionUtil.aidl2hidlPhraseSoundModel(model);
-
- mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
+ mHandle = mHalService.loadPhraseSoundModel(model, this);
setState(ModelState.LOADED);
mLoadedModels.put(mHandle, this);
@@ -507,18 +416,9 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
return mSession.mSessionHandle;
}
- private void startRecognition(@NonNull RecognitionConfig config,
- @NonNull List<Runnable> callbacks) {
- if (!mRecognitionAvailable) {
- // Recognition is unavailable - send an abort event immediately.
- callbacks.add(this::notifyAbort);
- return;
- }
- android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
- ConversionUtil.aidl2hidlRecognitionConfig(config);
- hidlConfig.base.header.captureDevice = mSession.mDeviceHandle;
- hidlConfig.base.header.captureHandle = mSession.mIoHandle;
- mHalService.startRecognition(mHandle, hidlConfig, this, 0);
+ private void startRecognition(@NonNull RecognitionConfig config) {
+ mHalService.startRecognition(mHandle, mSession.mDeviceHandle,
+ mSession.mIoHandle, config);
setState(ModelState.ACTIVE);
}
@@ -537,7 +437,7 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
// This call is idempotent in order to avoid races.
return;
}
- mHalService.getModelState(mHandle);
+ mHalService.forceRecognitionEvent(mHandle);
}
@@ -553,78 +453,47 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
@Nullable
private ModelParameterRange queryModelParameterSupport(int modelParam) {
- return ConversionUtil.hidl2aidlModelParameterRange(
- mHalService.queryParameter(mHandle,
- ConversionUtil.aidl2hidlModelParameter(modelParam)));
+ return mHalService.queryParameter(mHandle, modelParam);
}
- /**
- * Abort the recognition, if active.
- * @param callbacks Will be appended with a list of callbacks that need to be invoked
- * after this method returns, without holding the module lock.
- */
- private void abortActiveRecognition(List<Runnable> callbacks) {
- // If we're inactive, do nothing.
- if (getState() != ModelState.ACTIVE) {
- return;
+ @Override
+ public void recognitionCallback(int modelHandle,
+ @NonNull RecognitionEvent recognitionEvent) {
+ ISoundTriggerCallback callback;
+ synchronized (SoundTriggerModule.this) {
+ if (recognitionEvent.status != RecognitionStatus.FORCED) {
+ setState(ModelState.LOADED);
+ }
+ callback = mCallback;
}
- // Stop recognition.
- stopRecognition();
-
- // Notify the client that recognition has been aborted.
- callbacks.add(this::notifyAbort);
- }
-
- /** Notify the client that recognition has been aborted. */
- private void notifyAbort() {
+ // The callback must be invoked outside of the lock.
try {
- switch (mModelType) {
- case SoundModelType.GENERIC: {
- android.media.soundtrigger_middleware.RecognitionEvent event =
- newEmptyRecognitionEvent();
- event.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.type = SoundModelType.GENERIC;
- mCallback.onRecognition(mHandle, event);
- }
- break;
-
- case SoundModelType.KEYPHRASE: {
- android.media.soundtrigger_middleware.PhraseRecognitionEvent event =
- newEmptyPhraseRecognitionEvent();
- event.common.status =
- android.media.soundtrigger_middleware.RecognitionStatus.ABORTED;
- event.common.type = SoundModelType.KEYPHRASE;
- mCallback.onPhraseRecognition(mHandle, event);
- }
- break;
-
- default:
- Log.e(TAG, "Unknown model type: " + mModelType);
-
+ if (callback != null) {
+ callback.onRecognition(mHandle, recognitionEvent, mSession.mSessionHandle);
}
} catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
}
}
@Override
- public void recognitionCallback(
- @NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
- int cookie) {
- RecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
- aidlEvent.captureSession = mSession.mSessionHandle;
+ public void phraseRecognitionCallback(int modelHandle,
+ @NonNull PhraseRecognitionEvent phraseRecognitionEvent) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.status != RecognitionStatus.FORCED) {
+ if (phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
+ callback = mCallback;
}
+
// The callback must be invoked outside of the lock.
try {
- mCallback.onRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ mCallback.onPhraseRecognition(mHandle, phraseRecognitionEvent,
+ mSession.mSessionHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -632,22 +501,17 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
@Override
- public void phraseRecognitionCallback(
- @NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
- int cookie) {
- PhraseRecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
- aidlEvent.common.captureSession = mSession.mSessionHandle;
-
+ public void modelUnloaded(int modelHandle) {
+ ISoundTriggerCallback callback;
synchronized (SoundTriggerModule.this) {
- if (aidlEvent.common.status != RecognitionStatus.FORCED) {
- setState(ModelState.LOADED);
- }
+ callback = mCallback;
}
// The callback must be invoked outside of the lock.
try {
- mCallback.onPhraseRecognition(mHandle, aidlEvent);
+ if (callback != null) {
+ callback.onModelUnloaded(modelHandle);
+ }
} catch (RemoteException e) {
// We're not expecting any exceptions here.
throw e.rethrowAsRuntimeException();
@@ -655,33 +519,4 @@ class SoundTriggerModule implements IHwBinder.DeathRecipient {
}
}
}
-
- /**
- * Creates a default-initialized recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static RecognitionEvent newEmptyRecognitionEvent() {
- RecognitionEvent result = new RecognitionEvent();
- result.data = new byte[0];
- return result;
- }
-
- /**
- * Creates a default-initialized phrase recognition event.
- *
- * Non-nullable object fields are default constructed.
- * Non-nullable array fields are initialized to 0 length.
- *
- * @return The event.
- */
- private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() {
- PhraseRecognitionEvent result = new PhraseRecognitionEvent();
- result.common = newEmptyRecognitionEvent();
- result.phraseExtras = new PhraseRecognitionExtra[0];
- return result;
- }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index e05c468186ed..4d5212114907 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -17,21 +17,18 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.Nullable;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
-
-import com.android.internal.util.Preconditions;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
import java.util.Objects;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Utilities for asserting the validity of various data types used by this module.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a436e6b3787b..cdab91bbaef8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -132,7 +133,7 @@ public interface StatusBarManagerInternal {
/** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen);
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName);
/** @see com.android.internal.statusbar.IStatusBar#showTransient */
void showTransient(int displayId, @InternalInsetsType int[] types);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3a7e13b8d872..ff7e903e80d8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -60,6 +60,7 @@ import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -526,13 +527,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
@Override
public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities,
+ String packageName) {
getUiState(displayId).setBarAttributes(appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
if (mBar != null) {
try {
mBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- navbarColorManagedByIme, behavior, isFullscreen);
+ navbarColorManagedByIme, behavior, requestedVisibilities, packageName);
} catch (RemoteException ex) { }
}
}
@@ -1103,13 +1105,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return state;
}
- private class UiState {
+ private static class UiState {
private @Appearance int mAppearance = 0;
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
- private ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
+ private final ArraySet<Integer> mTransientBarTypes = new ArraySet<>();
private boolean mNavbarColorManagedByIme = false;
private @Behavior int mBehavior;
- private boolean mFullscreen = false;
+ private InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
+ private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
private int mImeWindowVis = 0;
@@ -1119,12 +1122,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
private void setBarAttributes(@Appearance int appearance,
AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
- @Behavior int behavior, boolean isFullscreen) {
+ @Behavior int behavior, InsetsVisibilities requestedVisibilities,
+ String packageName) {
mAppearance = appearance;
mAppearanceRegions = appearanceRegions;
mNavbarColorManagedByIme = navbarColorManagedByIme;
mBehavior = behavior;
- mFullscreen = isFullscreen;
+ mRequestedVisibilities = requestedVisibilities;
+ mPackageName = packageName;
}
private void showTransient(@InternalInsetsType int[] types) {
@@ -1244,8 +1249,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
- state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen,
- transientBarTypes);
+ state.mNavbarColorManagedByIme, state.mBehavior, state.mRequestedVisibilities,
+ state.mPackageName, transientBarTypes);
}
}
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 8d79a81fe11e..0b34eb8234f0 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -368,16 +368,12 @@ public final class StorageSessionController {
mExternalStorageServicePackageName = provider.applicationInfo.packageName;
mExternalStorageServiceAppId = UserHandle.getAppId(provider.applicationInfo.uid);
- Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
- intent.setPackage(mExternalStorageServicePackageName);
- ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+ ServiceInfo serviceInfo = resolveExternalStorageServiceAsUser(UserHandle.USER_SYSTEM);
+ if (serviceInfo == null) {
throw new ExternalStorageServiceException(
"No valid ExternalStorageService component found");
}
- ServiceInfo serviceInfo = resolveInfo.serviceInfo;
ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
if (!Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE
.equals(serviceInfo.permission)) {
@@ -490,4 +486,24 @@ public final class StorageSessionController {
private boolean shouldHandle(@Nullable VolumeInfo vol) {
return !mIsResetting && (vol == null || isSupportedVolume(vol));
}
+
+ /**
+ * Returns {@code true} if the given user supports external storage,
+ * {@code false} otherwise.
+ */
+ public boolean supportsExternalStorage(int userId) {
+ return resolveExternalStorageServiceAsUser(userId) != null;
+ }
+
+ private ServiceInfo resolveExternalStorageServiceAsUser(int userId) {
+ Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE);
+ intent.setPackage(mExternalStorageServicePackageName);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveServiceAsUser(intent,
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId);
+ if (resolveInfo == null) {
+ return null;
+ }
+
+ return resolveInfo.serviceInfo;
+ }
}
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index fe977f8b3921..ac2d76e89d76 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -28,8 +28,10 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ServiceConfigAccessor;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
@@ -63,6 +65,7 @@ public final class ServerFlags {
KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE,
KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
})
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.SOURCE)
@interface DeviceConfigKey {}
@@ -72,40 +75,39 @@ public final class ServerFlags {
* {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link
* ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED =
+ public static final @DeviceConfigKey String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED =
"location_time_zone_detection_feature_supported";
/**
* The key for the server flag that can override the device config for whether the primary
* location time zone provider is enabled, disabled, or (for testing) in simulation mode.
*/
- @DeviceConfigKey
- public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+ public static final @DeviceConfigKey String
+ KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
"primary_location_time_zone_provider_mode_override";
/**
* The key for the server flag that can override the device config for whether the secondary
* location time zone provider is enabled or disabled, or (for testing) in simulation mode.
*/
- @DeviceConfigKey
- public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+ public static final @DeviceConfigKey String
+ KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
"secondary_location_time_zone_provider_mode_override";
/**
* The key for the minimum delay after location time zone detection has been enabled before the
* location time zone manager can report it is uncertain about the time zone.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
+ public static final @DeviceConfigKey String
+ KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
"location_time_zone_detection_uncertainty_delay_millis";
/**
* The key for the timeout passed to a location time zone provider that tells it how long it has
* to provide an explicit first suggestion without being declared uncertain.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
+ public static final @DeviceConfigKey String
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
"ltpz_init_timeout_millis";
/**
@@ -113,8 +115,8 @@ public final class ServerFlags {
* #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
* manager before the location time zone provider will actually be declared uncertain.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
+ public static final @DeviceConfigKey String
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
"ltpz_init_timeout_fuzz_millis";
/**
@@ -123,16 +125,16 @@ public final class ServerFlags {
* disable the feature by turning off the master location switch, or by disabling automatic time
* zone detection.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE =
+ public static final @DeviceConfigKey String
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE =
"location_time_zone_detection_setting_enabled_override";
/**
* The key for the default value used to determine whether location time zone detection is
* enabled when the user hasn't explicitly set it yet.
*/
- @DeviceConfigKey
- public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
+ public static final @DeviceConfigKey String
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT =
"location_time_zone_detection_setting_enabled_default";
/**
@@ -140,16 +142,14 @@ public final class ServerFlags {
* of strings that will be passed to {@link TimeDetectorStrategy#stringToOrigin(String)}.
* All values must be recognized or the override value will be ignored.
*/
- @DeviceConfigKey
- public static final String KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE =
+ public static final @DeviceConfigKey String KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE =
"time_detector_origin_priorities_override";
/**
* The key to override the time detector lower bound configuration. The values is the number of
* milliseconds since the beginning of the Unix epoch.
*/
- @DeviceConfigKey
- public static final String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE =
+ public static final @DeviceConfigKey String KEY_TIME_DETECTOR_LOWER_BOUND_MILLIS_OVERRIDE =
"time_detector_lower_bound_millis_override";
@GuardedBy("mListeners")
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
new file mode 100644
index 000000000000..f1bfea760792
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.timedetector."
+ }
+ ]
+ },
+ {
+ "name": "CtsTimeTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index ff5060e6618e..acabb6e393a8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -53,24 +53,19 @@ public interface TimeDetectorStrategy extends Dumpable {
@interface Origin {}
/** Used when a time value originated from a telephony signal. */
- @Origin
- int ORIGIN_TELEPHONY = 1;
+ @Origin int ORIGIN_TELEPHONY = 1;
/** Used when a time value originated from a user / manual settings. */
- @Origin
- int ORIGIN_MANUAL = 2;
+ @Origin int ORIGIN_MANUAL = 2;
/** Used when a time value originated from a network signal. */
- @Origin
- int ORIGIN_NETWORK = 3;
+ @Origin int ORIGIN_NETWORK = 3;
/** Used when a time value originated from a gnss signal. */
- @Origin
- int ORIGIN_GNSS = 4;
+ @Origin int ORIGIN_GNSS = 4;
/** Used when a time value originated from an externally specified signal. */
- @Origin
- int ORIGIN_EXTERNAL = 5;
+ @Origin int ORIGIN_EXTERNAL = 5;
/** Processes the suggested time from telephony sources. */
void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 357c23222658..e751a7bf2c26 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -42,6 +42,7 @@ import com.android.server.timezonedetector.ArrayMapWithHistory;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;
@@ -321,12 +322,16 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
ipw.println("mEnvironment.isAutoTimeDetectionEnabled()="
+ mEnvironment.isAutoTimeDetectionEnabled());
- ipw.println("mEnvironment.elapsedRealtimeMillis()=" + mEnvironment.elapsedRealtimeMillis());
- ipw.println("mEnvironment.systemClockMillis()=" + mEnvironment.systemClockMillis());
+ long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+ ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n",
+ Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis);
+ long systemClockMillis = mEnvironment.systemClockMillis();
+ ipw.printf("mEnvironment.systemClockMillis()=%s (%s)\n",
+ Instant.ofEpochMilli(systemClockMillis), systemClockMillis);
ipw.println("mEnvironment.systemClockUpdateThresholdMillis()="
+ mEnvironment.systemClockUpdateThresholdMillis());
Instant autoTimeLowerBound = mEnvironment.autoTimeLowerBound();
- ipw.printf("mEnvironment.autoTimeLowerBound()=%s(%s)\n",
+ ipw.printf("mEnvironment.autoTimeLowerBound()=%s (%s)\n",
autoTimeLowerBound, autoTimeLowerBound.toEpochMilli());
String priorities =
Arrays.stream(mEnvironment.autoOriginPriorities())
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
index 27b50d892144..9eb6a458e8e3 100644
--- a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -26,6 +26,10 @@ import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
import android.util.proto.ProtoOutputStream;
import java.io.ByteArrayOutputStream;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -44,14 +48,13 @@ public final class MetricsTimeZoneDetectorState {
@IntDef(prefix = "DETECTION_MODE_",
value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@interface DetectionMode {};
- @DetectionMode
- public static final int DETECTION_MODE_MANUAL = 0;
- @DetectionMode
- public static final int DETECTION_MODE_GEO = 1;
- @DetectionMode
- public static final int DETECTION_MODE_TELEPHONY = 2;
+ public static final @DetectionMode int DETECTION_MODE_MANUAL = 0;
+ public static final @DetectionMode int DETECTION_MODE_GEO = 1;
+ public static final @DetectionMode int DETECTION_MODE_TELEPHONY = 2;
@NonNull
private final ConfigurationInternal mConfigurationInternal;
@@ -132,8 +135,7 @@ public final class MetricsTimeZoneDetectorState {
* Returns the detection mode the device is currently using, which can be influenced by various
* things besides the user's setting.
*/
- @DetectionMode
- public int getDetectionMode() {
+ public @DetectionMode int getDetectionMode() {
if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) {
return DETECTION_MODE_MANUAL;
} else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) {
diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
index 50875308db7d..dfefb8fc8335 100644
--- a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
+++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
@@ -33,7 +33,7 @@ import java.util.function.Function;
class OrdinalGenerator<T> {
private final ArraySet<T> mKnownIds = new ArraySet<>();
- private final @NonNull Function<T, T> mCanonicalizationFunction;
+ @NonNull private final Function<T, T> mCanonicalizationFunction;
OrdinalGenerator(@NonNull Function<T, T> canonicalizationFunction) {
mCanonicalizationFunction = Objects.requireNonNull(canonicalizationFunction);
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 4a1d9c40a11c..58281306c085 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -46,19 +46,12 @@ import java.util.Set;
public final class ServiceConfigAccessor {
@StringDef(prefix = "PROVIDER_MODE_",
- value = { PROVIDER_MODE_SIMULATED, PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
+ value = { PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
@Retention(RetentionPolicy.SOURCE)
- @Target(ElementType.TYPE_USE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@interface ProviderMode {}
/**
- * The "simulated" provider mode.
- * For use with {@link #getPrimaryLocationTimeZoneProviderMode()} and {@link
- * #getSecondaryLocationTimeZoneProviderMode()}.
- */
- public static final @ProviderMode String PROVIDER_MODE_SIMULATED = "simulated";
-
- /**
* The "disabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
* and {@link #getSecondaryLocationTimeZoneProviderMode()}.
*/
@@ -86,11 +79,9 @@ public final class ServiceConfigAccessor {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
}));
- // 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 1 minute when primary provider is fully implemented
+ private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
- Duration.ofSeconds(20);
+ Duration.ofMinutes(1);
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
private static final Object SLOCK = new Object();
@@ -112,6 +103,47 @@ public final class ServiceConfigAccessor {
@NonNull private final ServerFlags mServerFlags;
+ /**
+ * The mode to use for the primary location time zone provider in a test. Setting this
+ * disables some permission checks.
+ * This state is volatile: it is never written to storage / never survives a reboot. This is to
+ * avoid a test provider accidentally being left configured on a device.
+ * See also {@link #resetVolatileTestConfig()}.
+ */
+ @Nullable
+ private String mTestPrimaryLocationTimeZoneProviderMode;
+
+ /**
+ * The package name to use for the primary location time zone provider in a test.
+ * This state is volatile: it is never written to storage / never survives a reboot. This is to
+ * avoid a test provider accidentally being left configured on a device.
+ * See also {@link #resetVolatileTestConfig()}.
+ */
+ @Nullable
+ private String mTestPrimaryLocationTimeZoneProviderPackageName;
+
+ /**
+ * See {@link #mTestPrimaryLocationTimeZoneProviderMode}; this is the equivalent for the
+ * secondary provider.
+ */
+ @Nullable
+ private String mTestSecondaryLocationTimeZoneProviderMode;
+
+ /**
+ * See {@link #mTestPrimaryLocationTimeZoneProviderPackageName}; this is the equivalent for the
+ * secondary provider.
+ */
+ @Nullable
+ private String mTestSecondaryLocationTimeZoneProviderPackageName;
+
+ /**
+ * Whether to record state changes for tests.
+ * This state is volatile: it is never written to storage / never survives a reboot. This is to
+ * avoid a test state accidentally being left configured on a device.
+ * See also {@link #resetVolatileTestConfig()}.
+ */
+ private boolean mRecordProviderStateChanges;
+
private ServiceConfigAccessor(@NonNull Context context) {
mContext = Objects.requireNonNull(context);
@@ -202,23 +234,98 @@ public final class ServiceConfigAccessor {
defaultEnabled);
}
+ /** Returns the package name of the app hosting the primary location time zone provider. */
@NonNull
public String getPrimaryLocationTimeZoneProviderPackageName() {
+ if (mTestPrimaryLocationTimeZoneProviderMode != null) {
+ // In test mode: use the test setting value.
+ return mTestPrimaryLocationTimeZoneProviderPackageName;
+ }
return mContext.getResources().getString(
R.string.config_primaryLocationTimeZoneProviderPackageName);
}
+ /**
+ * Sets the package name of the app hosting the primary location time zone provider for tests.
+ * Setting a {@code null} value means the provider is to be disabled.
+ * The values are reset with {@link #resetVolatileTestConfig()}.
+ */
+ public void setTestPrimaryLocationTimeZoneProviderPackageName(
+ @Nullable String testPrimaryLocationTimeZoneProviderPackageName) {
+ mTestPrimaryLocationTimeZoneProviderPackageName =
+ testPrimaryLocationTimeZoneProviderPackageName;
+ mTestPrimaryLocationTimeZoneProviderMode =
+ mTestPrimaryLocationTimeZoneProviderPackageName == null
+ ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
+ }
+
+ /**
+ * Returns {@code true} if the usual permission checks are to be bypassed for the primary
+ * provider. Returns {@code true} only if {@link
+ * #setTestPrimaryLocationTimeZoneProviderPackageName} has been called.
+ */
+ public boolean isTestPrimaryLocationTimeZoneProvider() {
+ return mTestPrimaryLocationTimeZoneProviderMode != null;
+ }
+
+ /** Returns the package name of the app hosting the secondary location time zone provider. */
@NonNull
public String getSecondaryLocationTimeZoneProviderPackageName() {
+ if (mTestSecondaryLocationTimeZoneProviderMode != null) {
+ // In test mode: use the test setting value.
+ return mTestSecondaryLocationTimeZoneProviderPackageName;
+ }
return mContext.getResources().getString(
R.string.config_secondaryLocationTimeZoneProviderPackageName);
}
/**
- * Returns {@code true} if the primary location time zone provider can be used.
+ * Sets the package name of the app hosting the secondary location time zone provider for tests.
+ * Setting a {@code null} value means the provider is to be disabled.
+ * The values are reset with {@link #resetVolatileTestConfig()}.
+ */
+ public void setTestSecondaryLocationTimeZoneProviderPackageName(
+ @Nullable String testSecondaryLocationTimeZoneProviderPackageName) {
+ mTestSecondaryLocationTimeZoneProviderPackageName =
+ testSecondaryLocationTimeZoneProviderPackageName;
+ mTestSecondaryLocationTimeZoneProviderMode =
+ mTestSecondaryLocationTimeZoneProviderPackageName == null
+ ? PROVIDER_MODE_DISABLED : PROVIDER_MODE_ENABLED;
+ }
+
+ /**
+ * Returns {@code true} if the usual permission checks are to be bypassed for the secondary
+ * provider. Returns {@code true} only if {@link
+ * #setTestSecondaryLocationTimeZoneProviderPackageName} has been called.
+ */
+ public boolean isTestSecondaryLocationTimeZoneProvider() {
+ return mTestSecondaryLocationTimeZoneProviderMode != null;
+ }
+
+ /**
+ * Enables/disables the state recording mode for tests. The value is reset with {@link
+ * #resetVolatileTestConfig()}.
+ */
+ public void setRecordProviderStateChanges(boolean enabled) {
+ mRecordProviderStateChanges = enabled;
+ }
+
+ /**
+ * Returns {@code true} if providers are expected to record their state changes for tests.
+ */
+ public boolean getRecordProviderStateChanges() {
+ return mRecordProviderStateChanges;
+ }
+
+ /**
+ * Returns the mode for the primary location time zone provider.
*/
@NonNull
public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
+ if (mTestPrimaryLocationTimeZoneProviderMode != null) {
+ // In test mode: use the test setting value.
+ return mTestPrimaryLocationTimeZoneProviderMode;
+ }
return mServerFlags.getOptionalString(
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
.orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
@@ -232,9 +339,13 @@ public final class ServiceConfigAccessor {
}
/**
- * Returns the mode for the secondary location time zone provider can be used.
+ * Returns the mode for the secondary location time zone provider.
*/
public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
+ if (mTestSecondaryLocationTimeZoneProviderMode != null) {
+ // In test mode: use the test setting value.
+ return mTestSecondaryLocationTimeZoneProviderMode;
+ }
return mServerFlags.getOptionalString(
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
.orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
@@ -300,6 +411,15 @@ public final class ServiceConfigAccessor {
DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
}
+ /** Clears all in-memory test config. */
+ public void resetVolatileTestConfig() {
+ mTestPrimaryLocationTimeZoneProviderPackageName = null;
+ mTestPrimaryLocationTimeZoneProviderMode = null;
+ mTestSecondaryLocationTimeZoneProviderPackageName = null;
+ mTestSecondaryLocationTimeZoneProviderMode = null;
+ mRecordProviderStateChanges = false;
+ }
+
private boolean getConfigBoolean(int providerEnabledConfigId) {
Resources resources = mContext.getResources();
return resources.getBoolean(providerEnabledConfigId);
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 91e172c9d153..b1ae626bf78d 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -7,6 +7,14 @@
"include-filter": "com.android.server.timezonedetector."
}
]
+ },
+ {
+ "name": "CtsTimeTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index f054c5756e20..9d340e4fde86 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -26,7 +26,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.RemoteCallback;
import android.util.IndentingPrintWriter;
import java.time.Duration;
@@ -45,9 +44,10 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
@NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
- @NonNull LocationTimeZoneProviderProxy proxy) {
+ @NonNull LocationTimeZoneProviderProxy proxy,
+ boolean recordStateChanges) {
super(providerMetricsLogger, threadingDomain, providerName,
- new ZoneInfoDbTimeZoneProviderEventPreProcessor());
+ new ZoneInfoDbTimeZoneProviderEventPreProcessor(), recordStateChanges);
mProxy = Objects.requireNonNull(proxy);
}
@@ -67,7 +67,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
@Override
public void onProviderUnbound() {
- handleProviderLost("onProviderUnbound()");
+ handleTemporaryFailure("onProviderUnbound()");
}
});
}
@@ -77,50 +77,6 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
mProxy.destroy();
}
- private void handleProviderLost(String reason) {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- ProviderState currentState = mCurrentState.get();
- switch (currentState.stateEnum) {
- case PROVIDER_STATE_STARTED_INITIALIZING:
- case PROVIDER_STATE_STARTED_UNCERTAIN:
- case PROVIDER_STATE_STARTED_CERTAIN: {
- // Losing a remote provider is treated as becoming uncertain.
- String msg = "handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState;
- debugLog(msg);
- // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
- // event == null
- ProviderState newState = currentState.newState(
- PROVIDER_STATE_STARTED_UNCERTAIN, null,
- currentState.currentUserConfiguration, msg);
- setCurrentState(newState, true);
- break;
- }
- case PROVIDER_STATE_STOPPED: {
- debugLog("handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState
- + ": No state change required, provider is stopped.");
- break;
- }
- case PROVIDER_STATE_PERM_FAILED:
- case PROVIDER_STATE_DESTROYED: {
- debugLog("handleProviderLost reason=" + reason
- + ", mProviderName=" + mProviderName
- + ", currentState=" + currentState
- + ": No state change required, provider is terminated.");
- break;
- }
- default: {
- throw new IllegalStateException("Unknown currentState=" + currentState);
- }
- }
- }
- }
-
private void handleOnProviderBound() {
mThreadingDomain.assertCurrentThread();
@@ -169,16 +125,6 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
mProxy.setRequest(request);
}
- /**
- * Passes the supplied test command to the current proxy.
- */
- @Override
- void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
- mThreadingDomain.assertCurrentThread();
-
- mProxy.handleTestCommand(testCommand, callback);
- }
-
@Override
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index d2190fdc5bea..76ef958baf22 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -33,7 +33,6 @@ import android.annotation.DurationMillisLong;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.RemoteCallback;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
@@ -166,12 +165,6 @@ class ControllerImpl extends LocationTimeZoneProviderController {
stopProviders();
mPrimaryProvider.destroy();
mSecondaryProvider.destroy();
-
- // If the controller has made a "certain" suggestion, it should make an uncertain
- // suggestion to cancel it.
- if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- makeSuggestion(createUncertainSuggestion("Controller is destroyed"));
- }
}
}
@@ -182,6 +175,16 @@ class ControllerImpl extends LocationTimeZoneProviderController {
// By definition, if both providers are stopped, the controller is uncertain.
cancelUncertaintyTimeout();
+
+ // If a previous "certain" suggestion has been made, then a new "uncertain"
+ // suggestion must now be made to indicate the controller {does not / no longer has}
+ // an opinion and will not be sending further updates (until at least the providers are
+ // re-started).
+ if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+ GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+ "Providers are stopping");
+ makeSuggestion(suggestion);
+ }
}
@GuardedBy("mSharedLock")
@@ -275,21 +278,6 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
} else {
stopProviders();
-
- // There can be an uncertainty timeout set if the controller most recently received
- // an uncertain event. This is a no-op if there isn't a timeout set.
- cancelUncertaintyTimeout();
-
- // If a previous "certain" suggestion has been made, then a new "uncertain"
- // suggestion must now be made to indicate the controller {does not / no longer has}
- // an opinion and will not be sending further updates (until at least the config
- // changes again and providers are re-started).
- if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
- "Provider is stopped:"
- + " primary=" + mPrimaryProvider.getCurrentState());
- makeSuggestion(suggestion);
- }
}
}
@@ -601,41 +589,14 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
/**
- * Passes a test command to the specified provider. If the provider name does not match a
- * known provider, then the command is logged and discarded.
- */
- void handleProviderTestCommand(
- @IntRange(from = 0, to = 1) int providerIndex, @NonNull TestCommand testCommand,
- @Nullable RemoteCallback callback) {
- mThreadingDomain.assertCurrentThread();
-
- LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerIndex);
- if (targetProvider == null) {
- warnLog("Unable to process test command:"
- + " providerIndex=" + providerIndex + ", testCommand=" + testCommand);
- return;
- }
-
- synchronized (mSharedLock) {
- try {
- targetProvider.handleTestCommand(testCommand, callback);
- } catch (Exception e) {
- warnLog("Unable to process test command:"
- + " providerIndex=" + providerIndex + ", testCommand=" + testCommand, e);
- }
- }
- }
-
- /**
- * Sets whether the controller should record provider state changes for later dumping via
- * {@link #getStateForTests()}.
+ * Clears recorded provider state changes (for use during tests).
*/
- void setProviderStateRecordingEnabled(boolean enabled) {
+ void clearRecordedProviderStates() {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
- mPrimaryProvider.setStateChangeRecordingEnabled(enabled);
- mSecondaryProvider.setStateChangeRecordingEnabled(enabled);
+ mPrimaryProvider.clearRecordedStates();
+ mSecondaryProvider.clearRecordedStates();
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index d8d44d47be62..8dbc520c583c 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -19,16 +19,13 @@ package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
-import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.RemoteCallback;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.service.timezone.TimeZoneProviderService;
@@ -50,9 +47,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
/**
* A service class that acts as a container for the {@link LocationTimeZoneProviderController},
@@ -70,12 +64,6 @@ import java.util.concurrent.atomic.AtomicReference;
* one indicated by {@link ThreadingDomain}. Because methods like {@link #dump} can be invoked on
* another thread, the service and its related objects must still be thread-safe.
*
- * <p>For testing / reproduction of bugs, it is possible to put providers into "simulation
- * mode" where the real binder clients are replaced by {@link
- * SimulatedLocationTimeZoneProviderProxy}. This means that the real client providers are never
- * bound (ensuring no real location events will be received) and simulated events / behaviors
- * can be injected via the command line.
- *
* <p>See {@code adb shell cmd location_time_zone_manager help}" for details and more options.
*/
public class LocationTimeZoneManagerService extends Binder {
@@ -247,6 +235,36 @@ public class LocationTimeZoneManagerService extends Binder {
}
}
+ /**
+ * Starts the service with fake provider package names configured for tests. The config is
+ * cleared when the service next stops.
+ *
+ * <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for
+ * completion, it cannot be called from the {@code mThreadingDomain} thread.
+ */
+ void startWithTestProviders(@Nullable String testPrimaryProviderPackageName,
+ @Nullable String testSecondaryProviderPackageName,
+ boolean recordProviderStateChanges) {
+ enforceManageTimeZoneDetectorPermission();
+
+ if (testPrimaryProviderPackageName == null && testSecondaryProviderPackageName == null) {
+ throw new IllegalArgumentException("One or both test package names must be provided.");
+ }
+
+ mThreadingDomain.postAndWait(() -> {
+ synchronized (mSharedLock) {
+ stopOnDomainThread();
+
+ mServiceConfigAccessor.setTestPrimaryLocationTimeZoneProviderPackageName(
+ testPrimaryProviderPackageName);
+ mServiceConfigAccessor.setTestSecondaryLocationTimeZoneProviderPackageName(
+ testSecondaryProviderPackageName);
+ mServiceConfigAccessor.setRecordProviderStateChanges(recordProviderStateChanges);
+ startOnDomainThread();
+ }
+ }, BLOCKING_OP_WAIT_DURATION_MILLIS);
+ }
+
private void startOnDomainThread() {
mThreadingDomain.assertCurrentThread();
@@ -295,6 +313,9 @@ public class LocationTimeZoneManagerService extends Binder {
mLocationTimeZoneDetectorController = null;
mEnvironment.destroy();
mEnvironment = null;
+
+ // Clear test state so it won't be used the next time the service is started.
+ mServiceConfigAccessor.resetVolatileTestConfig();
}
}
}
@@ -307,14 +328,14 @@ public class LocationTimeZoneManagerService extends Binder {
this, in, out, err, args, callback, resultReceiver);
}
- /** Sets this service into provider state recording mode for tests. */
- void setProviderStateRecordingEnabled(boolean enabled) {
+ /** Clears recorded provider state for tests. */
+ void clearRecordedProviderStates() {
enforceManageTimeZoneDetectorPermission();
mThreadingDomain.postAndWait(() -> {
synchronized (mSharedLock) {
if (mLocationTimeZoneDetectorController != null) {
- mLocationTimeZoneDetectorController.setProviderStateRecordingEnabled(enabled);
+ mLocationTimeZoneDetectorController.clearRecordedProviderStates();
}
}
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
@@ -344,48 +365,6 @@ public class LocationTimeZoneManagerService extends Binder {
}
}
- /**
- * Passes a {@link TestCommand} to the specified provider and waits for the response.
- */
- @NonNull
- Bundle handleProviderTestCommand(@IntRange(from = 0, to = 1) int providerIndex,
- @NonNull TestCommand testCommand) {
- enforceManageTimeZoneDetectorPermission();
-
- // Because this method blocks and posts work to the threading domain thread, it would cause
- // a deadlock if it were called by the threading domain thread.
- mThreadingDomain.assertNotCurrentThread();
-
- AtomicReference<Bundle> resultReference = new AtomicReference<>();
- CountDownLatch latch = new CountDownLatch(1);
- RemoteCallback remoteCallback = new RemoteCallback(x -> {
- resultReference.set(x);
- latch.countDown();
- });
-
- mThreadingDomain.post(() -> {
- synchronized (mSharedLock) {
- if (mLocationTimeZoneDetectorController == null) {
- remoteCallback.sendResult(null);
- return;
- }
- mLocationTimeZoneDetectorController.handleProviderTestCommand(
- providerIndex, testCommand, remoteCallback);
- }
- });
-
- try {
- // Wait, but not indefinitely.
- if (!latch.await(BLOCKING_OP_WAIT_DURATION_MILLIS, TimeUnit.MILLISECONDS)) {
- throw new RuntimeException("Command did not complete in time");
- }
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
-
- return resultReference.get();
- }
-
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
@Nullable String[] args) {
@@ -463,7 +442,8 @@ public class LocationTimeZoneManagerService extends Binder {
LocationTimeZoneProviderProxy proxy = createProxy();
ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
return new BinderLocationTimeZoneProvider(
- providerMetricsLogger, mThreadingDomain, mName, proxy);
+ providerMetricsLogger, mThreadingDomain, mName, proxy,
+ mServiceConfigAccessor.getRecordProviderStateChanges());
}
@GuardedBy("mSharedLock")
@@ -476,9 +456,7 @@ public class LocationTimeZoneManagerService extends Binder {
@NonNull
private LocationTimeZoneProviderProxy createProxy() {
String mode = getMode();
- if (Objects.equals(mode, PROVIDER_MODE_SIMULATED)) {
- return new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
+ if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
return new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
} else {
// mode == PROVIDER_MODE_OVERRIDE_ENABLED (or unknown).
@@ -486,7 +464,7 @@ public class LocationTimeZoneManagerService extends Binder {
}
}
- /** Returns the mode of the provider. */
+ /** Returns the mode of the provider (enabled/disabled). */
@NonNull
private String getMode() {
if (mIndex == 0) {
@@ -499,10 +477,19 @@ public class LocationTimeZoneManagerService extends Binder {
@NonNull
private RealLocationTimeZoneProviderProxy createRealProxy() {
String providerServiceAction = mServiceAction;
+ boolean isTestProvider = isTestProvider();
String providerPackageName = getPackageName();
return new RealLocationTimeZoneProviderProxy(
mContext, mHandler, mThreadingDomain, providerServiceAction,
- providerPackageName);
+ providerPackageName, isTestProvider);
+ }
+
+ private boolean isTestProvider() {
+ if (mIndex == 0) {
+ return mServiceConfigAccessor.isTestPrimaryLocationTimeZoneProvider();
+ } else {
+ return mServiceConfigAccessor.isTestSecondaryLocationTimeZoneProvider();
+ }
}
@NonNull
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 0f0de5004be9..3488956af571 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -16,11 +16,12 @@
package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
+import static android.app.time.LocationTimeZoneManager.NULL_PACKAGE_NAME_TOKEN;
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
+import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
-import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
-import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
+import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START_WITH_TEST_PROVIDERS;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
@@ -31,7 +32,6 @@ import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_T
import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
-import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
@@ -41,12 +41,12 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.time.GeolocationTimeZoneSuggestionProto;
import android.app.time.LocationTimeZoneManagerProto;
import android.app.time.LocationTimeZoneManagerServiceStateProto;
import android.app.time.TimeZoneProviderStateProto;
import android.app.timezonedetector.TimeZoneDetector;
-import android.os.Bundle;
import android.os.ShellCommand;
import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
@@ -79,14 +79,14 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
case SHELL_COMMAND_START: {
return runStart();
}
+ case SHELL_COMMAND_START_WITH_TEST_PROVIDERS: {
+ return runStartWithTestProviders();
+ }
case SHELL_COMMAND_STOP: {
return runStop();
}
- case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
- return runSendProviderTestCommand();
- }
- case SHELL_COMMAND_RECORD_PROVIDER_STATES: {
- return runRecordProviderStates();
+ case SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES: {
+ return runClearRecordedProviderStates();
}
case SHELL_COMMAND_DUMP_STATE: {
return runDumpControllerState();
@@ -105,47 +105,33 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
pw.printf(" Print this help text.\n");
pw.printf(" %s\n", SHELL_COMMAND_START);
pw.printf(" Starts the service, creating location time zone providers.\n");
+ pw.printf(" %s <primary package name|%2$s> <secondary package name|%2$s>"
+ + " <record states>\n",
+ SHELL_COMMAND_START_WITH_TEST_PROVIDERS, NULL_PACKAGE_NAME_TOKEN);
+ pw.printf(" Starts the service with test provider packages configured / provider"
+ + " permission checks disabled.\n");
+ pw.printf(" <record states> - true|false, determines whether state recording is enabled."
+ + "\n");
+ pw.printf(" See %s and %s.\n", SHELL_COMMAND_DUMP_STATE,
+ SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES);
pw.printf(" %s\n", SHELL_COMMAND_STOP);
pw.printf(" Stops the service, destroying location time zone providers.\n");
- pw.printf(" %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES);
- pw.printf(" Enables / disables provider state recording mode. See also %s. The default"
- + " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE);
- pw.printf(" Note: When enabled, this mode consumes memory and it is only intended for"
- + " testing.\n");
- pw.printf(" It should be disabled after use, or the device can be rebooted to"
- + " reset the mode to disabled.\n");
- pw.printf(" Disabling (or enabling repeatedly) clears any existing stored states.\n");
+ pw.printf(" %s\n", SHELL_COMMAND_CLEAR_RECORDED_PROVIDER_STATES);
+ pw.printf(" Clears recorded provider state. See also %s and %s.\n",
+ SHELL_COMMAND_START_WITH_TEST_PROVIDERS, SHELL_COMMAND_DUMP_STATE);
+ pw.printf(" Note: This is only intended for use during testing.\n");
pw.printf(" %s [%s]\n", SHELL_COMMAND_DUMP_STATE, DUMP_STATE_OPTION_PROTO);
pw.printf(" Dumps service state for tests as text or binary proto form.\n");
pw.printf(" See the LocationTimeZoneManagerServiceStateProto definition for details.\n");
- pw.printf(" %s <provider index> <test command>\n",
- SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
- pw.printf(" Passes a test command to the named provider.\n");
- pw.println();
- pw.printf("<provider index> = 0 (primary), 1 (secondary)\n");
- pw.println();
- pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
- pw.println();
- pw.printf("Provider <test command> encoding:\n");
- pw.println();
- TestCommand.printShellCommandEncodingHelp(pw);
- pw.println();
- pw.printf("Simulated provider mode can be used to test the system server behavior or to"
- + " reproduce bugs without the complexity of using real providers.\n");
- pw.println();
- pw.printf("The test commands for simulated providers are:\n");
- SimulatedLocationTimeZoneProviderProxy.printTestCommandShellHelp(pw);
- pw.println();
- pw.printf("Test commands cannot currently be passed to real provider implementations.\n");
pw.println();
pw.printf("This service is also affected by the following device_config flags in the"
+ " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
pw.printf(" %s\n", KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
- pw.printf(" Overrides the mode of the primary provider. Values=%s|%s|%s\n",
- PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+ pw.printf(" Overrides the mode of the primary provider. Values=%s|%s\n",
+ PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
pw.printf(" %s\n", KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
- pw.printf(" Overrides the mode of the secondary provider. Values=%s|%s|%s\n",
- PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+ pw.printf(" Overrides the mode of the secondary provider. Values=%s|%s\n",
+ PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED);
pw.printf(" %s\n", KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
pw.printf(" Sets the amount of time the service waits when uncertain before making an"
+ " 'uncertain' suggestion to the time zone detector.\n");
@@ -178,32 +164,38 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
return 0;
}
- private int runStop() {
+ private int runStartWithTestProviders() {
+ String testPrimaryProviderPackageName = parseProviderPackageName(getNextArgRequired());
+ String testSecondaryProviderPackageName = parseProviderPackageName(getNextArgRequired());
+ boolean recordProviderStateChanges = Boolean.parseBoolean(getNextArgRequired());
+
try {
- mService.stop();
+ mService.startWithTestProviders(testPrimaryProviderPackageName,
+ testSecondaryProviderPackageName, recordProviderStateChanges);
} catch (RuntimeException e) {
reportError(e);
return 1;
}
PrintWriter outPrintWriter = getOutPrintWriter();
- outPrintWriter.println("Service stopped");
+ outPrintWriter.println("Service started (test mode)");
return 0;
}
- private int runRecordProviderStates() {
- PrintWriter outPrintWriter = getOutPrintWriter();
- boolean enabled;
+ private int runStop() {
try {
- String nextArg = getNextArgRequired();
- enabled = Boolean.parseBoolean(nextArg);
+ mService.stop();
} catch (RuntimeException e) {
reportError(e);
return 1;
}
+ PrintWriter outPrintWriter = getOutPrintWriter();
+ outPrintWriter.println("Service stopped");
+ return 0;
+ }
- outPrintWriter.println("Setting provider state recording to " + enabled);
+ private int runClearRecordedProviderStates() {
try {
- mService.setProviderStateRecordingEnabled(enabled);
+ mService.clearRecordedProviderStates();
} catch (IllegalStateException e) {
reportError(e);
return 2;
@@ -293,47 +285,17 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
}
}
- private int runSendProviderTestCommand() {
- PrintWriter outPrintWriter = getOutPrintWriter();
-
- int providerIndex;
- TestCommand testCommand;
- try {
- providerIndex = parseProviderIndex(getNextArgRequired());
- testCommand = createTestCommandFromNextShellArg();
- } catch (RuntimeException e) {
- reportError(e);
- return 1;
- }
-
- outPrintWriter.println("Injecting testCommand=" + testCommand
- + " to providerIndex=" + providerIndex);
- try {
- Bundle result = mService.handleProviderTestCommand(providerIndex, testCommand);
- outPrintWriter.println(result);
- } catch (RuntimeException e) {
- reportError(e);
- return 2;
- }
- return 0;
- }
-
- @NonNull
- private TestCommand createTestCommandFromNextShellArg() {
- return TestCommand.createFromShellCommandArgs(this);
- }
-
- private void reportError(Throwable e) {
+ private void reportError(@NonNull Throwable e) {
PrintWriter errPrintWriter = getErrPrintWriter();
errPrintWriter.println("Error: ");
e.printStackTrace(errPrintWriter);
}
- private static int parseProviderIndex(@NonNull String providerIndexString) {
- int providerIndex = Integer.parseInt(providerIndexString);
- if (providerIndex < 0 || providerIndex > 1) {
- throw new IllegalArgumentException(providerIndexString);
+ @Nullable
+ private static String parseProviderPackageName(@NonNull String providerPackageNameString) {
+ if (providerPackageNameString.equals(NULL_PACKAGE_NAME_TOKEN)) {
+ return null;
}
- return providerIndex;
+ return providerPackageNameString;
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index e116a8742208..4e878333fe66 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -16,9 +16,6 @@
package com.android.server.timezonedetector.location;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
@@ -35,9 +32,7 @@ import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.RemoteCallback;
import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
@@ -48,6 +43,10 @@ import com.android.server.timezonedetector.ReferenceWithHistory;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@@ -101,34 +100,36 @@ abstract class LocationTimeZoneProvider implements Dumpable {
value = { PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_STARTED_INITIALIZING,
PROVIDER_STATE_STARTED_CERTAIN, PROVIDER_STATE_STARTED_UNCERTAIN,
PROVIDER_STATE_STOPPED, PROVIDER_STATE_PERM_FAILED, PROVIDER_STATE_DESTROYED })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
@interface ProviderStateEnum {}
/**
* Uninitialized value. Must not be used afte {@link LocationTimeZoneProvider#initialize}.
*/
- static final int PROVIDER_STATE_UNKNOWN = 0;
+ static final @ProviderStateEnum int PROVIDER_STATE_UNKNOWN = 0;
/**
* The provider is started and has not reported its first event.
*/
- static final int PROVIDER_STATE_STARTED_INITIALIZING = 1;
+ static final @ProviderStateEnum int PROVIDER_STATE_STARTED_INITIALIZING = 1;
/**
* The provider is started and most recently reported a "suggestion" event.
*/
- static final int PROVIDER_STATE_STARTED_CERTAIN = 2;
+ static final @ProviderStateEnum int PROVIDER_STATE_STARTED_CERTAIN = 2;
/**
* The provider is started and most recently reported an "uncertain" event.
*/
- static final int PROVIDER_STATE_STARTED_UNCERTAIN = 3;
+ static final @ProviderStateEnum int PROVIDER_STATE_STARTED_UNCERTAIN = 3;
/**
* The provider is stopped.
*
* This is the state after {@link #initialize} is called.
*/
- static final int PROVIDER_STATE_STOPPED = 4;
+ static final @ProviderStateEnum int PROVIDER_STATE_STOPPED = 4;
/**
* The provider has failed and cannot be restarted. This is a terminated state triggered by
@@ -136,16 +137,16 @@ abstract class LocationTimeZoneProvider implements Dumpable {
*
* Providers may enter this state any time after a provider is started.
*/
- static final int PROVIDER_STATE_PERM_FAILED = 5;
+ static final @ProviderStateEnum int PROVIDER_STATE_PERM_FAILED = 5;
/**
* The provider has been destroyed by the controller and cannot be restarted. Similar to
* {@link #PROVIDER_STATE_PERM_FAILED} except that a provider is set into this state.
*/
- static final int PROVIDER_STATE_DESTROYED = 6;
+ static final @ProviderStateEnum int PROVIDER_STATE_DESTROYED = 6;
/** The {@link LocationTimeZoneProvider} the state is for. */
- public final @NonNull LocationTimeZoneProvider provider;
+ @NonNull public final LocationTimeZoneProvider provider;
/** The state enum value of the current state. */
public final @ProviderStateEnum int stateEnum;
@@ -352,8 +353,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
/**
* Usually {@code false} but can be set to {@code true} for testing.
*/
- @GuardedBy("mSharedLock")
- private boolean mStateChangeRecording;
+ private final boolean mRecordStateChanges;
@GuardedBy("mSharedLock")
@NonNull
@@ -379,7 +379,8 @@ abstract class LocationTimeZoneProvider implements Dumpable {
LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
- @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
+ @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor,
+ boolean recordStateChanges) {
mThreadingDomain = Objects.requireNonNull(threadingDomain);
mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
@@ -387,6 +388,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mProviderName = Objects.requireNonNull(providerName);
mTimeZoneProviderEventPreProcessor =
Objects.requireNonNull(timeZoneProviderEventPreProcessor);
+ mRecordStateChanges = recordStateChanges;
}
/**
@@ -450,12 +452,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
abstract void onDestroy();
/**
- * Sets the provider into state recording mode for tests.
+ * Clears recorded state changes.
*/
- final void setStateChangeRecordingEnabled(boolean enabled) {
+ final void clearRecordedStates() {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
- mStateChangeRecording = enabled;
mRecordedStates.clear();
mRecordedStates.trimToSize();
}
@@ -472,12 +473,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
/**
- * Set the current state, for use by this class and subclasses only. If {@code #notifyChanges}
- * is {@code true} and {@code newState} is not equal to the old state, then {@link
- * ProviderListener#onProviderStateChange(ProviderState)} must be called on
- * {@link #mProviderListener}.
+ * Sets the current state. If {@code #notifyChanges} is {@code true} and {@code newState} is not
+ * equal to the old state, then {@link ProviderListener#onProviderStateChange(ProviderState)}
+ * will be called on {@link #mProviderListener}.
*/
- final void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
+ private void setCurrentState(@NonNull ProviderState newState, boolean notifyChanges) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
ProviderState oldState = mCurrentState.get();
@@ -485,7 +485,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
onSetCurrentState(newState);
if (!Objects.equals(newState, oldState)) {
mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
- if (mStateChangeRecording) {
+ if (mRecordStateChanges) {
mRecordedStates.add(newState);
}
if (notifyChanges) {
@@ -592,9 +592,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
PROVIDER_STATE_STOPPED, null, null, "stopUpdates() called");
setCurrentState(newState, false);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
onStopUpdates();
}
@@ -605,23 +603,6 @@ abstract class LocationTimeZoneProvider implements Dumpable {
*/
abstract void onStopUpdates();
- /**
- * Overridden by subclasses to handle the supplied {@link TestCommand}. If {@code callback} is
- * non-null, the default implementation sends a result {@link Bundle} with {@link
- * android.service.timezone.TimeZoneProviderService#TEST_COMMAND_RESULT_SUCCESS_KEY} set to
- * {@code false} and a "Not implemented" error message.
- */
- void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
- Objects.requireNonNull(testCommand);
-
- if (callback != null) {
- Bundle result = new Bundle();
- result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
- result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
- callback.sendResult(result);
- }
- }
-
/** For subclasses to invoke when a {@link TimeZoneProviderEvent} has been received. */
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
mThreadingDomain.assertCurrentThread();
@@ -656,9 +637,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
ProviderState newState = currentState.newState(
PROVIDER_STATE_PERM_FAILED, null, null, msg);
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
case EVENT_TYPE_SUGGESTION:
@@ -691,9 +670,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
ProviderState newState = currentState.newState(
PROVIDER_STATE_PERM_FAILED, null, null, msg);
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
@@ -709,9 +686,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
timeZoneProviderEvent, currentState.currentUserConfiguration,
"handleTimeZoneProviderEvent() when started");
setCurrentState(newState, true);
- if (mInitializationTimeoutQueue.hasQueued()) {
- mInitializationTimeoutQueue.cancel();
- }
+ cancelInitializationTimeoutIfSet();
return;
}
default: {
@@ -727,6 +702,52 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ /** For subclasses to invoke when needing to report a temporary failure. */
+ final void handleTemporaryFailure(String reason) {
+ mThreadingDomain.assertCurrentThread();
+
+ synchronized (mSharedLock) {
+ ProviderState currentState = mCurrentState.get();
+ switch (currentState.stateEnum) {
+ case PROVIDER_STATE_STARTED_INITIALIZING:
+ case PROVIDER_STATE_STARTED_UNCERTAIN:
+ case PROVIDER_STATE_STARTED_CERTAIN: {
+ // A temporary failure is treated as becoming uncertain.
+ String msg = "handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState;
+ debugLog(msg);
+ // This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
+ // event == null
+ ProviderState newState = currentState.newState(
+ PROVIDER_STATE_STARTED_UNCERTAIN, null,
+ currentState.currentUserConfiguration, msg);
+ setCurrentState(newState, true);
+ cancelInitializationTimeoutIfSet();
+ break;
+ }
+ case PROVIDER_STATE_STOPPED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is stopped.");
+ break;
+ }
+ case PROVIDER_STATE_PERM_FAILED:
+ case PROVIDER_STATE_DESTROYED: {
+ debugLog("handleProviderLost reason=" + reason
+ + ", mProviderName=" + mProviderName
+ + ", currentState=" + currentState
+ + ": No state change required, provider is terminated.");
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unknown currentState=" + currentState);
+ }
+ }
+ }
+ }
+
@GuardedBy("mSharedLock")
private void assertIsStarted() {
ProviderState currentState = mCurrentState.get();
@@ -751,6 +772,13 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
}
+ @GuardedBy("mSharedLock")
+ private void cancelInitializationTimeoutIfSet() {
+ if (mInitializationTimeoutQueue.hasQueued()) {
+ mInitializationTimeoutQueue.cancel();
+ }
+ }
+
@VisibleForTesting
Duration getInitializationTimeoutDelay() {
synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
index 43b1b5f017b2..7b1a77ce77d5 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.os.Handler;
-import android.os.RemoteCallback;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
@@ -113,13 +112,6 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
abstract void setRequest(@NonNull TimeZoneProviderRequest request);
/**
- * Processes the supplied test command. An optional callback can be supplied to listen for a
- * response.
- */
- abstract void handleTestCommand(@NonNull TestCommand testCommand,
- @Nullable RemoteCallback callback);
-
- /**
* Handles a {@link TimeZoneProviderEvent} from a remote process.
*/
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
index 1f45e828aad4..4ef819fdee21 100644
--- a/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
@@ -19,9 +19,6 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.os.Bundle;
-import android.os.RemoteCallback;
-import android.service.timezone.TimeZoneProviderService;
import android.util.IndentingPrintWriter;
/**
@@ -67,17 +64,6 @@ class NullLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
}
@Override
- void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
- if (callback != null) {
- Bundle result = new Bundle();
- result.putBoolean(TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY, false);
- result.putString(TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY,
- "Provider is disabled");
- callback.sendResult(result);
- }
- }
-
- @Override
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
synchronized (mSharedLock) {
ipw.println("{NullLocationTimeZoneProviderProxy}");
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index b5ac712a3522..fcac3e856913 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -18,16 +18,12 @@ package com.android.server.timezonedetector.location;
import static android.Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE;
import static android.Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteCallback;
import android.service.timezone.ITimeZoneProvider;
import android.service.timezone.ITimeZoneProviderManager;
import android.service.timezone.TimeZoneProviderSuggestion;
@@ -62,19 +58,27 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy im
RealLocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull Handler handler,
@NonNull ThreadingDomain threadingDomain, @NonNull String action,
- @NonNull String providerPackageName) {
+ @NonNull String providerPackageName, boolean isTestProvider) {
super(context, threadingDomain);
mManagerProxy = null;
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
Objects.requireNonNull(providerPackageName);
- mServiceWatcher = ServiceWatcher.create(context,
- handler,
- "RealLocationTimeZoneProviderProxy",
- new CurrentUserServiceSupplier(context, action,
- providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
- INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
- this);
+
+ CurrentUserServiceSupplier serviceSupplier;
+ if (isTestProvider) {
+ // For tests it is possible to bypass the provider service permission checks, since
+ // the tests are expected to install fake providers.
+ serviceSupplier = CurrentUserServiceSupplier.createUnsafeForTestsOnly(
+ context, action, providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
+ /*servicePermission=*/null);
+ } else {
+ serviceSupplier = CurrentUserServiceSupplier.create(context, action,
+ providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
+ INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE);
+ }
+ mServiceWatcher = ServiceWatcher.create(
+ context, handler, "RealLocationTimeZoneProviderProxy", serviceSupplier, this);
}
@Override
@@ -155,21 +159,6 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy im
});
}
- /**
- * A stubbed implementation.
- */
- @Override
- void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
- mThreadingDomain.assertCurrentThread();
-
- if (callback != null) {
- Bundle result = new Bundle();
- result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
- result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
- callback.sendResult(result);
- }
- }
-
@Override
public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
synchronized (mSharedLock) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
deleted file mode 100644
index 02b0a849c1b1..000000000000
--- a/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
+++ /dev/null
@@ -1,211 +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.timezonedetector.location;
-
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND;
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND;
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE;
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS;
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ;
-import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.RemoteCallback;
-import android.os.SystemClock;
-import android.service.timezone.TimeZoneProviderSuggestion;
-import android.util.IndentingPrintWriter;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.timezonedetector.ReferenceWithHistory;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * A replacement for a real binder proxy for use during integration testing
- * that can be used to inject simulated {@link LocationTimeZoneProviderProxy} behavior.
- */
-class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
-
- @GuardedBy("mSharedLock")
- @NonNull private TimeZoneProviderRequest mRequest;
-
- @GuardedBy("mSharedLock")
- @NonNull private final ReferenceWithHistory<String> mLastEvent = new ReferenceWithHistory<>(50);
-
- SimulatedLocationTimeZoneProviderProxy(
- @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
- super(context, threadingDomain);
- mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
- }
-
- @Override
- void onInitialize() {
- // No-op - nothing to do for the simulated provider.
- }
-
- @Override
- void onDestroy() {
- // No-op - nothing to do for the simulated provider.
- }
-
- void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
- mThreadingDomain.assertCurrentThread();
-
- Objects.requireNonNull(testCommand);
-
- synchronized (mSharedLock) {
- Bundle resultBundle = new Bundle();
- switch (testCommand.getName()) {
- case SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND: {
- mLastEvent.set("Simulating onProviderBound(), testCommand=" + testCommand);
- mThreadingDomain.post(this::onBindOnHandlerThread);
- resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
- break;
- }
- case SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND: {
- mLastEvent.set("Simulating onProviderUnbound(), testCommand=" + testCommand);
- mThreadingDomain.post(this::onUnbindOnHandlerThread);
- resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
- break;
- }
- case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE:
- case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN:
- case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
- if (!mRequest.sendUpdates()) {
- String errorMsg = "testCommand=" + testCommand
- + " is testing an invalid case:"
- + " updates are off. mRequest=" + mRequest;
- mLastEvent.set(errorMsg);
- resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
- resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
- break;
- }
- mLastEvent.set("Simulating TimeZoneProviderEvent, testCommand=" + testCommand);
- TimeZoneProviderEvent timeZoneProviderEvent =
- createTimeZoneProviderEventFromTestCommand(testCommand);
- handleTimeZoneProviderEvent(timeZoneProviderEvent);
- resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
- break;
- }
- default: {
- String errorMsg = "Unknown test event type. testCommand=" + testCommand;
- mLastEvent.set(errorMsg);
- resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
- resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
- break;
- }
- }
- if (callback != null) {
- callback.sendResult(resultBundle);
- }
- }
- }
-
- private void onBindOnHandlerThread() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- mListener.onProviderBound();
- }
- }
-
- private void onUnbindOnHandlerThread() {
- mThreadingDomain.assertCurrentThread();
-
- synchronized (mSharedLock) {
- mListener.onProviderUnbound();
- }
- }
-
- @Override
- final void setRequest(@NonNull TimeZoneProviderRequest request) {
- mThreadingDomain.assertCurrentThread();
-
- Objects.requireNonNull(request);
- synchronized (mSharedLock) {
- mLastEvent.set("Request received: " + request);
- mRequest = request;
- }
- }
-
- @Override
- public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
- synchronized (mSharedLock) {
- ipw.println("{SimulatedLocationTimeZoneProviderProxy}");
- ipw.println("mRequest=" + mRequest);
- ipw.println("mLastEvent=" + mLastEvent);
-
- ipw.increaseIndent();
- ipw.println("Last event history:");
- mLastEvent.dump(ipw);
- ipw.decreaseIndent();
- }
- }
-
- /**
- * Prints the command line options that to create a {@link TestCommand} that can be passed to
- * {@link #createTimeZoneProviderEventFromTestCommand(TestCommand)}.
- */
- static void printTestCommandShellHelp(@NonNull PrintWriter pw) {
- pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND);
- pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND);
- pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE);
- pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN);
- pw.printf("%s %s=string_array:<time zone id>[&<time zone id>]+\n",
- SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS,
- SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
- }
-
- @NonNull
- private static TimeZoneProviderEvent createTimeZoneProviderEventFromTestCommand(
- @NonNull TestCommand testCommand) {
- String name = testCommand.getName();
- switch (name) {
- case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE: {
- return TimeZoneProviderEvent.createPermanentFailureEvent("Simulated failure");
- }
- case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN: {
- return TimeZoneProviderEvent.createUncertainEvent();
- }
- case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
- Bundle args = testCommand.getArgs();
- String[] timeZoneIds = args.getStringArray(
- SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
- if (timeZoneIds == null) {
- throw new IllegalArgumentException("No "
- + SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ + " arg found");
- }
- TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
- .setTimeZoneIds(Arrays.asList(timeZoneIds))
- .setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
- .build();
- return TimeZoneProviderEvent.createSuggestionEvent(suggestion);
- }
- default: {
- String msg = String.format("Error: Unknown command name %s", name);
- throw new IllegalArgumentException(msg);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/timezonedetector/location/TestCommand.java b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
deleted file mode 100644
index 21482ea6ff79..000000000000
--- a/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
+++ /dev/null
@@ -1,194 +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.timezonedetector.location;
-
-import android.annotation.NonNull;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ShellCommand;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * A command used to trigger behaviors in a component during tests. Routing to the correct
- * component is not handled by this class. The meaning of the {@code name} and {@code args}
- * properties are component-specific.
- *
- * <p>{@link TestCommand}s can be encoded as arguments in a shell command. See
- * {@link #createFromShellCommandArgs(ShellCommand)} and {@link
- * #printShellCommandEncodingHelp(PrintWriter)}.
- */
-final class TestCommand {
-
- private static final Pattern SHELL_ARG_PATTERN = Pattern.compile("([^=]+)=([^:]+):(.*)");
- private static final Pattern SHELL_ARG_VALUE_SPLIT_PATTERN = Pattern.compile("&");
-
- @NonNull private final String mName;
- @NonNull private final Bundle mArgs;
-
- /** Creates a {@link TestCommand} from components. */
- private TestCommand(@NonNull String type, @NonNull Bundle args) {
- mName = Objects.requireNonNull(type);
- mArgs = Objects.requireNonNull(args);
- }
-
- @VisibleForTesting
- @NonNull
- public static TestCommand createForTests(@NonNull String type, @NonNull Bundle args) {
- return new TestCommand(type, args);
- }
-
- /**
- * Creates a {@link TestCommand} from a {@link ShellCommand}'s remaining arguments.
- *
- * See {@link #printShellCommandEncodingHelp(PrintWriter)} for encoding details.
- */
- @NonNull
- public static TestCommand createFromShellCommandArgs(@NonNull ShellCommand shellCommand) {
- String name = shellCommand.getNextArgRequired();
- Bundle args = new Bundle();
- String argKeyAndValue;
- while ((argKeyAndValue = shellCommand.getNextArg()) != null) {
- Matcher matcher = SHELL_ARG_PATTERN.matcher(argKeyAndValue);
- if (!matcher.matches()) {
- throw new IllegalArgumentException(
- argKeyAndValue + " does not match " + SHELL_ARG_PATTERN);
- }
- String key = matcher.group(1);
- String type = matcher.group(2);
- String encodedValue = matcher.group(3);
- Object value = getTypedValue(type, encodedValue);
- args.putObject(key, value);
- }
- return new TestCommand(name, args);
- }
-
- /**
- * Returns the command's name.
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Returns the arg values. Returns an empty bundle if there are no args.
- */
- @NonNull
- public Bundle getArgs() {
- return mArgs.deepCopy();
- }
-
- @Override
- public String toString() {
- return "TestCommand{"
- + "mName=" + mName
- + ", mArgs=" + mArgs
- + '}';
- }
-
- /**
- * Prints the text format that {@link #createFromShellCommandArgs(ShellCommand)} understands.
- */
- public static void printShellCommandEncodingHelp(@NonNull PrintWriter pw) {
- pw.println("Test commands are encoded on the command line as: <name> <arg>*");
- pw.println();
- pw.println("The <name> is a string");
- pw.println("The <arg> encoding is: \"key=type:value\"");
- pw.println();
- pw.println("e.g. \"myKey=string:myValue\" represents an argument with the key \"myKey\""
- + " and a string value of \"myValue\"");
- pw.println("Values are one or more URI-encoded strings separated by & characters. Only some"
- + " types support multiple values, e.g. string arrays.");
- pw.println();
- pw.println("Recognized types are: string, boolean, double, long, string_array.");
- pw.println();
- pw.println("When passing test commands via adb shell, the & can be escaped by quoting the"
- + " <arg> and escaping the & with \\");
- pw.println("For example:");
- pw.println(" $ adb shell ... my-command \"key1=string_array:value1\\&value2\"");
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- TestCommand that = (TestCommand) o;
- return mName.equals(that.mName)
- && mArgs.kindofEquals(that.mArgs);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mName, mArgs);
- }
-
-
- private static Object getTypedValue(String type, String encodedValue) {
- // The value is stored in a URL encoding. Multiple value types have values separated with
- // a & character.
- String[] values = SHELL_ARG_VALUE_SPLIT_PATTERN.split(encodedValue);
-
- // URI decode the values.
- for (int i = 0; i < values.length; i++) {
- values[i] = Uri.decode(values[i]);
- }
-
- switch (type) {
- case "boolean": {
- checkSingleValue(values);
- return Boolean.parseBoolean(values[0]);
- }
- case "double": {
- checkSingleValue(values);
- return Double.parseDouble(values[0]);
- }
- case "long": {
- checkSingleValue(values);
- return Long.parseLong(values[0]);
- }
- case "string": {
- checkSingleValue(values);
- return values[0];
- }
- case "string_array": {
- return values;
- }
- default: {
- throw new IllegalArgumentException("Unknown type: " + type);
- }
- }
-
- }
-
- private static void checkSingleValue(String[] values) {
- if (values.length != 1) {
- throw new IllegalArgumentException("Expected a single value, but there were multiple: "
- + Arrays.toString(values));
- }
- }
-}
diff --git a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
index 3e224e03fda0..7648795763ca 100644
--- a/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
@@ -22,6 +22,10 @@ import android.annotation.Nullable;
import android.service.timezone.TimeZoneProviderService;
import android.service.timezone.TimeZoneProviderSuggestion;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.Objects;
/**
@@ -31,31 +35,32 @@ final class TimeZoneProviderEvent {
@IntDef(prefix = "EVENT_TYPE_",
value = { EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUGGESTION, EVENT_TYPE_UNCERTAIN })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
public @interface EventType {}
/**
* The provider failed permanently. See {@link
* TimeZoneProviderService#reportPermanentFailure(Throwable)}
*/
- public static final int EVENT_TYPE_PERMANENT_FAILURE = 1;
+ public static final @EventType int EVENT_TYPE_PERMANENT_FAILURE = 1;
/**
* The provider made a suggestion. See {@link
* TimeZoneProviderService#reportSuggestion(TimeZoneProviderSuggestion)}
*/
- public static final int EVENT_TYPE_SUGGESTION = 2;
+ public static final @EventType int EVENT_TYPE_SUGGESTION = 2;
/**
* The provider was uncertain about the time zone. See {@link
* TimeZoneProviderService#reportUncertain()}
*/
- public static final int EVENT_TYPE_UNCERTAIN = 3;
+ public static final @EventType int EVENT_TYPE_UNCERTAIN = 3;
private static final TimeZoneProviderEvent UNCERTAIN_EVENT =
new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, null, null);
- @EventType
- private final int mType;
+ private final @EventType int mType;
@Nullable
private final TimeZoneProviderSuggestion mSuggestion;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 5723e1dcceb5..ee30fa2ac928 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -50,8 +50,6 @@ public final class ClientProfile {
*/
private final int mProcessId;
- private boolean mIsForeground;
-
/**
* All the clients that share the same resource would be under the same group id.
*
@@ -90,6 +88,12 @@ public final class ClientProfile {
private int mUsingCiCamId = INVALID_RESOURCE_ID;
/**
+ * If the priority is overwritten through
+ * {@link TunerResourceManagerService#setPriority(int, int)}.
+ */
+ private boolean mIsPriorityOverwritten = false;
+
+ /**
* Optional arbitrary priority value given by the client.
*
* <p>This value can override the default priorotiy calculated from
@@ -121,17 +125,10 @@ public final class ClientProfile {
}
/**
- * Set the current isForeground status.
- */
- public void setForeground(boolean isForeground) {
- mIsForeground = isForeground;
- }
-
- /**
- * Get the previous recorded isForeground status.
+ * If the client priority is overwrttien.
*/
- public boolean isForeground() {
- return mIsForeground;
+ public boolean isPriorityOverwritten() {
+ return mIsPriorityOverwritten;
}
public int getGroupId() {
@@ -153,6 +150,17 @@ public final class ClientProfile {
mPriority = priority;
}
+ /**
+ * Overwrite the client priority.
+ */
+ public void overwritePriority(int priority) {
+ if (priority < 0) {
+ return;
+ }
+ mIsPriorityOverwritten = true;
+ mPriority = priority;
+ }
+
public void setNiceValue(int niceValue) {
mNiceValue = niceValue;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 988582da53ea..22f0adaee4cf 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -23,7 +23,6 @@ import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.Context;
import android.media.IResourceManagerService;
import android.media.tv.TvInputManager;
-import android.media.tv.tuner.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
import android.media.tv.tunerresourcemanager.ITunerResourceManager;
@@ -31,6 +30,7 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile;
import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -507,9 +507,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde
.useCase(profile.useCase)
.processId(pid)
.build();
- clientProfile.setForeground(checkIsForeground(pid));
clientProfile.setPriority(
- getClientPriority(profile.useCase, clientProfile.isForeground()));
+ getClientPriority(profile.useCase, checkIsForeground(pid)));
addClientProfile(clientId[0], clientProfile, listener);
}
@@ -547,8 +546,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
return false;
}
- profile.setForeground(checkIsForeground(profile.getProcessId()));
- profile.setPriority(priority);
+ profile.overwritePriority(priority);
profile.setNiceValue(niceValue);
return true;
@@ -694,7 +692,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
} else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
// Record the frontend id with the lowest client priority among all the
// in use frontends when no available frontend has been found.
- int priority = getOwnerClientPriority(fr.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(fr.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityFrHandle = fr.getHandle();
currentLowestPriority = priority;
@@ -760,7 +758,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
} else {
// Record the lnb id with the lowest client priority among all the
// in use lnb when no available lnb has been found.
- int priority = getOwnerClientPriority(lnb.getOwnerClientId());
+ int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
if (currentLowestPriority > priority) {
inUseLowestPriorityLnbHandle = lnb.getHandle();
currentLowestPriority = priority;
@@ -818,7 +816,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
for (int ownerId : cas.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -867,7 +865,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
for (int ownerId : ciCam.getOwnerClientIds()) {
// Record the client id with lowest priority that is using the current Cas system.
- int priority = getOwnerClientPriority(ownerId);
+ int priority = updateAndGetOwnerClientPriority(ownerId);
if (currentLowestPriority > priority) {
lowestPriorityOwnerId = ownerId;
currentLowestPriority = priority;
@@ -966,18 +964,17 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
@VisibleForTesting
- // This mothod is to sync up the request client's foreground/background status and update
- // the client priority accordingly whenever new resource request comes in.
- protected void clientPriorityUpdateOnRequest(ClientProfile requestProfile) {
- int pid = requestProfile.getProcessId();
- boolean currentIsForeground = checkIsForeground(pid);
- if (requestProfile.isForeground() == currentIsForeground) {
+ // This mothod is to sync up the request/holder client's foreground/background status and update
+ // the client priority accordingly whenever a new resource request comes in.
+ protected void clientPriorityUpdateOnRequest(ClientProfile profile) {
+ if (profile.isPriorityOverwritten()) {
// To avoid overriding the priority set through updateClientPriority API.
return;
}
- requestProfile.setForeground(currentIsForeground);
- requestProfile.setPriority(
- getClientPriority(requestProfile.getUseCase(), currentIsForeground));
+ int pid = profile.getProcessId();
+ boolean currentIsForeground = checkIsForeground(pid);
+ profile.setPriority(
+ getClientPriority(profile.getUseCase(), currentIsForeground));
}
@VisibleForTesting
@@ -1154,13 +1151,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde
}
/**
- * Get the owner client's priority.
+ * Update and get the owner client's priority.
*
* @param clientId the owner client id.
* @return the priority of the owner client.
*/
- private int getOwnerClientPriority(int clientId) {
- return getClientProfile(clientId).getPriority();
+ private int updateAndGetOwnerClientPriority(int clientId) {
+ ClientProfile profile = getClientProfile(clientId);
+ clientPriorityUpdateOnRequest(profile);
+ return profile.getPriority();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 4e453f378cbc..fd1995ddec58 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -695,7 +695,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
// Both direct boot aware and unaware packages are fine as we
// will do filtering at query time to avoid multiple parsing.
final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, SYSTEM_UID);
if (pi != null && sourcePkg.equals(pi.packageName)) {
int targetUid = mPmInternal.getPackageUid(
targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
@@ -759,9 +759,10 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
if (DEBUG) Slog.v(TAG,
"Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
+ // Unchecked call, passing the system's uid as the calling uid to the getProviderInfo
final String authority = grantUri.uri.getAuthority();
final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
- MATCH_DEBUG_TRIAGED_MISSING);
+ MATCH_DEBUG_TRIAGED_MISSING, SYSTEM_UID);
if (pi == null) {
Slog.w(TAG, "No content provider found for grant: " + grantUri.toSafeString());
return;
@@ -812,7 +813,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
final String authority = grantUri.uri.getAuthority();
final ProviderInfo pi = getProviderInfo(authority, grantUri.sourceUserId,
- MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUid);
if (pi == null) {
Slog.w(TAG, "No content provider found for permission revoke: "
+ grantUri.toSafeString());
@@ -1056,11 +1057,6 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub {
}
}
- private ProviderInfo getProviderInfo(String authority, int userHandle, int pmFlags) {
- return mPmInternal.resolveContentProvider(authority,
- PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userHandle);
- }
-
private ProviderInfo getProviderInfo(String authority, int userHandle, int pmFlags,
int callingUid) {
return mPmInternal.resolveContentProvider(authority,
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index cc41f617ab8c..62afcc86792c 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -1,7 +1,7 @@
per-file Snappable.java = file:/services/core/java/com/android/server/pm/OWNERS
per-file Snappable.java = shombert@google.com
-per-file SnapShot* = file:/services/core/java/com/android/server/pm/OWNERS
-per-file SnapShot* = shombert@google.com
+per-file Snapshot* = file:/services/core/java/com/android/server/pm/OWNERS
+per-file Snapshot* = shombert@google.com
per-file Watchable* = file:/services/core/java/com/android/server/pm/OWNERS
per-file Watchable* = shombert@google.com
per-file Watched* = file:/services/core/java/com/android/server/pm/OWNERS
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index e447a23cf331..4ae058d6665b 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -33,6 +33,7 @@ import android.util.proto.ProtoOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
/** Represents a vibration request to the vibrator service. */
@@ -58,6 +59,7 @@ final class Vibration {
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_SETTINGS,
+ IGNORED_SUPERSEDED,
}
/** Start time in CLOCK_BOOTTIME base. */
@@ -90,6 +92,9 @@ final class Vibration {
private long mEndTimeDebug;
private Status mStatus;
+ /** A {@link CountDownLatch} to enable waiting for completion. */
+ private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
+
Vibration(IBinder token, int id, CombinedVibration effect,
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
@@ -118,6 +123,12 @@ final class Vibration {
}
mStatus = status;
mEndTimeDebug = System.currentTimeMillis();
+ mCompletionLatch.countDown();
+ }
+
+ /** Waits indefinitely until another thread calls {@link #end(Status)} on this vibration. */
+ public void waitForEnd() throws InterruptedException {
+ mCompletionLatch.await();
}
/**
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 0f4f58b32c1b..630bf0a01a40 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -98,7 +98,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
}
private final Object mLock = new Object();
- private final WorkSource mWorkSource = new WorkSource();
+ private final WorkSource mWorkSource;
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
private final VibrationSettings mVibrationSettings;
@@ -119,9 +119,8 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
mVibrationSettings = vibrationSettings;
mDeviceEffectAdapter = effectAdapter;
mCallbacks = callbacks;
+ mWorkSource = new WorkSource(mVibration.uid);
mWakeLock = wakeLock;
- mWorkSource.set(vib.uid);
- mWakeLock.setWorkSource(mWorkSource);
mBatteryStatsService = batteryStatsService;
CombinedVibration effect = vib.getEffect();
@@ -152,6 +151,7 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
+ mWakeLock.setWorkSource(mWorkSource);
mWakeLock.acquire();
try {
mVibration.token.linkToDeath(this, 0);
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 2a47512bb147..0efc940dff93 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -144,9 +144,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// 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 (mNextVibration != null
+ && !isSystemHapticFeedback(mNextVibration.getVibration())) {
+ clearNextVibrationLocked(Vibration.Status.CANCELLED);
+ }
if (mCurrentVibration != null
&& !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
- mNextVibration = null;
mCurrentVibration.cancel();
}
}
@@ -336,17 +339,27 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override // Binder call
public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
+ vibrateInternal(uid, opPkg, effect, attrs, reason, token);
+ }
+
+ /**
+ * An internal-only version of vibrate that allows the caller access to the {@link Vibration}.
+ * The Vibration is only returned if it is ongoing after this method returns.
+ */
+ @Nullable
+ private Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
+ @Nullable VibrationAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
if (token == null) {
Slog.e(TAG, "token must not be null");
- return;
+ return null;
}
enforceUpdateAppOpsStatsPermission(uid);
if (!isEffectValid(effect)) {
- return;
+ return null;
}
attrs = fixupVibrationAttributes(attrs);
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
@@ -357,13 +370,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
- return;
+ return vib;
}
ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
- return;
+ return vib;
}
final long ident = Binder.clearCallingIdentity();
@@ -375,6 +388,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (status != Vibration.Status.RUNNING) {
endVibrationLocked(vib, status);
}
+ return vib;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -401,7 +415,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
- mNextVibration = null;
+ clearNextVibrationLocked(Vibration.Status.CANCELLED);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
@@ -453,7 +467,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
- new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
+ new VibratorManagerShellCommand(cb.getShellCallbackBinder())
+ .exec(this, in, out, err, args, cb, resultReceiver);
}
@VisibleForTesting
@@ -521,7 +536,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mCurrentVibration == null) {
return startVibrationThreadLocked(vibThread);
}
-
+ // If there's already a vibration queued (waiting for the previous one to finish
+ // cancelling), end it cleanly and replace it with the new one.
+ clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED);
mNextVibration = vibThread;
return Vibration.Status.RUNNING;
} finally {
@@ -1300,6 +1317,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ /** Clears mNextVibration if set, ending it cleanly */
+ private void clearNextVibrationLocked(Vibration.Status endStatus) {
+ if (mNextVibration != null) {
+ endVibrationLocked(mNextVibration.getVibration(), endStatus);
+ mNextVibration = null;
+ }
+ }
+
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
@VisibleForTesting
final class ExternalVibratorService extends IExternalVibratorService.Stub {
@@ -1347,7 +1372,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// 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;
+ clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
mCurrentVibration.cancelImmediately();
cancelingVibration = mCurrentVibration;
}
@@ -1454,6 +1479,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final class CommonOptions {
public boolean force = false;
public String description = "Shell command";
+ public boolean background = false;
CommonOptions() {
String nextArg;
@@ -1463,6 +1489,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
getNextArgRequired(); // consume "-f"
force = true;
break;
+ case "-B":
+ getNextArgRequired(); // consume "-B"
+ background = true;
+ break;
case "-d":
getNextArgRequired(); // consume "-d"
description = getNextArgRequired();
@@ -1475,10 +1505,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- private final IBinder mToken;
+ private final IBinder mShellCallbacksToken;
- private VibratorManagerShellCommand(IBinder token) {
- mToken = token;
+ private VibratorManagerShellCommand(IBinder shellCallbacksToken) {
+ mShellCallbacksToken = shellCallbacksToken;
}
@Override
@@ -1520,12 +1550,28 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- private int runMono() {
- CommonOptions commonOptions = new CommonOptions();
- CombinedVibration effect = CombinedVibration.createParallel(nextEffect());
+ /**
+ * Runs a CombinedVibration using the configured common options and attributes.
+ */
+ private void runVibrate(CommonOptions commonOptions, CombinedVibration combined) {
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, effect, attrs,
- commonOptions.description, mToken);
+ // If running in the background, bind to death of the server binder rather than the
+ // client, and the cancel command likewise uses the server binder reference to
+ // only cancel background vibrations.
+ IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
+ : mShellCallbacksToken;
+ Vibration vib = vibrateInternal(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combined,
+ attrs, commonOptions.description, deathBinder);
+ if (vib != null && !commonOptions.background) {
+ try {
+ vib.waitForEnd();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ private int runMono() {
+ runVibrate(new CommonOptions(), CombinedVibration.createParallel(nextEffect()));
return 0;
}
@@ -1537,9 +1583,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int vibratorId = Integer.parseInt(getNextArgRequired());
combination.addVibrator(vibratorId, nextEffect());
}
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
- commonOptions.description, mToken);
+ runVibrate(commonOptions, combination.combine());
return 0;
}
@@ -1551,14 +1595,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int vibratorId = Integer.parseInt(getNextArgRequired());
combination.addNext(vibratorId, nextEffect());
}
- VibrationAttributes attrs = createVibrationAttributes(commonOptions);
- vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
- commonOptions.description, mToken);
+ runVibrate(commonOptions, combination.combine());
return 0;
}
private int runCancel() {
- cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, mToken);
+ // Cancel is only needed if the vibration was run in the background, otherwise it's
+ // terminated by the shell command ending. In these cases, the token was that of the
+ // service rather than the client.
+ cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, VibratorManagerService.this);
return 0;
}
@@ -1741,7 +1786,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" wait time in milliseconds.");
pw.println(" If -a is provided, the command accepts a second argument for ");
pw.println(" amplitude, in a scale of 1-255.");
- pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
+ pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
pw.println("(<duration> [<amplitude>] [<frequency>])...");
pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
@@ -1777,6 +1822,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println("Common Options:");
pw.println(" -f");
pw.println(" Force. Ignore Do Not Disturb setting.");
+ pw.println(" -B");
+ pw.println(" Run in the background; without this option the shell cmd will");
+ pw.println(" block until the vibration has completed.");
pw.println(" -d <description>");
pw.println(" Add description to the vibration.");
pw.println("");
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 771332071756..e48593c3d3c3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.wallpaper;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -1816,26 +1817,26 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void onUnlockUser(final int userId) {
- TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("on-unlock-user-" + userId);
- try {
- synchronized (mLock) {
- if (mCurrentUserId == userId) {
- if (mWaitingForUnlock) {
- // the desired wallpaper is not direct-boot aware, load it now
- final WallpaperData systemWallpaper =
- getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- switchWallpaper(systemWallpaper, null);
- notifyCallbacksLocked(systemWallpaper);
- }
-
- // Make sure that the SELinux labeling of all the relevant files is correct.
- // This corrects for mislabeling bugs that might have arisen from move-to
- // operations involving the wallpaper files. This isn't timing-critical,
- // so we do it in the background to avoid holding up the user unlock operation.
- if (!mUserRestorecon.get(userId)) {
- mUserRestorecon.put(userId, true);
- Runnable relabeler = () -> {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ if (mWaitingForUnlock) {
+ // the desired wallpaper is not direct-boot aware, load it now
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
+ notifyCallbacksLocked(systemWallpaper);
+ }
+
+ // Make sure that the SELinux labeling of all the relevant files is correct.
+ // This corrects for mislabeling bugs that might have arisen from move-to
+ // operations involving the wallpaper files. This isn't timing-critical,
+ // so we do it in the background to avoid holding up the user unlock operation.
+ if (!mUserRestorecon.get(userId)) {
+ mUserRestorecon.put(userId, true);
+ Runnable relabeler = () -> {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("Wallpaper_selinux_restorecon-" + userId);
+ try {
final File wallpaperDir = getWallpaperDir(userId);
for (String filename : sPerUserFiles) {
File f = new File(wallpaperDir, filename);
@@ -1843,13 +1844,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
SELinux.restorecon(f);
}
}
- };
- BackgroundThread.getHandler().post(relabeler);
- }
+ } finally {
+ t.traceEnd();
+ }
+ };
+ BackgroundThread.getHandler().post(relabeler);
}
}
- } finally {
- t.traceEnd();
}
}
@@ -1868,7 +1869,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
void switchUser(int userId, IRemoteCallback reply) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
- t.traceBegin("switch-user-" + userId);
+ t.traceBegin("Wallpaper_switch-user-" + userId);
try {
final WallpaperData systemWallpaper;
final WallpaperData lockWallpaper;
@@ -2045,7 +2046,21 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ private boolean hasCrossUserPermission() {
+ final int interactPermission =
+ mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
+ return interactPermission == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
public boolean hasNamedWallpaper(String name) {
+ final int callingUser = UserHandle.getCallingUserId();
+ final boolean allowCrossUser = hasCrossUserPermission();
+ if (DEBUG) {
+ Slog.d(TAG, "hasNamedWallpaper() caller " + Binder.getCallingUid()
+ + " cross-user?: " + allowCrossUser);
+ }
+
synchronized (mLock) {
List<UserInfo> users;
final long ident = Binder.clearCallingIdentity();
@@ -2055,6 +2070,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Binder.restoreCallingIdentity(ident);
}
for (UserInfo user: users) {
+ if (!allowCrossUser && callingUser != user.id) {
+ // No cross-user information for callers without permission
+ continue;
+ }
+
// ignore managed profiles
if (user.isManagedProfile()) {
continue;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index a24319f7a98c..94e6ca2675d8 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CALLBACK;
+import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK;
import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -33,15 +35,19 @@ import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
+import static com.android.server.accessibility.AccessibilityTraceProto.CPU_STATS;
import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.LOGGING_TYPE;
import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
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.WindowTracing.WINSCOPE_EXT;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
+import android.accessibilityservice.AccessibilityTrace;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -70,7 +76,7 @@ import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.util.ArraySet;
-import android.util.IntArray;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
@@ -84,6 +90,7 @@ import android.view.SurfaceControl;
import android.view.ViewConfiguration;
import android.view.WindowInfo;
import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -99,8 +106,6 @@ import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallba
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -116,21 +121,16 @@ final class AccessibilityController {
private static final String TAG = AccessibilityController.class.getSimpleName();
private static final Object STATIC_LOCK = new Object();
- static AccessibilityControllerInternal
+ static AccessibilityControllerInternalImpl
getAccessibilityControllerInternal(WindowManagerService service) {
return AccessibilityControllerInternalImpl.getInstance(service);
}
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowManagerService mService;
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- AccessibilityController(WindowManagerService service) {
- mService = service;
- mAccessibilityTracing = AccessibilityTracing.getInstance(service);
- }
-
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
@@ -138,10 +138,17 @@ final class AccessibilityController {
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
+ AccessibilityController(WindowManagerService service) {
+ mService = service;
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(service);
+ }
+
boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setMagnificationCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; callbacks={" + callbacks + "}");
}
boolean result = false;
@@ -176,54 +183,32 @@ final class AccessibilityController {
*
* @param displayId The logical display id.
* @param callback The callback.
- * @return {@code false} if display id is not valid or an embedded display.
*/
- boolean setWindowsForAccessibilityCallback(int displayId,
+ void setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".setWindowsForAccessibilityCallback",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; callback={" + callback + "}");
}
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
- if (dc == null) {
- return false;
- }
if (callback != null) {
WindowsForAccessibilityObserver observer =
mWindowsForAccessibilityObserver.get(displayId);
- if (isEmbeddedDisplay(dc)) {
- // If this display is an embedded one, its window observer should have been set from
- // window manager after setting its parent window. But if its window observer is
- // empty, that means this mapping didn't be set, and needs to do this again.
- // This happened when accessibility window observer is disabled and enabled again.
- if (observer == null) {
- handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
- }
- return false;
- } else if (observer != null) {
+ if (observer != null) {
final String errorMessage = "Windows for accessibility callback of display "
+ displayId + " already set!";
Slog.e(TAG, errorMessage);
if (Build.IS_DEBUGGABLE) {
throw new IllegalStateException(errorMessage);
}
- removeObserverOfEmbeddedDisplay(observer);
mWindowsForAccessibilityObserver.remove(displayId);
}
observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
mWindowsForAccessibilityObserver.put(displayId, observer);
mAllObserversInitialized &= observer.mInitialized;
} else {
- if (isEmbeddedDisplay(dc)) {
- // If this display is an embedded one, its window observer should be removed along
- // with the window observer of its parent display removed because the window
- // observer of the embedded display and its parent display is the same, and would
- // be removed together when stopping the window tracking of its parent display. So
- // here don't need to do removing window observer of the embedded display again.
- return true;
- }
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver == null) {
@@ -234,16 +219,15 @@ final class AccessibilityController {
throw new IllegalStateException(errorMessage);
}
}
- removeObserverOfEmbeddedDisplay(windowsForA11yObserver);
mWindowsForAccessibilityObserver.remove(displayId);
}
- return true;
}
void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".performComputeChangedWindowsNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; forceSend=" + forceSend);
}
WindowsForAccessibilityObserver observer = null;
@@ -260,8 +244,10 @@ final class AccessibilityController {
}
void setMagnificationSpec(int displayId, MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; spec={" + spec + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -276,8 +262,9 @@ final class AccessibilityController {
}
void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+ "}");
}
@@ -288,9 +275,10 @@ final class AccessibilityController {
}
void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; rectangle={" + rectangle + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -301,9 +289,11 @@ final class AccessibilityController {
}
void onWindowLayersChanged(int displayId) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowLayersChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayId=" + displayId);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -316,15 +306,18 @@ final class AccessibilityController {
}
}
- void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+ void onDisplaySizeChanged(DisplayContent displayContent) {
+
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onRotationChanged",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayContent={" + displayContent + "}");
}
final int displayId = displayContent.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRotationChanged(displayContent);
+ displayMagnifier.onDisplaySizeChanged(displayContent);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
@@ -334,8 +327,9 @@ final class AccessibilityController {
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -346,8 +340,10 @@ final class AccessibilityController {
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
final int displayId = windowState.getDisplayId();
@@ -364,9 +360,9 @@ final class AccessibilityController {
void onWindowFocusChangedNot(int displayId) {
// Not relevant for the display magnifier.
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onWindowFocusChangedNot",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "displayId=" + displayId);
}
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
@@ -426,12 +422,10 @@ final class AccessibilityController {
}
void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- TAG + ".onSomeWindowResizedOrMoved",
- "displayIds={" + displayIds.toString() + "}",
- "".getBytes(),
- callingUid);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onSomeWindowResizedOrMoved",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
+ "displayIds={" + displayIds.toString() + "}", "".getBytes(), callingUid);
}
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
@@ -444,9 +438,10 @@ final class AccessibilityController {
}
void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transaction={" + t + "}");
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -457,8 +452,9 @@ final class AccessibilityController {
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}");
}
final int displayId = windowState.getDisplayId();
@@ -470,17 +466,19 @@ final class AccessibilityController {
}
boolean hasCallbacks() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
+ | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".hasCallbacks",
+ FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver.size() > 0);
}
void setForceShowMagnifiableBounds(int displayId, boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
- "displayId=" + displayId + "; show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
@@ -489,47 +487,10 @@ final class AccessibilityController {
}
}
- void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
- WindowState parentWindow) {
- handleWindowObserverOfEmbeddedDisplay(
- embeddedDisplayId, parentWindow, Binder.getCallingUid());
- }
-
- void handleWindowObserverOfEmbeddedDisplay(
- int embeddedDisplayId, WindowState parentWindow, int callingUid) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
- "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
- + parentWindow + "}",
- "".getBytes(),
- callingUid);
- }
- if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
- return;
- }
- // Finds the parent display of this embedded display
- final int parentDisplayId;
- WindowState candidate = parentWindow;
- while (candidate != null) {
- parentWindow = candidate;
- candidate = parentWindow.getDisplayContent().getParentWindow();
- }
- parentDisplayId = parentWindow.getDisplayId();
- // Uses the observer of parent display
- final WindowsForAccessibilityObserver windowsForA11yObserver =
- mWindowsForAccessibilityObserver.get(parentDisplayId);
-
- if (windowsForA11yObserver != null) {
- windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId);
- // Replaces the observer of embedded display to the one of parent display
- mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver);
- }
- }
-
void onImeSurfaceShownChanged(WindowState windowState, boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(TAG + ".onImeSurfaceShownChanged",
- "windowState=" + windowState + "; shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState=" + windowState + ";shown=" + shown);
}
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
@@ -555,23 +516,6 @@ final class AccessibilityController {
+ "mWindowsForAccessibilityObserver=" + mWindowsForAccessibilityObserver);
}
- private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver
- observerOfParentDisplay) {
- final IntArray embeddedDisplayIdList =
- observerOfParentDisplay.getAndClearEmbeddedDisplayIdList();
-
- for (int index = 0; index < embeddedDisplayIdList.size(); index++) {
- final int embeddedDisplayId = embeddedDisplayIdList.get(index);
- mWindowsForAccessibilityObserver.remove(embeddedDisplayId);
- }
- }
-
- private static boolean isEmbeddedDisplay(DisplayContent dc) {
- final Display display = dc.getDisplay();
-
- return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null;
- }
-
/**
* This class encapsulates the functionality related to display magnification.
*/
@@ -580,7 +524,7 @@ final class AccessibilityController {
private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
- private static final boolean DEBUG_ROTATION = false;
+ private static final boolean DEBUG_DISPLAY_SIZE = false;
private static final boolean DEBUG_LAYERS = false;
private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
private static final boolean DEBUG_VIEWPORT_WINDOW = false;
@@ -599,7 +543,7 @@ final class AccessibilityController {
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final MagnificationCallbacks mCallbacks;
@@ -618,11 +562,13 @@ final class AccessibilityController {
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowManagerService={" + windowManagerService + "}; displayContent={"
+ displayContent + "}; display={" + display + "}; callbacks={"
+ callbacks + "}");
@@ -630,9 +576,9 @@ final class AccessibilityController {
}
void setMagnificationSpec(MagnificationSpec spec) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
}
mMagnifedViewport.updateMagnificationSpec(spec);
mMagnifedViewport.recomputeBounds();
@@ -642,25 +588,26 @@ final class AccessibilityController {
}
void setForceShowMagnifiableBounds(boolean show) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
}
mForceShowMagnifiableBounds = show;
mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
}
boolean isForceShowingMagnifiableBounds() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
return mForceShowMagnifiableBounds;
}
void onRectangleOnScreenRequested(Rect rectangle) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onRectangleOnScreenRequested",
+ FLAGS_MAGNIFICATION_CALLBACK, "rectangle={" + rectangle + "}");
}
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -683,8 +630,9 @@ final class AccessibilityController {
}
void onWindowLayersChanged() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(
+ LOG_TAG + ".onWindowLayersChanged", FLAGS_MAGNIFICATION_CALLBACK);
}
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
@@ -693,23 +641,24 @@ final class AccessibilityController {
mService.scheduleAnimationLocked();
}
- void onRotationChanged(DisplayContent displayContent) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+ void onDisplaySizeChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onDisplaySizeChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayContent={" + displayContent + "}");
}
- if (DEBUG_ROTATION) {
+ if (DEBUG_DISPLAY_SIZE) {
final int rotation = displayContent.getRotation();
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChanged();
- mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+ mMagnifedViewport.onDisplaySizeChanged();
+ mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
}
void onAppWindowTransition(int displayId, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onAppWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"displayId=" + displayId + "; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -733,8 +682,9 @@ final class AccessibilityController {
}
void onWindowTransition(WindowState windowState, int transition) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onWindowTransition",
+ FLAGS_MAGNIFICATION_CALLBACK,
"windowState={" + windowState + "}; transition=" + transition);
}
if (DEBUG_WINDOW_TRANSITIONS) {
@@ -791,18 +741,18 @@ final class AccessibilityController {
}
void onImeSurfaceShownChanged(boolean shown) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".onImeSurfaceShownChanged", "shown=" + shown);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".onImeSurfaceShownChanged",
+ FLAGS_MAGNIFICATION_CALLBACK, "shown=" + shown);
}
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED,
shown ? 1 : 0, 0).sendToTarget();
}
MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
- "windowState={" + windowState + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpecForWindow",
+ FLAGS_MAGNIFICATION_CALLBACK, "windowState={" + windowState + "}");
}
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
if (spec != null && !spec.isNop()) {
@@ -814,8 +764,9 @@ final class AccessibilityController {
}
void getMagnificationRegion(Region outMagnificationRegion) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
+ FLAGS_MAGNIFICATION_CALLBACK,
"outMagnificationRegion={" + outMagnificationRegion + "}");
}
// Make sure we're working with the most current bounds
@@ -824,25 +775,26 @@ final class AccessibilityController {
}
void destroy() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".destroy", FLAGS_MAGNIFICATION_CALLBACK);
}
mMagnifedViewport.destroyWindow();
}
// Can be called outside of a surface transaction
void showMagnificationBoundsIfNeeded() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK);
}
mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
.sendToTarget();
}
void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
- "transition={" + t + "}");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK, "transition={" + t + "}");
}
mMagnifedViewport.drawWindowIfNeeded(t);
}
@@ -887,7 +839,8 @@ final class AccessibilityController {
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mDisplay.getRealSize(mScreenSize);
+
+ getDisplaySizeLocked(mScreenSize);
final int centerXY = mScreenSize.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
@@ -917,7 +870,7 @@ final class AccessibilityController {
}
void recomputeBounds() {
- mDisplay.getRealSize(mScreenSize);
+ getDisplaySizeLocked(mScreenSize);
final int screenWidth = mScreenSize.x;
final int screenHeight = mScreenSize.y;
@@ -1052,9 +1005,10 @@ final class AccessibilityController {
|| windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
}
- void onRotationChanged() {
+ void onDisplaySizeChanged() {
// If we are showing the magnification border, hide it immediately so
- // the user does not see strange artifacts during rotation. The screenshot
+ // the user does not see strange artifacts during display size changed caused by
+ // rotation or folding/unfolding the device. In the rotation case, the screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
if (isMagnifying() || isForceShowingMagnifiableBounds()) {
@@ -1112,6 +1066,12 @@ final class AccessibilityController {
}, false /* traverseTopToBottom */ );
}
+ private void getDisplaySizeLocked(Point outSize) {
+ final Rect bounds =
+ mDisplayContent.getConfiguration().windowConfiguration.getBounds();
+ outSize.set(bounds.width(), bounds.height());
+ }
+
void dump(PrintWriter pw, String prefix) {
mWindow.dump(pw, prefix);
}
@@ -1155,7 +1115,7 @@ final class AccessibilityController {
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
final int layer =
mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
- WindowManagerService.TYPE_LAYER_MULTIPLIER;
+ WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
mDisplayContent.getDisplayId(), "Magnification Overlay");
@@ -1226,7 +1186,7 @@ final class AccessibilityController {
void updateSize() {
synchronized (mService.mGlobalLock) {
- mDisplay.getRealSize(mScreenSize);
+ getDisplaySizeLocked(mScreenSize);
mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
PixelFormat.RGBA_8888);
invalidate(mDirtyRect);
@@ -1365,7 +1325,7 @@ final class AccessibilityController {
public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
- public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+ public static final int MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
public static final int MESSAGE_NOTIFY_IME_WINDOW_VISIBILITY_CHANGED = 6;
@@ -1397,9 +1357,8 @@ final class AccessibilityController {
mCallbacks.onUserContextChanged();
} break;
- case MESSAGE_NOTIFY_ROTATION_CHANGED: {
- final int rotation = message.arg1;
- mCallbacks.onRotationChanged(rotation);
+ case MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED: {
+ mCallbacks.onDisplaySizeChanged();
} break;
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
@@ -1482,7 +1441,7 @@ final class AccessibilityController {
private final Handler mHandler;
- private final AccessibilityTracing mAccessibilityTracing;
+ private final AccessibilityControllerInternalImpl mAccessibilityTracing;
private final WindowsForAccessibilityCallback mCallback;
@@ -1490,8 +1449,6 @@ final class AccessibilityController {
private final long mRecurringAccessibilityEventsIntervalMillis;
- private final IntArray mEmbeddedDisplayIdList = new IntArray(0);
-
// Set to true if initializing window population complete.
private boolean mInitialized;
@@ -1502,24 +1459,26 @@ final class AccessibilityController {
mCallback = callback;
mDisplayId = displayId;
mHandler = new MyHandler(mService.mH.getLooper());
- mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
+ mAccessibilityTracing =
+ AccessibilityController.getAccessibilityControllerInternal(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
computeChangedWindows(true);
}
void performComputeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
- "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".performComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows(forceSend);
}
void scheduleComputeChangedWindows() {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".scheduleComputeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK);
}
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
@@ -1527,21 +1486,6 @@ final class AccessibilityController {
}
}
- IntArray getAndClearEmbeddedDisplayIdList() {
- final IntArray returnedArray = new IntArray(mEmbeddedDisplayIdList.size());
- returnedArray.addAll(mEmbeddedDisplayIdList);
- mEmbeddedDisplayIdList.clear();
-
- return returnedArray;
- }
-
- void addEmbeddedDisplay(int displayId) {
- if (displayId == mDisplayId) {
- return;
- }
- mEmbeddedDisplayIdList.add(displayId);
- }
-
boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
@@ -1594,9 +1538,9 @@ final class AccessibilityController {
* @param forceSend Send the windows the accessibility even if they haven't changed.
*/
void computeChangedWindows(boolean forceSend) {
- if (mAccessibilityTracing.isEnabled()) {
- mAccessibilityTracing.logState(
- LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".computeChangedWindows",
+ FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK, "forceSend=" + forceSend);
}
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
@@ -1708,12 +1652,7 @@ final class AccessibilityController {
addedWindows.clear();
// Gets the top focused display Id and window token for supporting multi-display.
- // If this top focused display is an embedded one, using its parent display as the
- // top focused display.
- final DisplayContent topFocusedDisplayContent =
- mService.mRoot.getTopFocusedDisplayContent();
- topFocusedDisplayId = isEmbeddedDisplay(topFocusedDisplayContent) ? mDisplayId
- : topFocusedDisplayContent.getDisplayId();
+ topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
@@ -1917,8 +1856,6 @@ final class AccessibilityController {
public String toString() {
return "WindowsForAccessibilityObserver{"
+ "mDisplayId=" + mDisplayId
- + ", mEmbeddedDisplayIdList="
- + Arrays.toString(mEmbeddedDisplayIdList.toArray())
+ ", mInitialized=" + mInitialized
+ '}';
}
@@ -1945,8 +1882,8 @@ final class AccessibilityController {
private static final class AccessibilityControllerInternalImpl
implements AccessibilityControllerInternal {
- private static AccessibilityControllerInternal sInstance;
- static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+ private static AccessibilityControllerInternalImpl sInstance;
+ static AccessibilityControllerInternalImpl getInstance(WindowManagerService service) {
synchronized (STATIC_LOCK) {
if (sInstance == null) {
sInstance = new AccessibilityControllerInternalImpl(service);
@@ -1956,18 +1893,23 @@ final class AccessibilityController {
}
private final AccessibilityTracing mTracing;
+ private volatile long mEnabledTracingFlags;
+
private AccessibilityControllerInternalImpl(WindowManagerService service) {
mTracing = AccessibilityTracing.getInstance(service);
+ mEnabledTracingFlags = 0L;
}
@Override
- public void startTrace() {
+ public void startTrace(long loggingTypes) {
+ mEnabledTracingFlags = loggingTypes;
mTracing.startTrace();
}
@Override
public void stopTrace() {
mTracing.stopTrace();
+ mEnabledTracingFlags = 0L;
}
@Override
@@ -1975,19 +1917,37 @@ final class AccessibilityController {
return mTracing.isEnabled();
}
+ boolean isTracingEnabled(long flags) {
+ return (flags & mEnabledTracingFlags) != 0L;
+ }
+
+ void logTrace(String where, long loggingTypes) {
+ logTrace(where, loggingTypes, "");
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams) {
+ logTrace(where, loggingTypes, callingParams, "".getBytes(), Binder.getCallingUid());
+ }
+
+ void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid,
+ new HashSet<String>(Arrays.asList("logTrace")));
+ }
+
@Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
+ ignoreStackEntries);
}
@Override
- public void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId) {
- mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp,
- processId, threadId);
+ public void logTrace(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
+ mTracing.logState(where, loggingTypes, callingParams, a11yDump, callingUid, callStack,
+ timeStamp, processId, threadId, ignoreStackEntries);
}
}
@@ -2002,9 +1962,10 @@ final class AccessibilityController {
}
}
+ private static final int CPU_STATS_COUNT = 5;
private static final int BUFFER_CAPACITY = 1024 * 1024 * 12;
- private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
- private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
+ private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace"
+ + WINSCOPE_EXT;
private static final String TAG = "AccessibilityTracing";
private static final long MAGIC_NUMBER_VALUE =
((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
@@ -2034,13 +1995,6 @@ final class AccessibilityController {
return;
}
synchronized (mLock) {
- try {
- Files.createDirectories(Paths.get(TRACE_DIRECTORY));
- mTraceFile.createNewFile();
- } catch (Exception e) {
- Slog.e(TAG, "Error: Failed to create trace file.");
- return;
- }
mEnabled = true;
mBuffer.resetBuffer();
}
@@ -2071,106 +2025,150 @@ final class AccessibilityController {
/**
* Write an accessibility trace log entry.
*/
- void logState(String where) {
+ void logState(String where, long loggingTypes) {
if (!mEnabled) {
return;
}
- logState(where, "");
+ logState(where, loggingTypes, "");
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams) {
+ void logState(String where, long loggingTypes, String callingParams) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, "".getBytes());
+ logState(where, loggingTypes, callingParams, "".getBytes());
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump) {
if (!mEnabled) {
return;
}
- logState(where, callingParams, a11yDump, Binder.getCallingUid());
+ logState(where, loggingTypes, callingParams, a11yDump, Binder.getCallingUid(),
+ new HashSet<String>(Arrays.asList("logState")));
}
/**
* Write an accessibility trace log entry.
*/
- void logState(
- String where, String callingParams, byte[] a11yDump, int callingUid) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
-
- logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+ ignoreStackEntries.add("logState");
+ logState(where, loggingTypes, callingParams, a11yDump, callingUid, stackTraceElements,
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
-
- log(where, callingParams, a11yDump, callingUid, stackTrace,
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, stackTrace,
SystemClock.elapsedRealtimeNanos(),
Process.myPid() + ":" + Application.getProcessName(),
- Thread.currentThread().getId() + ":" + Thread.currentThread().getName());
+ Thread.currentThread().getId() + ":" + Thread.currentThread().getName(),
+ ignoreStackEntries);
}
/**
* Write an accessibility trace log entry.
*/
- void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) {
+ void logState(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp, int processId,
+ long threadId, Set<String> ignoreStackEntries) {
if (!mEnabled) {
return;
}
- log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp,
- String.valueOf(processId), String.valueOf(threadId));
+ log(where, loggingTypes, callingParams, a11yDump, callingUid, callingStack, timeStamp,
+ String.valueOf(processId), String.valueOf(threadId), ignoreStackEntries);
}
- private String toStackTraceString(StackTraceElement[] stackTraceElements) {
+ private String toStackTraceString(StackTraceElement[] stackTraceElements,
+ Set<String> ignoreStackEntries) {
+
if (stackTraceElements == null) {
return "";
}
+
StringBuilder stringBuilder = new StringBuilder();
- boolean skip = true;
- for (int i = 0; i < stackTraceElements.length; i++) {
- if (stackTraceElements[i].toString().contains(
- AccessibilityTracing.class.getSimpleName())) {
- skip = false;
- } else if (!skip) {
- stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ int i = 0;
+
+ // Skip the first a few elements until after any ignoreStackEntries
+ int firstMatch = -1;
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // found the first stack element containing the ignorable stack entries
+ firstMatch = i;
+ break;
+ }
+ }
+ if (firstMatch < 0) {
+ // Haven't found the first match yet, continue
+ i++;
+ } else {
+ break;
}
}
+ int lastMatch = firstMatch;
+ if (i < stackTraceElements.length) {
+ i++;
+ // Found the first match. Now look for the last match.
+ while (i < stackTraceElements.length) {
+ for (String ele : ignoreStackEntries) {
+ if (stackTraceElements[i].toString().contains(ele)) {
+ // This is a match. Look at the next stack element.
+ lastMatch = i;
+ break;
+ }
+ }
+ if (lastMatch != i) {
+ // Found a no-match.
+ break;
+ }
+ i++;
+ }
+ }
+
+ i = lastMatch + 1;
+ while (i < stackTraceElements.length) {
+ stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ i++;
+ }
return stringBuilder.toString();
}
/**
* Write the current state to the buffer
*/
- private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callingStack, long timeStamp, String processName,
- String threadName) {
+ private void log(String where, long loggingTypes, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] callingStack, long timeStamp,
+ String processName, String threadName, Set<String> ignoreStackEntries) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = timeStamp;
- args.arg2 = where;
- args.arg3 = processName;
- args.arg4 = threadName;
- args.arg5 = callingUid;
- args.arg6 = callingParams;
- args.arg7 = callingStack;
- args.arg8 = a11yDump;
- mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+ args.arg2 = loggingTypes;
+ args.arg3 = where;
+ args.arg4 = processName;
+ args.arg5 = threadName;
+ args.arg6 = ignoreStackEntries;
+ args.arg7 = callingParams;
+ args.arg8 = callingStack;
+ args.arg9 = a11yDump;
+
+ mHandler.obtainMessage(
+ LogHandler.MESSAGE_LOG_TRACE_ENTRY, callingUid, 0, args).sendToTarget();
}
/**
@@ -2199,8 +2197,6 @@ final class AccessibilityController {
LocalServices.getService(PackageManagerInternal.class);
long tokenOuter = os.start(ENTRY);
- String callingStack =
- toStackTraceString((StackTraceElement[]) args.arg7);
long reportedTimeStampNanos = (long) args.arg1;
long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -2213,19 +2209,32 @@ final class AccessibilityController {
os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos);
os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString());
- os.write(WHERE, (String) args.arg2);
- os.write(PROCESS_NAME, (String) args.arg3);
- os.write(THREAD_ID_NAME, (String) args.arg4);
- os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5));
- os.write(CALLING_PARAMS, (String) args.arg6);
+
+ long loggingTypes = (long) args.arg2;
+ List<String> loggingTypeNames =
+ AccessibilityTrace.getNamesOfLoggingTypes(loggingTypes);
+
+ for (String type : loggingTypeNames) {
+ os.write(LOGGING_TYPE, type);
+ }
+ os.write(WHERE, (String) args.arg3);
+ os.write(PROCESS_NAME, (String) args.arg4);
+ os.write(THREAD_ID_NAME, (String) args.arg5);
+ os.write(CALLING_PKG, pmInternal.getNameForUid(message.arg1));
+ os.write(CALLING_PARAMS, (String) args.arg7);
+
+ String callingStack = toStackTraceString(
+ (StackTraceElement[]) args.arg8, (Set<String>) args.arg6);
+
os.write(CALLING_STACKS, callingStack);
- os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8);
+ os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
synchronized (mService.mGlobalLock) {
mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
}
os.end(tokenInner);
+ os.write(CPU_STATS, printCpuStats(reportedTimeStampNanos));
os.end(tokenOuter);
synchronized (mLock) {
@@ -2258,5 +2267,15 @@ final class AccessibilityController {
Slog.e(TAG, "Unable to write buffer to file", e);
}
}
+
+ /**
+ * Returns the string of CPU stats.
+ */
+ private String printCpuStats(long timeStampNanos) {
+ Pair<String, String> stats = mService.mAmInternal.getAppProfileStatsForDebugging(
+ timeStampNanos, CPU_STATS_COUNT);
+
+ return stats.first + stats.second;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 3a4faf73bfe1..043e2dd07087 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -21,6 +21,8 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -28,6 +30,11 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -35,10 +42,9 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -53,6 +59,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -64,16 +71,19 @@ import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
+import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
import android.window.SizeConfigurationBuckets;
+import android.window.TransitionInfo;
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.uri.NeededUriGrants;
import com.android.server.vr.VrManagerInternal;
@@ -188,7 +198,7 @@ class ActivityClientController extends IActivityClientController.Stub {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
r = ActivityRecord.isInRootTaskLocked(token);
if (r != null) {
- if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
// The activity was requested to restart from
// {@link #restartActivityProcessIfVisible}.
restartingName = r.app.mName;
@@ -535,6 +545,27 @@ class ActivityClientController extends IActivityClientController.Stub {
}
@Override
+ @Nullable
+ public IBinder getActivityTokenBelow(IBinder activityToken) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken);
+ if (ar == null) {
+ return null;
+ }
+ final ActivityRecord below = ar.getTask().getActivityBelow(ar);
+ if (below != null && below.getUid() == ar.getUid()) {
+ return below.appToken.asBinder();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return null;
+ }
+
+ @Override
public ComponentName getCallingActivity(IBinder token) {
synchronized (mGlobalLock) {
final ActivityRecord r = getCallingRecord(token);
@@ -557,20 +588,45 @@ class ActivityClientController extends IActivityClientController.Stub {
@Override
public int getLaunchedFromUid(IBinder token) {
+ if (!canGetLaunchedFrom()) {
+ return INVALID_UID;
+ }
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- return r != null ? r.launchedFromUid : android.os.Process.INVALID_UID;
+ return r != null ? r.launchedFromUid : INVALID_UID;
}
}
@Override
public String getLaunchedFromPackage(IBinder token) {
+ if (!canGetLaunchedFrom()) {
+ return null;
+ }
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
return r != null ? r.launchedFromPackage : null;
}
}
+ /** Whether the caller can get the package or uid that launched its activity. */
+ private boolean canGetLaunchedFrom() {
+ final int uid = Binder.getCallingUid();
+ if (UserHandle.getAppId(uid) == SYSTEM_UID) {
+ return true;
+ }
+ final PackageManagerInternal pm = mService.mWindowManager.mPmInternal;
+ final AndroidPackage callingPkg = pm.getPackage(uid);
+ if (callingPkg == null) {
+ return false;
+ }
+ if (callingPkg.isSignedWithPlatformKey()) {
+ return true;
+ }
+ final String[] installerNames = pm.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.getUserId(uid));
+ return installerNames.length > 0 && callingPkg.getPackageName().equals(installerNames[0]);
+ }
+
@Override
public void setRequestedOrientation(IBinder token, int requestedOrientation) {
final long origId = Binder.clearCallingIdentity();
@@ -1010,10 +1066,14 @@ class ActivityClientController extends IActivityClientController.Stub {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
+ if (r != null && r.isState(RESUMED, PAUSING)) {
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null, null,
r.mOverrideTaskTransition);
+ mService.getTransitionController().setOverrideAnimation(
+ TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
+ enterAnim, exitAnim, r.mOverrideTaskTransition),
+ null /* startCallback */, null /* finishCallback */);
}
}
Binder.restoreCallingIdentity(origId);
@@ -1153,7 +1213,7 @@ class ActivityClientController extends IActivityClientController.Stub {
try {
final Intent baseActivityIntent;
final boolean launchedFromHome;
-
+ final boolean isLastRunningActivity;
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
if (r == null) return;
@@ -1165,22 +1225,25 @@ class ActivityClientController extends IActivityClientController.Stub {
return;
}
- final Intent baseIntent = r.getTask().getBaseIntent();
- final boolean activityIsBaseActivity = baseIntent != null
- && r.mActivityComponent.equals(baseIntent.getComponent());
- baseActivityIntent = activityIsBaseActivity ? r.intent : null;
+ final Task task = r.getTask();
+ isLastRunningActivity = task.topRunningActivity() == r;
+
+ final boolean isBaseActivity = r.mActivityComponent.equals(task.realActivity);
+ baseActivityIntent = isBaseActivity ? r.intent : null;
+
launchedFromHome = r.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME);
}
// 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
+ // 1. The activity is the last running activity in the task.
+ // 2. The current activity is the base activity for the task.
+ // 3. 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
+ if (baseActivityIntent != null && isLastRunningActivity
&& ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent))
|| isLauncherActivity(baseActivityIntent.getComponent()))) {
moveActivityTaskToBack(token, false /* nonRoot */);
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 494f49663b67..ce1592d87fcd 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -187,6 +187,10 @@ class ActivityMetricsLogger {
return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
}
+ boolean hasActiveTransitionInfo() {
+ return mAssociatedTransitionInfo != null;
+ }
+
boolean contains(ActivityRecord r) {
return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r);
}
@@ -300,9 +304,12 @@ class ActivityMetricsLogger {
return;
}
if (mLastLaunchedActivity != null) {
- // Transfer the launch cookie because it is a consecutive launch event.
+ // Transfer the launch cookie and launch root task because it is a consecutive
+ // launch event.
r.mLaunchCookie = mLastLaunchedActivity.mLaunchCookie;
mLastLaunchedActivity.mLaunchCookie = null;
+ r.mLaunchRootTask = mLastLaunchedActivity.mLaunchRootTask;
+ mLastLaunchedActivity.mLaunchRootTask = null;
}
mLastLaunchedActivity = r;
if (!r.noDisplay && !r.isReportedDrawn()) {
@@ -333,10 +340,11 @@ class ActivityMetricsLogger {
}
/** Only keep the records which can be drawn. */
- void updatePendingDraw() {
+ void updatePendingDraw(boolean keepInitializing) {
for (int i = mPendingDrawActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mPendingDrawActivities.get(i);
- if (!r.mVisibleRequested) {
+ if (!r.mVisibleRequested
+ && !(keepInitializing && r.isState(ActivityRecord.State.INITIALIZING))) {
if (DEBUG_METRICS) Slog.i(TAG, "Discard pending draw " + r);
mPendingDrawActivities.remove(i);
}
@@ -630,6 +638,7 @@ class ActivityMetricsLogger {
if (crossPackage) {
startLaunchTrace(info);
}
+ scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
return;
}
@@ -651,13 +660,7 @@ class ActivityMetricsLogger {
// As abort for no process switch.
launchObserverNotifyIntentFailed();
}
- if (launchedActivity.mDisplayContent.isSleeping()) {
- // It is unknown whether the activity can be drawn or not, e.g. it depends on the
- // keyguard states and the attributes or flags set by the activity. If the activity
- // keeps invisible in the grace period, the tracker will be cancelled so it won't get
- // a very long launch time that takes unlocking as the end of launch.
- scheduleCheckActivityToBeDrawn(launchedActivity, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
- }
+ scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
// If the previous transitions are no longer visible, abort them to avoid counting the
// launch time when resuming from back stack. E.g. launch 2 independent tasks in a short
@@ -665,13 +668,23 @@ class ActivityMetricsLogger {
// visible such as after the top task is finished.
for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
final TransitionInfo prevInfo = mTransitionInfoList.get(i);
- prevInfo.updatePendingDraw();
+ prevInfo.updatePendingDraw(false /* keepInitializing */);
if (prevInfo.allDrawn()) {
abort(prevInfo, "nothing will be drawn");
}
}
}
+ private void scheduleCheckActivityToBeDrawnIfSleeping(@NonNull ActivityRecord r) {
+ if (r.mDisplayContent.isSleeping()) {
+ // It is unknown whether the activity can be drawn or not, e.g. it depends on the
+ // keyguard states and the attributes or flags set by the activity. If the activity
+ // keeps invisible in the grace period, the tracker will be cancelled so it won't get
+ // a very long launch time that takes unlocking as the end of launch.
+ scheduleCheckActivityToBeDrawn(r, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
+ }
+ }
+
/**
* Notifies the tracker that all windows of the app have been drawn.
*
@@ -741,7 +754,9 @@ class ActivityMetricsLogger {
info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
info.mReason = activityToReason.valueAt(index);
info.mLoggedTransitionStarting = true;
- info.updatePendingDraw();
+ // Do not remove activity in initializing state because the transition may be started
+ // by starting window. The initializing activity may be requested to visible soon.
+ info.updatePendingDraw(true /* keepInitializing */);
if (info.allDrawn()) {
done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
timestampNs);
@@ -775,7 +790,7 @@ class ActivityMetricsLogger {
Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested
+ " state=" + r.getState() + " finishing=" + r.finishing);
}
- if (r.isState(Task.ActivityState.RESUMED) && r.mDisplayContent.isSleeping()) {
+ if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) {
// The activity may be launching while keyguard is locked. The keyguard may be dismissed
// after the activity finished relayout, so skip the visibility check to avoid aborting
// the tracking of launch event.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 308df2f4e9a0..099dad49c5ec 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -129,6 +129,17 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.APP_STOPPED;
import static com.android.server.wm.ActivityRecordProto.CLIENT_VISIBLE;
@@ -191,18 +202,7 @@ import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESTARTING_PROCESS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -273,6 +273,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -303,6 +304,7 @@ import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
@@ -316,18 +318,18 @@ import android.window.SplashScreen;
import android.window.SplashScreenView;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
+import android.window.TransitionInfo.AnimationOptions;
import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledFunction;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
@@ -338,7 +340,6 @@ import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.TransitionInfoSnapshot;
import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.Task.ActivityState;
import com.android.server.wm.WindowManagerService.H;
import com.android.server.wm.utils.InsetUtils;
@@ -347,6 +348,7 @@ import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -490,7 +492,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
UriPermissionOwner uriPermissions; // current special URI access perms.
WindowProcessController app; // if non-null, hosting application
- private ActivityState mState; // current state we are in
+ private State mState; // current state we are in
private Bundle mIcicle; // last saved activity state
private PersistableBundle mPersistentState; // last persistently saved activity state
private boolean mHaveState = true; // Indicates whether the last saved state of activity is
@@ -540,11 +542,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final ActivityTaskSupervisor mTaskSupervisor;
final RootWindowContainer mRootWindowContainer;
- static final int STARTING_WINDOW_NOT_SHOWN = 0;
- static final int STARTING_WINDOW_SHOWN = 1;
- static final int STARTING_WINDOW_REMOVED = 2;
- int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
-
// Tracking splash screen status from previous activity
boolean mSplashScreenStyleEmpty = false;
@@ -552,6 +549,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
static final int LAUNCH_SOURCE_TYPE_APPLICATION = 4;
+
+ enum State {
+ INITIALIZING,
+ STARTED,
+ RESUMED,
+ PAUSING,
+ PAUSED,
+ STOPPING,
+ STOPPED,
+ FINISHING,
+ DESTROYING,
+ DESTROYED,
+ RESTARTING_PROCESS
+ }
+
/**
* The type of launch source.
*/
@@ -648,6 +660,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean allDrawn;
private boolean mLastAllDrawn;
+ /**
+ * Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
+ * Actiivty was part of a syncset, all windows were ready by the time the sync was ready (vs.
+ * only the top-occluding ones). The assumption here is if some were not ready, they were
+ * covered with starting-window/splash-screen.
+ */
+ boolean mLastAllReadyAtSync = false;
+
private boolean mLastContainsShowWhenLockedWindow;
private boolean mLastContainsDismissKeyguardWindow;
private boolean mLastContainsTurnScreenOnWindow;
@@ -722,6 +742,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean startingDisplayed;
boolean startingMoved;
+ /**
+ * If it is non-null, it requires all activities who have the same starting data to be drawn
+ * to remove the starting window.
+ * TODO(b/189385912): Remove starting window related fields after migrating them to task.
+ */
+ private StartingData mSharedStartingData;
+
boolean mHandleExitSplashScreen;
@TransferSplashScreenState
int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
@@ -791,6 +818,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpOutNonDecorBounds = new Rect();
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -802,6 +830,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Tracking cookie for the launch of this activity and it's task.
IBinder mLaunchCookie;
+ // Tracking indicated launch root in order to propagate it among trampoline activities.
+ WindowContainerToken mLaunchRootTask;
+
// Entering PiP is usually done in two phases, we put the task into pinned mode first and
// SystemUi sets the pinned mode on activity after transition is done.
boolean mWaitForEnteringPinnedMode;
@@ -856,19 +887,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
};
- private static String startingWindowStateToString(int state) {
- switch (state) {
- case STARTING_WINDOW_NOT_SHOWN:
- return "STARTING_WINDOW_NOT_SHOWN";
- case STARTING_WINDOW_SHOWN:
- return "STARTING_WINDOW_SHOWN";
- case STARTING_WINDOW_REMOVED:
- return "STARTING_WINDOW_REMOVED";
- default:
- return "unknown state=" + state;
- }
- }
-
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final long now = SystemClock.uptimeMillis();
@@ -1012,6 +1030,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print("launchCookie=");
pw.println(mLaunchCookie);
}
+ if (mLaunchRootTask != null) {
+ pw.print(prefix);
+ pw.print("mLaunchRootTask=");
+ pw.println(mLaunchRootTask);
+ }
pw.print(prefix); pw.print("mHaveState="); pw.print(mHaveState);
pw.print(" mIcicle="); pw.println(mIcicle);
pw.print(prefix); pw.print("state="); pw.print(mState);
@@ -1020,9 +1043,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" finishing="); pw.println(finishing);
pw.print(prefix); pw.print("keysPaused="); pw.print(keysPaused);
pw.print(" inHistory="); pw.print(inHistory);
- pw.print(" idle="); pw.print(idle);
- pw.print(" mStartingWindowState=");
- pw.println(startingWindowStateToString(mStartingWindowState));
+ pw.print(" idle="); pw.println(idle);
pw.print(prefix); pw.print("occludesParent="); pw.print(occludesParent());
pw.print(" noDisplay="); pw.print(noDisplay);
pw.print(" immersive="); pw.print(immersive);
@@ -1067,6 +1088,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
+ if (mSharedStartingData != null) {
+ pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
+ }
if (mStartingWindow != null || mStartingSurface != null
|| startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow);
@@ -1140,6 +1164,76 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterboxUiController.dump(pw, prefix);
}
+ static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
+ String prefix, String label, boolean complete, boolean brief, boolean client,
+ String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
+ if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
+ return false;
+ }
+
+ final boolean full = !brief && (complete || !r.isInHistory());
+ if (needNL) {
+ pw.println("");
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ String innerPrefix = prefix + " ";
+ String[] args = new String[0];
+ if (lastTask != r.getTask()) {
+ lastTask = r.getTask();
+ pw.print(prefix);
+ pw.print(full ? "* " : " ");
+ pw.println(lastTask);
+ if (full) {
+ lastTask.dump(pw, prefix + " ");
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ if (lastTask.intent != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.println(lastTask.intent.toInsecureString());
+ }
+ }
+ }
+ pw.print(prefix); pw.print(full ? "* " : " "); pw.print(label);
+ pw.print(" #"); pw.print(index); pw.print(": ");
+ pw.println(r);
+ if (full) {
+ r.dump(pw, innerPrefix, true /* dumpAll */);
+ } else if (complete) {
+ // Complete + brief == give a summary. Isn't that obvious?!?
+ pw.print(innerPrefix);
+ pw.println(r.intent.toInsecureString());
+ if (r.app != null) {
+ pw.print(innerPrefix);
+ pw.println(r.app);
+ }
+ }
+ if (client && r.attachedToProcess()) {
+ // flush anything that is already in the PrintWriter since the thread is going
+ // to write to the file descriptor directly
+ pw.flush();
+ try {
+ TransferPipe tp = new TransferPipe();
+ try {
+ r.app.getThread().dumpActivity(
+ tp.getWriteFd(), r.appToken, innerPrefix, args);
+ // Short timeout, since blocking here can deadlock with the application.
+ tp.go(fd, 2000);
+ } finally {
+ tp.kill();
+ }
+ } catch (IOException e) {
+ pw.println(innerPrefix + "Failure while dumping the activity: " + e);
+ } catch (RemoteException e) {
+ pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
+ }
+ }
+ return true;
+ }
+
void setAppTimeTracker(AppTimeTracker att) {
appTimeTracker = att;
}
@@ -1249,11 +1343,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updatePictureInPictureMode(null, false);
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
- computeConfigurationAfterMultiWindowModeChange();
// If the activity is in stopping or stopped state, for instance, it's in the
// split screen task and not the top one, the last configuration it should keep
// is the one before multi-window mode change.
- final ActivityState state = getState();
+ final State state = getState();
if (state != STOPPED && state != STOPPING) {
ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
true /* ignoreVisibility */);
@@ -1276,31 +1369,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// precede the configuration change from the resize.
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
- if (targetRootTaskBounds != null && !targetRootTaskBounds.isEmpty()) {
- computeConfigurationAfterMultiWindowModeChange();
- }
ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
true /* ignoreVisibility */);
}
}
- private void computeConfigurationAfterMultiWindowModeChange() {
- final Configuration newConfig = new Configuration();
- newConfig.setTo(task.getRequestedOverrideConfiguration());
- Rect outBounds = newConfig.windowConfiguration.getBounds();
- final Configuration parentConfig = task.getParent().getConfiguration();
- task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
- task.computeConfigResourceOverrides(newConfig, parentConfig);
- }
-
Task getTask() {
return task;
}
+ @Nullable
+ TaskFragment getTaskFragment() {
+ WindowContainer parent = getParent();
+ return parent != null ? parent.asTaskFragment() : null;
+ }
+
@Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final Task oldTask = oldParent != null ? (Task) oldParent : null;
- final Task newTask = newParent != null ? (Task) newParent : null;
+ void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
+ final TaskFragment oldParent = (TaskFragment) rawOldParent;
+ final TaskFragment newParent = (TaskFragment) rawNewParent;
+ final Task oldTask = oldParent != null ? oldParent.getTask() : null;
+ final Task newTask = newParent != null ? newParent.getTask() : null;
this.task = newTask;
super.onParentChanged(newParent, oldParent);
@@ -1358,11 +1447,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
updateColorTransform();
- if (oldTask != null) {
- oldTask.cleanUpActivityReferences(this);
+ if (oldParent != null) {
+ oldParent.cleanUpActivityReferences(this);
}
- if (newTask != null && isState(RESUMED)) {
- newTask.setResumedActivity(this, "onParentChanged");
+
+ if (newParent != null && isState(RESUMED)) {
+ newParent.setResumedActivity(this, "onParentChanged");
+ if (mStartingWindow != null && mStartingData != null
+ && mStartingData.mAssociatedTask == null && newParent.isEmbedded()) {
+ // The starting window should keep covering its task when the activity is
+ // reparented to a task fragment that may not fill the task bounds.
+ associateStartingDataWithTask();
+ overrideConfigurationPropagation(mStartingWindow, task);
+ }
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -1702,6 +1799,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
mHandoverLaunchDisplayId = options.getLaunchDisplayId();
mLaunchCookie = options.getLaunchCookie();
+ mLaunchRootTask = options.getLaunchRootTask();
}
mPersistentState = persistentState;
@@ -1783,6 +1881,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
task.setRootProcess(proc);
}
proc.addActivityIfNeeded(this);
+
+ // Update the associated task fragment after setting the process, since it's required for
+ // filtering to only report activities that belong to the same process.
+ final TaskFragment tf = getTaskFragment();
+ if (tf != null) {
+ tf.sendTaskFragmentInfoChanged();
+ }
}
boolean hasProcess() {
@@ -1912,9 +2017,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ @VisibleForTesting
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,
+ ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) {
// If the display is frozen, we won't do anything until the actual window is
// displayed so there is no reason to put in the starting window.
@@ -1973,7 +2079,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
applyStartingWindowTheme(pkg, resolvedTheme);
- if (transferStartingWindow(transferFrom)) {
+ if (from != null && transferStartingWindow(from)) {
return true;
}
@@ -1998,6 +2104,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
+ if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
+ // Associate with the task so if this activity is resized by task fragment later, the
+ // starting window can keep the same bounds as the task.
+ associateStartingDataWithTask();
+ }
scheduleAddStartingWindow();
return true;
}
@@ -2246,15 +2357,37 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- void removeStartingWindow() {
- removeStartingWindowAnimation(true /* prepareAnimation */);
+ void attachStartingWindow(@NonNull WindowState startingWindow) {
+ mStartingWindow = startingWindow;
+ if (mStartingData != null && mStartingData.mAssociatedTask != null) {
+ // Associate the configuration of starting window with the task.
+ overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask);
+ }
}
- void removeStartingWindowAnimation(boolean prepareAnimation) {
+ private void associateStartingDataWithTask() {
+ mStartingData.mAssociatedTask = task;
+ task.forAllActivities(r -> {
+ if (r.mVisibleRequested && !r.firstWindowDrawn) {
+ r.mSharedStartingData = mStartingData;
+ }
+ });
+ }
+
+ void removeStartingWindow() {
if (transferSplashScreenIfNeeded()) {
return;
}
+ removeStartingWindowAnimation(true /* prepareAnimation */);
+ }
+
+ void removeStartingWindowAnimation(boolean prepareAnimation) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
+ if (mSharedStartingData != null) {
+ mSharedStartingData.mAssociatedTask.forAllActivities(r -> {
+ r.mSharedStartingData = null;
+ });
+ }
if (mStartingWindow == null) {
if (mStartingData != null) {
// Starting window has not been added yet, but it is scheduled to be added.
@@ -2311,38 +2444,25 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- private void removeAppTokenFromDisplay() {
- if (mWmService.mRoot == null) return;
-
- final DisplayContent dc = mWmService.mRoot.getDisplayContent(getDisplayId());
- if (dc == null) {
- Slog.w(TAG, "removeAppTokenFromDisplay: Attempted to remove token: "
- + appToken + " from non-existing displayId=" + getDisplayId());
- return;
- }
- // Resume key dispatching if it is currently paused before we remove the container.
- resumeKeyDispatchingLocked();
- dc.removeAppToken(appToken.asBinder());
- }
-
/**
- * Reparents this activity into {@param newTask} at the provided {@param position}. The caller
- * should ensure that the {@param newTask} is not already the parent of this activity.
+ * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
+ * caller should ensure that the {@param newTaskFrag} is not already the parent of this
+ * activity.
*/
- void reparent(Task newTask, int position, String reason) {
+ void reparent(TaskFragment newTaskFrag, int position, String reason) {
if (getParent() == null) {
Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
return;
}
- final Task prevTask = task;
- if (prevTask == newTask) {
- throw new IllegalArgumentException(reason + ": task=" + newTask
+ final TaskFragment prevTaskFrag = getTaskFragment();
+ if (prevTaskFrag == newTaskFrag) {
+ throw new IllegalArgumentException(reason + ": task fragment =" + newTaskFrag
+ " is already the parent of r=" + this);
}
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
- + " to task=%d at %d", this, task.mTaskId, position);
- reparent(newTask, position);
+ + " to new task fragment in task=%d at %d", this, task.mTaskId, position);
+ reparent(newTaskFrag, position);
}
private boolean isHomeIntent(Intent intent) {
@@ -2734,7 +2854,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* @return Whether AppOps allows this package to enter picture-in-picture.
*/
- private boolean checkEnterPictureInPictureAppOpsState() {
+ boolean checkEnterPictureInPictureAppOpsState() {
return mAtmService.getAppOpsManager().checkOpNoThrow(
OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
}
@@ -2874,7 +2994,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@interface FinishRequest {}
/**
- * See {@link #finishIfPossible(int, Intent, String, boolean)}
+ * See {@link #finishIfPossible(int, Intent, NeededUriGrants, String, boolean)}
*/
@FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
return finishIfPossible(Activity.RESULT_CANCELED,
@@ -2908,7 +3028,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Task rootTask = getRootTask();
- final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getResumedActivity() == null)
+ final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null)
&& rootTask.isFocusedRootTaskOnDisplay()
// Do not adjust focus task because the task will be reused to launch new activity.
&& !task.isClearingToReuseTask();
@@ -2987,12 +3107,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Tell window manager to prepare for this one to be removed.
setVisibility(false);
- if (task.getPausingActivity() == null) {
+ if (getTaskFragment().getPausingActivity() == null) {
ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this);
if (DEBUG_USER_LEAVING) {
Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false");
}
- task.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
+ getTaskFragment().startPausing(false /* userLeaving */, false /* uiSleeping */,
null /* resuming */, "finish");
}
@@ -3123,8 +3243,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Clear last paused activity to ensure top activity can be resumed during sleeping.
if (isNextNotYetVisible && mDisplayContent.isSleeping()
- && next == next.getRootTask().mLastPausedActivity) {
- next.getRootTask().mLastPausedActivity = null;
+ && next == next.getTaskFragment().mLastPausedActivity) {
+ next.getTaskFragment().clearLastPausedActivity();
}
if (isCurrentVisible) {
@@ -3311,8 +3431,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (DEBUG_SWITCH) {
final Task task = getTask();
Slog.v(TAG_SWITCH, "Safely destroying " + this + " in state " + getState()
- + " resumed=" + task.getResumedActivity()
- + " pausing=" + task.getPausingActivity()
+ + " resumed=" + task.getTopResumedActivity()
+ + " pausing=" + task.getTopPausingActivity()
+ " for reason " + reason);
}
return destroyImmediately(reason);
@@ -3336,7 +3456,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
setState(DESTROYED, "removeFromHistory");
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
detachFromProcess();
- removeAppTokenFromDisplay();
+ // Resume key dispatching if it is currently paused before we remove the container.
+ resumeKeyDispatchingLocked();
+ mDisplayContent.removeAppToken(appToken);
cleanUpActivityServices();
removeUriPermissionsLocked();
@@ -3354,10 +3476,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
finishing = true;
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
+ taskFragment.sendTaskFragmentInfoChanged();
+ }
if (stopped) {
abortAndClearOptionsAnimation();
}
- mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
+ if (mAtmService.getTransitionController().isCollecting()) {
+ // We don't want the finishing to change the transition ready state since there will not
+ // be corresponding setReady for finishing.
+ mAtmService.getTransitionController().collectExistenceChange(this);
+ } else {
+ mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
+ }
}
/**
@@ -3391,7 +3523,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* Note: Call before {@link #removeFromHistory(String)}.
*/
void cleanUp(boolean cleanServices, boolean setState) {
- task.cleanUpActivityReferences(this);
+ getTaskFragment().cleanUpActivityReferences(this);
clearLastParentBeforePip();
// Clean up the splash screen if it was still displayed.
@@ -3499,7 +3631,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// failed more than twice. Skip activities that's already finishing cleanly by itself.
remove = false;
} else if ((!mHaveState && !stateNotNeeded
- && !isState(ActivityState.RESTARTING_PROCESS)) || finishing) {
+ && !isState(State.RESTARTING_PROCESS)) || finishing) {
// Don't currently have state for the activity, or it is finishing -- always remove it.
remove = true;
} else if (!mVisibleRequested && launchCount > 2
@@ -3537,18 +3669,33 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
cleanUp(true /* cleanServices */, true /* setState */);
if (remove) {
+ if (mStartingData != null && mVisible && task != null) {
+ // A corner case that the app terminates its trampoline activity on a separated
+ // process by killing itself. Transfer the starting window to the next activity
+ // which will be visible, so the dead activity can be removed immediately (no
+ // longer animating) and the reveal animation can play normally on next activity.
+ final ActivityRecord top = task.topRunningActivity();
+ if (top != null && !top.mVisible && top.shouldBeVisible()) {
+ top.transferStartingWindow(this);
+ }
+ }
removeFromHistory("appDied");
}
}
@Override
void removeImmediately() {
- if (!finishing) {
+ if (mState != DESTROYED) {
+ Slog.w(TAG, "Force remove immediately " + this + " state=" + mState);
// If Task#removeImmediately is called directly with alive activities, ensure that the
// activities are destroyed and detached from process.
destroyImmediately("removeImmediately");
+ // Complete the destruction immediately because this activity will not be found in
+ // hierarchy, it is unable to report completion.
+ destroyed("removeImmediately");
+ } else {
+ onRemovedFromDisplay();
}
- onRemovedFromDisplay();
super.removeImmediately();
}
@@ -3762,12 +3909,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- boolean transferStartingWindow(IBinder transferFrom) {
- final ActivityRecord fromActivity = getDisplayContent().getActivityRecord(transferFrom);
- if (fromActivity == null) {
- return false;
- }
-
+ private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
final WindowState tStartingWindow = fromActivity.mStartingWindow;
if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
// In this case, the starting icon has already been displayed, so start
@@ -3788,6 +3930,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Transfer the starting window over to the new token.
mStartingData = fromActivity.mStartingData;
+ mSharedStartingData = fromActivity.mSharedStartingData;
mStartingSurface = fromActivity.mStartingSurface;
startingDisplayed = fromActivity.startingDisplayed;
fromActivity.startingDisplayed = false;
@@ -3850,6 +3993,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Moving pending starting from %s to %s", fromActivity, this);
mStartingData = fromActivity.mStartingData;
+ mSharedStartingData = fromActivity.mSharedStartingData;
fromActivity.mStartingData = null;
fromActivity.startingMoved = true;
scheduleAddStartingWindow();
@@ -3868,16 +4012,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* immediately finishes after, so we have to transfer T to M.
*/
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
- final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
- this, PooledLambda.__(ActivityRecord.class));
- task.forAllActivities(p);
- p.recycle();
- }
-
- private boolean transferStartingWindow(ActivityRecord fromActivity) {
- if (fromActivity == this) return true;
-
- return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
+ task.forAllActivities(fromActivity -> {
+ if (fromActivity == this) return true;
+ return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity);
+ });
}
void checkKeyguardFlagsChanged() {
@@ -4223,6 +4361,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
final int animationType = pendingOptions.getAnimationType();
final DisplayContent displayContent = getDisplayContent();
+ AnimationOptions options = null;
+ IRemoteCallback startCallback = null;
+ IRemoteCallback finishCallback = null;
switch (animationType) {
case ANIM_CUSTOM:
displayContent.mAppTransition.overridePendingAppTransition(
@@ -4232,11 +4373,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getAnimationStartedListener(),
pendingOptions.getAnimationFinishedListener(),
pendingOptions.getOverrideTaskTransition());
+ options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
+ pendingOptions.getOverrideTaskTransition());
+ startCallback = pendingOptions.getAnimationStartedListener();
+ finishCallback = pendingOptions.getAnimationFinishedListener();
break;
case ANIM_CLIP_REVEAL:
displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeClipRevealAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4248,6 +4397,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
+ options = AnimationOptions.makeScaleUpAnimOptions(
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getWidth(), pendingOptions.getHeight());
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4263,6 +4415,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getAnimationStartedListener(),
scaleUp);
+ options = AnimationOptions.makeThumnbnailAnimOptions(buffer,
+ pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
+ startCallback = pendingOptions.getAnimationStartedListener();
if (intent.getSourceBounds() == null && buffer != null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),
@@ -4302,6 +4457,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
case ANIM_OPEN_CROSS_PROFILE_APPS:
displayContent.mAppTransition
.overridePendingAppTransitionStartCrossProfileApps();
+ options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
case ANIM_UNDEFINED:
@@ -4310,6 +4466,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
break;
}
+
+ if (options != null) {
+ mAtmService.getTransitionController().setOverrideAnimation(options,
+ startCallback, finishCallback);
+ }
}
void clearAllDrawn() {
@@ -4442,6 +4603,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean getDeferHidingClient() {
+ return mDeferHidingClient;
+ }
+
@Override
boolean isVisible() {
// If the activity isn't hidden then it is considered visible and there is no need to check
@@ -4606,7 +4771,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && mAtmService.getTransitionController().inTransition()) {
+ if (!visible && mAtmService.getTransitionController().inTransition(this)) {
return;
}
// If we are preparing an app transition, then delay changing
@@ -4725,10 +4890,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* this has become invisible.
*/
private void postApplyAnimation(boolean visible) {
+ final boolean usingShellTransitions =
+ mAtmService.getTransitionController().getTransitionPlayer() != null;
final boolean delayed = isAnimating(PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
- if (!delayed) {
+ if (!delayed && !usingShellTransitions) {
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
// which we might have done because we were in closing/opening apps list.
@@ -4747,8 +4914,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isAnimating(PARENTS,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+ || usingShellTransitions) {
setClientVisible(visible);
}
@@ -4857,7 +5024,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return mCurrentLaunchCanTurnScreenOn;
}
- void setState(ActivityState state, String reason) {
+ void setState(State state, String reason) {
ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
this, getState(), state, reason);
@@ -4869,8 +5036,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mState = state;
- if (task != null) {
- task.onActivityStateChanged(this, state, reason);
+ if (getTaskFragment() != null) {
+ getTaskFragment().onActivityStateChanged(this, state, reason);
}
// The WindowManager interprets the app stopping signal as
@@ -4930,44 +5097,42 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- ActivityState getState() {
+ State getState() {
return mState;
}
/**
* Returns {@code true} if the Activity is in the specified state.
*/
- boolean isState(ActivityState state) {
+ boolean isState(State state) {
return state == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2) {
+ boolean isState(State state1, State state2) {
return state1 == mState || state2 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3) {
+ boolean isState(State state1, State state2, State state3) {
return state1 == mState || state2 == mState || state3 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4) {
+ boolean isState(State state1, State state2, State state3, State state4) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState;
}
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState;
}
@@ -4975,8 +5140,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Returns {@code true} if the Activity is in one of the specified states.
*/
- boolean isState(ActivityState state1, ActivityState state2, ActivityState state3,
- ActivityState state4, ActivityState state5, ActivityState state6) {
+ boolean isState(State state1, State state2, State state3, State state4, State state5,
+ State state6) {
return state1 == mState || state2 == mState || state3 == mState || state4 == mState
|| state5 == mState || state6 == mState;
}
@@ -5033,6 +5198,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void notifyAppStopped() {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
mAppStopped = true;
+ firstWindowDrawn = false;
// This is to fix the edge case that auto-enter-pip is finished in Launcher but app calls
// setAutoEnterEnabled(false) and transitions to STOPPED state, see b/191930787.
// Clear any surface transactions and content overlay in this case.
@@ -5174,7 +5340,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// returns. Just need to confirm this reasoning makes sense.
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STARTED, STOPPING, STOPPED, PAUSED);
- if (deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
+ if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
// Go ahead and just put the activity in pip if it supports auto-pip.
mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
return;
@@ -5190,13 +5357,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
supportsEnterPipOnTaskSwitch = false;
break;
case RESUMED:
- // If the app is capable of entering PIP, we should try pausing it now
- // so it can PIP correctly.
- if (deferHidingClient) {
- task.startPausingLocked(false /* uiSleeping */,
- null /* resuming */, "makeInvisible");
- break;
- }
case INITIALIZING:
case PAUSING:
case PAUSED:
@@ -5293,7 +5453,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private boolean shouldBeResumed(ActivityRecord activeActivity) {
return shouldMakeActive(activeActivity) && isFocusable()
- && getTask().getVisibility(activeActivity) == TASK_VISIBILITY_VISIBLE
+ && getTaskFragment().getVisibility(activeActivity)
+ == TASK_FRAGMENT_VISIBILITY_VISIBLE
&& canResumeByCompat();
}
@@ -5347,7 +5508,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!task.hasChild(this)) {
throw new IllegalStateException("Activity not found in its task");
}
- return task.topRunningActivity() == this;
+ return getTaskFragment().topRunningActivity() == this;
}
void handleAlreadyVisible() {
@@ -5436,16 +5597,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STATES, "Activity paused: token=%s, timeout=%b", appToken,
timeout);
- if (task != null) {
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
removePauseTimeout();
- final ActivityRecord pausingActivity = task.getPausingActivity();
+ final ActivityRecord pausingActivity = taskFragment.getPausingActivity();
if (pausingActivity == this) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSED: %s %s", this,
(timeout ? "(due to timeout)" : " (pause complete)"));
mAtmService.deferWindowLayout();
try {
- task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+ taskFragment.completePause(true /* resumeNext */, null /* resumingActivity */);
} finally {
mAtmService.continueWindowLayout();
}
@@ -5800,7 +5962,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
+ void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// stop tracking
mSplashScreenStyleEmpty = true;
@@ -5816,7 +5978,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// own stuff.
win.cancelAnimation();
}
- removeStartingWindow();
+
+ // Remove starting window directly if is in a pure task. Otherwise if it is associated with
+ // a task (e.g. nested task fragment), then remove only if all visible windows in the task
+ // are drawn.
+ final Task associatedTask =
+ mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
+ if (associatedTask == null) {
+ removeStartingWindow();
+ } else if (associatedTask.getActivity(
+ r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
+ // The last drawn activity may not be the one that owns the starting window.
+ final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
+ if (r != null) {
+ r.removeStartingWindow();
+ }
+ }
updateReportedVisibilityLocked();
}
@@ -5860,6 +6037,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (task != null) {
task.setHasBeenVisible(true);
}
+ // Clear indicated launch root task because there's no trampoline activity to expect after
+ // the windows are drawn.
+ mLaunchRootTask = null;
}
/** Called when the windows associated app window container are visible. */
@@ -6108,9 +6288,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return this;
}
// Try to use the one which is closest to top.
- ActivityRecord r = rootTask.getResumedActivity();
+ ActivityRecord r = rootTask.getTopResumedActivity();
if (r == null) {
- r = rootTask.getPausingActivity();
+ r = rootTask.getTopPausingActivity();
}
if (r != null) {
return r;
@@ -6171,6 +6351,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return null;
}
+ @Nullable
+ static ActivityRecord isInAnyTask(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ return (r != null && r.isAttached()) ? r : null;
+ }
+
/**
* @return display id to which this record is attached,
* {@link android.view.Display#INVALID_DISPLAY} if not attached.
@@ -6188,7 +6374,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// This would be redundant.
return false;
}
- if (isState(RESUMED) || getRootTask() == null || this == task.getPausingActivity()
+ if (isState(RESUMED) || getRootTask() == null
+ || this == getTaskFragment().getPausingActivity()
|| !mHaveState || !stopped) {
// We're not ready for this kind of thing.
return false;
@@ -6375,13 +6562,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final boolean newSingleActivity = !newTask && !activityCreated
&& task.getActivity((r) -> !r.finishing && r != this) == null;
- final boolean shown = addStartingWindow(packageName, resolvedTheme,
+ final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- prev != null ? prev.appToken : null,
- newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+ prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
- if (shown) {
- mStartingWindowState = STARTING_WINDOW_SHOWN;
+ if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
+ Slog.d(TAG, "Scheduled starting window for " + this);
}
}
@@ -6393,14 +6579,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* It should only be called if this activity is behind other fullscreen activity.
*/
void cancelInitializing() {
- if (mStartingWindowState == STARTING_WINDOW_SHOWN) {
+ if (mStartingData != null) {
// Remove orphaned starting window.
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
- mStartingWindowState = STARTING_WINDOW_REMOVED;
removeStartingWindowAnimation(false /* prepareAnimation */);
}
- if (isState(INITIALIZING) && !shouldBeVisible(
- true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
+ if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
// Remove the unknown visibility record because an invisible activity shouldn't block
// the keyguard transition.
mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
@@ -6445,17 +6629,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
- boolean hasWindowsAlive() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- // No need to loop through child windows as the answer should be the same as that of the
- // parent window.
- if (!(mChildren.get(i)).mAppDied) {
- return true;
- }
- }
- return false;
- }
-
void setWillReplaceWindows(boolean animate) {
ProtoLog.d(WM_DEBUG_ADD_REMOVE,
"Marking app token %s with replacing windows.", this);
@@ -6758,8 +6931,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final Configuration displayConfig = mDisplayContent.getConfiguration();
return getDisplayContent().mAppTransition.createThumbnailAspectScaleAnimationLocked(
- appRect, insets, thumbnailHeader, task, displayConfig.uiMode,
- displayConfig.orientation);
+ appRect, insets, thumbnailHeader, task, displayConfig.orientation);
}
@Override
@@ -6906,7 +7078,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform(task);
+ mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform();
// Perform rotation animation according to the rotation of this activity.
startFreezingScreen(originalDisplayRotation);
// This activity may relaunch or perform configuration change so once it has reported drawn,
@@ -7184,7 +7356,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the activity has requested override bounds, the configuration needs to be
// computed accordingly.
if (!matchParentBounds()) {
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig,
+ newParentConfiguration);
}
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
@@ -7299,7 +7472,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// Since bounds has changed, the configuration needs to be computed accordingly.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
/**
@@ -7315,8 +7488,60 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
- * change and the requested orientation is different from the parent.
+ * In some cases, applying insets to bounds changes the orientation. For example, if a
+ * close-to-square display rotates to portrait to respect a portrait orientation activity, after
+ * insets such as the status and nav bars are applied, the activity may actually have a
+ * landscape orientation. This method checks whether the orientations of the activity window
+ * with and without insets match or if the orientation with insets already matches the
+ * requested orientation. If not, it may be necessary to letterbox the window.
+ * @param parentBounds are the new parent bounds passed down to the activity and should be used
+ * to compute the stable bounds.
+ * @param outStableBounds will store the stable bounds, which are the bounds with insets
+ * applied, if orientation is not respected when insets are applied.
+ * Otherwise outStableBounds will be empty. Stable bounds should be used
+ * to compute letterboxed bounds if orientation is not respected when
+ * insets are applied.
+ */
+ private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds) {
+ outStableBounds.setEmpty();
+ if (mDisplayContent == null) {
+ return true;
+ }
+ // Only need to make changes if activity sets an orientation
+ final int requestedOrientation = getRequestedConfigurationOrientation();
+ if (requestedOrientation == ORIENTATION_UNDEFINED) {
+ return true;
+ }
+ // Compute parent orientation from bounds
+ final int orientation = parentBounds.height() >= parentBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // Compute orientation from stable parent bounds (= parent bounds with insets applied)
+ final Task task = getTask();
+ task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
+ outStableBounds /* outStableBounds */, parentBounds /* bounds */,
+ mDisplayContent.getDisplayInfo());
+ final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ // If orientation does not match the orientation with insets applied, then a
+ // display rotation will not be enough to respect orientation. However, even if they do
+ // not match but the orientation with insets applied matches the requested orientation, then
+ // there is no need to modify the bounds because when insets are applied, the activity will
+ // have the desired orientation.
+ final boolean orientationRespectedWithInsets = orientation == orientationWithInsets
+ || orientationWithInsets == requestedOrientation;
+ if (orientationRespectedWithInsets) {
+ outStableBounds.setEmpty();
+ }
+ return orientationRespectedWithInsets;
+ }
+
+ /**
+ * Computes bounds (letterbox or pillarbox) when either:
+ * 1. The parent doesn't handle the orientation change and the requested orientation is
+ * different from the parent (see {@link DisplayContent#setIgnoreOrientationRequest()}.
+ * 2. The parent handling the orientation is not enough. This occurs when the display rotation
+ * may not be enough to respect orientation requests (see {@link
+ * ActivityRecord#orientationRespectedWithInsets}).
*
* <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
* in this method.
@@ -7324,9 +7549,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig,
int windowingMode) {
mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
- if (handlesOrientationChangeFromDescendant()) {
+ final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ // If orientation is respected when insets are applied, then stableBounds will be empty.
+ boolean orientationRespectedWithInsets =
+ orientationRespectedWithInsets(parentBounds, stableBounds);
+ if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
// No need to letterbox because of fixed orientation. Display will handle
- // fixed-orientation requests.
+ // fixed-orientation requests and a display rotation is enough to respect requested
+ // orientation with insets applied.
return;
}
if (WindowConfiguration.inMultiWindowMode(windowingMode) && isResizeable()) {
@@ -7346,7 +7577,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the activity requires a different orientation (either by override or activityInfo),
// make it fit the available bounds by scaling down its bounds.
final int forcedOrientation = getRequestedConfigurationOrientation();
- if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
+ if (forcedOrientation == ORIENTATION_UNDEFINED
+ || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) {
return;
}
@@ -7358,31 +7590,42 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
- final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app
+ // bounds or stable bounds to unify aspect ratio logic.
+ final Rect parentBoundsWithInsets = orientationRespectedWithInsets
+ ? newParentConfig.windowConfiguration.getAppBounds() : stableBounds;
final Rect containingBounds = new Rect();
- final Rect containingAppBounds = new Rect();
- // Need to shrink the containing bounds into a square because the parent orientation does
- // not match the activity requested orientation.
+ final Rect containingBoundsWithInsets = new Rect();
+ // Need to shrink the containing bounds into a square because the parent orientation
+ // does not match the activity requested orientation.
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- // Shrink height to match width. Position height within app bounds.
- final int bottom = Math.min(parentAppBounds.top + parentBounds.width(),
- parentAppBounds.bottom);
- containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right,
+ // Landscape is defined as width > height. Make the container respect landscape
+ // orientation by shrinking height to one less than width. Landscape activity will be
+ // vertically centered within parent bounds with insets, so position vertical bounds
+ // within parent bounds with insets to prevent insets from unnecessarily trimming
+ // vertical bounds.
+ final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1,
+ parentBoundsWithInsets.bottom);
+ containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right,
bottom);
- containingAppBounds.set(parentAppBounds.left, parentAppBounds.top,
- parentAppBounds.right, bottom);
+ containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
+ parentBoundsWithInsets.right, bottom);
} else {
- // Shrink width to match height. Position width within app bounds.
- final int right = Math.min(parentAppBounds.left + parentBounds.height(),
- parentAppBounds.right);
- containingBounds.set(parentAppBounds.left, parentBounds.top, right,
+ // Portrait is defined as width <= height. Make the container respect portrait
+ // orientation by shrinking width to match height. Portrait activity will be
+ // horizontally centered within parent bounds with insets, so position horizontal bounds
+ // within parent bounds with insets to prevent insets from unnecessarily trimming
+ // horizontal bounds.
+ final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(),
+ parentBoundsWithInsets.right);
+ containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right,
parentBounds.bottom);
- containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right,
- parentAppBounds.bottom);
+ containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
+ right, parentBoundsWithInsets.bottom);
}
- Rect mTmpFullBounds = new Rect(resolvedBounds);
+ // Store the current bounds to be able to revert to size compat mode values below if needed.
+ final Rect prevResolvedBounds = new Rect(resolvedBounds);
resolvedBounds.set(containingBounds);
// Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
@@ -7393,13 +7636,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
// Apply aspect ratio to resolved bounds
- mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds,
+ mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
containingBounds, desiredAspectRatio, true);
- // Vertically center if orientation is landscape. Bounds will later be horizontally centered
- // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
+ // Vertically center if orientation is landscape. Center within parent bounds with insets
+ // to ensure that insets do not trim height. Bounds will later be horizontally centered in
+ // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int offsetY = parentBounds.centerY() - resolvedBounds.centerY();
+ final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY();
resolvedBounds.offset(0, offsetY);
}
@@ -7411,14 +7655,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// The app shouldn't be resized, we only do fixed orientation letterboxing if the
// compat bounds are also from the same fixed orientation letterbox. Otherwise,
// clear the fixed orientation bounds to show app in size compat mode.
- resolvedBounds.set(mTmpFullBounds);
+ resolvedBounds.set(prevResolvedBounds);
return;
}
}
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
- task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
+ newParentConfig);
mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds);
}
@@ -7446,7 +7691,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
// restrict, the bounds should be the requested override bounds.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
getFixedRotationTransformDisplayInfo());
}
}
@@ -7506,10 +7751,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
// later, so the calculation is simpler that doesn't need to involve offset from parent.
- task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+ getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
mCompatDisplayInsets);
// Use current screen layout as source because the size of app is independent to parent.
- resolvedConfig.screenLayout = Task.computeScreenLayoutOverride(
+ resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride(
getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
resolvedConfig.screenHeightDp);
@@ -7653,6 +7898,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (getUid() == SYSTEM_UID) {
return false;
}
+ // Do not sandbox to activity window bounds if the feature is disabled.
+ if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) {
+ return false;
+ }
// Never apply sandboxing to an app that should be explicitly excluded from the config.
if (info != null && info.neverSandboxDisplayApis()) {
return false;
@@ -7827,7 +8076,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (task == null || rootTask == null
|| (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
- && !fixedOrientationLetterboxed)
+ && !fixedOrientationLetterboxed)
|| (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
|| isInVrUiMode(getConfiguration())) {
// We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
@@ -7920,7 +8169,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
// container app bounds. Otherwise the entire container bounds are available.
if (!outBounds.equals(containingBounds)) {
- // The horizontal position should not cover insets.
+ // The horizontal position should not cover insets (e.g. display cutout).
outBounds.left = containingAppBounds.left;
}
@@ -8076,7 +8325,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
- startFreezingScreenLocked(globalChanges);
+ if (!mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ startFreezingScreenLocked(globalChanges);
+ }
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
@@ -8679,6 +8930,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* compatibility mode activity compute the configuration without relying on its current display.
*/
static class CompatDisplayInsets {
+ /** The original rotation the compat insets were computed in */
+ final @Rotation int mOriginalRotation;
/** The container width on rotation 0. */
private final int mWidth;
/** The container height on rotation 0. */
@@ -8705,6 +8958,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Constructs the environment to simulate the bounds behavior of the given container. */
CompatDisplayInsets(DisplayContent display, ActivityRecord container,
@Nullable Rect fixedOrientationBounds) {
+ mOriginalRotation = display.getRotation();
mIsFloating = container.getWindowConfiguration().tasksAreFloating();
if (mIsFloating) {
final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8851,7 +9105,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
outAppBounds.offset(insets.left, insets.top);
} else if (rotation != ROTATION_UNDEFINED) {
// Ensure the app bounds won't overlap with insets.
- Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]);
+ TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
+ mNonDecorInsets[rotation]);
}
}
}
@@ -8884,7 +9139,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
record.mAdapter.mRootTaskBounds, task.getWindowConfiguration(),
false /*isNotInRecents*/,
record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
- record.mStartBounds, task.getTaskInfo());
+ record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
}
@Override
@@ -8920,6 +9175,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
+ @Override
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ // This override is just for getting metrics. allFinished needs to be checked before
+ // finish because finish resets all the states.
+ mLastAllReadyAtSync = allSyncFinished();
+ super.finishSync(outMergedTransaction, cancel);
+ }
+
static class Builder {
private final ActivityTaskManagerService mAtmService;
private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index 8540fa7cf347..30c7b232fcc8 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import android.util.ArraySet;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b6f2f243040e..b71ad2ea7102 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -27,6 +27,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -38,6 +39,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -488,6 +490,27 @@ public class ActivityStartController {
return START_SUCCESS;
}
+ /**
+ * Starts an activity in the TaskFragment.
+ * @param taskFragment TaskFragment {@link TaskFragment} to start the activity in.
+ * @param activityIntent intent to start the activity.
+ * @param activityOptions ActivityOptions to start the activity with.
+ * @param resultTo the caller activity
+ * @return the start result.
+ */
+ int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
+ @NonNull Intent activityIntent, @Nullable Bundle activityOptions,
+ @Nullable IBinder resultTo) {
+ return obtainStarter(activityIntent, "startActivityInTaskFragment")
+ .setActivityOptions(activityOptions)
+ .setInTaskFragment(taskFragment)
+ .setResultTo(resultTo)
+ .setRequestCode(-1)
+ .setCallingUid(Binder.getCallingUid())
+ .setCallingPid(Binder.getCallingPid())
+ .execute();
+ }
+
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9a25fc2d272..f6757f599d7c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.Manifest.permission.ACTIVITY_EMBEDDING;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.ActivityManager.START_ABORTED;
@@ -23,11 +24,13 @@ import static android.app.ActivityManager.START_CANCELED;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
+import static android.app.ActivityManager.START_PERMISSION_DENIED;
import static android.app.ActivityManager.START_RETURN_INTENT_TO_CALLER;
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -58,6 +61,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -74,7 +78,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -177,6 +180,7 @@ class ActivityStarter {
private int mPreferredWindowingMode;
private Task mInTask;
+ private TaskFragment mInTaskFragment;
@VisibleForTesting
boolean mAddingToTask;
private Task mReuseTask;
@@ -190,7 +194,6 @@ class ActivityStarter {
private Task mTargetTask;
private boolean mMovedToFront;
private boolean mNoAnimation;
- private boolean mKeepCurTransition;
private boolean mAvoidMoveToFront;
private boolean mFrozeTaskList;
private boolean mTransientLaunch;
@@ -342,6 +345,7 @@ class ActivityStarter {
boolean avoidMoveToFront;
ActivityRecord[] outActivity;
Task inTask;
+ TaskFragment inTaskFragment;
String reason;
ProfilerInfo profilerInfo;
Configuration globalConfig;
@@ -392,6 +396,7 @@ class ActivityStarter {
componentSpecified = false;
outActivity = null;
inTask = null;
+ inTaskFragment = null;
reason = null;
profilerInfo = null;
globalConfig = null;
@@ -407,7 +412,7 @@ class ActivityStarter {
/**
* Adopts all values from passed in request.
*/
- void set(Request request) {
+ void set(@NonNull Request request) {
caller = request.caller;
intent = request.intent;
intentGrants = request.intentGrants;
@@ -432,6 +437,7 @@ class ActivityStarter {
componentSpecified = request.componentSpecified;
outActivity = request.outActivity;
inTask = request.inTask;
+ inTaskFragment = request.inTaskFragment;
reason = request.reason;
profilerInfo = request.profilerInfo;
globalConfig = request.globalConfig;
@@ -574,6 +580,7 @@ class ActivityStarter {
mPreferredWindowingMode = starter.mPreferredWindowingMode;
mInTask = starter.mInTask;
+ mInTaskFragment = starter.mInTaskFragment;
mAddingToTask = starter.mAddingToTask;
mReuseTask = starter.mReuseTask;
@@ -585,7 +592,6 @@ class ActivityStarter {
mTargetRootTask = starter.mTargetRootTask;
mMovedToFront = starter.mMovedToFront;
mNoAnimation = starter.mNoAnimation;
- mKeepCurTransition = starter.mKeepCurTransition;
mAvoidMoveToFront = starter.mAvoidMoveToFront;
mFrozeTaskList = starter.mFrozeTaskList;
@@ -737,7 +743,7 @@ class ActivityStarter {
Slog.w(TAG, "Unable to find app for caller " + mRequest.caller + " (pid="
+ mRequest.callingPid + ") when starting: " + mRequest.intent.toString());
SafeActivityOptions.abort(mRequest.activityOptions);
- return ActivityManager.START_PERMISSION_DENIED;
+ return START_PERMISSION_DENIED;
}
}
@@ -835,6 +841,7 @@ class ActivityStarter {
final int startFlags = request.startFlags;
final SafeActivityOptions options = request.activityOptions;
Task inTask = request.inTask;
+ TaskFragment inTaskFragment = request.inTaskFragment;
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -850,7 +857,7 @@ class ActivityStarter {
} else {
Slog.w(TAG, "Unable to find app for caller " + caller + " (pid=" + callingPid
+ ") when starting: " + intent.toString());
- err = ActivityManager.START_PERMISSION_DENIED;
+ err = START_PERMISSION_DENIED;
}
}
@@ -864,7 +871,7 @@ class ActivityStarter {
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
- sourceRecord = mRootWindowContainer.isInAnyTask(resultTo);
+ sourceRecord = ActivityRecord.isInAnyTask(resultTo);
if (DEBUG_RESULTS) {
Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord);
}
@@ -1175,8 +1182,8 @@ class ActivityStarter {
}
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
- request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
- restrictedBgActivity, intentGrants);
+ request.voiceInteractor, startFlags, true /* doResume */, checkedOptions,
+ inTask, inTaskFragment, restrictedBgActivity, intentGrants);
if (request.outActivity != null) {
request.outActivity[0] = mLastStartActivityRecord;
@@ -1502,7 +1509,7 @@ class ActivityStarter {
final Task targetTask = r.getTask() != null
? r.getTask()
: mTargetTask;
- if (startedActivityRootTask == null || targetTask == null) {
+ if (startedActivityRootTask == null || targetTask == null || !targetTask.isAttached()) {
return;
}
@@ -1546,10 +1553,12 @@ class ActivityStarter {
* Here also ensures that the starting activity is removed if the start wasn't successful.
*/
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, Task inTask,
- boolean restrictedBgActivity, NeededUriGrants intentGrants) {
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ int startFlags, boolean doResume, ActivityOptions options, Task inTask,
+ TaskFragment inTaskFragment, boolean restrictedBgActivity,
+ NeededUriGrants intentGrants) {
int result = START_CANCELED;
+ boolean startResultSuccessful = false;
final Task startedActivityRootTask;
// Create a transition now to record the original intent of actions taken within
@@ -1564,11 +1573,26 @@ class ActivityStarter {
newTransition.setRemoteTransition(remoteTransition);
}
mService.getTransitionController().collect(r);
+ // TODO(b/188669821): Remove when navbar reparenting moves to shell
+ if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
+ && r.getOptions().getTransientLaunch()) {
+ mService.getTransitionController().setIsLegacyRecents();
+ }
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
+ startFlags, doResume, options, inTask, inTaskFragment, restrictedBgActivity,
+ intentGrants);
+ startResultSuccessful = ActivityManager.isStartResultSuccessful(result);
+ final boolean taskAlwaysOnTop = options != null && options.getTaskAlwaysOnTop();
+ // Apply setAlwaysOnTop when starting an Activity is successful regardless of creating
+ // a new Activity or recycling the existing Activity.
+ if (taskAlwaysOnTop && startResultSuccessful) {
+ final Task targetRootTask =
+ mTargetRootTask != null ? mTargetRootTask : mTargetTask.getRootTask();
+ targetRootTask.setAlwaysOnTop(true);
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
startedActivityRootTask = handleStartResult(r, result);
@@ -1576,7 +1600,7 @@ class ActivityStarter {
mSupervisor.mUserLeaving = false;
// Transition housekeeping
- if (!ActivityManager.isStartResultSuccessful(result)) {
+ if (!startResultSuccessful) {
if (newTransition != null) {
newTransition.abort();
}
@@ -1595,7 +1619,8 @@ class ActivityStarter {
statusBar.collapsePanels();
}
}
- if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
+ final boolean started = result == START_SUCCESS || result == START_TASK_TO_FRONT;
+ if (started) {
// The activity is started new rather than just brought forward, so record
// it as an existence change.
mService.getTransitionController().collectExistenceChange(r);
@@ -1603,9 +1628,9 @@ class ActivityStarter {
if (newTransition != null) {
mService.getTransitionController().requestStartTransition(newTransition,
mTargetTask, remoteTransition);
- } else {
+ } else if (started) {
// Make the collecting transition wait until this request is ready.
- mService.getTransitionController().setReady(false);
+ mService.getTransitionController().setReady(r, false);
}
}
}
@@ -1665,15 +1690,15 @@ class ActivityStarter {
*
* Note: This method should only be called from {@link #startActivityUnchecked}.
*/
-
// TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
@VisibleForTesting
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
- boolean restrictedBgActivity, NeededUriGrants intentGrants) {
- setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
- voiceInteractor, restrictedBgActivity);
+ TaskFragment inTaskFragment, boolean restrictedBgActivity,
+ NeededUriGrants intentGrants) {
+ setInitialState(r, options, inTask, inTaskFragment, doResume, startFlags, sourceRecord,
+ voiceSession, voiceInteractor, restrictedBgActivity);
computeLaunchingTaskFlags();
@@ -1681,6 +1706,8 @@ class ActivityStarter {
mIntent.setFlags(mLaunchFlags);
+ // Get top task at beginning because the order may be changed when reusing existing task.
+ final Task prevTopTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task reusedTask = getReusableTask();
// If requested, freeze the task list
@@ -1739,11 +1766,6 @@ class ActivityStarter {
if (!mAvoidMoveToFront && mDoResume) {
mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask);
- if (mOptions != null) {
- if (mOptions.getTaskAlwaysOnTop()) {
- mTargetRootTask.setAlwaysOnTop(true);
- }
- }
if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.mInternal.isDreaming()) {
// Launching underneath dream activity (fullscreen, always-on-top). Run the launch-
// -behind transition so the Activity gets created and starts in visible state.
@@ -1765,24 +1787,23 @@ class ActivityStarter {
UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/,
resultToUid /*visible*/, true /*direct*/);
}
+ final Task startedTask = mStartActivity.getTask();
if (newTask) {
- EventLogTags.writeWmCreateTask(mStartActivity.mUserId,
- mStartActivity.getTask().mTaskId);
+ EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId);
}
- mStartActivity.logStartActivity(
- EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
+ mStartActivity.logStartActivity(EventLogTags.WM_CREATE_ACTIVITY, startedTask);
- mTargetRootTask.mLastPausedActivity = null;
+ mStartActivity.getTaskFragment().clearLastPausedActivity();
mRootWindowContainer.startPowerModeLaunchIfNeeded(
false /* forceSend */, mStartActivity);
+ final boolean isTaskSwitch = startedTask != prevTopTask;
mTargetRootTask.startActivityLocked(mStartActivity,
topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
- mKeepCurTransition, mOptions, sourceRecord);
+ isTaskSwitch, mOptions, sourceRecord);
if (mDoResume) {
- final ActivityRecord topTaskActivity =
- mStartActivity.getTask().topRunningActivityLocked();
+ final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
if (!mTargetRootTask.isTopActivityFocusable()
|| (topTaskActivity != null && topTaskActivity.isTaskOverlay()
&& mStartActivity != topTaskActivity)) {
@@ -1816,8 +1837,8 @@ class ActivityStarter {
mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);
// Update the recent tasks list immediately when the activity starts
- mSupervisor.mRecentTasks.add(mStartActivity.getTask());
- mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
+ mSupervisor.mRecentTasks.add(startedTask);
+ mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);
return START_SUCCESS;
@@ -1931,10 +1952,41 @@ class ActivityStarter {
}
}
+ if (mInTaskFragment != null && mInTaskFragment.getTask() != null) {
+ final int hostUid = mInTaskFragment.getTask().effectiveUid;
+ final int embeddingUid = targetTask != null ? targetTask.effectiveUid : r.getUid();
+ if (!canTaskBeEmbedded(hostUid, embeddingUid)) {
+ Slog.e(TAG, "Cannot embed activity to a task owned by " + hostUid + " targetTask= "
+ + targetTask);
+ return START_PERMISSION_DENIED;
+ }
+ }
+
return START_SUCCESS;
}
/**
+ * Return {@code true} if the {@param task} can embed another task.
+ * @param hostUid the uid of the host task
+ * @param embeddedUid the uid of the task the are going to be embedded
+ */
+ private boolean canTaskBeEmbedded(int hostUid, int embeddedUid) {
+ // Allowing the embedding if the task is owned by system.
+ if (hostUid == Process.SYSTEM_UID) {
+ return true;
+ }
+
+ // Allowing embedding if the host task is owned by an app that has the ACTIVITY_EMBEDDING
+ // permission
+ if (mService.checkPermission(ACTIVITY_EMBEDDING, -1, hostUid) == PERMISSION_GRANTED) {
+ return true;
+ }
+
+ // Allowing embedding if it is from the same app that owned the task
+ return hostUid == embeddedUid;
+ }
+
+ /**
* Prepare the target task to be reused for this launch, which including:
* - Position the target task on valid root task on preferred display.
* - Comply to the specified activity launch flags
@@ -2023,7 +2075,7 @@ 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();
-
+
mLastStartActivityRecord = targetTaskTop;
return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;
}
@@ -2049,7 +2101,7 @@ class ActivityStarter {
}
// For paranoia, make sure we have correctly resumed the top activity.
- topRootTask.mLastPausedActivity = null;
+ top.getTaskFragment().clearLastPausedActivity();
if (mDoResume) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
@@ -2145,7 +2197,7 @@ class ActivityStarter {
task.moveActivityToFrontLocked(act);
act.updateOptionsLocked(mOptions);
deliverNewIntent(act, intentGrants);
- mTargetRootTask.mLastPausedActivity = null;
+ act.getTaskFragment().clearLastPausedActivity();
} else {
mAddingToTask = true;
}
@@ -2213,6 +2265,7 @@ class ActivityStarter {
mPreferredWindowingMode = WINDOWING_MODE_UNDEFINED;
mInTask = null;
+ mInTaskFragment = null;
mAddingToTask = false;
mReuseTask = null;
@@ -2224,7 +2277,6 @@ class ActivityStarter {
mTargetTask = null;
mMovedToFront = false;
mNoAnimation = false;
- mKeepCurTransition = false;
mAvoidMoveToFront = false;
mFrozeTaskList = false;
mTransientLaunch = false;
@@ -2240,9 +2292,9 @@ class ActivityStarter {
}
private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
- boolean doResume, int startFlags, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- boolean restrictedBgActivity) {
+ TaskFragment inTaskFragment, boolean doResume, int startFlags,
+ ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
+ IVoiceInteractor voiceInteractor, boolean restrictedBgActivity) {
reset(false /* clearRequest */);
mStartActivity = r;
@@ -2332,6 +2384,11 @@ class ActivityStarter {
}
mTransientLaunch = mOptions.getTransientLaunch();
mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
+
+ if (inTaskFragment == null) {
+ inTaskFragment = TaskFragment.fromTaskFragmentToken(
+ mOptions.getLaunchTaskFragmentToken(), mService);
+ }
}
mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? sourceRecord : null;
@@ -2345,6 +2402,7 @@ class ActivityStarter {
Slog.w(TAG, "Starting activity in task not in recents: " + inTask);
mInTask = null;
}
+ mInTaskFragment = inTaskFragment;
mStartFlags = startFlags;
// If the onlyIfNeeded flag is set, then we can do this if the activity being launched
@@ -2568,13 +2626,28 @@ class ActivityStarter {
/**
* Figure out which task and activity to bring to front when we have found an existing matching
* activity record in history. May also clear the task if needed.
+ *
* @param intentActivity Existing matching activity.
* @return {@link ActivityRecord} brought to front.
*/
private void setTargetRootTaskIfNeeded(ActivityRecord intentActivity) {
- mTargetRootTask = intentActivity.getRootTask();
- mTargetRootTask.mLastPausedActivity = null;
+ intentActivity.getTaskFragment().clearLastPausedActivity();
Task intentTask = intentActivity.getTask();
+
+ // Only update the target-root-task when it is not indicated.
+ if (mTargetRootTask == null) {
+ if (mSourceRecord != null && mSourceRecord.mLaunchRootTask != null) {
+ // Inherit the target-root-task from source to ensure trampoline activities will be
+ // launched into the same root task.
+ mTargetRootTask = Task.fromWindowContainerToken(mSourceRecord.mLaunchRootTask);
+ } else {
+ final Task launchRootTask =
+ getLaunchRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions);
+ mTargetRootTask =
+ launchRootTask != null ? launchRootTask : intentActivity.getRootTask();
+ }
+ }
+
// If the target task is not in the front, then we need to bring it to the front...
// except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
// the same behavior as if a new instance was being started, which means not bringing it
@@ -2602,16 +2675,14 @@ class ActivityStarter {
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- final Task launchRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags,
- intentTask, mOptions);
- if (launchRootTask == null || launchRootTask == mTargetRootTask) {
+ if (mTargetRootTask == intentActivity.getRootTask()) {
// TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
// tasks hierarchies.
if (mTargetRootTask != intentTask
&& mTargetRootTask != intentTask.getParent().asTask()) {
intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
false /* includingParents */);
- intentTask = intentTask.getParent().asTask();
+ intentTask = intentTask.getParent().asTaskFragment().getTask();
}
// If the task is in multi-windowing mode, the activity may already be on
// the top (visible to user but not the global top), then the result code
@@ -2628,7 +2699,7 @@ class ActivityStarter {
"bringingFoundTaskToFront");
mMovedToFront = !wasTopOfVisibleRootTask;
} else {
- intentTask.reparent(launchRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
+ intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
mMovedToFront = true;
}
@@ -2700,11 +2771,29 @@ class ActivityStarter {
mIntentDelivered = true;
}
- private void addOrReparentStartingActivity(Task parent, String reason) {
- if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
- parent.addChild(mStartActivity);
+ private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
+ TaskFragment newParent = task;
+ if (mInTaskFragment != null) {
+ // mInTaskFragment is created and added to the leaf task by task fragment organizer's
+ // request. If the task was resolved and different than mInTaskFragment, reparent the
+ // task to mInTaskFragment for embedding.
+ if (mInTaskFragment.getTask() != task) {
+ task.reparent(mInTaskFragment, POSITION_TOP);
+ } else {
+ newParent = mInTaskFragment;
+ }
+ } else {
+ // Use the child TaskFragment (if any) as the new parent if the activity can be embedded
+ final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */,
+ false /* includingEmbeddedTask */);
+ newParent = top != null ? top.getTaskFragment() : task;
+ }
+
+ if (mStartActivity.getTaskFragment() == null
+ || mStartActivity.getTaskFragment() == newParent) {
+ newParent.addChild(mStartActivity, POSITION_TOP);
} else {
- mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
+ mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
}
}
@@ -2936,6 +3025,11 @@ class ActivityStarter {
return this;
}
+ ActivityStarter setInTaskFragment(TaskFragment taskFragment) {
+ mRequest.inTaskFragment = taskFragment;
+ return this;
+ }
+
ActivityStarter setWaitResult(WaitResult result) {
mRequest.waitResult = result;
return this;
@@ -3019,5 +3113,7 @@ class ActivityStarter {
pw.print(mDoResume);
pw.print(" mAddingToTask=");
pw.println(mAddingToTask);
+ pw.print(" mInTaskFragment=");
+ pw.println(mInTaskFragment);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 1759cdeb60d7..5174a38d5edc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -621,4 +621,7 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
String callingPackage);
+
+ /** Called when the device is waking up */
+ public abstract void notifyWakingUp();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f3ba56a03aef..19cf14f70e0a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -64,6 +64,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_WAKE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
@@ -253,7 +254,6 @@ import com.android.server.am.PendingIntentController;
import com.android.server.am.PendingIntentRecord;
import com.android.server.am.UserState;
import com.android.server.firewall.IntentFirewall;
-import com.android.server.inputmethod.InputMethodSystemProperty;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -732,6 +732,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
WindowOrganizerController mWindowOrganizerController;
TaskOrganizerController mTaskOrganizerController;
+ TaskFragmentOrganizerController mTaskFragmentOrganizerController;
@Nullable
private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
@@ -805,6 +806,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
mWindowOrganizerController = new WindowOrganizerController(this);
mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
+ mTaskFragmentOrganizerController =
+ mWindowOrganizerController.mTaskFragmentOrganizerController;
}
public void onSystemReady() {
@@ -1222,8 +1225,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
final Task topFocusedRootTask = getTopDisplayFocusedRootTask();
- if (topFocusedRootTask != null && topFocusedRootTask.getResumedActivity() != null
- && topFocusedRootTask.getResumedActivity().info.applicationInfo.uid
+ if (topFocusedRootTask != null && topFocusedRootTask.getTopResumedActivity() != null
+ && topFocusedRootTask.getTopResumedActivity().info.applicationInfo.uid
== Binder.getCallingUid()) {
mAppSwitchesAllowed = true;
}
@@ -1537,7 +1540,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
sourceToken = resultTo;
}
- sourceRecord = mRootWindowContainer.isInAnyTask(sourceToken);
+ sourceRecord = ActivityRecord.isInAnyTask(sourceToken);
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + sourceToken);
}
@@ -1881,25 +1884,42 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void setFocusedTask(int taskId) {
enforceTaskPermission("setFocusedTask()");
- ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
final long callingId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_ATTACHED_TASK_ONLY);
- if (task == null) {
- return;
- }
- final ActivityRecord r = task.topRunningActivityLocked();
- if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
+ setFocusedTask(taskId, null /* touchedActivity */);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
+ void setFocusedTask(int taskId, ActivityRecord touchedActivity) {
+ ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId,
+ touchedActivity);
+ final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ return;
+ }
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r == null) {
+ return;
+ }
+
+ if (r.moveFocusableActivityToTop("setFocusedTask")) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ } else if (touchedActivity != null && touchedActivity.isFocusable()) {
+ final TaskFragment parent = touchedActivity.getTaskFragment();
+ if (parent != null && parent.isEmbedded()) {
+ // Set the focused app directly if the focused window is currently embedded
+ final DisplayContent displayContent = touchedActivity.getDisplayContent();
+ displayContent.setFocusedApp(touchedActivity);
+ mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ true /* updateInputWindows */);
+ }
+ }
+ }
+
@Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
@@ -3425,7 +3445,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public IWindowOrganizerController getWindowOrganizerController() {
- enforceTaskPermission("getWindowOrganizerController()");
return mWindowOrganizerController;
}
@@ -3769,6 +3788,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ @Override
+ public void detachNavigationBarFromApp(@NonNull IBinder transition) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "detachNavigationBarFromApp");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ getTransitionController().legacyDetachNavigationBarFromApp(transition);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void dumpLastANRLocked(PrintWriter pw) {
pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
if (mLastANRState == null) {
@@ -5036,11 +5069,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* @param imeContainer The DisplayArea that contains the IME window.
*/
void onImeWindowSetOnDisplayArea(final int pid, @NonNull final DisplayArea imeContainer) {
- // Don't update process-level configuration for Multi-Client IME process since other
- // IMEs on other displays will also receive this configuration change due to IME
- // services use the same application config/context.
- if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
-
if (pid == MY_PID || pid < 0) {
ProtoLog.w(WM_DEBUG_CONFIGURATION,
"Trying to update display configuration for system/invalid process.");
@@ -5055,6 +5083,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
process.registerDisplayAreaConfigurationListener(imeContainer);
}
+ @Override
+ public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
+ mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+ "setRunningRemoteTransition");
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ // Also only allow a process which is already runningRemoteAnimation to mark another
+ // process.
+ final WindowProcessController callingProc = getProcessController(callingPid,
+ callingUid);
+ if (callingProc == null || !callingProc.isRunningRemoteTransition()) {
+ final String msg = "Can't call setRunningRemoteTransition from a process (pid="
+ + callingPid + " uid=" + callingUid + ") which isn't itself running a "
+ + "remote transition.";
+ Slog.e(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final WindowProcessController wpc = getProcessController(caller);
+ if (wpc == null) {
+ Slog.w(TAG, "Unable to find process for application " + caller);
+ return;
+ }
+ wpc.setRunningRemoteAnimation(true /* running */);
+ callingProc.addRemoteAnimationDelegate(wpc);
+ }
+ }
+
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
@@ -6445,6 +6501,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return ActivityTaskManagerService.this.hasSystemAlertWindowPermission(callingUid,
callingPid, callingPackage);
}
+
+ @Override
+ public void notifyWakingUp() {
+ // Start a transition for waking. This is needed for showWhenLocked activities.
+ getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */,
+ null /* trigger */, mRootWindowContainer.getDefaultDisplay());
+ }
}
final class PackageConfigurationUpdaterImpl implements
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index e3459a1edc0f..e593c1c1e656 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -49,6 +49,9 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
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.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
@@ -73,8 +76,6 @@ import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_R
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.TAG_CLEANUP;
@@ -140,7 +141,6 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.TransferPipe;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -151,7 +151,6 @@ import com.android.server.am.UserState;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -353,6 +352,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
*/
private int mVisibilityTransactionDepth;
+ /**
+ * Whether to the visibility updates that started from {@code RootWindowContainer} should be
+ * deferred.
+ */
+ private boolean mDeferRootVisibilityUpdate;
+
private ActivityMetricsLogger mActivityMetricsLogger;
/** Check if placing task or activity on specified display is allowed. */
@@ -842,6 +847,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
proc.getThread(), r.appToken);
final boolean isTransitionForward = r.isTransitionForward();
+ IBinder fragmentToken = null;
+ if (r.getTaskFragment().getTaskFragmentOrganizerPid() == r.getPid()) {
+ fragmentToken = r.getTaskFragment().getFragmentToken();
+ }
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
@@ -853,7 +862,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
- r.getLaunchedFromBubble()));
+ r.getLaunchedFromBubble(), fragmentToken));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1377,7 +1386,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
- 0 /* flags */, task, options != null ? options.getRemoteTransition() : null);
+ 0 /* flags */, task, task /* readyGroupRef */,
+ options != null ? options.getRemoteTransition() : null);
reason = reason + " findTaskToMoveToFront";
boolean reparented = false;
if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -1551,7 +1561,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return;
}
if (task.isVisible()) {
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+ if (mService.getTransitionController().isCollecting()) {
+ // We don't want the finishing to change the transition ready state since there will
+ // not be corresponding setReady for finishing.
+ mService.getTransitionController().collectExistenceChange(task);
+ } else {
+ mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+ }
} else {
// Removing a non-visible task doesn't require a transition, but if there is one
// collecting, this should be a member just in case.
@@ -1991,76 +2007,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
String prefix, String label, boolean complete, boolean brief, boolean client,
String dumpPackage, boolean needNL, Runnable header, Task lastTask) {
- String innerPrefix = null;
- String[] args = null;
boolean printed = false;
- for (int i=list.size()-1; i>=0; i--) {
+ for (int i = list.size() - 1; i >= 0; i--) {
final ActivityRecord r = list.get(i);
- if (dumpPackage != null && !dumpPackage.equals(r.packageName)) {
- continue;
- }
- if (innerPrefix == null) {
- innerPrefix = prefix + " ";
- args = new String[0];
- }
- printed = true;
- final boolean full = !brief && (complete || !r.isInHistory());
- if (needNL) {
- pw.println("");
- needNL = false;
- }
- if (header != null) {
- header.run();
- header = null;
- }
- if (lastTask != r.getTask()) {
- lastTask = r.getTask();
- pw.print(prefix);
- pw.print(full ? "* " : " ");
- pw.println(lastTask);
- if (full) {
- lastTask.dump(pw, prefix + " ");
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- if (lastTask.intent != null) {
- pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureString());
- }
- }
- }
- pw.print(prefix); pw.print(full ? " * " : " "); pw.print(label);
- pw.print(" #"); pw.print(i); pw.print(": ");
- pw.println(r);
- if (full) {
- r.dump(pw, innerPrefix, true /* dumpAll */);
- } else if (complete) {
- // Complete + brief == give a summary. Isn't that obvious?!?
- pw.print(innerPrefix); pw.println(r.intent.toInsecureString());
- if (r.app != null) {
- pw.print(innerPrefix); pw.println(r.app);
- }
- }
- if (client && r.attachedToProcess()) {
- // flush anything that is already in the PrintWriter since the thread is going
- // to write to the file descriptor directly
- pw.flush();
- try {
- TransferPipe tp = new TransferPipe();
- try {
- r.app.getThread().dumpActivity(
- tp.getWriteFd(), r.appToken, innerPrefix, args);
- // Short timeout, since blocking here can deadlock with the application.
- tp.go(fd, 2000);
- } finally {
- tp.kill();
- }
- } catch (IOException e) {
- pw.println(innerPrefix + "Failure while dumping the activity: " + e);
- } catch (RemoteException e) {
- pw.println(innerPrefix + "Got a RemoteException while dumping the activity");
- }
- needNL = true;
- }
+ ActivityRecord.dumpActivity(fd, pw, i, r, prefix, label, complete, brief,
+ client, dumpPackage, needNL, header, lastTask);
+ lastTask = r.getTask();
+ header = null;
+ needNL = client && r.attachedToProcess();
}
return printed;
}
@@ -2087,7 +2041,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
void updateTopResumedActivityIfNeeded() {
final ActivityRecord prevTopActivity = mTopResumedActivity;
final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask == null || topRootTask.getResumedActivity() == prevTopActivity) {
+ if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) {
if (mService.isSleepingLocked()) {
// There won't be a next resumed activity. The top process should still be updated
// according to the current top focused activity.
@@ -2109,7 +2063,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
// Update the current top activity.
- mTopResumedActivity = topRootTask.getResumedActivity();
+ mTopResumedActivity = topRootTask.getTopResumedActivity();
scheduleTopResumedActivityStateIfNeeded();
mService.updateTopApp(mTopResumedActivity);
@@ -2349,6 +2303,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
return mVisibilityTransactionDepth > 0;
}
+ void setDeferRootVisibilityUpdate(boolean deferUpdate) {
+ mDeferRootVisibilityUpdate = deferUpdate;
+ }
+
+ boolean isRootVisibilityUpdateDeferred() {
+ return mDeferRootVisibilityUpdate;
+ }
+
/**
* Called when the state or visibility of an attached activity is changed.
*
@@ -2416,8 +2378,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
String processName = null;
int uid = 0;
synchronized (mService.mGlobalLock) {
- if (r.attachedToProcess()
- && r.isState(Task.ActivityState.RESTARTING_PROCESS)) {
+ if (r.attachedToProcess() && r.isState(RESTARTING_PROCESS)) {
processName = r.app.mName;
uid = r.app.mUid;
}
@@ -2635,7 +2596,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
boolean matches(ActivityRecord r) {
- return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r);
+ if (!mLaunchingState.hasActiveTransitionInfo()) {
+ return mTargetComponent.equals(r.mActivityComponent);
+ }
+ return mLaunchingState.contains(r);
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index c1b287ff8077..174b3965f11a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -78,10 +78,7 @@ import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpe
import static com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation;
import static com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
-import static com.android.internal.policy.TransitionAnimation.THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+import static com.android.internal.policy.TransitionAnimation.prepareThumbnailAnimationWithDuration;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.server.wm.AppTransitionProto.APP_TRANSITION_STATE;
@@ -102,12 +99,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Picture;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.Debug;
@@ -132,7 +124,6 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
-import android.view.animation.ClipRectAnimation;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import android.view.animation.ScaleAnimation;
@@ -222,6 +213,7 @@ public class AppTransition implements Dump {
private int mNextAppTransitionEnter;
private int mNextAppTransitionExit;
private int mNextAppTransitionInPlace;
+ private boolean mNextAppTransitionIsSync;
// Keyed by WindowContainer hashCode.
private final SparseArray<AppTransitionAnimationSpec> mNextAppTransitionAnimationsSpecs
@@ -357,6 +349,13 @@ public class AppTransition implements Dump {
fetchAppTransitionSpecsFromFuture();
}
+ void abort() {
+ if (mRemoteAnimationController != null) {
+ mRemoteAnimationController.cancelAnimation("aborted");
+ }
+ clear();
+ }
+
boolean isRunning() {
return mAppTransitionState == APP_STATE_RUNNING;
}
@@ -475,6 +474,7 @@ public class AppTransition implements Dump {
mNextAppTransitionAnimationsSpecsFuture = null;
mDefaultNextAppTransitionAnimationSpec = null;
mAnimationFinishedCallback = null;
+ mNextAppTransitionIsSync = false;
}
void freeze() {
@@ -641,24 +641,6 @@ public class AppTransition implements Dump {
/**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
- Animation prepareThumbnailAnimationWithDuration(@Nullable Animation a, int appWidth,
- int appHeight, long duration, Interpolator interpolator) {
- if (a != null) {
- if (duration > 0) {
- a.setDuration(duration);
- }
- a.setFillAfter(true);
- if (interpolator != null) {
- a.setInterpolator(interpolator);
- }
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- }
- return a;
- }
-
- /**
- * Prepares the specified animation with a standard duration, interpolator, etc.
- */
Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
@@ -678,56 +660,16 @@ public class AppTransition implements Dump {
}
/**
- * Return the current thumbnail transition state.
- */
- int getThumbnailTransitionState(boolean enter) {
- if (enter) {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
- }
- } else {
- if (mNextAppTransitionScaleUp) {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
- } else {
- return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
- }
- }
- }
-
- /**
* Creates an overlay with a background color and a thumbnail for the cross profile apps
* animation.
*/
HardwareBuffer createCrossProfileAppsThumbnail(
@DrawableRes int thumbnailDrawableRes, Rect frame) {
- final int width = frame.width();
- final int height = frame.height();
-
- final Picture picture = new Picture();
- final Canvas canvas = picture.beginRecording(width, height);
- canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
- final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
- final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
- drawable.setBounds(
- (width - thumbnailSize) / 2,
- (height - thumbnailSize) / 2,
- (width + thumbnailSize) / 2,
- (height + thumbnailSize) / 2);
- drawable.setTint(mContext.getColor(android.R.color.white));
- drawable.draw(canvas);
- picture.endRecording();
-
- return Bitmap.createBitmap(picture).getHardwareBuffer();
+ return mTransitionAnimation.createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
}
Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
- final Animation animation =
- mTransitionAnimation.loadCrossProfileAppThumbnailEnterAnimation();
- return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
- appRect.height(), 0, null);
+ return mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(appRect);
}
/**
@@ -735,115 +677,14 @@ public class AppTransition implements Dump {
* when a thumbnail is specified with the pending animation override.
*/
Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
- HardwareBuffer thumbnailHeader, WindowContainer container, int uiMode,
- int orientation) {
- Animation a;
- final int thumbWidthI = thumbnailHeader.getWidth();
- final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
- final int thumbHeightI = thumbnailHeader.getHeight();
- final int appWidth = appRect.width();
-
- float scaleW = appWidth / thumbWidth;
- getNextAppTransitionStartRect(container, mTmpRect);
- final float fromX;
- float fromY;
- final float toX;
- float toY;
- final float pivotX;
- final float pivotY;
- if (shouldScaleDownThumbnailTransition(uiMode, orientation)) {
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
-
- // For the curved translate animation to work, the pivot points needs to be at the
- // same absolute position as the one from the real surface.
- toX = mTmpRect.width() / 2 * (scaleW - 1f) + appRect.left;
- toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
- pivotX = mTmpRect.width() / 2;
- pivotY = appRect.height() / 2 / scaleW;
- if (mGridLayoutRecentsEnabled) {
- // In the grid layout, the header is displayed above the thumbnail instead of
- // overlapping it.
- fromY -= thumbHeightI;
- toY -= thumbHeightI * scaleW;
- }
- } else {
- pivotX = 0;
- pivotY = 0;
- fromX = mTmpRect.left;
- fromY = mTmpRect.top;
- toX = appRect.left;
- toY = appRect.top;
- }
- final long duration = getAspectScaleDuration();
- final Interpolator interpolator = getAspectScaleInterpolator();
- if (mNextAppTransitionScaleUp) {
- // Animation up from the thumbnail to the full screen
- Animation scale = new ScaleAnimation(1f, scaleW, 1f, scaleW, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(1f, 0f);
- alpha.setInterpolator(mThumbnailFadeOutInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(fromX, toX, fromY, toY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- mTmpFromClipRect.set(0, 0, thumbWidthI, thumbHeightI);
- mTmpToClipRect.set(appRect);
-
- // Containing frame is in screen space, but we need the clip rect in the
- // app space.
- mTmpToClipRect.offsetTo(0, 0);
- mTmpToClipRect.right = (int) (mTmpToClipRect.right / scaleW);
- mTmpToClipRect.bottom = (int) (mTmpToClipRect.bottom / scaleW);
-
- if (contentInsets != null) {
- mTmpToClipRect.inset((int) (-contentInsets.left * scaleW),
- (int) (-contentInsets.top * scaleW),
- (int) (-contentInsets.right * scaleW),
- (int) (-contentInsets.bottom * scaleW));
- }
-
- Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
- clipAnim.setInterpolator(interpolator);
- clipAnim.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- set.addAnimation(clipAnim);
- a = set;
- } else {
- // Animation down from the full screen to the thumbnail
- Animation scale = new ScaleAnimation(scaleW, 1f, scaleW, 1f, pivotX, pivotY);
- scale.setInterpolator(interpolator);
- scale.setDuration(duration);
- Animation alpha = new AlphaAnimation(0f, 1f);
- alpha.setInterpolator(mThumbnailFadeInInterpolator);
- alpha.setDuration(duration);
- Animation translate = createCurvedMotion(toX, fromX, toY, fromY);
- translate.setInterpolator(interpolator);
- translate.setDuration(duration);
-
- // This AnimationSet uses the Interpolators assigned above.
- AnimationSet set = new AnimationSet(false);
- set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
- set.addAnimation(translate);
- a = set;
-
- }
- return prepareThumbnailAnimationWithDuration(a, appWidth, appRect.height(), 0,
- null);
+ HardwareBuffer thumbnailHeader, WindowContainer container, int orientation) {
+ AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
+ container.hashCode());
+ return mTransitionAnimation.createThumbnailAspectScaleAnimationLocked(appRect,
+ contentInsets, thumbnailHeader, orientation, spec != null ? spec.rect : null,
+ mDefaultNextAppTransitionAnimationSpec != null
+ ? mDefaultNextAppTransitionAnimationSpec.rect : null,
+ mNextAppTransitionScaleUp);
}
private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) {
@@ -1043,7 +884,7 @@ public class AppTransition implements Dump {
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = mTransitionAnimation.createClipRevealAnimationLocked(
+ a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
transit, enter, frame, displayFrame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
@@ -1052,7 +893,7 @@ public class AppTransition implements Dump {
+ "transit=%s Callers=%s",
a, appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = mTransitionAnimation.createScaleUpAnimationLocked(transit, enter, frame,
+ a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1064,8 +905,8 @@ public class AppTransition implements Dump {
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
- a = mTransitionAnimation.createThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), frame, transit, thumbnailHeader,
+ a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
+ mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1080,9 +921,9 @@ public class AppTransition implements Dump {
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
container.hashCode());
- a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), orientation, transit, frame,
- insets, surfaceInsets, stableInsets, freeform, spec != null ? spec.rect : null,
+ a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
+ mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
+ stableInsets, freeform, spec != null ? spec.rect : null,
mDefaultNextAppTransitionAnimationSpec != null
? mDefaultNextAppTransitionAnimationSpec.rect : null);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1314,13 +1155,19 @@ public class AppTransition implements Dump {
}
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+ overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */);
+ }
+
+ void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+ boolean sync) {
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
isTransitionSet(), remoteAnimationAdapter);
- if (isTransitionSet()) {
+ if (isTransitionSet() && !mNextAppTransitionIsSync) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
remoteAnimationAdapter, mHandler);
+ mNextAppTransitionIsSync = sync;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index c869ec67776d..d6b0086d8dd9 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -464,7 +464,8 @@ public class AppTransitionController {
}
final RemoteAnimationAdapter adapter =
getRemoteAnimationOverride(animLpActivity, transit, activityTypes);
- if (adapter != null) {
+ if (adapter != null
+ && mDisplayContent.mAppTransition.getRemoteAnimationController() == null) {
mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
}
}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index faeb4ba36748..4355b3855bb7 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -166,6 +166,10 @@ class BLASTSyncEngine {
setReady(id, true);
}
+ boolean isReady(int id) {
+ return mActiveSyncs.get(id).mReady;
+ }
+
/**
* Aborts the sync (ie. it doesn't wait for ready or anything to finish)
*/
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index d52e9b608cdb..6fafc0291427 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -34,6 +34,7 @@ import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_
import static com.android.server.wm.ConfigurationContainerProto.OVERRIDE_CONFIGURATION;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -111,6 +112,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
*/
+ @NonNull
public Configuration getConfiguration() {
return mFullConfiguration;
}
@@ -170,11 +172,13 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
/** Returns requested override configuration applied to this configuration container. */
+ @NonNull
public Configuration getRequestedOverrideConfiguration() {
return mRequestedOverrideConfiguration;
}
/** Returns the resolved override configuration. */
+ @NonNull
Configuration getResolvedOverrideConfiguration() {
return mResolvedOverrideConfiguration;
}
@@ -203,6 +207,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* Get merged override configuration from the top of the hierarchy down to this particular
* instance. This should be reported to client as override config.
*/
+ @NonNull
public Configuration getMergedOverrideConfiguration() {
return mMergedOverrideConfiguration;
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index baa27e34d625..99f6fd4771b7 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -495,8 +495,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
DisplayAreaInfo getDisplayAreaInfo() {
- DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
+ final DisplayAreaInfo info = new DisplayAreaInfo(mRemoteToken.toWindowContainerToken(),
getDisplayContent().getDisplayId(), mFeatureId);
+ final RootDisplayArea root = getRootDisplayArea();
+ info.rootDisplayAreaId = root == null ? getDisplayContent().mFeatureId : root.mFeatureId;
info.configuration.setTo(getConfiguration());
return info;
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 47d7c9d1279d..3d7ac6c1a3f8 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.annotation.Nullable;
import android.os.Bundle;
@@ -135,12 +136,6 @@ import java.util.function.BiFunction;
*/
class DisplayAreaPolicyBuilder {
- /**
- * Key to specify the {@link RootDisplayArea} to attach the window to. Should be used by the
- * function passed in from {@link #setSelectRootForWindowFunc(BiFunction)}
- */
- static final String KEY_ROOT_DISPLAY_AREA_ID = "root_display_area_id";
-
@Nullable private HierarchyBuilder mRootHierarchyBuilder;
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
new ArrayList<>();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dbc1116ad389..1cd65dca5483 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -62,7 +62,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
@@ -95,6 +94,7 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_A
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
@@ -117,7 +117,6 @@ import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -134,7 +133,6 @@ import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_
import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -167,13 +165,11 @@ import android.graphics.Region.Op;
import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerInternal;
import android.metrics.LogMaker;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -198,10 +194,10 @@ import android.view.ISystemGestureExclusionListener;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
-import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.MagnificationSpec;
import android.view.PrivacyIndicatorBounds;
import android.view.RemoteAnimationDefinition;
@@ -298,6 +294,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private final SurfaceControl mWindowingLayer;
+ /**
+ * The window token of the layer of the hierarchy to mirror, or null if this DisplayContent
+ * is not being used for layer mirroring.
+ */
+ @VisibleForTesting IBinder mTokenToMirror = null;
+
+ /**
+ * The surface for mirroring the contents of this hierarchy.
+ */
+ private SurfaceControl mMirroredSurface = null;
+
+ /**
+ * The last bounds of the DisplayArea to mirror.
+ */
+ private Rect mLastMirroredDisplayAreaBounds = null;
+
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -362,6 +374,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
boolean mIsSizeForced = false;
/**
+ * Overridden display size and metrics to activity window bounds. Set via
+ * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging.
+ * @see WindowManagerService#setSandboxDisplayApis(int, boolean)
+ */
+ private boolean mSandboxDisplayApis = true;
+
+ /**
* Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
* but can be set from Settings or via shell command "adb shell wm density".
* @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
@@ -426,6 +445,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Accessed directly by all users.
private boolean mLayoutNeeded;
int pendingLayoutChanges;
+ boolean mLayoutAndAssignWindowLayersScheduled;
/**
* Used to gate application window layout until we have sent the complete configuration.
@@ -498,11 +518,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
WindowState mCurrentFocus = null;
/**
- * The last focused window that we've notified the client that the focus is changed.
- */
- WindowState mLastFocus = null;
-
- /**
* The foreground app of this display. Windows below this app cannot be the focused window. If
* the user taps on the area outside of the task of the focused app, we will notify AM about the
* new task the user wants to interact with.
@@ -635,8 +650,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private WindowState mParentWindow;
private Point mLocationInParentWindow = new Point();
- private SurfaceControl mParentSurfaceControl;
- private InputWindowHandle mPortalWindowHandle;
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -690,6 +703,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// well and thus won't change the top resumed / focused record
boolean mDontMoveToTop;
+ private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -771,6 +786,21 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTmpWindow = null;
return true;
}
+
+ // If the candidate activity is currently being embedded in the focused task, the
+ // activity cannot be focused unless it is on the same TaskFragment as the focusedApp's.
+ TaskFragment parent = activity.getTaskFragment();
+ if (parent != null && parent.isEmbedded()) {
+ Task hostTask = focusedApp.getTask();
+ if (hostTask.isEmbedded()) {
+ // Use the hosting task if the current task is embedded.
+ hostTask = hostTask.getParent().asTaskFragment().getTask();
+ }
+ if (activity.isDescendantOf(hostTask)
+ && activity.getTaskFragment() != focusedApp.getTaskFragment()) {
+ return false;
+ }
+ }
}
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
@@ -1020,6 +1050,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
+ mAtmService.getTransitionController().registerLegacyListener(
+ mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
@@ -1104,6 +1136,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+
+ // Check if this DisplayContent is for a new VirtualDisplay, that should use layer mirroring
+ // to capture the contents of a DisplayArea.
+ startMirrorIfNeeded();
}
boolean isReady() {
@@ -1234,12 +1270,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// removing from parent.
token.getParent().removeChild(token);
}
- if (token.hasChild(prevDc.mLastFocus)) {
- // If the reparent window token contains previous display's last focus window, means
- // it will end up to gain window focus on the target display, so it should not be
- // notified that it lost focus from the previous display.
- prevDc.mLastFocus = null;
- }
}
addWindowToken(token.token, token);
@@ -1919,10 +1949,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
}
-
- if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onRotationChanged(this);
- }
}
void configureDisplayPolicy() {
@@ -2470,6 +2496,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Update IME parent if needed.
updateImeParent();
+ // Update mirroring surface for MediaProjection, if this DisplayContent is being used
+ // for layer mirroring.
+ if (mMirroredSurface != null) {
+ // Retrieve the size of the DisplayArea to mirror, and continue with the update if the
+ // bounds have changed.
+ final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
+ mTokenToMirror);
+ if (wc != null && mLastMirroredDisplayAreaBounds != null) {
+ // Retrieve the size of the DisplayArea to mirror, and continue with the update
+ // if the bounds or orientation has changed.
+ final Rect displayAreaBounds = wc.getDisplayContent().getBounds();
+ int displayAreaOrientation = wc.getDisplayContent().getOrientation();
+ if (!mLastMirroredDisplayAreaBounds.equals(displayAreaBounds)
+ || lastOrientation != displayAreaOrientation) {
+ updateMirroredSurface(mWmService.mTransactionFactory.get(), displayAreaBounds);
+ }
+ }
+ }
+
if (lastOrientation != getConfiguration().orientation) {
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)
@@ -2490,7 +2535,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
boolean isVisibleRequested() {
- return isVisible();
+ return isVisible() && !mRemoved && !mRemoving;
}
@Override
@@ -2984,7 +3029,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Override
void removeIfPossible() {
- if (isAnimating(TRANSITION | PARENTS)) {
+ if (isAnimating(TRANSITION | PARENTS)
+ // isAnimating is a legacy transition query and will be removed, so also add a
+ // check for whether this is in a shell-transition when not using legacy.
+ || mAtmService.getTransitionController().inTransition()) {
mDeferredRemoval = true;
return;
}
@@ -3105,7 +3153,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
- mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
+ } else {
+ mAppTransition.dumpDebug(proto, APP_TRANSITION);
+ }
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -3193,9 +3245,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
pw.print(" mCurrentFocus="); pw.println(mCurrentFocus);
- if (mLastFocus != mCurrentFocus) {
- pw.print(" mLastFocus="); pw.println(mLastFocus);
- }
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
if (mFixedRotationLaunchingApp != null) {
pw.println(" mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp);
@@ -3426,15 +3475,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- onWindowFocusChanged(oldFocus, newFocus);
-
- int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
+ getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
- focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
@@ -3442,16 +3488,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- // The change in focus caused us to need to do a layout. Okay.
- setLayoutNeeded();
- if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
- performLayout(true /*initial*/, updateInputWindows);
- } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
- mWmService.mRoot.performSurfacePlacement();
- }
- }
-
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for doing this part.
getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
@@ -3478,7 +3514,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mWmService.mAccessibilityController));
}
- mLastFocus = mCurrentFocus;
return true;
}
@@ -3486,20 +3521,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
accessibilityController.onWindowFocusChangedNot(getDisplayId());
}
- private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
- final Task focusedTask = newFocus != null ? newFocus.getTask() : null;
- final Task unfocusedTask = oldFocus != null ? oldFocus.getTask() : null;
- if (focusedTask == unfocusedTask) {
- return;
- }
- if (focusedTask != null) {
- focusedTask.onWindowFocusChanged(true /* hasFocus */);
- }
- if (unfocusedTask != null) {
- unfocusedTask.onWindowFocusChanged(false /* hasFocus */);
- }
- }
-
/**
* Set the new focused app to this display.
*
@@ -3523,7 +3544,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s",
newFocus, getDisplayId(), Debug.getCallers(4));
+ final Task oldTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
+ final Task newTask = newFocus != null ? newFocus.getTask() : null;
mFocusedApp = newFocus;
+ if (oldTask != newTask) {
+ if (oldTask != null) oldTask.onAppFocusChanged(false);
+ if (newTask != null) newTask.onAppFocusChanged(true);
+ }
+
getInputMonitor().setFocusedAppLw(newFocus);
updateTouchExcludeRegion();
return true;
@@ -3796,6 +3824,23 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate();
}
+ /** @see WindowManagerInternal#onToggleImeRequested */
+ void onShowImeRequested() {
+ if (mImeLayeringTarget == null || mInputMethodWindow == null) {
+ return;
+ }
+ // If IME window will be shown on the rotated activity, share the transformed state to
+ // IME window so it can compute rotated frame with rotated configuration.
+ if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) {
+ mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken);
+ // Hide the window until the rotation is done to avoid intermediate artifacts if the
+ // parent surface of IME container is changed.
+ if (mFadeRotationAnimationController != null) {
+ mFadeRotationAnimationController.hideImmediately(mInputMethodWindow.mToken);
+ }
+ }
+ }
+
@VisibleForTesting
void setImeLayeringTarget(WindowState target) {
mImeLayeringTarget = target;
@@ -4221,7 +4266,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "Desired input method target: " + imFocus);
Slog.i(TAG_WM, "Current focus: " + mCurrentFocus + " displayId=" + mDisplayId);
- Slog.i(TAG_WM, "Last focus: " + mLastFocus + " displayId=" + mDisplayId);
}
if (DEBUG_INPUT_METHOD) {
@@ -4346,6 +4390,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
true /* inTraversal, must call performTraversalInTrans... below */);
}
+ updateMirroring();
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
@@ -4364,14 +4409,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private void updateBounds() {
calculateBounds(mDisplayInfo, mTmpBounds);
setBounds(mTmpBounds);
- if (mPortalWindowHandle != null && mParentSurfaceControl != null) {
- mPortalWindowHandle.touchableRegion.getBounds(mTmpRect);
- if (!mTmpBounds.equals(mTmpRect)) {
- mPortalWindowHandle.touchableRegion.set(mTmpBounds);
- getPendingTransaction().setInputWindowInfo(
- mParentSurfaceControl, mPortalWindowHandle);
- }
- }
}
// Determines the current display bounds based on the current state
@@ -4511,6 +4548,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ // clear first just in case.
+ mTmpActivityList.clear();
// Time to remove any exiting applications?
forAllRootTasks(task -> {
final ArrayList<ActivityRecord> activities = task.mExitingActivities;
@@ -4518,16 +4557,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final ActivityRecord activity = activities.get(j);
if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
&& (!activity.mIsExiting || activity.isEmpty())) {
- // Make sure there is no animation running on this activity, so any windows
- // associated with it will be removed as soon as their animations are
- // complete.
- cancelAnimation();
- ProtoLog.v(WM_DEBUG_ADD_REMOVE,
- "performLayout: Activity exiting now removed %s", activity);
- activity.removeIfPossible();
+ mTmpActivityList.add(activity);
}
}
});
+ if (!mTmpActivityList.isEmpty()) {
+ // Make sure there is no animation running on this activity, so any windows
+ // associated with it will be removed as soon as their animations are
+ // complete.
+ cancelAnimation();
+ }
+ for (int i = 0; i < mTmpActivityList.size(); ++i) {
+ final ActivityRecord activity = mTmpActivityList.get(i);
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "performLayout: Activity exiting now removed %s", activity);
+ activity.removeIfPossible();
+ }
+ // Clear afterwards so we don't hold references.
+ mTmpActivityList.clear();
}
@Override
@@ -4598,7 +4645,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
- if (task.isOrganized()) {
+ // TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove
+ // the special case when it does.
+ if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
return true;
}
@@ -4951,10 +5000,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ /**
+ * @deprecated new transition should use {@link #requestTransitionAndLegacyPrepare(int, int)}
+ */
+ @Deprecated
void prepareAppTransition(@WindowManager.TransitionType int transit) {
prepareAppTransition(transit, 0 /* flags */);
}
+ /**
+ * @deprecated new transition should use {@link #requestTransitionAndLegacyPrepare(int, int)}
+ */
+ @Deprecated
void prepareAppTransition(@WindowManager.TransitionType int transit,
@WindowManager.TransitionFlags int flags) {
final boolean prepared = mAppTransition.prepareAppTransition(transit, flags);
@@ -4967,14 +5024,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Helper that both requests a transition (using the new transition system) and prepares
* the legacy transition system. Use this when both systems have the same start-point.
*
- * @see TransitionController#requestTransitionIfNeeded(int, int, WindowContainer)
+ * @see TransitionController#requestTransitionIfNeeded(int, int, WindowContainer,
+ * WindowContainer)
* @see AppTransition#prepareAppTransition
*/
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
@WindowManager.TransitionFlags int flags) {
prepareAppTransition(transit, flags);
mAtmService.getTransitionController().requestTransitionIfNeeded(transit, flags,
- null /* trigger */);
+ null /* trigger */, this);
}
/** @see #requestTransitionAndLegacyPrepare(int, int) */
@@ -4982,11 +5040,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
@Nullable WindowContainer trigger) {
prepareAppTransition(transit);
mAtmService.getTransitionController().requestTransitionIfNeeded(transit, 0 /* flags */,
- trigger);
+ trigger, this);
}
void executeAppTransition() {
- mAtmService.getTransitionController().setReady();
+ mAtmService.getTransitionController().setReady(this);
if (mAppTransition.isTransitionSet()) {
ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
"Execute app transition: %s, displayId: %d Callers=%s",
@@ -4996,6 +5054,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
+ void cancelAppTransition() {
+ if (!mAppTransition.isTransitionSet() || mAppTransition.isRunning()) return;
+ mAppTransition.abort();
+ }
+
/**
* Update pendingLayoutChanges after app transition has finished.
*/
@@ -5029,6 +5092,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
+ // TODO(b/191375840): decouple "forwardness" from transition system.
+ if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ @WindowManager.TransitionType int type =
+ mAtmService.getTransitionController().getCollectingTransitionType();
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
+ }
return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
|| mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
}
@@ -5352,31 +5421,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
/**
- * Create a portal window handle for input. This window transports any touch to the display
- * indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
- *
- * @param name The name of the portal window handle.
- * @return the new portal window handle.
- */
- private InputWindowHandle createPortalWindowHandle(String name) {
- // Let surface flinger to set the display ID of this input window handle because we don't
- // know which display the parent surface control is on.
- final InputWindowHandle portalWindowHandle = new InputWindowHandle(
- null /* inputApplicationHandle */, INVALID_DISPLAY);
- portalWindowHandle.name = name;
- portalWindowHandle.token = new Binder();
- portalWindowHandle.layoutParamsFlags =
- FLAG_SPLIT_TOUCH | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL;
- getBounds(mTmpBounds);
- portalWindowHandle.touchableRegion.set(mTmpBounds);
- portalWindowHandle.scaleFactor = 1f;
- portalWindowHandle.ownerPid = Process.myPid();
- portalWindowHandle.ownerUid = Process.myUid();
- portalWindowHandle.portalToDisplayId = mDisplayId;
- return portalWindowHandle;
- }
-
- /**
* @see IWindowManager#setForwardedInsets
*/
public void setForwardedInsets(Insets insets) {
@@ -5612,6 +5656,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
+ @Override
+ void onResize() {
+ super.onResize();
+ if (mWmService.mAccessibilityController != null) {
+ mWmService.mAccessibilityController.onDisplaySizeChanged(this);
+ }
+ }
+
/**
* If the launching rotated activity ({@link #mFixedRotationLaunchingApp}) is null, it simply
* applies the rotation to display. Otherwise because the activity has shown as rotated, the
@@ -5854,6 +5906,148 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return true;
}
+ /**
+ * Sets if Display APIs should be sandboxed to the activity window bounds.
+ */
+ void setSandboxDisplayApis(boolean sandboxDisplayApis) {
+ mSandboxDisplayApis = sandboxDisplayApis;
+ }
+
+ /**
+ * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds,
+ * {@code false} otherwise. Default to true, unless set for debugging purposes.
+ */
+ boolean sandboxDisplayApis() {
+ return mSandboxDisplayApis;
+ }
+
+ /**
+ * Start mirroring to this DisplayContent if it does not have its own content. Captures the
+ * content of a WindowContainer indicated by a WindowToken. If unable to start mirroring, falls
+ * back to original MediaProjection approach.
+ */
+ private void startMirrorIfNeeded() {
+ // Only mirror if this display does not have its own content.
+ if (mLastHasContent) {
+ return;
+ }
+ // Given the WindowToken of the DisplayArea to mirror, retrieve the associated
+ // SurfaceControl.
+ IBinder tokenToMirror = mWmService.mDisplayManagerInternal.getWindowTokenClientToMirror(
+ mDisplayId);
+
+ if (tokenToMirror == null) {
+ // This DisplayContent instance is not involved in layer mirroring. If the display
+ // has been created for capturing, fall back to prior MediaProjection approach.
+ return;
+ }
+ final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
+ tokenToMirror);
+ if (wc == null) {
+ // Un-set the window token to mirror for this VirtualDisplay, to fall back to the
+ // original MediaProjection approach.
+ mWmService.mDisplayManagerInternal.setWindowTokenClientToMirror(mDisplayId, null);
+ return;
+ }
+ SurfaceControl sc = wc.getDisplayContent().getSurfaceControl();
+
+ // Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
+ mMirroredSurface = SurfaceControl.mirrorSurface(sc);
+ SurfaceControl.Transaction transaction = mWmService.mTransactionFactory.get()
+ // Set the mMirroredSurface's parent to the root SurfaceControl for this
+ // DisplayContent. This brings the new mirrored hierarchy under this DisplayContent,
+ // so SurfaceControl will write the layers of this hierarchy to the output surface
+ // provided by the app.
+ .reparent(mMirroredSurface, mSurfaceControl)
+ // Reparent the SurfaceControl of this DisplayContent to null, to prevent content
+ // being added to it. This ensures that no app launched explicitly on the
+ // VirtualDisplay will show up as part of the mirrored content.
+ .reparent(mWindowingLayer, null);
+ // Retrieve the size of the DisplayArea to mirror.
+ updateMirroredSurface(transaction, wc.getDisplayContent().getBounds());
+ mTokenToMirror = tokenToMirror;
+
+ // No need to clean up. In SurfaceFlinger, parents hold references to their children. The
+ // mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is
+ // holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up
+ // when the VirtualDisplay is destroyed - which will clean up this DisplayContent.
+ }
+
+ /**
+ * Start or stop mirroring if this DisplayContent now has content, or no longer has content.
+ */
+ private void updateMirroring() {
+ if (mLastHasContent && mMirroredSurface != null) {
+ // Display now has content, so stop mirroring to it.
+ mWmService.mTransactionFactory.get()
+ // Remove the reference to mMirroredSurface, to clean up associated memory.
+ .remove(mMirroredSurface)
+ // Reparent the SurfaceControl of this DisplayContent back to mSurfaceControl,
+ // to allow content to be added to it. This allows this DisplayContent to stop
+ // mirroring and show content normally.
+ .reparent(mWindowingLayer, mSurfaceControl).apply();
+ // Stop mirroring by destroying the reference to the mirrored layer.
+ mMirroredSurface = null;
+ // Do not un-set the token, in case content is removed and mirroring should begin again.
+ } else if (!mLastHasContent && mMirroredSurface == null) {
+ // Display no longer has content, so start mirroring to it.
+ startMirrorIfNeeded();
+ }
+ }
+
+ /**
+ * Apply transformations to the mirrored surface to ensure the captured contents are scaled to
+ * fit and centred in the output surface.
+ *
+ * @param transaction the transaction to include transformations of mMirroredSurface
+ * to. Transaction is not applied before returning.
+ * @param displayAreaBounds bounds of the DisplayArea to mirror to the surface provided by
+ * the app.
+ */
+ @VisibleForTesting
+ void updateMirroredSurface(SurfaceControl.Transaction transaction,
+ Rect displayAreaBounds) {
+ // Retrieve the default size of the surface the app provided to
+ // MediaProjection#createVirtualDisplay. Note the app is the consumer of the surface,
+ // since it reads out buffers from the surface, and SurfaceFlinger is the producer since
+ // it writes the mirrored layers to the buffers.
+ final Point surfaceSize = mWmService.mDisplayManagerInternal.getDisplaySurfaceDefaultSize(
+ mDisplayId);
+
+ // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
+ // output surface.
+ float scaleX = surfaceSize.x / (float) displayAreaBounds.width();
+ float scaleY = surfaceSize.y / (float) displayAreaBounds.height();
+ float scale = Math.min(scaleX, scaleY);
+ int scaledWidth = Math.round(scale * (float) displayAreaBounds.width());
+ int scaledHeight = Math.round(scale * (float) displayAreaBounds.height());
+
+ // Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored
+ // contents in the output surface.
+ int shiftedX = 0;
+ if (scaledWidth != surfaceSize.x) {
+ shiftedX = (surfaceSize.x - scaledWidth) / 2;
+ }
+ int shiftedY = 0;
+ if (scaledHeight != surfaceSize.y) {
+ shiftedY = (surfaceSize.y - scaledHeight) / 2;
+ }
+
+ transaction
+ // Crop the area to capture to exclude the 'extra' wallpaper that is used
+ // for parallax (b/189930234).
+ .setWindowCrop(mMirroredSurface, displayAreaBounds.width(),
+ displayAreaBounds.height())
+ // Scale the root mirror SurfaceControl, based upon the size difference between the
+ // source (DisplayArea to capture) and output (surface the app reads images from).
+ .setMatrix(mMirroredSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
+ // Position needs to be updated when the mirrored DisplayArea has changed, since
+ // the content will no longer be centered in the output surface.
+ .setPosition(mMirroredSurface, shiftedX /* x */, shiftedY /* y */)
+ .apply();
+ mLastMirroredDisplayAreaBounds = new Rect(displayAreaBounds);
+ }
+
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
@@ -5998,7 +6192,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
class RemoteInsetsControlTarget implements InsetsControlTarget {
private final IDisplayWindowInsetsController mRemoteInsetsController;
- private final InsetsState mRequestedInsetsState = new InsetsState();
+ private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
mRemoteInsetsController = controller;
@@ -6060,15 +6254,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (type == ITYPE_IME) {
return getInsetsStateController().getImeSourceProvider().isImeShowing();
}
- return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
+ return mRequestedVisibilities.getVisibility(type);
}
- void updateRequestedVisibility(InsetsState state) {
- for (int i = 0; i < InsetsState.SIZE; i++) {
- final InsetsSource source = state.peekSource(i);
- if (source == null) continue;
- mRequestedInsetsState.addSource(source);
- }
+ void setRequestedVisibilities(InsetsVisibilities requestedVisibilities) {
+ mRequestedVisibilities.set(requestedVisibilities);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 73d31bf7e0c8..1647eba19206 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -16,12 +16,9 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.util.RotationUtils.deltaRotation;
@@ -39,6 +36,7 @@ import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.ViewRootImpl.computeWindowBounds;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -56,6 +54,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
@@ -142,6 +141,7 @@ import android.view.InsetsFlags;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
@@ -173,6 +173,9 @@ import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -313,17 +316,41 @@ public class DisplayPolicy {
private WindowState mSystemUiControllingWindow;
+ // Candidate window to determine the color of navigation bar. The window needs to be top
+ // fullscreen-app windows or dim layers that are intersecting with the window frame of status
+ // bar.
+ private WindowState mNavBarColorWindowCandidate;
+
+ // The window to determine opacity and background of translucent navigation bar. The window
+ // needs to be opaque.
+ private WindowState mNavBarBackgroundWindow;
+
+ /**
+ * Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
+ * the conditions of being candidate window.
+ */
+ private final ArrayList<WindowState> mStatusBarColorWindows = new ArrayList<>();
+
+ /**
+ * Windows to determine opacity and background of translucent status bar. The window needs to be
+ * opaque
+ */
+ private final ArrayList<WindowState> mStatusBarBackgroundWindows = new ArrayList<>();
+
+ private String mFocusedApp;
private int mLastDisableFlags;
private int mLastAppearance;
- private int mLastFullscreenAppearance;
- private int mLastDockedAppearance;
private int mLastBehavior;
- private final Rect mNonDockedRootTaskBounds = new Rect();
- private final Rect mDockedRootTaskBounds = new Rect();
- private final Rect mLastNonDockedRootTaskBounds = new Rect();
- private final Rect mLastDockedRootTaskBounds = new Rect();
+ private InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
+ private AppearanceRegion[] mLastStatusBarAppearanceRegions;
+
+ /** The union of checked bounds while fetching {@link #mStatusBarColorWindows}. */
+ private final Rect mStatusBarColorCheckedBounds = new Rect();
+
+ /** The union of checked bounds while fetching {@link #mStatusBarBackgroundWindows}. */
+ private final Rect mStatusBarBackgroundCheckedBounds = new Rect();
- // What we last reported to system UI about whether the focused window is fullscreen/immersive.
+ // What we last reported to input dispatcher about whether the focused window is fullscreen.
private boolean mLastFocusIsFullscreen = false;
// If nonzero, a panic gesture was performed at that time in uptime millis and is still pending.
@@ -333,19 +360,15 @@ public class DisplayPolicy {
private static final Rect sTmpRect = new Rect();
private static final Rect sTmpNavFrame = new Rect();
private static final Rect sTmpStatusFrame = new Rect();
+ private static final Rect sTmpDecorFrame = new Rect();
private static final Rect sTmpScreenDecorFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
private static final Rect sTmpDisplayFrameBounds = new Rect();
private WindowState mTopFullscreenOpaqueWindowState;
- private WindowState mTopFullscreenOpaqueOrDimmingWindowState;
- private WindowState mTopDockedOpaqueWindowState;
- private WindowState mTopDockedOpaqueOrDimmingWindowState;
private boolean mTopIsFullscreen;
private boolean mForceStatusBar;
private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
- private boolean mForcingShowNavBar;
- private int mForcingShowNavBarLayer;
private boolean mForceShowSystemBars;
private boolean mShowingDream;
@@ -430,8 +453,10 @@ public class DisplayPolicy {
final int displayId = displayContent.getDisplayId();
- mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
- mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ mBarContentFrames.put(TYPE_STATUS_BAR, new Rect());
+ mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect());
+ }
final Resources r = mContext.getResources();
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
@@ -613,6 +638,8 @@ public class DisplayPolicy {
}
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
+ mService.mAtmService.getTransitionController().registerLegacyListener(
+ mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -846,6 +873,20 @@ public class DisplayPolicy {
}
/**
+ * Only trusted overlays are allowed to use FLAG_SLIPPERY.
+ */
+ static int sanitizeFlagSlippery(int flags, int privateFlags, String name) {
+ if ((flags & FLAG_SLIPPERY) == 0) {
+ return flags;
+ }
+ if ((privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
+ return flags;
+ }
+ Slog.w(TAG, "Removing FLAG_SLIPPERY for non-trusted overlay " + name);
+ return flags & ~FLAG_SLIPPERY;
+ }
+
+ /**
* Sanitize the layout parameters coming from a client. Allows the policy
* to do things like ensure that windows of a specific type can't take
* input focus.
@@ -925,6 +966,8 @@ public class DisplayPolicy {
if (mExtraNavBarAlt == win) {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
}
+
+ attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName());
}
/**
@@ -1079,7 +1122,9 @@ public class DisplayPolicy {
mStatusBar = win;
final TriConsumer<DisplayFrames, WindowState, Rect> frameProvider =
(displayFrames, windowState, rect) -> {
- rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ rect.bottom = rect.top + getStatusBarHeight(displayFrames);
+ }
};
mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
@@ -1089,18 +1134,22 @@ public class DisplayPolicy {
mNavigationBar = win;
mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win,
(displayFrames, windowState, inOutFrame) -> {
-
- // In Gesture Nav, navigation bar frame is larger than frame to
- // calculate inset.
- if (navigationBarPosition(displayFrames.mDisplayWidth,
- displayFrames.mDisplayHeight,
- displayFrames.mRotation) == NAV_BAR_BOTTOM
- && !mNavButtonForcedVisible) {
- sTmpRect.set(inOutFrame);
- sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
- inOutFrame.top = sTmpRect.bottom
- - getNavigationBarHeight(displayFrames.mRotation,
- mDisplayContent.getConfiguration().uiMode);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ inOutFrame.inset(windowState.getLayoutingAttrs(
+ displayFrames.mRotation).providedInternalInsets);
+ } else {
+ // In Gesture Nav, navigation bar frame is larger than frame to
+ // calculate inset.
+ if (navigationBarPosition(displayFrames.mDisplayWidth,
+ displayFrames.mDisplayHeight,
+ displayFrames.mRotation) == NAV_BAR_BOTTOM
+ && !mNavButtonForcedVisible) {
+ sTmpRect.set(inOutFrame);
+ sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe);
+ inOutFrame.top = sTmpRect.bottom
+ - getNavigationBarHeight(displayFrames.mRotation,
+ mDisplayContent.getConfiguration().uiMode);
+ }
}
},
@@ -1161,7 +1210,14 @@ public class DisplayPolicy {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
break;
}
- mDisplayContent.setInsetProvider(insetsType, win, null);
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ mDisplayContent.setInsetProvider(insetsType, win, null);
+ } else {
+ mDisplayContent.setInsetProvider(insetsType, win, (displayFrames,
+ windowState, inOutFrame) -> inOutFrame.inset(
+ windowState.getLayoutingAttrs(displayFrames.mRotation)
+ .providedInternalInsets));
+ }
}
}
break;
@@ -1252,8 +1308,17 @@ public class DisplayPolicy {
}
private int getStatusBarHeight(DisplayFrames displayFrames) {
- return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
- displayFrames.mDisplayCutoutSafe.top);
+ int statusBarHeight;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mStatusBar != null) {
+ statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height;
+ } else {
+ statusBarHeight = 0;
+ }
+ } else {
+ statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation];
+ }
+ return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top);
}
WindowState getStatusBar() {
@@ -1388,7 +1453,7 @@ public class DisplayPolicy {
/**
* @return true if the system bars are forced to stay visible
*/
- public boolean areSystemBarsForcedShownLw(WindowState windowState) {
+ public boolean areSystemBarsForcedShownLw() {
return mForceShowSystemBars;
}
@@ -1423,13 +1488,30 @@ public class DisplayPolicy {
WindowFrames simulatedWindowFrames, SparseArray<Rect> contentFrames,
Consumer<Rect> layout) {
win.setSimulatedWindowFrames(simulatedWindowFrames);
+ final int requestedHeight = win.mRequestedHeight;
+ final int requestedWidth = win.mRequestedWidth;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ // Without a full layout process, in order to layout the system bars correctly, we need
+ // to set the requested size and the initial display frames to the window.
+ WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation);
+ win.setRequestedSize(params.width, params.height);
+ sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight);
+ simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */,
+ sTmpDecorFrame /* displayFrame */);
+ simulatedWindowFrames.mIsSimulatingDecorWindow = true;
+ }
final Rect contentFrame = new Rect();
try {
layout.accept(contentFrame);
} finally {
win.setSimulatedWindowFrames(null);
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ win.setRequestedSize(requestedWidth, requestedHeight);
+ }
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ contentFrames.put(win.mAttrs.type, contentFrame);
}
- contentFrames.put(win.mAttrs.type, contentFrame);
mDisplayContent.getInsetsStateController().computeSimulatedState(
win, displayFrames, simulatedWindowFrames);
}
@@ -1440,15 +1522,31 @@ public class DisplayPolicy {
* some temporal states, but doesn't change the window frames used to show on screen.
*/
void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray<Rect> barContentFrames) {
- final WindowFrames simulatedWindowFrames = new WindowFrames();
if (mNavigationBar != null) {
- simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
- barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
- contentFrame));
+ final WindowFrames simulatedWindowFrames = new WindowFrames();
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> simulateLayoutForContentFrame(displayFrames,
+ mNavigationBar, contentFrame));
+ } else {
+ simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames,
+ barContentFrames, contentFrame -> layoutNavigationBar(displayFrames,
+ contentFrame));
+ }
}
if (mStatusBar != null) {
- simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
- barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+ final WindowFrames simulatedWindowFrames = new WindowFrames();
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> simulateLayoutForContentFrame(displayFrames,
+ mStatusBar, contentFrame));
+ } else {
+ simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames,
+ barContentFrames,
+ contentFrame -> layoutStatusBar(displayFrames, contentFrame));
+ }
}
}
@@ -1467,7 +1565,7 @@ public class DisplayPolicy {
windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
sTmpStatusFrame /* displayFrame */);
// Let the status bar determine its size.
- mStatusBar.computeFrameAndUpdateSourceFrame();
+ mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames);
// For layout, the status bar is always at the top with our fixed height.
int statusBarBottom = displayFrames.mUnrestricted.top
@@ -1518,18 +1616,18 @@ public class DisplayPolicy {
} else if (navBarPosition == NAV_BAR_RIGHT) {
// Landscape screen; nav bar goes to the right.
navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right)
- - getNavigationBarWidth(rotation, uiMode);
+ - getNavigationBarWidth(rotation, uiMode, navBarPosition);
} else if (navBarPosition == NAV_BAR_LEFT) {
// Seascape screen; nav bar goes to the left.
navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left)
- + getNavigationBarWidth(rotation, uiMode);
+ + getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
// Compute the final frame.
final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
windowFrames.setFrames(navigationFrame /* parentFrame */,
navigationFrame /* displayFrame */);
- mNavigationBar.computeFrameAndUpdateSourceFrame();
+ mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames);
sTmpRect.set(windowFrames.mFrame);
sTmpRect.intersect(displayFrames.mDisplayCutoutSafe);
contentFrame.set(sTmpRect);
@@ -1538,6 +1636,16 @@ public class DisplayPolicy {
return navBarPosition;
}
+ private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win,
+ Rect simulatedContentFrame) {
+ layoutWindowLw(win, null /* attached */, displayFrames);
+ final Rect contentFrame = sTmpRect;
+ contentFrame.set(win.getLayoutingWindowFrames().mFrame);
+ // Excluding the display cutout before set to the simulated content frame.
+ contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
+ simulatedContentFrame.set(contentFrame);
+ }
+
private boolean canReceiveInput(WindowState win) {
boolean notFocusable =
(win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0;
@@ -1559,12 +1667,12 @@ public class DisplayPolicy {
* @param displayFrames The display frames.
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
- if (win == mNavigationBar) {
+ if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) {
mNavigationBarPosition = layoutNavigationBar(displayFrames,
mBarContentFrames.get(TYPE_NAVIGATION_BAR));
return;
}
- if ((win == mStatusBar && !canReceiveInput(win))) {
+ if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) {
layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR));
return;
}
@@ -1572,7 +1680,7 @@ public class DisplayPolicy {
// Skip layout of the window when in transition to pip mode.
return;
}
- final WindowManager.LayoutParams attrs = win.getAttrs();
+ final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
final int type = attrs.type;
final int fl = attrs.flags;
@@ -1580,7 +1688,7 @@ public class DisplayPolicy {
final int sim = attrs.softInputMode;
displayFrames = win.getDisplayFrames(displayFrames);
- final WindowFrames windowFrames = win.getWindowFrames();
+ final WindowFrames windowFrames = win.getLayoutingWindowFrames();
sTmpLastParentFrame.set(windowFrames.mParentFrame);
final Rect pf = windowFrames.mParentFrame;
@@ -1591,7 +1699,13 @@ public class DisplayPolicy {
final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR;
final InsetsState state = win.getInsetsState();
- computeWindowBounds(attrs, state, win.mToken.getBounds(), df);
+ if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) {
+ // Override the bounds in window token has many side effects. Directly use the display
+ // frame set for the simulated layout for this case.
+ computeWindowBounds(attrs, state, df, df);
+ } else {
+ computeWindowBounds(attrs, state, win.getBounds(), df);
+ }
if (attached == null) {
pf.set(df);
if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) {
@@ -1691,7 +1805,17 @@ public class DisplayPolicy {
windowFrames.setContentChanged(true);
}
- win.computeFrameAndUpdateSourceFrame();
+ win.computeFrameAndUpdateSourceFrame(displayFrames);
+ if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) {
+ if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) {
+ // Make sure that the zone we're avoiding for the cutout is at least as tall as the
+ // status bar; otherwise fullscreen apps will end up cutting halfway into the status
+ // bar.
+ displayFrames.mDisplayCutoutSafe.top = Math.max(
+ displayFrames.mDisplayCutoutSafe.top,
+ windowFrames.mFrame.bottom);
+ }
+ }
}
WindowState getTopFullscreenOpaqueWindow() {
@@ -1707,12 +1831,13 @@ public class DisplayPolicy {
*/
public void beginPostLayoutPolicyLw() {
mTopFullscreenOpaqueWindowState = null;
- mTopFullscreenOpaqueOrDimmingWindowState = null;
- mTopDockedOpaqueWindowState = null;
- mTopDockedOpaqueOrDimmingWindowState = null;
+ mNavBarColorWindowCandidate = null;
+ mNavBarBackgroundWindow = null;
+ mStatusBarColorWindows.clear();
+ mStatusBarBackgroundWindows.clear();
+ mStatusBarColorCheckedBounds.setEmpty();
+ mStatusBarBackgroundCheckedBounds.setEmpty();
mForceStatusBar = false;
- mForcingShowNavBar = false;
- mForcingShowNavBarLayer = -1;
mAllowLockscreenWhenOn = false;
mShowingDream = false;
@@ -1731,20 +1856,22 @@ public class DisplayPolicy {
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
applyKeyguardPolicy(win, imeTarget);
- final int fl = attrs.flags;
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
- && attrs.type == TYPE_INPUT_METHOD) {
- mForcingShowNavBar = true;
- mForcingShowNavBarLayer = win.getSurfaceLayer();
+
+ // Check if the freeform window overlaps with the navigation bar area.
+ final boolean isOverlappingWithNavBar = isOverlappingWithNavBar(win, mNavigationBar);
+ if (isOverlappingWithNavBar && !mIsFreeformWindowOverlappingWithNavBar
+ && win.inFreeformWindowingMode()) {
+ mIsFreeformWindowOverlappingWithNavBar = true;
+ }
+
+ if (!affectsSystemUi) {
+ return;
}
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
- final int windowingMode = win.getWindowingMode();
- final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
- windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
+ if (mTopFullscreenOpaqueWindowState == null) {
+ final int fl = attrs.flags;
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
mForceStatusBar = true;
}
@@ -1757,68 +1884,60 @@ public class DisplayPolicy {
}
}
- // For app windows that are not attached, we decide if all windows in the app they
- // represent should be hidden or if we should hide the lockscreen. For attached app
- // windows we defer the decision to the window it is attached to.
- if (appWindow && attached == null) {
- if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
- mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
- if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
- mAllowLockscreenWhenOn = true;
- }
- }
+ if (appWindow && attached == null && attrs.isFullscreen()
+ && (fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+ mAllowLockscreenWhenOn = true;
}
}
- // Voice interaction overrides both top fullscreen and top docked.
- if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION && attrs.isFullscreen()) {
+ // Check the windows that overlap with system bars to determine system bars' appearance.
+ if ((appWindow && attached == null && attrs.isFullscreen())
+ || attrs.type == TYPE_VOICE_INTERACTION) {
+ // Record the top-fullscreen-app-window which will be used to determine system UI
+ // controlling window.
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
- if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
}
- if (mTopDockedOpaqueWindowState == null) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
+
+ // Cache app windows that is overlapping with the status bar to determine appearance
+ // of status bar.
+ if (mStatusBar != null
+ && sTmpRect.setIntersect(win.getFrame(), mStatusBar.getFrame())
+ && !mStatusBarBackgroundCheckedBounds.contains(sTmpRect)) {
+ mStatusBarBackgroundWindows.add(win);
+ mStatusBarBackgroundCheckedBounds.union(sTmpRect);
+ if (!mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+ mStatusBarColorWindows.add(win);
+ mStatusBarColorCheckedBounds.union(sTmpRect);
}
}
- }
- // Keep track of the window if it's dimming but not necessarily fullscreen.
- if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
- && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
- mTopFullscreenOpaqueOrDimmingWindowState = win;
- }
-
- // We need to keep track of the top "fullscreen" opaque window for the docked root task
- // separately, because both the "real fullscreen" opaque window and the one for the docked
- // root task can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
- if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
- && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mTopDockedOpaqueWindowState = win;
- if (mTopDockedOpaqueOrDimmingWindowState == null) {
- mTopDockedOpaqueOrDimmingWindowState = win;
+ // Cache app window that overlaps with the navigation bar area to determine opacity
+ // and appearance of the navigation bar. We only need to cache one window because
+ // there should be only one overlapping window if it's not in gesture navigation
+ // mode; if it's in gesture navigation mode, the navigation bar will be
+ // NAV_BAR_FORCE_TRANSPARENT and its appearance won't be decided by overlapping
+ // windows.
+ if (isOverlappingWithNavBar) {
+ if (mNavBarColorWindowCandidate == null) {
+ mNavBarColorWindowCandidate = win;
+ }
+ if (mNavBarBackgroundWindow == null) {
+ mNavBarBackgroundWindow = win;
+ }
+ }
+ } else if (win.isDimming()) {
+ // For dimming window whose host bounds is overlapping with system bars, it can be
+ // used to determine colors but not opacity of system bars.
+ if (mStatusBar != null
+ && sTmpRect.setIntersect(win.getBounds(), mStatusBar.getFrame())
+ && !mStatusBarColorCheckedBounds.contains(sTmpRect)) {
+ mStatusBarColorWindows.add(win);
+ mStatusBarColorCheckedBounds.union(sTmpRect);
+ }
+ if (isOverlappingWithNavBar && mNavBarColorWindowCandidate == null) {
+ mNavBarColorWindowCandidate = win;
}
- }
-
- // Check if the freeform window overlaps with the navigation bar area.
- final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null;
- if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode()
- && isOverlappingWithNavBar(win, navBarWin)) {
- mIsFreeformWindowOverlappingWithNavBar = true;
- }
-
- // Also keep track of any windows that are dimming but not necessarily fullscreen in the
- // docked root task.
- if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
- && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mTopDockedOpaqueOrDimmingWindowState = win;
}
}
@@ -1882,11 +2001,7 @@ public class DisplayPolicy {
mTopIsFullscreen = topIsFullscreen;
}
- if (updateSystemUiVisibilityLw()) {
- // If the navigation bar has been hidden or shown, we need to do another
- // layout pass to update that window.
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- }
+ updateSystemBarAttributes();
if (mShowingDream != mLastShowingDream) {
mLastShowingDream = mShowingDream;
@@ -2120,18 +2235,47 @@ public class DisplayPolicy {
return mUiContext;
}
- private int getNavigationBarWidth(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarWidthForRotationInCarMode[rotation];
+ private int getNavigationBarWidth(int rotation, int uiMode, int position) {
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ LayoutParams lp = mNavigationBar.mAttrs;
+ if (lp.paramsForRotation != null
+ && lp.paramsForRotation.length == 4
+ && lp.paramsForRotation[rotation] != null) {
+ lp = lp.paramsForRotation[rotation];
+ }
+ if (position == NAV_BAR_LEFT) {
+ if (lp.width > lp.providedInternalInsets.right) {
+ return lp.width - lp.providedInternalInsets.right;
+ } else {
+ return 0;
+ }
+ } else if (position == NAV_BAR_RIGHT) {
+ if (lp.width > lp.providedInternalInsets.left) {
+ return lp.width - lp.providedInternalInsets.left;
+ } else {
+ return 0;
+ }
+ }
+ return lp.width;
} else {
- return mNavigationBarWidthForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarWidthForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarWidthForRotationDefault[rotation];
+ }
}
}
void notifyDisplayReady() {
mHandler.post(() -> {
final int displayId = getDisplayId();
- getStatusBarManagerInternal().onDisplayReady(displayId);
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ statusBar.onDisplayReady(displayId);
+ }
final WallpaperManagerInternal wpMgr = LocalServices
.getService(WallpaperManagerInternal.class);
if (wpMgr != null) {
@@ -2151,7 +2295,7 @@ public class DisplayPolicy {
if (hasNavigationBar()) {
final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);
if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {
- width -= getNavigationBarWidth(rotation, uiMode);
+ width -= getNavigationBarWidth(rotation, uiMode, navBarPosition);
}
}
if (displayCutout != null) {
@@ -2160,11 +2304,23 @@ public class DisplayPolicy {
return width;
}
- private int getNavigationBarHeight(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
+ @VisibleForTesting
+ int getNavigationBarHeight(int rotation, int uiMode) {
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation);
+ if (lp.height < lp.providedInternalInsets.top) {
+ return 0;
+ }
+ return lp.height - lp.providedInternalInsets.top;
} else {
- return mNavigationBarHeightForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarHeightForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarHeightForRotationDefault[rotation];
+ }
}
}
@@ -2181,10 +2337,17 @@ public class DisplayPolicy {
* @return navigation bar frame height
*/
private int getNavigationBarFrameHeight(int rotation, int uiMode) {
- if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
- return mNavigationBarHeightForRotationInCarMode[rotation];
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ if (mNavigationBar == null) {
+ return 0;
+ }
+ return mNavigationBar.mAttrs.height;
} else {
- return mNavigationBarFrameHeightForRotationDefault[rotation];
+ if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+ return mNavigationBarHeightForRotationInCarMode[rotation];
+ } else {
+ return mNavigationBarFrameHeightForRotationDefault[rotation];
+ }
}
}
@@ -2305,9 +2468,9 @@ public class DisplayPolicy {
if (position == NAV_BAR_BOTTOM) {
outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);
} else if (position == NAV_BAR_RIGHT) {
- outInsets.right = getNavigationBarWidth(displayRotation, uiMode);
+ outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);
} else if (position == NAV_BAR_LEFT) {
- outInsets.left = getNavigationBarWidth(displayRotation, uiMode);
+ outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);
}
}
@@ -2333,6 +2496,17 @@ public class DisplayPolicy {
@NavigationBarPosition
int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
+ if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) {
+ final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity;
+ switch (gravity) {
+ case Gravity.LEFT:
+ return NAV_BAR_LEFT;
+ case Gravity.RIGHT:
+ return NAV_BAR_RIGHT;
+ default:
+ return NAV_BAR_BOTTOM;
+ }
+ }
if (navigationBarCanMove() && displayWidth > displayHeight) {
if (displayRotation == Surface.ROTATION_270) {
return NAV_BAR_LEFT;
@@ -2367,18 +2541,13 @@ public class DisplayPolicy {
/**
* A new window has been focused.
*/
- public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
+ public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
mFocusedWindow = newFocus;
mLastFocusedWindow = lastFocus;
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
}
- if (updateSystemUiVisibilityLw()) {
- // If the navigation bar has been hidden or shown, we need to do another
- // layout pass to update that window.
- return FINISH_LAYOUT_REDO_LAYOUT;
- }
- return 0;
+ updateSystemBarAttributes();
}
private void requestTransientBars(WindowState swipeTarget) {
@@ -2454,21 +2623,18 @@ public class DisplayPolicy {
return mDisplayContent.getInsetsPolicy();
}
- void resetSystemUiVisibilityLw() {
+ void resetSystemBarAttributes() {
mLastDisableFlags = 0;
- updateSystemUiVisibilityLw();
+ updateSystemBarAttributes();
}
- /**
- * @return {@code true} if the update may affect the layout.
- */
- boolean updateSystemUiVisibilityLw() {
+ void updateSystemBarAttributes() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return false;
+ return;
}
// The immersive mode confirmation should never affect the system bar visibility, otherwise
@@ -2484,86 +2650,63 @@ public class DisplayPolicy {
: lastFocusCanReceiveKeys ? mLastFocusedWindow
: mTopFullscreenOpaqueWindowState;
if (winCandidate == null) {
- return false;
+ return;
}
}
final WindowState win = winCandidate;
mSystemUiControllingWindow = win;
- mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
-
- final boolean inSplitScreen =
- mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
- if (inSplitScreen) {
- mService.getRootTaskBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- mDockedRootTaskBounds);
- } else {
- mDockedRootTaskBounds.setEmpty();
- }
- mService.getRootTaskBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_UNDEFINED, mNonDockedRootTaskBounds);
- final int fullscreenAppearance = getStatusBarAppearance(mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = getStatusBarAppearance(mTopDockedOpaqueWindowState,
- mTopDockedOpaqueOrDimmingWindowState);
+ final int displayId = getDisplayId();
final int disableFlags = win.getDisableFlags();
final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);
- final WindowState navColorWin = chooseNavigationColorWindowLw(
- mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
+ final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
final boolean isNavbarColorManagedByIme =
navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
- final int appearance = updateLightNavigationBarLw(
- win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState,
- mTopFullscreenOpaqueOrDimmingWindowState,
- mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance;
+ final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
+ navColorWin) | opaqueAppearance;
final int behavior = win.mAttrs.insetsFlags.behavior;
+ final String focusedApp = win.mAttrs.packageName;
final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
|| !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
- if (mLastDisableFlags == disableFlags
- && mLastAppearance == appearance
- && mLastFullscreenAppearance == fullscreenAppearance
- && mLastDockedAppearance == dockedAppearance
+ final AppearanceRegion[] appearanceRegions =
+ new AppearanceRegion[mStatusBarColorWindows.size()];
+ for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+ final WindowState windowState = mStatusBarColorWindows.get(i);
+ appearanceRegions[i] = new AppearanceRegion(
+ getStatusBarAppearance(windowState, windowState),
+ new Rect(windowState.getFrame()));
+ }
+ if (mLastDisableFlags != disableFlags) {
+ mLastDisableFlags = disableFlags;
+ final String cause = win.toString();
+ callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
+ cause));
+ }
+ if (mLastAppearance == appearance
&& mLastBehavior == behavior
+ && mRequestedVisibilities.equals(win.getRequestedVisibilities())
+ && Objects.equals(mFocusedApp, focusedApp)
&& mLastFocusIsFullscreen == isFullscreen
- && mLastNonDockedRootTaskBounds.equals(mNonDockedRootTaskBounds)
- && mLastDockedRootTaskBounds.equals(mDockedRootTaskBounds)) {
- return false;
+ && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
+ return;
}
if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
&& ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
mService.mInputManager.setSystemUiLightsOut(
isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
}
- mLastDisableFlags = disableFlags;
+ final InsetsVisibilities requestedVisibilities =
+ new InsetsVisibilities(win.getRequestedVisibilities());
mLastAppearance = appearance;
- mLastFullscreenAppearance = fullscreenAppearance;
- mLastDockedAppearance = dockedAppearance;
mLastBehavior = behavior;
+ mRequestedVisibilities = requestedVisibilities;
+ mFocusedApp = focusedApp;
mLastFocusIsFullscreen = isFullscreen;
- mLastNonDockedRootTaskBounds.set(mNonDockedRootTaskBounds);
- mLastDockedRootTaskBounds.set(mDockedRootTaskBounds);
- final Rect fullscreenRootTaskBounds = new Rect(mNonDockedRootTaskBounds);
- final Rect dockedRootTaskBounds = new Rect(mDockedRootTaskBounds);
- final AppearanceRegion[] appearanceRegions = inSplitScreen
- ? new AppearanceRegion[]{
- new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds),
- new AppearanceRegion(dockedAppearance, dockedRootTaskBounds)}
- : new AppearanceRegion[]{
- new AppearanceRegion(fullscreenAppearance, fullscreenRootTaskBounds)};
- String cause = win.toString();
- mHandler.post(() -> {
- StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
- if (statusBar != null) {
- final int displayId = getDisplayId();
- statusBar.setDisableFlags(displayId, disableFlags, cause);
- statusBar.onSystemBarAttributesChanged(displayId, appearance, appearanceRegions,
- isNavbarColorManagedByIme, behavior, isFullscreen);
-
- }
- });
- return true;
+ mLastStatusBarAppearanceRegions = appearanceRegions;
+ callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
+ appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
+ requestedVisibilities, focusedApp));
}
private int getStatusBarAppearance(WindowState opaque, WindowState opaqueOrDimming) {
@@ -2574,10 +2717,18 @@ public class DisplayPolicy {
: 0;
}
+ private void callStatusBarSafely(Consumer<StatusBarManagerInternal> consumer) {
+ mHandler.post(() -> {
+ StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
+ if (statusBar != null) {
+ consumer.accept(statusBar);
+ }
+ });
+ }
+
@VisibleForTesting
@Nullable
- static WindowState chooseNavigationColorWindowLw(WindowState opaque,
- WindowState opaqueOrDimming, WindowState imeWindow,
+ static WindowState chooseNavigationColorWindowLw(WindowState candidate, WindowState imeWindow,
@NavigationBarPosition int navBarPosition) {
// If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
// window can be navigation color window.
@@ -2586,71 +2737,59 @@ public class DisplayPolicy {
&& navBarPosition == NAV_BAR_BOTTOM
&& (imeWindow.mAttrs.flags
& WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
-
- if (opaque != null && opaqueOrDimming == opaque) {
- // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
- // unless IME window is also eligible, since currently the IME window is always show
- // above the opaque fullscreen app window, regardless of the IME target window.
- // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
- return imeWindowCanNavColorWindow ? imeWindow : opaque;
- }
-
- if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
- // No dimming window is involved. Determine the result only with the IME window.
- return imeWindowCanNavColorWindow ? imeWindow : null;
- }
-
if (!imeWindowCanNavColorWindow) {
- // No IME window is involved. Determine the result only with opaqueOrDimming.
- return opaqueOrDimming;
+ // No IME window is involved. Determine the result only with candidate window.
+ return candidate;
}
- // The IME window and the dimming window are competing. Check if the dimming window can be
- // IME target or not.
- if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) {
- // The IME window is above the dimming window.
- return imeWindow;
- } else {
- // The dimming window is above the IME window.
- return opaqueOrDimming;
+ if (candidate != null && candidate.isDimming()) {
+ // The IME window and the dimming window are competing. Check if the dimming window can
+ // be IME target or not.
+ if (LayoutParams.mayUseInputMethod(candidate.mAttrs.flags)) {
+ // The IME window is above the dimming window.
+ return imeWindow;
+ } else {
+ // The dimming window is above the IME window.
+ return candidate;
+ }
}
+
+ return imeWindow;
}
@VisibleForTesting
- int updateLightNavigationBarLw(int appearance, WindowState opaque,
- WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) {
-
- if (navColorWin != null) {
- if (navColorWin == imeWindow || navColorWin == opaque) {
- // Respect the light flag.
- appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
- appearance |= navColorWin.mAttrs.insetsFlags.appearance
- & APPEARANCE_LIGHT_NAVIGATION_BARS;
- } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
- // Clear the light flag for dimming window.
- appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
- }
- }
- if (!isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+ int updateLightNavigationBarLw(int appearance, WindowState navColorWin) {
+ if (navColorWin == null || navColorWin.isDimming()
+ || !isLightBarAllowed(navColorWin, ITYPE_NAVIGATION_BAR)) {
+ // Clear the light flag while not allowed.
appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ return appearance;
}
+
+ // Respect the light flag of navigation color window.
+ appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS;
+ appearance |= navColorWin.mAttrs.insetsFlags.appearance
+ & APPEARANCE_LIGHT_NAVIGATION_BARS;
return appearance;
}
private int updateSystemBarsLw(WindowState win, int disableFlags) {
- final boolean dockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean resizing = mDisplayContent.getDockedDividerController().isResizing();
-
- // We need to force system bars when the docked root task is visible, when the freeform
- // root task is focused but also when we are resizing for the transitions when docked
- // root task visibility changes.
- mForceShowSystemBars = dockedRootTaskVisible || win.inFreeformWindowingMode() || resizing;
+ final TaskDisplayArea defaultTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final boolean multiWindowTaskVisible =
+ defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ || defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_MULTI_WINDOW);
+ final boolean freeformRootTaskVisible =
+ defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
+
+ // We need to force showing system bars when the multi-window or freeform root task is
+ // visible.
+ mForceShowSystemBars = multiWindowTaskVisible || freeformRootTaskVisible;
+ mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
int appearance = APPEARANCE_OPAQUE_NAVIGATION_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
-
appearance = configureStatusBarOpacity(appearance);
- appearance = configureNavBarOpacity(appearance, dockedRootTaskVisible, resizing);
+ appearance = configureNavBarOpacity(appearance, multiWindowTaskVisible,
+ freeformRootTaskVisible);
final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
final long now = SystemClock.uptimeMillis();
@@ -2696,7 +2835,24 @@ public class DisplayPolicy {
private Rect getBarContentFrameForWindow(WindowState win, int windowType) {
final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType);
- return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType);
+ if (rotatedBarFrame != null) {
+ return rotatedBarFrame;
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mBarContentFrames.get(windowType);
+ }
+ // We only need a window specific information for the fixed rotation, use raw insets state
+ // for all other cases.
+ InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState();
+ final Rect tmpRect = new Rect();
+ if (windowType == TYPE_NAVIGATION_BAR) {
+ tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame());
+ }
+ if (windowType == TYPE_STATUS_BAR) {
+ tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame());
+ }
+ tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe);
+ return tmpRect;
}
/**
@@ -2731,17 +2887,19 @@ public class DisplayPolicy {
/** @return the current visibility flags with the status bar opacity related flags toggled. */
private int configureStatusBarOpacity(int appearance) {
- final boolean fullscreenDrawsBackground =
- drawsBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsBackground =
- drawsBarBackground(mTopDockedOpaqueWindowState);
+ boolean drawBackground = true;
+ boolean isFullyTransparentAllowed = true;
+ for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+ final WindowState window = mStatusBarBackgroundWindows.get(i);
+ drawBackground &= drawsBarBackground(window);
+ isFullyTransparentAllowed &= isFullyTransparentAllowed(window, TYPE_STATUS_BAR);
+ }
- if (fullscreenDrawsBackground && dockedDrawsBackground) {
+ if (drawBackground) {
appearance &= ~APPEARANCE_OPAQUE_STATUS_BARS;
}
- if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_STATUS_BAR)
- || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_STATUS_BAR)) {
+ if (!isFullyTransparentAllowed) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
}
@@ -2752,53 +2910,35 @@ public class DisplayPolicy {
* @return the current visibility flags with the nav-bar opacity related flags toggled based
* on the nav bar opacity rules chosen by {@link #mNavBarOpacityMode}.
*/
- private int configureNavBarOpacity(int appearance, boolean dockedRootTaskVisible,
- boolean isDockedDividerResizing) {
- final boolean freeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- final boolean fullscreenDrawsBackground =
- drawsBarBackground(mTopFullscreenOpaqueWindowState);
- final boolean dockedDrawsBackground =
- drawsBarBackground(mTopDockedOpaqueWindowState);
+ private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible,
+ boolean freeformRootTaskVisible) {
+ final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow);
if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) {
- if (fullscreenDrawsBackground && dockedDrawsBackground) {
+ if (drawBackground) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else if (dockedRootTaskVisible) {
- appearance = setNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) {
- if (dockedRootTaskVisible || freeformRootTaskVisible || isDockedDividerResizing) {
+ if (multiWindowTaskVisible || freeformRootTaskVisible) {
if (mIsFreeformWindowOverlappingWithNavBar) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else {
- appearance = setNavBarOpaqueFlag(appearance);
}
- } else if (fullscreenDrawsBackground) {
+ } else if (drawBackground) {
appearance = clearNavBarOpaqueFlag(appearance);
}
} else if (mNavBarOpacityMode == NAV_BAR_TRANSLUCENT_WHEN_FREEFORM_OPAQUE_OTHERWISE) {
- if (isDockedDividerResizing) {
- appearance = setNavBarOpaqueFlag(appearance);
- } else if (freeformRootTaskVisible) {
+ if (freeformRootTaskVisible) {
appearance = clearNavBarOpaqueFlag(appearance);
- } else {
- appearance = setNavBarOpaqueFlag(appearance);
}
}
- if (!isFullyTransparentAllowed(mTopFullscreenOpaqueWindowState, TYPE_NAVIGATION_BAR)
- || !isFullyTransparentAllowed(mTopDockedOpaqueWindowState, TYPE_NAVIGATION_BAR)) {
+ if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, TYPE_NAVIGATION_BAR)) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
}
return appearance;
}
- private int setNavBarOpaqueFlag(int appearance) {
- return appearance | APPEARANCE_OPAQUE_NAVIGATION_BARS;
- }
-
private int clearNavBarOpaqueFlag(int appearance) {
return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS;
}
@@ -2838,7 +2978,7 @@ public class DisplayPolicy {
return;
}
mPendingPanicGestureUptime = SystemClock.uptimeMillis();
- updateSystemUiVisibilityLw();
+ updateSystemBarAttributes();
}
}
};
@@ -2899,6 +3039,7 @@ public class DisplayPolicy {
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.println("DisplayPolicy");
prefix += " ";
+ final String prefixInner = prefix + " ";
pw.print(prefix);
pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer);
pw.print(" mDeskDockEnablesAccelerometer=");
@@ -2966,14 +3107,27 @@ public class DisplayPolicy {
pw.print(prefix); pw.print("mTopFullscreenOpaqueWindowState=");
pw.println(mTopFullscreenOpaqueWindowState);
}
- if (mTopFullscreenOpaqueOrDimmingWindowState != null) {
- pw.print(prefix); pw.print("mTopFullscreenOpaqueOrDimmingWindowState=");
- pw.println(mTopFullscreenOpaqueOrDimmingWindowState);
+ if (mNavBarColorWindowCandidate != null) {
+ pw.print(prefix); pw.print("mNavBarColorWindowCandidate=");
+ pw.println(mNavBarColorWindowCandidate);
}
- if (mForcingShowNavBar) {
- pw.print(prefix); pw.print("mForcingShowNavBar="); pw.println(mForcingShowNavBar);
- pw.print(prefix); pw.print("mForcingShowNavBarLayer=");
- pw.println(mForcingShowNavBarLayer);
+ if (mNavBarBackgroundWindow != null) {
+ pw.print(prefix); pw.print("mNavBarBackgroundWindow=");
+ pw.println(mNavBarBackgroundWindow);
+ }
+ if (!mStatusBarColorWindows.isEmpty()) {
+ pw.print(prefix); pw.println("mStatusBarColorWindows=");
+ for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mStatusBarColorWindows.get(i);
+ pw.print(prefixInner); pw.println(win);
+ }
+ }
+ if (!mStatusBarBackgroundWindows.isEmpty()) {
+ pw.print(prefix); pw.println("mStatusBarBackgroundWindows=");
+ for (int i = mStatusBarBackgroundWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mStatusBarBackgroundWindows.get(i);
+ pw.print(prefixInner); pw.println(win);
+ }
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
@@ -3011,7 +3165,7 @@ public class DisplayPolicy {
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
lp.setFitInsetsTypes(0);
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
if (ActivityManager.isHighEndGfx()) {
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.privateFlags |=
@@ -3055,13 +3209,17 @@ public class DisplayPolicy {
}
@VisibleForTesting
- static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
+ static boolean isOverlappingWithNavBar(@NonNull WindowState targetWindow,
+ WindowState navBarWindow) {
if (navBarWindow == null || !navBarWindow.isVisible()
|| targetWindow.mActivityRecord == null || !targetWindow.isVisible()) {
return false;
}
- return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame());
+ // When the window is dimming means it's requesting dim layer to its host container, so
+ // checking whether it's overlapping with navigation bar by its container's bounds.
+ return Rect.intersects(targetWindow.isDimming()
+ ? targetWindow.getBounds() : targetWindow.getFrame(), navBarWindow.getFrame());
}
/**
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index d12d07ab7448..d073f94bd577 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -291,12 +291,14 @@ class DragDropController {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Drag into new candidate view @ " + window.asBinder());
}
+ mCallback.get().dragRecipientEntered(window);
}
void dragRecipientExited(IWindow window) {
if (DEBUG_DRAG) {
Slog.d(TAG_WM, "Drag from old candidate view @ " + window.asBinder());
}
+ mCallback.get().dragRecipientExited(window);
}
/**
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 316c20ba5c47..fed4f6205a3b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,30 +16,33 @@
package com.android.server.wm;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.Task.TAG_VISIBILITY;
import android.annotation.Nullable;
import android.util.Slog;
+import java.util.ArrayList;
+
/** Helper class to ensure activities are in the right visible state for a container. */
class EnsureActivitiesVisibleHelper {
- private final Task mTask;
+ private final TaskFragment mTaskFragment;
private ActivityRecord mTop;
private ActivityRecord mStarting;
private boolean mAboveTop;
private boolean mContainerShouldBeVisible;
- private boolean mBehindFullscreenActivity;
+ private boolean mBehindFullyOccludedContainer;
private int mConfigChanges;
private boolean mPreserveWindows;
private boolean mNotifyClients;
- EnsureActivitiesVisibleHelper(Task container) {
- mTask = container;
+ EnsureActivitiesVisibleHelper(TaskFragment container) {
+ mTaskFragment = container;
}
/**
- * Update all attributes except {@link mTask} to use in subsequent calculations.
+ * Update all attributes except {@link mTaskFragment} to use in subsequent calculations.
*
* @param starting The activity that is being started
* @param configChanges Parts of the configuration that changed for this activity for evaluating
@@ -51,12 +54,12 @@ class EnsureActivitiesVisibleHelper {
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
- mTop = mTask.topRunningActivity();
+ mTop = mTaskFragment.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
// are now visible.
mAboveTop = mTop != null;
- mContainerShouldBeVisible = mTask.shouldBeVisible(mStarting);
- mBehindFullscreenActivity = !mContainerShouldBeVisible;
+ mContainerShouldBeVisible = mTaskFragment.shouldBeVisible(mStarting);
+ mBehindFullyOccludedContainer = !mContainerShouldBeVisible;
mConfigChanges = configChanges;
mPreserveWindows = preserveWindows;
mNotifyClients = notifyClients;
@@ -85,22 +88,55 @@ class EnsureActivitiesVisibleHelper {
Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+ " configChanges=0x" + Integer.toHexString(configChanges));
}
- if (mTop != null) {
- mTask.checkTranslucentActivityWaiting(mTop);
+ if (mTop != null && mTaskFragment.asTask() != null) {
+ // TODO(14709632): Check if this needed to be implemented in TaskFragment.
+ mTaskFragment.asTask().checkTranslucentActivityWaiting(mTop);
}
// We should not resume activities that being launched behind because these
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
- && mTask.isTopActivityFocusable()
- && (starting == null || !starting.isDescendantOf(mTask));
-
- mTask.forAllActivities(a -> {
- setActivityVisibilityState(a, starting, resumeTopActivity);
- });
- if (mTask.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- mTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
+ && mTaskFragment.isTopActivityFocusable()
+ && (starting == null || !starting.isDescendantOf(mTaskFragment));
+
+ ArrayList<TaskFragment> adjacentTaskFragments = null;
+ for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mTaskFragment.mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final TaskFragment childTaskFragment = child.asTaskFragment();
+ childTaskFragment.updateActivityVisibilities(starting, configChanges,
+ preserveWindows, notifyClients);
+ mBehindFullyOccludedContainer |= childTaskFragment.getBounds().equals(
+ mTaskFragment.getBounds());
+ if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
+ mAboveTop = false;
+ }
+
+ if (mBehindFullyOccludedContainer) {
+ continue;
+ }
+
+ if (adjacentTaskFragments != null && adjacentTaskFragments.contains(
+ childTaskFragment)) {
+ // Everything behind two adjacent TaskFragments are occluded.
+ mBehindFullyOccludedContainer = true;
+ continue;
+ }
+
+ final TaskFragment adjacentTaskFrag = childTaskFragment.getAdjacentTaskFragment();
+ if (adjacentTaskFrag != null) {
+ if (adjacentTaskFragments == null) {
+ adjacentTaskFragments = new ArrayList<>();
+ }
+ adjacentTaskFragments.add(adjacentTaskFrag);
+ }
+ } else if (child.asActivityRecord() != null) {
+ setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
+ }
+ }
+ if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
}
}
@@ -112,7 +148,7 @@ class EnsureActivitiesVisibleHelper {
}
mAboveTop = false;
- r.updateVisibilityIgnoringKeyguard(mBehindFullscreenActivity);
+ r.updateVisibilityIgnoringKeyguard(mBehindFullyOccludedContainer);
final boolean reallyVisible = r.shouldBeVisibleUnchecked();
// Check whether activity should be visible without Keyguard influence
@@ -122,12 +158,14 @@ class EnsureActivitiesVisibleHelper {
if (DEBUG_VISIBILITY) {
Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " containerVisible=" + mContainerShouldBeVisible
- + " behindFullscreen=" + mBehindFullscreenActivity);
+ + " behindFullyOccluded=" + mBehindFullyOccludedContainer);
}
- mBehindFullscreenActivity = true;
+ mBehindFullyOccludedContainer = true;
} else {
- mBehindFullscreenActivity = false;
+ mBehindFullyOccludedContainer = false;
}
+ } else if (r.isState(INITIALIZING)) {
+ r.cancelInitializing();
}
if (reallyVisible) {
@@ -173,24 +211,25 @@ class EnsureActivitiesVisibleHelper {
Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState()
+ " containerShouldBeVisible=" + mContainerShouldBeVisible
- + " behindFullscreenActivity=" + mBehindFullscreenActivity
+ + " behindFullyOccludedContainer=" + mBehindFullyOccludedContainer
+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
}
r.makeInvisible();
}
- if (!mBehindFullscreenActivity && mTask.isActivityTypeHome() && r.isRootOfTask()) {
+ if (!mBehindFullyOccludedContainer && mTaskFragment.isActivityTypeHome()
+ && r.isRootOfTask()) {
if (DEBUG_VISIBILITY) {
- Slog.v(TAG_VISIBILITY, "Home task: at " + mTask
+ Slog.v(TAG_VISIBILITY, "Home task: at " + mTaskFragment
+ " containerShouldBeVisible=" + mContainerShouldBeVisible
- + " behindFullscreenActivity=" + mBehindFullscreenActivity);
+ + " behindOccludedParentContainer=" + mBehindFullyOccludedContainer);
}
// No other task in the root home task should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
// them. However, when they don't have the wallpaper behind them, we want to
// show activities in the next application root task behind them vs. another
// task in the root home task like recents.
- mBehindFullscreenActivity = true;
+ mBehindFullyOccludedContainer = true;
}
}
@@ -219,7 +258,8 @@ class EnsureActivitiesVisibleHelper {
r.setVisibility(true);
}
if (r != starting) {
- mTask.mTaskSupervisor.startSpecificActivity(r, andResume, true /* checkConfig */);
+ mTaskFragment.mTaskSupervisor.startSpecificActivity(r, andResume,
+ true /* checkConfig */);
}
}
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index eab3f108d17a..52a7ac75e2dc 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -42,6 +42,9 @@ public class FadeRotationAnimationController extends FadeAnimationController {
/** A runnable which gets called when the {@link #show()} is called. */
private Runnable mOnShowRunnable;
+ /** Whether to use constant zero alpha animation. */
+ private boolean mHideImmediately;
+
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
@@ -51,6 +54,10 @@ public class FadeRotationAnimationController extends FadeAnimationController {
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
+ if (mFrozenTimeoutRunnable != null) {
+ // Hide the windows immediately because screen should have been covered by screenshot.
+ mHideImmediately = true;
+ }
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
final WindowState navigationBar = displayPolicy.getNavigationBar();
if (navigationBar != null) {
@@ -120,6 +127,15 @@ public class FadeRotationAnimationController extends FadeAnimationController {
}
}
+ /** Hides the window immediately until it is drawn in new rotation. */
+ void hideImmediately(WindowToken windowToken) {
+ final boolean original = mHideImmediately;
+ mHideImmediately = true;
+ mTargetWindowTokens.add(windowToken);
+ fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+ mHideImmediately = original;
+ }
+
/** Returns {@code true} if the window is handled by this controller. */
boolean isHandledToken(WindowToken token) {
return token == mNavBarToken || isTargetToken(token);
@@ -145,8 +161,7 @@ public class FadeRotationAnimationController extends FadeAnimationController {
@Override
public Animation getFadeOutAnimation() {
- if (mFrozenTimeoutRunnable != null) {
- // Hide the window immediately because screen should have been covered by screenshot.
+ if (mHideImmediately) {
return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
}
return super.getFadeOutAnimation();
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index da47328691c0..4f6a693b8c3f 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_D
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Trace;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
@@ -90,6 +91,16 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
onSourceChanged();
}
+ @Override
+ void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
+ if (target != null && target.getWindow() != null) {
+ // ime control target could be a different window.
+ // Refer WindowState#getImeControlTarget().
+ target = target.getWindow().getImeControlTarget();
+ }
+ super.updateControlForTarget(target, force);
+ }
+
private void onSourceChanged() {
if (mLastSource.equals(mSource)) {
return;
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 747d3652e150..f3b9cdfd39e0 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
@@ -420,7 +421,7 @@ public class ImmersiveModeConfirmation {
}
final Bundle options = new Bundle();
- options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+ options.putInt(KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
return options;
}
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index aa7e6c9c80fc..18a2c601f6d3 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -30,6 +30,7 @@ import android.util.Slog;
import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.input.InputManagerService;
@@ -181,8 +182,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
@Override
public int getPointerLayer() {
return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
- * WindowManagerService.TYPE_LAYER_MULTIPLIER
- + WindowManagerService.TYPE_LAYER_OFFSET;
+ * WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER
+ + WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
}
/** Callback to get pointer display id. */
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c781a13f7db..f8af43bee24d 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -18,7 +18,6 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
@@ -39,6 +38,7 @@ 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_SECURE_SYSTEM_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -47,6 +47,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
@@ -66,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.function.Consumer;
@@ -100,6 +102,15 @@ final class InputMonitor {
private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
/**
+ * Set when recents (overview) is active as part of a shell transition. While set, any focus
+ * going to the recents activity will be redirected to the Recents input consumer. Since we
+ * draw the live-tile above the recents activity, we also need to provide that activity as a
+ * z-layering reference so that we can place the recents input consumer above it.
+ */
+ private WeakReference<ActivityRecord> mActiveRecentsActivity = null;
+ private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null;
+
+ /**
* Representation of a input consumer that the policy has added to the window manager to consume
* input events going to windows below it.
*/
@@ -278,6 +289,7 @@ final class InputMonitor {
inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures);
inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
inputWindowHandle.setVisible(w.isVisible());
+ inputWindowHandle.setWindowToken(w.mClient);
final boolean focusable = w.canReceiveKeys()
&& (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
@@ -307,7 +319,10 @@ final class InputMonitor {
boolean useSurfaceCrop = false;
final Task task = w.getTask();
if (task != null) {
- if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+ // TODO(b/165794636): Remove the special case for freeform window once drag resizing is
+ // handled by WM shell.
+ if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+ && !task.inFreeformWindowingMode()) {
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
// be applied using the SurfaceControl hierarchy from the Organizer. This means
// we need to make sure that these changes in crop are reflected in the input
@@ -392,6 +407,21 @@ final class InputMonitor {
}
/**
+ * Inform InputMonitor when recents is active so it can enable the recents input consumer.
+ * @param activity The active recents activity. {@code null} means recents is not active.
+ * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer.
+ */
+ void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
+ final boolean clear = activity == null;
+ mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
+ mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
+ }
+
+ private static <T> T getWeak(WeakReference<T> ref) {
+ return ref != null ? ref.get() : null;
+ }
+
+ /**
* Called when the current input focus changes.
*/
private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {
@@ -401,8 +431,10 @@ final class InputMonitor {
if (recentsAnimationInputConsumer != null && focus != null) {
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
- final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
- && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord);
+ final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null
+ && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord))
+ // Shell transitions doesn't use RecentsAnimationController
+ || getWeak(mActiveRecentsActivity) != null;
if (shouldApplyRecentsInputConsumer) {
requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
recentsAnimationInputConsumer.mName);
@@ -502,6 +534,14 @@ final class InputMonitor {
mInDrag = inDrag;
resetInputConsumers(mInputTransaction);
+ // Update recents input consumer layer if active
+ if (mAddRecentsAnimationInputConsumerHandle
+ && getWeak(mActiveRecentsActivity) != null) {
+ final WindowContainer layer = getWeak(mActiveRecentsLayerRef);
+ mRecentsAnimationInputConsumer.show(mInputTransaction,
+ layer != null ? layer : getWeak(mActiveRecentsActivity));
+ mAddRecentsAnimationInputConsumerHandle = false;
+ }
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
updateInputFocusRequest(mRecentsAnimationInputConsumer);
@@ -536,10 +576,12 @@ final class InputMonitor {
final int privateFlags = w.mAttrs.privateFlags;
+ // This only works for legacy transitions.
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle)) {
- mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
+ mRecentsAnimationInputConsumer.show(mInputTransaction,
+ recentsAnimationController.getHighestLayerActivity());
mAddRecentsAnimationInputConsumerHandle = false;
}
}
@@ -617,7 +659,6 @@ final class InputMonitor {
inputWindowHandle.setScaleFactor(1f);
inputWindowHandle.setLayoutParamsFlags(
FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE);
- inputWindowHandle.setPortalToDisplayId(INVALID_DISPLAY);
inputWindowHandle.clearTouchableRegion();
inputWindowHandle.setTouchableRegionCrop(null);
}
@@ -650,6 +691,7 @@ final class InputMonitor {
|| type == TYPE_DOCK_DIVIDER
|| type == TYPE_ACCESSIBILITY_OVERLAY
|| type == TYPE_INPUT_CONSUMER
- || type == TYPE_VOICE_INTERACTION;
+ || type == TYPE_VOICE_INTERACTION
+ || type == TYPE_STATUS_BAR_ADDITIONAL;
}
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 7a4d13c2d697..6ce7b4232b07 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
+import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
@@ -223,14 +224,6 @@ class InputWindowHandleWrapper {
mChanged = true;
}
- void setPortalToDisplayId(int displayId) {
- if (mHandle.portalToDisplayId == displayId) {
- return;
- }
- mHandle.portalToDisplayId = displayId;
- mChanged = true;
- }
-
void setFrame(int left, int top, int right, int bottom) {
if (mHandle.frameLeft == left && mHandle.frameTop == top && mHandle.frameRight == right
&& mHandle.frameBottom == bottom) {
@@ -275,6 +268,14 @@ class InputWindowHandleWrapper {
mChanged = true;
}
+ void setWindowToken(IWindow windowToken) {
+ if (mHandle.getWindow() == windowToken) {
+ return;
+ }
+ mHandle.setWindowToken(windowToken);
+ mChanged = true;
+ }
+
@Override
public String toString() {
return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f2f192686ad5..53af563ac0c4 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -18,8 +18,6 @@ package com.android.server.wm;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
@@ -55,6 +53,7 @@ import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.DisplayThread;
+import com.android.server.statusbar.StatusBarManagerInternal;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -135,15 +134,19 @@ class InsetsPolicy {
abortTransient();
}
mFocusedWin = focusedWin;
- boolean forceShowsSystemBarsForWindowingMode = forceShowsSystemBarsForWindowingMode();
- InsetsControlTarget statusControlTarget = getStatusControlTarget(focusedWin,
- forceShowsSystemBarsForWindowingMode);
- InsetsControlTarget navControlTarget = getNavControlTarget(focusedWin,
- forceShowsSystemBarsForWindowingMode);
- mStateController.onBarControlTargetChanged(statusControlTarget,
- getFakeControlTarget(focusedWin, statusControlTarget),
+ final InsetsControlTarget statusControlTarget =
+ getStatusControlTarget(focusedWin, false /* fake */);
+ final InsetsControlTarget navControlTarget =
+ getNavControlTarget(focusedWin, false /* fake */);
+ mStateController.onBarControlTargetChanged(
+ statusControlTarget,
+ statusControlTarget == mDummyControlTarget
+ ? getStatusControlTarget(focusedWin, true /* fake */)
+ : null,
navControlTarget,
- getFakeControlTarget(focusedWin, navControlTarget));
+ navControlTarget == mDummyControlTarget
+ ? getNavControlTarget(focusedWin, true /* fake */)
+ : null);
mStatusBar.updateVisibility(statusControlTarget, ITYPE_STATUS_BAR);
mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
}
@@ -167,8 +170,12 @@ class InsetsPolicy {
changed = true;
}
if (changed) {
- mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.showTransient(
+ mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ }
updateBarControlTarget(mFocusedWin);
// The leashes can be created while updating bar control target. The surface transaction
@@ -282,9 +289,11 @@ class InsetsPolicy {
abortTypes.add(type);
}
}
- if (abortTypes.size() > 0) {
- mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
- abortTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (abortTypes.size() > 0 && statusBarManagerInternal != null) {
+ statusBarManagerInternal.abortTransient(
+ mDisplayContent.getDisplayId(), abortTypes.toArray());
}
}
}
@@ -294,19 +303,17 @@ class InsetsPolicy {
* updateBarControlTarget(mFocusedWin) after this invocation.
*/
private void abortTransient() {
- mPolicy.getStatusBarManagerInternal().abortTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
+ StatusBarManagerInternal statusBarManagerInternal = mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.abortTransient(
+ mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
+ }
mShowingTransientTypes.clear();
}
- private @Nullable InsetsControlTarget getFakeControlTarget(@Nullable WindowState focused,
- InsetsControlTarget realControlTarget) {
- return realControlTarget == mDummyControlTarget ? focused : null;
- }
-
private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
- boolean forceShowsSystemBarsForWindowingMode) {
- if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1) {
+ boolean fake) {
+ if (mShowingTransientTypes.indexOf(ITYPE_STATUS_BAR) != -1 && !fake) {
return mDummyControlTarget;
}
final WindowState notificationShade = mPolicy.getNotificationShade();
@@ -319,13 +326,12 @@ class InsetsPolicy {
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (forceShowsSystemBarsForWindowingMode) {
- // Status bar is forcibly shown for the windowing mode which is a steady state.
- // We don't want the client to control the status bar, and we will dispatch the real
- // visibility of status bar to the client.
+ if (mPolicy.areSystemBarsForcedShownLw()) {
+ // Status bar is forcibly shown. We don't want the client to control the status bar, and
+ // we will dispatch the real visibility of status bar to the client.
return null;
}
- if (forceShowsStatusBarTransiently()) {
+ if (forceShowsStatusBarTransiently() && !fake) {
// Status bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
@@ -351,13 +357,13 @@ class InsetsPolicy {
}
private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin,
- boolean forceShowsSystemBarsForWindowingMode) {
+ boolean fake) {
final WindowState imeWin = mDisplayContent.mInputMethodWindow;
if (imeWin != null && imeWin.isVisible()) {
// Force showing navigation bar while IME is visible.
return null;
}
- if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1) {
+ if (mShowingTransientTypes.indexOf(ITYPE_NAVIGATION_BAR) != -1 && !fake) {
return mDummyControlTarget;
}
if (focusedWin == mPolicy.getNotificationShade()) {
@@ -369,13 +375,12 @@ class InsetsPolicy {
focusedWin.mAttrs.packageName);
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (forceShowsSystemBarsForWindowingMode) {
- // Navigation bar is forcibly shown for the windowing mode which is a steady state.
- // We don't want the client to control the navigation bar, and we will dispatch the real
- // visibility of navigation bar to the client.
+ if (mPolicy.areSystemBarsForcedShownLw()) {
+ // Navigation bar is forcibly shown. We don't want the client to control the navigation
+ // bar, and we will dispatch the real visibility of navigation bar to the client.
return null;
}
- if (forceShowsNavigationBarTransiently()) {
+ if (forceShowsNavigationBarTransiently() && !fake) {
// Navigation bar is forcibly shown transiently, and its new visibility won't be
// dispatched to the client so that we can keep the layout stable. We will dispatch the
// fake control to the client, so that it can re-show the bar during this scenario.
@@ -417,19 +422,6 @@ class InsetsPolicy {
&& (win.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
}
- private boolean forceShowsSystemBarsForWindowingMode() {
- final boolean isDockedRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
- final boolean isFreeformRootTaskVisible = mDisplayContent.getDefaultTaskDisplayArea()
- .isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
-
- // We need to force system bars when the docked root task is visible, when the freeform
- // root task is visible but also when we are resizing for the transitions when docked
- // root task visibility changes.
- return isDockedRootTaskVisible || isFreeformRootTaskVisible || isResizing;
- }
-
@VisibleForTesting
void startAnimation(boolean show, Runnable callback) {
int typesReady = 0;
@@ -474,8 +466,12 @@ class InsetsPolicy {
final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
if (mState != state) {
mState = state;
- mPolicy.getStatusBarManagerInternal().setWindowState(
- mDisplayContent.getDisplayId(), mId, state);
+ StatusBarManagerInternal statusBarManagerInternal =
+ mPolicy.getStatusBarManagerInternal();
+ if (statusBarManagerInternal != null) {
+ statusBarManagerInternal.setWindowState(
+ mDisplayContent.getDisplayId(), mId, state);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7daebff2ccc2..f3e52f28ba8b 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -307,11 +307,6 @@ class InsetsSourceProvider {
// to control the window for now.
return;
}
- if (target != null && target.getWindow() != null) {
- // ime control target could be a different window.
- // Refer WindowState#getImeControlTarget().
- target = target.getWindow().getImeControlTarget();
- }
if (mWin != null && mWin.getSurfaceControl() == null) {
// if window doesn't have a surface, set it null and return.
@@ -381,8 +376,11 @@ class InsetsSourceProvider {
return;
}
mClientVisible = clientVisible;
- mDisplayContent.mWmService.mH.obtainMessage(
- LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget();
+ if (!mDisplayContent.mLayoutAndAssignWindowLayersScheduled) {
+ mDisplayContent.mLayoutAndAssignWindowLayersScheduled = true;
+ mDisplayContent.mWmService.mH.obtainMessage(
+ LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget();
+ }
updateVisibility();
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 655007cf3cd1..2c4adcbf1404 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -385,7 +385,7 @@ class InsetsStateController {
if (changed) {
notifyInsetsChanged();
mDisplayContent.updateSystemGestureExclusion();
- mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw();
+ mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index fe1020c86041..6f3edbcff4c0 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -27,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_W
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -49,6 +51,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.WindowManager;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -158,6 +161,7 @@ class KeyguardController {
final boolean keyguardChanged = (keyguardShowing != mKeyguardShowing)
|| (mKeyguardGoingAway && keyguardShowing && !aodChanged);
if (!keyguardChanged && !aodChanged) {
+ setWakeTransitionReady();
return;
}
EventLogTags.writeWmSetKeyguardShown(
@@ -203,6 +207,15 @@ class KeyguardController {
updateKeyguardSleepToken();
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
+ setWakeTransitionReady();
+ }
+
+ private void setWakeTransitionReady() {
+ if (mWindowManager.mAtmService.getTransitionController().getCollectingTransitionType()
+ == WindowManager.TRANSIT_WAKE) {
+ mWindowManager.mAtmService.getTransitionController().setReady(
+ mRootWindowContainer.getDefaultDisplay());
+ }
}
/**
@@ -224,8 +237,14 @@ class KeyguardController {
mAodShowing ? 1 : 0,
1 /* keyguardGoingAway */,
"keyguardGoingAway");
- mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
- TRANSIT_KEYGUARD_GOING_AWAY, convertTransitFlags(flags));
+ final int transitFlags = convertTransitFlags(flags);
+ final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
+ dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
+ // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
+ // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
+ // away.
+ dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
+ TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
@@ -265,7 +284,7 @@ class KeyguardController {
}
private int convertTransitFlags(int keyguardGoingAwayFlags) {
- int result = 0;
+ int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
}
@@ -334,6 +353,7 @@ class KeyguardController {
for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx--) {
final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
+ if (display.isRemoving() || display.isRemoved()) continue;
final KeyguardDisplayState state = getDisplayState(display.mDisplayId);
state.updateVisibility(this, display);
requestDismissKeyguard |= state.mRequestDismissKeyguard;
@@ -366,10 +386,10 @@ class KeyguardController {
mService.deferWindowLayout();
try {
mRootWindowContainer.getDefaultDisplay()
- .prepareAppTransition(
+ .requestTransitionAndLegacyPrepare(
isDisplayOccluded(DEFAULT_DISPLAY)
? TRANSIT_KEYGUARD_OCCLUDE
- : TRANSIT_KEYGUARD_UNOCCLUDE);
+ : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
// When the occluding activity also turns on the display, visibility of the activity
// can be committed before KEYGUARD_OCCLUDE transition is handled.
// Set mRequestForceTransition flag to make sure that the app transition animation
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3dbe79df6722..5a249a5599bb 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -58,10 +58,12 @@ public class Letterbox {
private final LetterboxSurface mLeft = new LetterboxSurface("left");
private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
private final LetterboxSurface mRight = new LetterboxSurface("right");
- // Prevents wallpaper from peeking through near rounded corners. It's not included in
- // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
- // or attachInput.
- private final LetterboxSurface mBehind = new LetterboxSurface("behind");
+ // One surface that fills the whole window is used over multiple surfaces to:
+ // - Prevents wallpaper from peeking through near rounded corners.
+ // - For "blurred wallpaper" background, to avoid having visible border between surfaces.
+ // One surface approach isn't always preferred over multiple surfaces due to rendering cost
+ // for overlaping an app window and letterbox surfaces.
+ private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
/**
@@ -104,7 +106,7 @@ public class Letterbox {
mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
- mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
+ mFullWindowSurface.layout(outer.left, outer.top, outer.right, outer.bottom, surfaceOrigin);
}
@@ -168,37 +170,46 @@ public class Letterbox {
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
- mBehind.remove();
+ mFullWindowSurface.remove();
}
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
public boolean needsApplySurfaceChanges() {
+ if (useFullWindowSurface()) {
+ return mFullWindowSurface.needsApplySurfaceChanges();
+ }
for (LetterboxSurface surface : mSurfaces) {
if (surface.needsApplySurfaceChanges()) {
return true;
}
}
- if (mAreCornersRounded.get() && mBehind.needsApplySurfaceChanges()) {
- return true;
- }
return false;
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.applySurfaceChanges(t);
- }
- if (mAreCornersRounded.get()) {
- mBehind.applySurfaceChanges(t);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.applySurfaceChanges(t);
+
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.remove();
+ }
} else {
- mBehind.remove();
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.applySurfaceChanges(t);
+ }
+
+ mFullWindowSurface.remove();
}
}
/** Enables touches to slide into other neighboring surfaces. */
void attachInput(WindowState win) {
- for (LetterboxSurface surface : mSurfaces) {
- surface.attachInput(win);
+ if (useFullWindowSurface()) {
+ mFullWindowSurface.attachInput(win);
+ } else {
+ for (LetterboxSurface surface : mSurfaces) {
+ surface.attachInput(win);
+ }
}
}
@@ -208,6 +219,16 @@ public class Letterbox {
surface.mInputInterceptor.mWindowHandle.displayId = displayId;
}
}
+ if (mFullWindowSurface.mInputInterceptor != null) {
+ mFullWindowSurface.mInputInterceptor.mWindowHandle.displayId = displayId;
+ }
+ }
+
+ /**
+ * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
+ */
+ private boolean useFullWindowSurface() {
+ return mAreCornersRounded.get() || mHasWallpaperBackgroundSupplier.get();
}
private static class InputInterceptor {
@@ -308,6 +329,10 @@ public class Letterbox {
mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
}
+ boolean isRemoved() {
+ return mSurface != null || mInputInterceptor != null;
+ }
+
public void remove() {
if (mSurface != null) {
mTransactionFactory.get().remove(mSurface).apply();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7174e68b06f4..eb7087cbc722 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.Color;
import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -105,12 +104,20 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
- @VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
+ * Resets the aspect ratio of letterbox for fixed orientation to {@link
+ * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
+ */
+ void resetFixedOrientationLetterboxAspectRatio() {
+ mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
+ }
+
+ /**
* Gets the aspect ratio of letterbox for fixed orientation.
*/
float getFixedOrientationLetterboxAspectRatio() {
@@ -118,6 +125,25 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+ * both it and a value of {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+ * and corners of the activity won't be rounded.
+ */
+ void setLetterboxActivityCornersRadius(int cornersRadius) {
+ mLetterboxActivityCornersRadius = cornersRadius;
+ }
+
+ /**
+ * Resets corners raidus for activities presented in the letterbox mode to {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+ */
+ void resetLetterboxActivityCornersRadius() {
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ }
+
+ /**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
@@ -140,6 +166,25 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundColor;
}
+
+ /**
+ * Sets color of letterbox background which is used when {@link
+ * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+ * fallback for other backfround types.
+ */
+ void setLetterboxBackgroundColor(Color color) {
+ mLetterboxBackgroundColor = color;
+ }
+
+ /**
+ * Resets color of letterbox background to {@link
+ * com.android.internal.R.color.config_letterboxBackgroundColor}.
+ */
+ void resetLetterboxBackgroundColor() {
+ mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
+ com.android.internal.R.color.config_letterboxBackgroundColor));
+ }
+
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -149,6 +194,19 @@ final class LetterboxConfiguration {
return mLetterboxBackgroundType;
}
+ /** Sets letterbox background type. */
+ void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+ mLetterboxBackgroundType = backgroundType;
+ }
+
+ /**
+ * Resets cletterbox background type to {@link
+ * com.android.internal.R.integer.config_letterboxBackgroundType}.
+ */
+ void resetLetterboxBackgroundType() {
+ mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+ }
+
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
@@ -178,6 +236,27 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides alpha of a black scrim shown over wallpaper for {@link
+ * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
+ *
+ * <p>If given value is < 0 or >= 1, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
+ * and 0.0 (transparent) is instead.
+ */
+ void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
+ }
+
+ /**
+ * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
+ */
+ void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
+ mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
+ }
+
+ /**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -185,6 +264,28 @@ final class LetterboxConfiguration {
}
/**
+ * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
+ * {@link mLetterboxBackgroundType}.
+ *
+ * <p> If given value <= 0, both it and a value of {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
+ * and 0 is used instead.
+ */
+ void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
+ mLetterboxBackgroundWallpaperBlurRadius = radius;
+ }
+
+ /**
+ * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
+ * mLetterboxBackgroundType} to {@link
+ * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
+ */
+ void resetLetterboxBackgroundWallpaperBlurRadius() {
+ mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
+ }
+
+ /**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
@@ -211,9 +312,17 @@ final class LetterboxConfiguration {
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
- @VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
+ /**
+ * Resets horizontal position of a center of the letterboxed app window to {@link
+ * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
+ */
+ void resetLetterboxHorizontalPositionMultiplier() {
+ mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index d230936b5d51..88941eb4d369 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -138,7 +138,7 @@ class NonAppWindowAnimationAdapter implements AnimationAdapter {
mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
new Rect(), null, mWindowContainer.getPrefixOrderIndex(),
mWindowContainer.getLastSurfacePosition(), mWindowContainer.getBounds(), null,
- mWindowContainer.getWindowConfiguration(), true, null, null, null,
+ mWindowContainer.getWindowConfiguration(), true, null, null, null, false,
mWindowContainer.getWindowType());
return mTarget;
}
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 7e95e7d2aa8c..6014a87ae8f7 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -211,7 +211,9 @@ class PinnedTaskController {
}
mFreezingTaskConfig = true;
mDestRotatedBounds = new Rect(bounds);
- continueOrientationChange();
+ if (!mService.mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ continueOrientationChange();
+ }
}
/**
@@ -317,15 +319,11 @@ class PinnedTaskController {
}
/** Resets the states which were used to perform fixed rotation with PiP task. */
- void onCancelFixedRotationTransform(Task task) {
+ void onCancelFixedRotationTransform() {
mFreezingTaskConfig = false;
mDeferOrientationChanging = false;
mDestRotatedBounds = null;
mPipTransaction = null;
- if (!task.isOrganized()) {
- // Force clearing Task#mForceNotOrganized because the display didn't rotate.
- task.onConfigurationChanged(task.getParent().getConfiguration());
- }
}
/**
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 455f568d7523..dca0bbda78cf 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1108,13 +1108,15 @@ class RecentTasks {
}
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
- removeForAddTask(task);
+ final int removedIndex = removeForAddTask(task);
task.inRecents = true;
if (!isAffiliated || needAffiliationFix) {
// If this is a simple non-affiliated task, or we had some failure trying to
// handle it as part of an affilated task, then just place it at the top.
- mTasks.add(0, task);
+ // But if the list is frozen, adding the task to the removed index to keep the order.
+ int indexToAdd = mFreezeTaskListReordering && removedIndex != -1 ? removedIndex : 0;
+ mTasks.add(indexToAdd, task);
notifyTaskAdded(task);
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
} else if (isAffiliated) {
@@ -1344,7 +1346,8 @@ class RecentTasks {
+ " activityType=" + task.getActivityType()
+ " windowingMode=" + task.getWindowingMode()
+ " isAlwaysOnTopWhenVisible=" + task.isAlwaysOnTopWhenVisible()
- + " intentFlags=" + task.getBaseIntent().getFlags());
+ + " intentFlags=" + task.getBaseIntent().getFlags()
+ + " isEmbedded=" + task.isEmbedded());
}
switch (task.getActivityType()) {
@@ -1390,6 +1393,11 @@ class RecentTasks {
return false;
}
+ // Ignore the task if it is a embedded task
+ if (task.isEmbedded()) {
+ return false;
+ }
+
return true;
}
@@ -1482,14 +1490,14 @@ class RecentTasks {
* If needed, remove oldest existing entries in recents that are for the same kind
* of task as the given one.
*/
- private void removeForAddTask(Task task) {
+ private int removeForAddTask(Task task) {
// The adding task will be in recents so it is not hidden.
mHiddenTasks.remove(task);
final int removeIndex = findRemoveIndexForAddTask(task);
if (removeIndex == -1) {
// Nothing to trim
- return;
+ return removeIndex;
}
// There is a similar task that will be removed for the addition of {@param task}, but it
@@ -1511,6 +1519,7 @@ class RecentTasks {
}
}
notifyTaskPersisterLocked(removedTask, false /* flush */);
+ return removeIndex;
}
/**
@@ -1518,11 +1527,6 @@ class RecentTasks {
* list (if any).
*/
private int findRemoveIndexForAddTask(Task task) {
- if (mFreezeTaskListReordering) {
- // Defer removing tasks due to the addition of new tasks until the task list is unfrozen
- return -1;
- }
-
final int recentsCount = mTasks.size();
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b1bdc11ecee4..ba1cf8ae06df 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -25,6 +25,8 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -123,6 +125,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s",
targetActivity.getConfiguration());
}
+ } else if (mDefaultTaskDisplayArea.getActivity(
+ ActivityRecord::occludesParent, false /* traverseTopToBottom */) == null) {
+ // Skip because none of above activities can occlude the target activity. The preload
+ // should be done silently in background without being visible.
+ return;
} else {
// Create the activity record. Because the activity is invisible, this doesn't really
// start the client.
@@ -149,8 +156,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan
// Invisible activity should be stopped. If the recents activity is alive and its doesn't
// need to relaunch by current configuration, then it may be already in stopped state.
- if (!targetActivity.isState(Task.ActivityState.STOPPING,
- Task.ActivityState.STOPPED)) {
+ if (!targetActivity.isState(STOPPING, STOPPED)) {
// Add to stopping instead of stop immediately. So the client has the chance to perform
// traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more
// things (e.g. the measure can be done earlier). The actual stop will be performed when
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e346e3ec7db9..d7dc3061f250 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -1102,6 +1102,23 @@ public class RecentsAnimationController implements DeathRecipient {
return mTargetActivityRecord.findMainWindow();
}
+ /**
+ * Returns the activity with the highest layer, or null if none is found.
+ */
+ public ActivityRecord getHighestLayerActivity() {
+ int highestLayer = Integer.MIN_VALUE;
+ Task highestLayerTask = null;
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+ int layer = adapter.mTask.getPrefixOrderIndex();
+ if (layer > highestLayer) {
+ highestLayer = layer;
+ highestLayerTask = adapter.mTask;
+ }
+ }
+ return highestLayerTask.getTopMostActivity();
+ }
+
boolean isAnimatingTask(Task task) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
if (task == mPendingAnimations.get(i).mTask) {
@@ -1201,7 +1218,8 @@ public class RecentsAnimationController implements DeathRecipient {
!topApp.fillsParent(), new Rect(),
insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
mLocalBounds, mBounds, mTask.getWindowConfiguration(),
- mIsRecentTaskInvisible, null, null, mTask.getTaskInfo());
+ mIsRecentTaskInvisible, null, null, mTask.getTaskInfo(),
+ topApp.checkEnterPictureInPictureAppOpsState());
return mTarget;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bd688a618c63..079868d0dec1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,10 +37,10 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -53,6 +53,11 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -70,14 +75,9 @@ import static com.android.server.wm.KeyguardController.KEYGUARD_SLEEP_TOKEN_TAG;
import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT;
import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -365,8 +365,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return false;
}
+ if (matchingCandidate(task)) {
+ return true;
+ }
+
+ // Looking for the embedded tasks (if any)
+ return !task.isLeafTaskFragment() && task.forAllLeafTaskFragments(
+ this::matchingCandidate);
+ }
+
+ boolean matchingCandidate(TaskFragment taskFragment) {
+ final Task task = taskFragment.asTask();
+ if (task == null) {
+ return false;
+ }
+
// Overlays should not be considered as the task's logical top activity.
- final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+ // Activities of the tasks that embedded from this one should not be used.
+ final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */,
+ false /* includingEmbeddedTask */);
+
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
@@ -509,6 +527,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
mTaskSupervisor.updateTopResumedActivityIfNeeded();
}
+ @Override
+ boolean isAttached() {
+ return true;
+ }
+
/**
* Called when DisplayWindowSettings values may change.
*/
@@ -817,8 +840,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Initialize state of exiting tokens.
- final int numDisplays = mChildren.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.setExitingTokensHasVisible(false);
}
@@ -855,6 +877,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
+ mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
@@ -867,7 +890,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
@@ -929,12 +952,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
// Time to remove any exiting tokens?
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = mChildren.size() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.removeExistingTokensIfPossible();
}
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
@@ -1865,7 +1888,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (focusedRootTask == null) {
return null;
}
- final ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity != null && resumedActivity.app != null) {
return resumedActivity;
}
@@ -1887,11 +1910,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// foreground.
WindowProcessController fgApp = getItemFromRootTasks(rootTask -> {
if (isTopDisplayFocusedRootTask(rootTask)) {
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity != null) {
return resumedActivity.app;
- } else if (rootTask.getPausingActivity() != null) {
- return rootTask.getPausingActivity().app;
+ } else if (rootTask.getTopPausingActivity() != null) {
+ return rootTask.getTopPausingActivity().app;
}
}
return null;
@@ -1917,7 +1940,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return;
}
- if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) {
+ if (rootTask.getVisibility(null /* starting */)
+ == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
return;
}
@@ -1974,7 +1998,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- if (mTaskSupervisor.inActivityVisibilityUpdate()) {
+ if (mTaskSupervisor.inActivityVisibilityUpdate()
+ || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
// Don't do recursive work.
return;
}
@@ -2186,7 +2211,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// display area, so reparent.
rootTask.reparent(taskDisplayArea, true /* onTop */);
}
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CHANGE, rootTask);
+ mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
@@ -2391,7 +2416,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayShouldSleep) {
rootTask.goToSleepIfPossible(false /* shuttingDown */);
} else {
- rootTask.awakeFromSleepingLocked();
+ rootTask.forAllLeafTasksAndLeafTaskFragments(
+ taskFragment -> taskFragment.awakeFromSleeping(),
+ true /* traverseTopToBottom */);
if (rootTask.isFocusedRootTaskOnDisplay()
&& !mTaskSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2768,8 +2795,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.getState()
- + " resumed=" + r.getTask().getResumedActivity() + " pausing="
- + r.getTask().getPausingActivity() + " for reason "
+ + " resumed=" + r.getTask().getTopResumedActivity() + " pausing="
+ + r.getTask().getTopPausingActivity() + " for reason "
+ mDestroyAllActivitiesReason);
}
@@ -2803,7 +2830,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
r.detachFromProcess();
- r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
TRANSIT_FLAG_APP_CRASHED);
r.destroyIfPossible("handleAppCrashed");
@@ -3356,7 +3382,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (rootTask == null || !rootTask.hasActivity()) {
continue;
}
- final ActivityRecord resumedActivity = rootTask.getResumedActivity();
+ final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
if (resumedActivity == null || !resumedActivity.idle) {
ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
+ "not idle", rootTask.getRootTaskId(), resumedActivity);
@@ -3371,7 +3397,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allResumedActivitiesVisible() {
boolean[] foundResumed = {false};
final boolean foundInvisibleResumedActivity = forAllRootTasks(rootTask -> {
- final ActivityRecord r = rootTask.getResumedActivity();
+ final ActivityRecord r = rootTask.getTopResumedActivity();
if (r != null) {
if (!r.nowVisible) {
return true;
@@ -3389,7 +3415,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allPausedActivitiesComplete() {
boolean[] pausing = {true};
final boolean hasActivityNotCompleted = forAllLeafTasks(task -> {
- final ActivityRecord r = task.getPausingActivity();
+ final ActivityRecord r = task.getTopPausingActivity();
if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: "
+ "r=%s state=%s", r, r.getState());
@@ -3432,14 +3458,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}, true /* traverseTopToBottom */);
}
- void cancelInitializingActivities() {
- forAllRootTasks(task -> {
- // We don't want to clear starting window for activities that aren't occluded
- // as we need to display their starting window until they are done initializing.
- task.forAllOccludedActivities(ActivityRecord::cancelInitializing);
- });
- }
-
Task anyTaskForId(int id) {
return anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE);
}
@@ -3519,11 +3537,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return task;
}
- ActivityRecord isInAnyTask(IBinder token) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- return (r != null && r.isDescendantOf(this)) ? r : null;
- }
-
@VisibleForTesting
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
int flags, int callingUid, ArraySet<Integer> profileIds) {
@@ -3564,6 +3577,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
+ // TODO(b/191434136): handle this properly when we add multi-window support on secondary
+ // display.
private void calculateDefaultMinimalSizeOfResizeableTasks() {
final Resources res = mService.mContext.getResources();
final float minimalSize = res.getDimension(
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 7ba772c18455..1533245a75ca 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -43,7 +43,11 @@ class RunningTasks {
// Comparator to sort by last active time (descending)
private static final Comparator<Task> LAST_ACTIVE_TIME_COMPARATOR =
- (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+ (o1, o2) -> {
+ return o1.lastActiveTime == o2.lastActiveTime
+ ? Integer.signum(o2.mTaskId - o1.mTaskId) :
+ Long.signum(o2.lastActiveTime - o1.lastActiveTime);
+ };
private final TreeSet<Task> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 4892005631ba..2d4aef682d62 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -78,6 +78,18 @@ public class SafeActivityOptions {
}
/**
+ * Constructs a new instance from a bundle and provided pid/uid.
+ *
+ * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+ */
+ static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
+ return bOptions != null
+ ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
+ callingPid, callingUid)
+ : null;
+ }
+
+ /**
* Constructs a new instance and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
* this object.
@@ -91,6 +103,17 @@ public class SafeActivityOptions {
}
/**
+ * Constructs a new instance.
+ *
+ * @param options The options to wrap.
+ */
+ private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
+ mOriginalCallingPid = callingPid;
+ mOriginalCallingUid = callingUid;
+ mOriginalOptions = options;
+ }
+
+ /**
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
* method.
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 58363f2316ea..d440a14d6199 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
@@ -30,8 +31,6 @@ import static com.android.server.wm.ScreenRotationAnimationProto.STARTED;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -91,13 +90,6 @@ import java.io.PrintWriter;
class ScreenRotationAnimation {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
- /*
- * Layers for screen rotation animation. We put these layers above
- * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
- */
- private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
- private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
-
private final Context mContext;
private final DisplayContent mDisplayContent;
private final float[] mTmpFloats = new float[9];
@@ -423,7 +415,7 @@ class ScreenRotationAnimation {
finalWidth * 2, finalHeight * 2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
- SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
+ SCREEN_FREEZE_LAYER_BASE, mDisplayContent, false, mEnterBlackFrameLayer);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index e28201245d9b..0b5677724fc3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -69,6 +69,7 @@ import android.view.IWindowSessionCallback;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -113,7 +114,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
private float mLastReportedAnimatorScale;
private String mPackageName;
private String mRelayoutTag;
- private final InsetsState mDummyRequestedVisibility = new InsetsState();
+ private final InsetsVisibilities mDummyRequestedVisibilities = new InsetsVisibilities();
private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0];
public Session(WindowManagerService service, IWindowSessionCallback callback) {
@@ -187,29 +188,28 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
@Override
public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, InsetsState requestedVisibility,
+ int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), requestedVisibility, outInputChannel, outInsetsState,
+ UserHandle.getUserId(mUid), requestedVisibilities, outInputChannel, outInsetsState,
outActiveControls);
}
-
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
- int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
+ int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
- requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
+ requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
}
@Override
public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, InsetsState outInsetsState) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
- UserHandle.getUserId(mUid), mDummyRequestedVisibility, null /* outInputChannel */,
+ UserHandle.getUserId(mUid), mDummyRequestedVisibilities, null /* outInputChannel */,
outInsetsState, mDummyControls);
}
@@ -624,12 +624,12 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public void insetsModified(IWindow window, InsetsState state) {
+ public void updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities) {
synchronized (mService.mGlobalLock) {
final WindowState windowState = mService.windowForClientLocked(this, window,
false /* throwOnError */);
if (windowState != null) {
- windowState.updateRequestedVisibility(state);
+ windowState.setRequestedVisibilities(visibilities);
windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState);
}
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index c671e3835abc..8b1befbefd99 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -32,6 +32,12 @@ public abstract class StartingData {
*/
boolean mIsTransitionForward;
+ /**
+ * Non-null if the starting window should cover the bounds of associated task. It is assigned
+ * when the parent activity of starting window may be put in a partial area of the task.
+ */
+ Task mAssociatedTask;
+
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 48a7bdec2b94..cdf6b08b1c57 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -27,6 +27,7 @@ import android.graphics.Rect;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import android.view.WindowManagerPolicyConstants;
class StrictModeFlash {
private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -52,7 +53,7 @@ class StrictModeFlash {
.build();
// one more than Watermark? arbitrary.
- t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);
+ t.setLayer(ctrl, WindowManagerPolicyConstants.STRICT_MODE_LAYER);
t.setPosition(ctrl, 0, 0);
t.show(ctrl);
// Ensure we aren't considered as obscuring for Input purposes.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ced5af126e49..73dda7476155 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,13 +27,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
@@ -43,7 +40,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
-import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
@@ -53,9 +49,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -67,7 +60,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
@@ -84,22 +76,18 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
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.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
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;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -112,7 +100,6 @@ import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
-import static com.android.server.wm.ActivityTaskSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
@@ -123,21 +110,12 @@ import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
import static com.android.server.wm.TaskProto.AFFINITY;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
import static com.android.server.wm.TaskProto.FILLS_PARENT;
import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
import static com.android.server.wm.TaskProto.RESIZE_MODE;
@@ -145,9 +123,8 @@ import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -170,14 +147,8 @@ import android.app.AppGlobals;
import android.app.IActivityController;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
-import android.app.ResultInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
-import android.app.servertransaction.ActivityResultItem;
-import android.app.servertransaction.ClientTransaction;
-import android.app.servertransaction.NewIntentItem;
-import android.app.servertransaction.PauseActivityItem;
-import android.app.servertransaction.ResumeActivityItem;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -208,7 +179,6 @@ import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -244,23 +214,21 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-class Task extends WindowContainer<WindowContainer> {
+/**
+ * {@link Task} is a TaskFragment that can contain a group of activities to perform a certain job.
+ * Activities of the same task affinities usually group in the same {@link Task}. A {@link Task}
+ * can also be an entity that showing in the Recents Screen for a job that user interacted with.
+ * A {@link Task} can also contain other {@link Task}s.
+ */
+class Task extends TaskFragment {
private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
- static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
- private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
- private static final String TAG_APP = TAG + POSTFIX_APP;
static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
- private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
- private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
- private static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK;
- private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
@@ -303,10 +271,6 @@ class Task extends WindowContainer<WindowContainer> {
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.
- private static final boolean SHOW_APP_STARTING_PREVIEW = true;
-
// How long to wait for all background Activities to redraw following a call to
// convertToTranslucent().
private static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
@@ -315,7 +279,6 @@ class Task extends WindowContainer<WindowContainer> {
// code.
static final int PERSIST_TASK_VERSION = 1;
- static final int INVALID_MIN_SIZE = -1;
private float mShadowRadius = 0;
/**
@@ -335,36 +298,6 @@ class Task extends WindowContainer<WindowContainer> {
// Do not move the root task as a part of reparenting
static final int REPARENT_LEAVE_ROOT_TASK_IN_PLACE = 2;
- @IntDef(prefix = {"TASK_VISIBILITY"}, value = {
- TASK_VISIBILITY_VISIBLE,
- TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
- TASK_VISIBILITY_INVISIBLE,
- })
- @interface TaskVisibility {}
-
- /** Task is visible. No other tasks on top that fully or partially occlude it. */
- static final int TASK_VISIBILITY_VISIBLE = 0;
-
- /** Task is partially occluded by other translucent task(s) on top of it. */
- static final int TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
-
- /** Task is completely invisible. */
- static final int TASK_VISIBILITY_INVISIBLE = 2;
-
- enum ActivityState {
- INITIALIZING,
- STARTED,
- RESUMED,
- PAUSING,
- PAUSED,
- STOPPING,
- STOPPED,
- FINISHING,
- DESTROYING,
- DESTROYED,
- RESTARTING_PROCESS
- }
-
// The topmost Activity passed to convertToTranslucent(). When non-null it means we are
// waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
// are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
@@ -446,7 +379,6 @@ class Task extends WindowContainer<WindowContainer> {
CharSequence lastDescription; // Last description captured for this item.
- Task mAdjacentTask; // Task adjacent to this one.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
Task mPrevAffiliate; // previous task in affiliated chain.
int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -458,21 +390,12 @@ class Task extends WindowContainer<WindowContainer> {
String mCallingPackage;
String mCallingFeatureId;
- private final Rect mTmpStableBounds = new Rect();
- private final Rect mTmpNonDecorBounds = new Rect();
- private final Rect mTmpBounds = new Rect();
- private final Rect mTmpInsets = new Rect();
- private final Rect mTmpFullBounds = new Rect();
private static final Rect sTmpBounds = new Rect();
// Last non-fullscreen bounds the task was launched in or resized to.
// The information is persisted and used to determine the appropriate root task to launch the
// task into on restore.
Rect mLastNonFullscreenBounds = null;
- // Minimal width and height of this task when it's resizeable. -1 means it should use the
- // default minimal width/height.
- int mMinWidth;
- int mMinHeight;
// The surface transition of the target when recents animation is finished.
// This is originally introduced to carry out the current surface control position and window
@@ -497,10 +420,6 @@ class Task extends WindowContainer<WindowContainer> {
/** Used by fillTaskInfo */
final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport();
- final ActivityTaskManagerService mAtmService;
- final ActivityTaskSupervisor mTaskSupervisor;
- final RootWindowContainer mRootWindowContainer;
-
/* Unique identifier for this task. */
final int mTaskId;
/* User for which this task was created. */
@@ -571,29 +490,6 @@ class Task extends WindowContainer<WindowContainer> {
/** ActivityRecords that are exiting, but still on screen for animations. */
final ArrayList<ActivityRecord> mExitingActivities = new ArrayList<>();
- /**
- * When we are in the process of pausing an activity, before starting the
- * next one, this variable holds the activity that is currently being paused.
- *
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mPausingActivity = null;
-
- /**
- * This is the last activity that we put into the paused state. This is
- * used to determine if we need to do an activity transition while sleeping,
- * when we normally hold the top activity paused.
- */
- ActivityRecord mLastPausedActivity = null;
-
- /**
- * Current activity that is resumed, or null if there is none.
- * Only set at leaf tasks.
- */
- @Nullable
- private ActivityRecord mResumedActivity = null;
-
private boolean mForceShowForAllUsers;
/** When set, will force the task to report as invisible. */
@@ -641,121 +537,6 @@ class Task extends WindowContainer<WindowContainer> {
}
private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
- private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
- new EnsureActivitiesVisibleHelper(this);
- private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
- new EnsureVisibleActivitiesConfigHelper();
- private class EnsureVisibleActivitiesConfigHelper {
- private boolean mUpdateConfig;
- private boolean mPreserveWindow;
- private boolean mBehindFullscreen;
-
- void reset(boolean preserveWindow) {
- mPreserveWindow = preserveWindow;
- mUpdateConfig = false;
- mBehindFullscreen = false;
- }
-
- void process(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
- return;
- }
- reset(preserveWindow);
-
- final PooledFunction f = PooledLambda.obtainFunction(
- EnsureVisibleActivitiesConfigHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
- f.recycle();
-
- if (mUpdateConfig) {
- // Ensure the resumed state of the focus activity if we updated the configuration of
- // any activity.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
-
- boolean processActivity(ActivityRecord r) {
- mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
- mBehindFullscreen |= r.occludesParent();
- return mBehindFullscreen;
- }
- }
-
- private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
- new CheckBehindFullscreenActivityHelper();
- private class CheckBehindFullscreenActivityHelper {
- private boolean mAboveTop;
- private boolean mBehindFullscreenActivity;
- private ActivityRecord mToCheck;
- private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
- private boolean mHandlingOccluded;
-
- private void reset(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- mToCheck = toCheck;
- mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
- mAboveTop = true;
- mBehindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The root task is not visible, so no activity in it should be displaying a
- // starting window. Mark all activities below top and behind fullscreen.
- mAboveTop = false;
- mBehindFullscreenActivity = true;
- }
-
- mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
- }
-
- boolean process(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- reset(toCheck, handleBehindFullscreenActivity);
-
- if (!mHandlingOccluded && mBehindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivity();
- final PooledFunction f = PooledLambda.obtainFunction(
- CheckBehindFullscreenActivityHelper::processActivity, this,
- PooledLambda.__(ActivityRecord.class), topActivity);
- forAllActivities(f);
- f.recycle();
-
- return mBehindFullscreenActivity;
- }
-
- /** Returns {@code true} to stop the outer loop and indicate the result is computed. */
- private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
- if (mAboveTop) {
- if (r == topActivity) {
- if (r == mToCheck) {
- // It is the top activity in a visible root task.
- mBehindFullscreenActivity = false;
- return true;
- }
- mAboveTop = false;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
-
- if (mHandlingOccluded) {
- // Iterating through all occluded activities.
- if (mBehindFullscreenActivity) {
- mHandleBehindFullscreenActivity.accept(r);
- }
- } else if (r == mToCheck) {
- return true;
- } else if (mBehindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- mBehindFullscreenActivity |= r.occludesParent();
- return false;
- }
- }
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
@@ -809,28 +590,6 @@ class Task extends WindowContainer<WindowContainer> {
// false.
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
- * from the task organizer.</li>
- * <li>Unlike other non-root tasks, it's direct children are visible to the task
- * organizer for ordering purposes.</li>
- * </ul>
- */
- @VisibleForTesting
- boolean mCreatedByOrganizer;
-
// Tracking cookie for the creation of this task.
IBinder mLaunchCookie;
@@ -858,11 +617,8 @@ class Task extends WindowContainer<WindowContainer> {
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
boolean _removeWithTaskOrganizer) {
- super(atmService.mWindowManager);
+ super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
- mAtmService = atmService;
- mTaskSupervisor = atmService.mTaskSupervisor;
- mRootWindowContainer = mAtmService.mRootWindowContainer;
mTaskId = _taskId;
mUserId = _userId;
mResizeMode = resizeMode;
@@ -875,7 +631,6 @@ class Task extends WindowContainer<WindowContainer> {
: new PersistedTaskSnapshotData();
// Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED).
setOrientation(SCREEN_ORIENTATION_UNSET);
- mRemoteToken = new RemoteToken(this);
affinityIntent = _affinityIntent;
affinity = _affinity;
rootAffinity = _rootAffinity;
@@ -913,7 +668,6 @@ class Task extends WindowContainer<WindowContainer> {
mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
- mCreatedByOrganizer = _createdByOrganizer;
mLaunchCookie = _launchCookie;
mDeferTaskAppear = _deferTaskAppear;
mRemoveWithTaskOrganizer = _removeWithTaskOrganizer;
@@ -942,13 +696,13 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
- private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
+ private void cleanUpResourcesForDestroy(WindowContainer<?> oldParent) {
if (hasChild()) {
return;
}
// This task is going away, so save the last state if necessary.
- saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
+ saveLaunchingStateIfNeeded(oldParent.getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -958,7 +712,7 @@ class Task extends WindowContainer<WindowContainer> {
} catch (RemoteException e) {
}
}
- if (autoRemoveFromRecents() || isVoiceSession) {
+ if (autoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
// Task creator asked to remove this when done, or this task was a voice
// interaction, so it should not remain on the recent tasks list.
mTaskSupervisor.mRecentTasks.remove(this);
@@ -1379,11 +1133,11 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- final DisplayContent display = newParent != null
- ? ((WindowContainer) newParent).getDisplayContent() : null;
- final DisplayContent oldDisplay = oldParent != null
- ? ((WindowContainer) oldParent).getDisplayContent() : null;
+ void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
+ final WindowContainer<?> newParent = (WindowContainer<?>) rawNewParent;
+ final WindowContainer<?> oldParent = (WindowContainer<?>) rawOldParent;
+ final DisplayContent display = newParent != null ? newParent.getDisplayContent() : null;
+ final DisplayContent oldDisplay = oldParent != null ? oldParent.getDisplayContent() : null;
mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
@@ -1424,7 +1178,7 @@ class Task extends WindowContainer<WindowContainer> {
}
if (oldParent != null) {
- final Task oldParentTask = ((WindowContainer) oldParent).asTask();
+ final Task oldParentTask = oldParent.asTask();
if (oldParentTask != null) {
final PooledConsumer c = PooledLambda.obtainConsumer(
Task::cleanUpActivityReferences, oldParentTask,
@@ -1442,6 +1196,12 @@ class Task extends WindowContainer<WindowContainer> {
}
if (newParent != null) {
+ // Surface of Task that will not be organized should be shown by default.
+ // See Task#showSurfaceOnCreation
+ if (!mCreatedByOrganizer && !canBeOrganized()) {
+ getSyncTransaction().show(mSurfaceControl);
+ }
+
// TODO: Ensure that this is actually necessary here
// Notify the voice session if required
if (voiceSession != null) {
@@ -1466,64 +1226,80 @@ class Task extends WindowContainer<WindowContainer> {
mRootWindowContainer.updateUIDsPresentOnDisplay();
}
- void cleanUpActivityReferences(ActivityRecord r) {
- // mPausingActivity is set at leaf task
- if (mPausingActivity != null && mPausingActivity == r) {
- mPausingActivity = null;
- }
-
- if (mResumedActivity != null && mResumedActivity == r) {
- setResumedActivity(null, "cleanUpActivityReferences");
+ /** Returns the currently topmost resumed activity. */
+ @Nullable
+ ActivityRecord getTopResumedActivity() {
+ if (!isLeafTask()) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord resumedActivity = mChildren.get(i).asTask().getTopResumedActivity();
+ if (resumedActivity != null) {
+ return resumedActivity;
+ }
+ }
}
- final WindowContainer parent = getParent();
- if (parent != null && parent.asTask() != null) {
- parent.asTask().cleanUpActivityReferences(r);
- return;
- }
- r.removeTimeouts();
- mExitingActivities.remove(r);
- }
-
- /** @return the currently resumed activity. */
- ActivityRecord getResumedActivity() {
- if (isLeafTask()) {
- return mResumedActivity;
+ final ActivityRecord taskResumedActivity = getResumedActivity();
+ ActivityRecord topResumedActivity = null;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final ActivityRecord[] resumedActivity = new ActivityRecord[1];
+ child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+ if (fragment.getResumedActivity() != null) {
+ resumedActivity[0] = fragment.getResumedActivity();
+ return true;
+ }
+ return false;
+ });
+ topResumedActivity = resumedActivity[0];
+ } else if (taskResumedActivity != null
+ && child.asActivityRecord() == taskResumedActivity) {
+ topResumedActivity = taskResumedActivity;
+ }
+ if (topResumedActivity != null) {
+ return topResumedActivity;
+ }
}
-
- final Task task = getTask(t -> t.mResumedActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mResumedActivity : null;
- }
-
- @VisibleForTesting
- void setPausingActivity(ActivityRecord pausing) {
- mPausingActivity = pausing;
+ return null;
}
/**
- * @return the currently pausing activity of this task or the topmost pausing activity of the
- * child tasks
+ * Returns the currently topmost pausing activity.
*/
- ActivityRecord getPausingActivity() {
- if (isLeafTask()) {
- return mPausingActivity;
+ @Nullable
+ ActivityRecord getTopPausingActivity() {
+ if (!isLeafTask()) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ ActivityRecord pausingActivity = mChildren.get(i).asTask().getTopPausingActivity();
+ if (pausingActivity != null) {
+ return pausingActivity;
+ }
+ }
}
- final Task task = getTask(t -> t.mPausingActivity != null, true /* traverseTopToBottom */);
- return task != null ? task.mPausingActivity : null;
- }
-
- void setResumedActivity(ActivityRecord r, String reason) {
- warnForNonLeafTask("setResumedActivity");
- if (mResumedActivity == r) {
- return;
+ final ActivityRecord taskPausingActivity = getPausingActivity();
+ ActivityRecord topPausingActivity = null;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ final ActivityRecord[] pausingActivity = new ActivityRecord[1];
+ child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
+ if (fragment.getPausingActivity() != null) {
+ pausingActivity[0] = fragment.getPausingActivity();
+ return true;
+ }
+ return false;
+ });
+ topPausingActivity = pausingActivity[0];
+ } else if (taskPausingActivity != null
+ && child.asActivityRecord() == taskPausingActivity) {
+ topPausingActivity = taskPausingActivity;
+ }
+ if (topPausingActivity != null) {
+ return topPausingActivity;
+ }
}
-
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
- "setResumedActivity task:" + this + " + from: "
- + mResumedActivity + " to:" + r + " reason:" + reason);
- mResumedActivity = r;
- mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ return null;
}
void updateTaskMovement(boolean toTop, int position) {
@@ -1562,11 +1338,6 @@ class Task extends WindowContainer<WindowContainer> {
mTaskId, mUserId);
}
- void setAdjacentTask(Task adjacent) {
- mAdjacentTask = adjacent;
- adjacent.mAdjacentTask = this;
- }
-
void setTaskToAffiliateWith(Task taskToAffiliateWith) {
closeRecentsChain();
mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -1612,14 +1383,6 @@ class Task extends WindowContainer<WindowContainer> {
return mFindRootHelper.findRoot(ignoreRelinquishIdentity, setToBottomIfNone);
}
- ActivityRecord getTopNonFinishingActivity() {
- return getTopNonFinishingActivity(true /* includeOverlays */);
- }
-
- ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
- return getTopActivity(false /*includeFinishing*/, includeOverlays);
- }
-
ActivityRecord topRunningActivityLocked() {
if (getParent() == null) {
return null;
@@ -1646,14 +1409,6 @@ class Task extends WindowContainer<WindowContainer> {
window.getBaseType() == TYPE_APPLICATION_STARTING) != null);
}
- ActivityRecord topActivityWithStartingWindow() {
- if (getParent() == null) {
- return null;
- }
- return getActivity((r) -> r.mStartingWindowState == STARTING_WINDOW_SHOWN
- && r.okToShowLocked());
- }
-
/**
* Return the number of running activities, and the number of non-finishing/initializing
* activities in the provided {@param reportOut} respectively.
@@ -1675,22 +1430,7 @@ class Task extends WindowContainer<WindowContainer> {
}
@Override
- public int getActivityType() {
- final int applicationType = super.getActivityType();
- if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
- return applicationType;
- }
- return getTopChild().getActivityType();
- }
-
- @Override
void addChild(WindowContainer child, int index) {
- // If this task had any child before we added this one.
- boolean hadChild = hasChild();
- // getActivityType() looks at the top child, so we need to read the type before adding
- // a new child in case the new child is on top and UNDEFINED.
- final int activityType = getActivityType();
-
index = getAdjustedChildPosition(child, index);
super.addChild(child, index);
@@ -1706,10 +1446,17 @@ class Task extends WindowContainer<WindowContainer> {
// now that this record is in a new task.
mRootWindowContainer.updateUIDsPresentOnDisplay();
- final ActivityRecord r = child.asActivityRecord();
- if (r == null) return;
+ // Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be
+ // passed from Task constructor.
+ final TaskFragment childTaskFrag = child.asTaskFragment();
+ if (childTaskFrag != null && childTaskFrag.asTask() == null) {
+ childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);
+ }
+ }
- r.inHistory = true;
+ /** Called when an {@link ActivityRecord} is added as a descendant */
+ void onDescendantActivityAdded(boolean hadChild, int activityType, ActivityRecord r) {
+ warnForNonLeafTask("onDescendantActivityAdded");
// Only set this based on the first activity
if (!hadChild) {
@@ -1736,10 +1483,6 @@ class Task extends WindowContainer<WindowContainer> {
updateEffectiveIntent();
}
- void addChild(ActivityRecord r) {
- addChild(r, Integer.MAX_VALUE /* add on top */);
- }
-
@Override
void removeChild(WindowContainer child) {
removeChild(child, "removeChild");
@@ -1759,7 +1502,7 @@ class Task extends WindowContainer<WindowContainer> {
if (DEBUG_TASK_MOVEMENT) {
Slog.d(TAG_WM, "removeChild: child=" + r + " reason=" + reason);
}
- super.removeChild(r);
+ super.removeChild(r, false /* removeSelfIfPossible */);
if (inPinnedWindowingMode()) {
// We normally notify listeners of task stack changes on pause, however root pinned task
@@ -1789,7 +1532,10 @@ class Task extends WindowContainer<WindowContainer> {
// Remove entire task if it doesn't have any activity left and it isn't marked for reuse
// or created by task organizer.
if (!isRootTask()) {
- getRootTask().removeChild(this, reason);
+ final WindowContainer<?> parent = getParent();
+ if (parent != null) {
+ parent.asTaskFragment().removeChild(this);
+ }
}
EventLogTags.writeWmTaskRemoved(mTaskId,
"removeChild:" + reason + " last r=" + r + " in t=" + this);
@@ -1821,11 +1567,12 @@ class Task extends WindowContainer<WindowContainer> {
return count > 0;
}
- private boolean autoRemoveFromRecents() {
+ private boolean autoRemoveFromRecents(TaskFragment oldParentFragment) {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
- // the user.
- return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
+ // the user, or it was being embedded in another Task.
+ return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()
+ || (oldParentFragment != null && oldParentFragment.isEmbedded()));
}
private void clearPinnedTaskIfNeed() {
@@ -1985,32 +1732,6 @@ class Task extends WindowContainer<WindowContainer> {
&& supportsMultiWindowInDisplayArea(tda);
}
- boolean supportsMultiWindow() {
- return supportsMultiWindowInDisplayArea(getDisplayArea());
- }
-
- /**
- * @return whether this task supports multi-window if it is in the given
- * {@link TaskDisplayArea}.
- */
- boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
- if (!mAtmService.mSupportsMultiWindow) {
- return false;
- }
- if (tda == null) {
- Slog.w(TAG_TASKS, "Can't find TaskDisplayArea to determine support for multi"
- + " window. Task id=" + mTaskId + " attached=" + isAttached());
- return false;
- }
-
- if (!isResizeable() && !tda.supportsNonResizableMultiWindow()) {
- // Not support non-resizable in multi window.
- return false;
- }
-
- return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
- }
-
/**
* Check whether this task can be launched on the specified display.
*
@@ -2146,60 +1867,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
- @NonNull Configuration parentConfig) {
- int minWidth = mMinWidth;
- int minHeight = mMinHeight;
- // If the task has no requested minimal size, we'd like to enforce a minimal size
- // so that the user can not render the task too small to manipulate. We don't need
- // to do this for the root pinned task as the bounds are controlled by the system.
- if (!inPinnedWindowingMode()) {
- final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
- final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- final int defaultMinSize = (int) (defaultMinSizeDp * density);
-
- if (minWidth == INVALID_MIN_SIZE) {
- minWidth = defaultMinSize;
- }
- if (minHeight == INVALID_MIN_SIZE) {
- minHeight = defaultMinSize;
- }
- }
- if (bounds.isEmpty()) {
- // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
- // do, we can just skip.
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
- return;
- }
- bounds.set(parentBounds);
- }
- final boolean adjustWidth = minWidth > bounds.width();
- final boolean adjustHeight = minHeight > bounds.height();
- if (!(adjustWidth || adjustHeight)) {
- return;
- }
-
- if (adjustWidth) {
- if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
- bounds.left = bounds.right - minWidth;
- } else {
- // Either left bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping left.
- bounds.right = bounds.left + minWidth;
- }
- }
- if (adjustHeight) {
- if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
- bounds.top = bounds.bottom - minHeight;
- } else {
- // Either top bounds match, or neither match, or the previous bounds were
- // fullscreen and we default to keeping top.
- bounds.bottom = bounds.top + minHeight;
- }
- }
- }
-
void setLastNonFullscreenBounds(Rect bounds) {
if (mLastNonFullscreenBounds == null) {
mLastNonFullscreenBounds = new Rect(bounds);
@@ -2208,32 +1875,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /**
- * This should be called when an child activity changes state. This should only
- * be called from
- * {@link ActivityRecord#setState(ActivityState, String)} .
- * @param record The {@link ActivityRecord} whose state has changed.
- * @param state The new state.
- * @param reason The reason for the change.
- */
- void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
- warnForNonLeafTask("onActivityStateChanged");
- if (record == mResumedActivity && state != RESUMED) {
- setResumedActivity(null, reason + " - onActivityStateChanged");
- }
-
- if (state == RESUMED) {
- if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
- Slog.v(TAG_ROOT_TASK, "set resumed activity to:" + record + " reason:" + reason);
- }
- setResumedActivity(record, reason + " - onActivityStateChanged");
- if (record == mRootWindowContainer.getTopResumedActivity()) {
- mAtmService.setResumedActivityUncheckLocked(record, reason);
- }
- mTaskSupervisor.mRecentTasks.add(record.getTask());
- }
- }
-
private void onConfigurationChangedInner(Configuration newParentConfig) {
// Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
// restore the last recorded non-fullscreen bounds.
@@ -2281,19 +1922,16 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- if (pipChanging) {
- // 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.
+ if (pipChanging && wasInPictureInPicture) {
+ // If the top activity is changing from PiP to fullscreen with fixed rotation,
+ // clear the crop and rotation matrix of task because fixed rotation will handle
+ // the transformation on activity level. This also avoids flickering caused by the
+ // latency of fullscreen task organizer configuring the surface.
final ActivityRecord r = topRunningActivity();
if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
- mForceNotOrganized = true;
+ getSyncTransaction().setWindowCrop(mSurfaceControl, null)
+ .setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]);
}
- } else {
- // If the display orientation change is done, let the corresponding task organizer take
- // back the control of this task.
- mForceNotOrganized = false;
}
saveLaunchingStateIfNeeded();
@@ -2378,6 +2016,156 @@ class Task extends WindowContainer<WindowContainer> {
}
}
+ void resolveLeafTaskOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) {
+ if (!isLeafTask()) {
+ return;
+ }
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ 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();
+
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Use empty bounds to indicate "fill parent".
+ outOverrideBounds.setEmpty();
+ // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
+ // the parent or display is smaller than the size, the content may be cropped.
+ return;
+ }
+
+ adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ computeFreeformBounds(outOverrideBounds, newParentConfig);
+ return;
+ }
+ }
+
+ void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
+ @NonNull Configuration parentConfig) {
+ int minWidth = mMinWidth;
+ int minHeight = mMinHeight;
+ // If the task has no requested minimal size, we'd like to enforce a minimal size
+ // so that the user can not render the task fragment too small to manipulate. We don't need
+ // to do this for the root pinned task as the bounds are controlled by the system.
+ if (!inPinnedWindowingMode()) {
+ final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
+ final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
+ if (minWidth == INVALID_MIN_SIZE) {
+ minWidth = defaultMinSize;
+ }
+ if (minHeight == INVALID_MIN_SIZE) {
+ minHeight = defaultMinSize;
+ }
+ }
+ if (bounds.isEmpty()) {
+ // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
+ // do, we can just skip.
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
+ return;
+ }
+ bounds.set(parentBounds);
+ }
+ final boolean adjustWidth = minWidth > bounds.width();
+ final boolean adjustHeight = minHeight > bounds.height();
+ if (!(adjustWidth || adjustHeight)) {
+ return;
+ }
+
+ if (adjustWidth) {
+ if (!previousBounds.isEmpty() && bounds.right == previousBounds.right) {
+ bounds.left = bounds.right - minWidth;
+ } else {
+ // Either left bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping left.
+ bounds.right = bounds.left + minWidth;
+ }
+ }
+ if (adjustHeight) {
+ if (!previousBounds.isEmpty() && bounds.bottom == previousBounds.bottom) {
+ bounds.top = bounds.bottom - minHeight;
+ } else {
+ // Either top bounds match, or neither match, or the previous bounds were
+ // fullscreen and we default to keeping top.
+ bounds.bottom = bounds.top + minHeight;
+ }
+ }
+ }
+
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+ private void computeFreeformBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
+ // by policy, make sure the window remains within parent somewhere
+ final float density =
+ ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+ final Rect parentBounds =
+ new Rect(newParentConfig.windowConfiguration.getBounds());
+ final DisplayContent display = getDisplayContent();
+ if (display != null) {
+ // If a freeform window moves below system bar, there is no way to move it again
+ // by touch. Because its caption is covered by system bar. So we exclude them
+ // from root task bounds. and then caption will be shown inside stable area.
+ final Rect stableBounds = new Rect();
+ display.getStableRect(stableBounds);
+ parentBounds.intersect(stableBounds);
+ }
+
+ fitWithinBounds(outBounds, parentBounds,
+ (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+ (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+ // Prevent to overlap caption with stable insets.
+ final int offsetTop = parentBounds.top - outBounds.top;
+ if (offsetTop > 0) {
+ outBounds.offset(0, offsetTop);
+ }
+ }
+
+ /**
+ * Adjusts bounds to stay within root task bounds.
+ *
+ * Since bounds might be outside of root task bounds, this method tries to move the bounds in
+ * a way that keep them unchanged, but be contained within the root task bounds.
+ *
+ * @param bounds Bounds to be adjusted.
+ * @param rootTaskBounds Bounds within which the other bounds should remain.
+ * @param overlapPxX The amount of px required to be visible in the X dimension.
+ * @param overlapPxY The amount of px required to be visible in the Y dimension.
+ */
+ private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
+ int overlapPxY) {
+ if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
+ return;
+ }
+
+ // For each side of the parent (eg. left), check if the opposing side of the window (eg.
+ // right) is at least overlap pixels away. If less, offset the window by that difference.
+ int horizontalDiff = 0;
+ // If window is smaller than overlap, use it's smallest dimension instead
+ int overlapLR = Math.min(overlapPxX, bounds.width());
+ if (bounds.right < (rootTaskBounds.left + overlapLR)) {
+ horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
+ } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
+ horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
+ }
+ int verticalDiff = 0;
+ int overlapTB = Math.min(overlapPxY, bounds.width());
+ if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
+ verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
+ } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
+ verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
+ }
+ bounds.offset(horizontalDiff, verticalDiff);
+ }
+
/**
* Initializes a change transition. See {@link SurfaceFreezer} for more information.
*/
@@ -2526,400 +2314,6 @@ class Task extends WindowContainer<WindowContainer> {
mTaskSupervisor.mLaunchParamsPersister.saveTask(this, display);
}
- /**
- * Adjust bounds to stay within root task bounds.
- *
- * Since bounds might be outside of root task bounds, this method tries to move the bounds in
- * a way that keep them unchanged, but be contained within the root task bounds.
- *
- * @param bounds Bounds to be adjusted.
- * @param rootTaskBounds Bounds within which the other bounds should remain.
- * @param overlapPxX The amount of px required to be visible in the X dimension.
- * @param overlapPxY The amount of px required to be visible in the Y dimension.
- */
- private static void fitWithinBounds(Rect bounds, Rect rootTaskBounds, int overlapPxX,
- int overlapPxY) {
- if (rootTaskBounds == null || rootTaskBounds.isEmpty() || rootTaskBounds.contains(bounds)) {
- return;
- }
-
- // For each side of the parent (eg. left), check if the opposing side of the window (eg.
- // right) is at least overlap pixels away. If less, offset the window by that difference.
- int horizontalDiff = 0;
- // If window is smaller than overlap, use it's smallest dimension instead
- int overlapLR = Math.min(overlapPxX, bounds.width());
- if (bounds.right < (rootTaskBounds.left + overlapLR)) {
- horizontalDiff = overlapLR - (bounds.right - rootTaskBounds.left);
- } else if (bounds.left > (rootTaskBounds.right - overlapLR)) {
- horizontalDiff = -(overlapLR - (rootTaskBounds.right - bounds.left));
- }
- int verticalDiff = 0;
- int overlapTB = Math.min(overlapPxY, bounds.width());
- if (bounds.bottom < (rootTaskBounds.top + overlapTB)) {
- verticalDiff = overlapTB - (bounds.bottom - rootTaskBounds.top);
- } else if (bounds.top > (rootTaskBounds.bottom - overlapTB)) {
- verticalDiff = -(overlapTB - (rootTaskBounds.bottom - bounds.top));
- }
- bounds.offset(horizontalDiff, verticalDiff);
- }
-
- /**
- * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
- * intersectBounds on a side, then the respective side will not be intersected.
- *
- * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
- * inset on that side is no-longer applicable. This scenario happens when a task's minimal
- * bounds are larger than the provided parent/display bounds.
- *
- * @param inOutBounds the bounds to intersect.
- * @param intersectBounds the bounds to intersect with.
- * @param intersectInsets insets to apply to intersectBounds before intersecting.
- */
- static void intersectWithInsetsIfFits(
- Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
- if (inOutBounds.right <= intersectBounds.right) {
- inOutBounds.right =
- Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
- }
- if (inOutBounds.bottom <= intersectBounds.bottom) {
- inOutBounds.bottom =
- Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
- }
- if (inOutBounds.left >= intersectBounds.left) {
- inOutBounds.left =
- Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
- }
- if (inOutBounds.top >= intersectBounds.top) {
- inOutBounds.top =
- Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
- }
- }
-
- /**
- * Gets bounds with non-decor and stable insets applied respectively.
- *
- * If bounds overhangs the display, those edges will not get insets. See
- * {@link #intersectWithInsetsIfFits}
- *
- * @param outNonDecorBounds where to place bounds with non-decor insets applied.
- * @param outStableBounds where to place bounds with stable insets applied.
- * @param bounds the bounds to inset.
- */
- private void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
- DisplayInfo displayInfo) {
- outNonDecorBounds.set(bounds);
- outStableBounds.set(bounds);
- final Task rootTask = getRootTask();
- if (rootTask == null || rootTask.mDisplayContent == null) {
- return;
- }
- mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
-
- final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
- policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
- displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
- intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
-
- policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
- intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
- }
-
- /**
- * Forces the app bounds related configuration can be computed by
- * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
- * ActivityRecord.CompatDisplayInsets)}.
- */
- private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
- final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (appBounds != null) {
- appBounds.setEmpty();
- }
- inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
- inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
- if (overrideDisplayInfo != null) {
- // Make sure the screen related configs can be computed by the provided display info.
- inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig) {
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- null /* compatInsets */);
- }
-
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- if (compatInsets != null) {
- // Make sure the app bounds can be computed by the compat insets.
- invalidateAppBoundsConfig(inOutConfig);
- }
- computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
- compatInsets);
- }
-
- /**
- * Calculates configuration values used by the client to get resources. This should be run
- * using app-facing bounds (bounds unmodified by animations or transient interactions).
- *
- * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
- * configuring an "inherit-bounds" window which means that all configuration settings would
- * just be inherited from the parent configuration.
- **/
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
- @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
- @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
- int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = parentConfig.windowConfiguration.getWindowingMode();
- }
-
- float density = inOutConfig.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = parentConfig.densityDpi;
- }
- density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
-
- // The bounds may have been overridden at this level. If the parent cannot cover these
- // bounds, the configuration is still computed according to the override bounds.
- final boolean insideParentBounds;
-
- final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
- final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
- if (resolvedBounds == null || resolvedBounds.isEmpty()) {
- mTmpFullBounds.set(parentBounds);
- insideParentBounds = true;
- } else {
- mTmpFullBounds.set(resolvedBounds);
- insideParentBounds = parentBounds.contains(resolvedBounds);
- }
-
- // Non-null compatibility insets means the activity prefers to keep its original size, so
- // out bounds doesn't need to be restricted by the parent or current display
- final boolean customContainerPolicy = compatInsets != null;
-
- Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds == null || outAppBounds.isEmpty()) {
- // App-bounds hasn't been overridden, so calculate a value for it.
- inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
- outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-
- if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
- final Rect containingAppBounds;
- if (insideParentBounds) {
- containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
- } else {
- // Restrict appBounds to display non-decor rather than parent because the
- // override bounds are beyond the parent. Otherwise, it won't match the
- // overridden bounds.
- final TaskDisplayArea displayArea = getDisplayArea();
- containingAppBounds = displayArea != null
- ? displayArea.getWindowConfiguration().getAppBounds() : null;
- }
- if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
- outAppBounds.intersect(containingAppBounds);
- }
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
- || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- } else if (!customContainerPolicy
- && (overrideDisplayInfo != null || getDisplayContent() != null)) {
- final DisplayInfo di = overrideDisplayInfo != null
- ? overrideDisplayInfo
- : getDisplayContent().getDisplayInfo();
-
- // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
- // area, i.e. the screen area without the system bars.
- // The non decor inset are areas that could never be removed in Honeycomb. See
- // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
- calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
- } else {
- // Apply the given non-decor and stable insets to calculate the corresponding bounds
- // for screen size of configuration.
- int rotation = inOutConfig.windowConfiguration.getRotation();
- if (rotation == ROTATION_UNDEFINED) {
- rotation = parentConfig.windowConfiguration.getRotation();
- }
- if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
- mTmpNonDecorBounds.set(mTmpFullBounds);
- mTmpStableBounds.set(mTmpFullBounds);
- compatInsets.getBoundsByRotation(mTmpBounds, rotation);
- intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
- compatInsets.mNonDecorInsets[rotation]);
- intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
- compatInsets.mStableInsets[rotation]);
- outAppBounds.set(mTmpNonDecorBounds);
- } else {
- // Set to app bounds because it excludes decor insets.
- mTmpNonDecorBounds.set(outAppBounds);
- mTmpStableBounds.set(outAppBounds);
- }
- }
-
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
- inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
- : overrideScreenWidthDp;
- }
- if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
- inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
- ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
- : overrideScreenHeightDp;
- }
-
- if (inOutConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- if (WindowConfiguration.isFloating(windowingMode)) {
- // For floating tasks, calculate the smallest width from the bounds of the task
- inOutConfig.smallestScreenWidthDp = (int) (
- Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- }
- // otherwise, it will just inherit
- }
- }
-
- if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
- inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- }
- if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
- // For calculating screen layout, we need to use the non-decor inset screen area for the
- // calculation for compatibility reasons, i.e. screen area without system bars that
- // could never go away in Honeycomb.
- int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
- int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
- // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
- // undefined so it can't be used.
- if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- compatScreenWidthDp = inOutConfig.screenWidthDp;
- }
- if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- compatScreenHeightDp = inOutConfig.screenHeightDp;
- }
- // Reducing the screen layout starting from its parent config.
- inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
- compatScreenWidthDp, compatScreenHeightDp);
- }
- }
-
- /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
- static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
- int screenHeightDp) {
- sourceScreenLayout = sourceScreenLayout
- & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
- final int longSize = Math.max(screenWidthDp, screenHeightDp);
- final int shortSize = Math.min(screenWidthDp, screenHeightDp);
- return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
- }
-
- @Override
- void resolveOverrideConfiguration(Configuration newParentConfig) {
- mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- super.resolveOverrideConfiguration(newParentConfig);
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
-
- // Resolve override windowing mode to fullscreen for home task (even on freeform
- // display), or split-screen if in split-screen mode.
- if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
- ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
- }
-
- // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
- // pinned windowing mode.
- if (!supportsMultiWindow()) {
- final int candidateWindowingMode =
- windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
- if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
- && candidateWindowingMode != WINDOWING_MODE_PINNED) {
- getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
- WINDOWING_MODE_FULLSCREEN);
- }
- }
-
- if (isLeafTask()) {
- resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
- }
- computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
- }
-
- private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
- Rect previousBounds) {
-
- int windowingMode =
- getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
- 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();
-
- if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
- // Use empty bounds to indicate "fill parent".
- outOverrideBounds.setEmpty();
- // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
- // the parent or display is smaller than the size, the content may be cropped.
- return;
- }
-
- adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- computeFreeformBounds(outOverrideBounds, newParentConfig);
- return;
- }
- }
-
- /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
- private void computeFreeformBounds(@NonNull Rect outBounds,
- @NonNull Configuration newParentConfig) {
- // by policy, make sure the window remains within parent somewhere
- final float density =
- ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
- final Rect parentBounds =
- new Rect(newParentConfig.windowConfiguration.getBounds());
- final DisplayContent display = getDisplayContent();
- if (display != null) {
- // If a freeform window moves below system bar, there is no way to move it again
- // by touch. Because its caption is covered by system bar. So we exclude them
- // from root task bounds. and then caption will be shown inside stable area.
- final Rect stableBounds = new Rect();
- display.getStableRect(stableBounds);
- parentBounds.intersect(stableBounds);
- }
-
- fitWithinBounds(outBounds, parentBounds,
- (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
- (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
-
- // Prevent to overlap caption with stable insets.
- final int offsetTop = parentBounds.top - outBounds.top;
- if (offsetTop > 0) {
- outBounds.offset(0, offsetTop);
- }
- }
-
Rect updateOverrideConfigurationFromLaunchBounds() {
// If the task is controlled by another organized task, do not set override
// configurations and let its parent (organized task) to control it;
@@ -2968,22 +2362,14 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- int getDisplayId() {
- final DisplayContent dc = getDisplayContent();
- return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
- }
-
/** @return Id of root task. */
int getRootTaskId() {
return getRootTask().mTaskId;
}
+ @Nullable
Task getRootTask() {
- final WindowContainer parent = getParent();
- if (parent == null) return this;
-
- final Task parentTask = parent.asTask();
- return parentTask == null ? this : parentTask.getRootTask();
+ return getRootTaskFragment().asTask();
}
/** @return the first organized task. */
@@ -3098,12 +2484,12 @@ class Task extends WindowContainer<WindowContainer> {
// and focused application if needed.
focusableTask.moveToFront(myReason);
// Top display focused root task is changed, update top resumed activity if needed.
- if (rootTask.getResumedActivity() != null) {
+ if (rootTask.getTopResumedActivity() != null) {
mTaskSupervisor.updateTopResumedActivityIfNeeded();
// Set focused app directly because if the next focused activity is already resumed
// (e.g. the next top activity is on a different display), there won't have activity
// state change to update it.
- mAtmService.setResumedActivityUncheckLocked(rootTask.getResumedActivity(), reason);
+ mAtmService.setResumedActivityUncheckLocked(rootTask.getTopResumedActivity(), reason);
}
return rootTask;
}
@@ -3152,17 +2538,16 @@ class Task extends WindowContainer<WindowContainer> {
// Figure-out min/max possible position depending on if child can show for current user.
int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0;
- int maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
- if (!hasChild(wc)) {
- // Increase the maxPosition because children size will grow once wc is added.
- ++maxPosition;
+ int maxPosition = minPosition;
+ if (size > 0) {
+ maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
}
// Factor in always-on-top children in max possible position.
if (!wc.isAlwaysOnTop()) {
// We want to place all non-always-on-top containers below always-on-top ones.
while (maxPosition > minPosition) {
- if (!mChildren.get(maxPosition - 1).isAlwaysOnTop()) break;
+ if (!mChildren.get(maxPosition).isAlwaysOnTop()) break;
--maxPosition;
}
}
@@ -3173,6 +2558,12 @@ class Task extends WindowContainer<WindowContainer> {
} else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
return POSITION_TOP;
}
+
+ // Increase the maxPosition because children size will grow once wc is added.
+ if (!hasChild(wc)) {
+ ++maxPosition;
+ }
+
// Reset position based on minimum/maximum possible positions.
return Math.min(Math.max(suggestedPosition, minPosition), maxPosition);
}
@@ -3193,25 +2584,12 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- @VisibleForTesting
- boolean hasWindowsAlive() {
- return getActivity(ActivityRecord::hasWindowsAlive) != null;
- }
-
- @VisibleForTesting
- boolean shouldDeferRemoval() {
- if (mChildren.isEmpty()) {
- // No reason to defer removal of a Task that doesn't have any child.
- return false;
- }
- return hasWindowsAlive() && getRootTask().isAnimating(TRANSITION | CHILDREN);
- }
-
@Override
void removeImmediately() {
removeImmediately("removeTask");
}
+ @Override
void removeImmediately(String reason) {
if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId);
if (mRemoving) {
@@ -3554,18 +2932,6 @@ class Task extends WindowContainer<WindowContainer> {
mForceShowForAllUsers = forceShowForAllUsers;
}
- @Override
- public boolean isAttached() {
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
- return taskDisplayArea != null && !taskDisplayArea.isRemoved();
- }
-
- @Override
- @Nullable
- TaskDisplayArea getDisplayArea() {
- return (TaskDisplayArea) super.getDisplayArea();
- }
-
/**
* When we are in a floating root task (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen root task
@@ -3576,70 +2942,55 @@ class Task extends WindowContainer<WindowContainer> {
return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
}
- /**
- * Returns true if the root task is translucent and can have other contents visible behind it if
- * needed. A root task is considered translucent if it don't contain a visible or
- * starting (about to be visible) activity that is fullscreen (opaque).
- * @param starting The currently starting activity or null if there is none.
- */
- @VisibleForTesting
- boolean isTranslucent(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return true;
- }
- final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
- PooledLambda.__(ActivityRecord.class), starting);
- final ActivityRecord opaque = getActivity(p);
- p.recycle();
- return opaque == null;
- }
-
- private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
- if (r.finishing) {
- // We don't factor in finishing activities when determining translucency since
- // they will be gone soon.
- return false;
- }
-
- if (!r.visibleIgnoringKeyguard && r != starting) {
- // Also ignore invisible activities that are not the currently starting
- // activity (about to be visible).
- return false;
- }
-
- if (r.occludesParent()) {
- // Root task isn't translucent if it has at least one fullscreen activity
- // that is visible.
- return true;
- }
- return false;
- }
-
/** Returns the top-most activity that occludes the given one, or {@code null} if none. */
@Nullable
ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
- final ActivityRecord top = getActivity(ActivityRecord::occludesParent,
- true /* traverseTopToBottom */, activity);
+ final ActivityRecord top = getActivity(r -> {
+ if (r == activity) {
+ // Reached the given activity, return the activity to stop searching.
+ return true;
+ }
+
+ if (!r.occludesParent()) {
+ return false;
+ }
+
+ TaskFragment parent = r.getTaskFragment();
+ if (parent == activity.getTaskFragment()) {
+ // Found it. This activity on top of the given activity on the same TaskFragment.
+ return true;
+ }
+ if (isSelfOrNonEmbeddedTask(parent.asTask())) {
+ // Found it. This activity is the direct child of a leaf Task without being
+ // embedded.
+ return true;
+ }
+ // The candidate activity is being embedded. Checking if the bounds of the containing
+ // TaskFragment equals to the outer TaskFragment.
+ TaskFragment grandParent = parent.getParent().asTaskFragment();
+ while (grandParent != null) {
+ if (!parent.getBounds().equals(grandParent.getBounds())) {
+ // Not occluding the grandparent.
+ break;
+ }
+ if (isSelfOrNonEmbeddedTask(grandParent.asTask())) {
+ // Found it. The activity occludes its parent TaskFragment and the parent
+ // TaskFragment also occludes its parent all the way up.
+ return true;
+ }
+ parent = grandParent;
+ grandParent = parent.getParent().asTaskFragment();
+ }
+ return false;
+ });
return top != activity ? top : null;
}
- /** Iterates through all occluded activities. */
- void forAllOccludedActivities(Consumer<ActivityRecord> handleOccludedActivity) {
- if (!shouldBeVisible(null /* starting */)) {
- // The root task is invisible so all activities are occluded.
- forAllActivities(handleOccludedActivity);
- return;
- }
- final ActivityRecord topOccluding = getOccludingActivityAbove(null);
- if (topOccluding == null) {
- // No activities are occluded.
- return;
+ private boolean isSelfOrNonEmbeddedTask(Task task) {
+ if (task == this) {
+ return true;
}
- // Invoke the callback on the activities behind the top occluding activity.
- forAllActivities(r -> {
- handleOccludedActivity.accept(r);
- return false;
- }, topOccluding, false /* includeBoundary */, true /* traverseTopToBottom */);
+ return task != null && !task.isEmbedded();
}
@Override
@@ -3691,36 +3042,11 @@ class Task extends WindowContainer<WindowContainer> {
return isAnimating(CHILDREN, ANIMATION_TYPE_RECENTS);
}
- @Override
- RemoteAnimationTarget createRemoteAnimationTarget(
- RemoteAnimationController.RemoteAnimationRecord record) {
- final ActivityRecord activity = getTopMostActivity();
- return activity != null ? activity.createRemoteAnimationTarget(record) : null;
- }
-
- @Override
- boolean canCreateRemoteAnimationTarget() {
- return true;
- }
-
WindowState getTopVisibleAppMainWindow() {
final ActivityRecord activity = getTopVisibleActivity();
return activity != null ? activity.findMainWindow() : null;
}
- ActivityRecord topRunningActivity() {
- return topRunningActivity(false /* focusableOnly */);
- }
-
- ActivityRecord topRunningActivity(boolean focusableOnly) {
- // Split into 2 to avoid object creation due to variable capture.
- if (focusableOnly) {
- return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
- } else {
- return getActivity(ActivityRecord::canBeTopRunning);
- }
- }
-
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
final PooledPredicate p = PooledLambda.obtainPredicate(Task::isTopRunningNonDelayed
, PooledLambda.__(ActivityRecord.class), notTop);
@@ -3775,16 +3101,6 @@ class Task extends WindowContainer<WindowContainer> {
});
}
- boolean isTopActivityFocusable() {
- final ActivityRecord r = topRunningActivity();
- return r != null ? r.isFocusable()
- : (isFocusable() && getWindowConfiguration().canReceiveKeys());
- }
-
- boolean isFocusableAndVisible() {
- return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
- }
-
void positionChildAtTop(ActivityRecord child) {
positionChildAt(child, POSITION_TOP);
}
@@ -3896,6 +3212,41 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
+ /** Iterates through all leaf task fragments and the leaf tasks. */
+ void forAllLeafTasksAndLeafTaskFragments(final Consumer<TaskFragment> callback,
+ boolean traverseTopToBottom) {
+ forAllLeafTasks(task -> {
+ if (task.isLeafTaskFragment()) {
+ callback.accept(task);
+ return;
+ }
+
+ // A leaf task that may contains both activities and task fragments.
+ boolean consumed = false;
+ if (traverseTopToBottom) {
+ for (int i = task.mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = task.mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ } else if (child.asActivityRecord() != null && !consumed) {
+ callback.accept(task);
+ consumed = true;
+ }
+ }
+ } else {
+ for (int i = 0; i < task.mChildren.size(); i++) {
+ final WindowContainer child = task.mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ } else if (child.asActivityRecord() != null && !consumed) {
+ callback.accept(task);
+ consumed = true;
+ }
+ }
+ }
+ }, traverseTopToBottom);
+ }
+
@Override
boolean forAllRootTasks(Function<Task, Boolean> callback, boolean traverseTopToBottom) {
return isRootTask() ? callback.apply(this) : false;
@@ -4015,19 +3366,9 @@ class Task extends WindowContainer<WindowContainer> {
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
- pw.println(prefix + "bounds=" + getBounds().toShortString());
- final String doublePrefix = prefix + " ";
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowContainer<?> child = mChildren.get(i);
- pw.println(prefix + "* " + child);
- // Only dump non-activity because full activity info is already printed by
- // RootWindowContainer#dumpActivities.
- if (child.asActivityRecord() == null) {
- child.dump(pw, doublePrefix, dumpAll);
- }
- }
if (!mExitingActivities.isEmpty()) {
+ final String doublePrefix = prefix + " ";
pw.println();
pw.println(prefix + "Exiting application tokens:");
for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
@@ -4066,6 +3407,9 @@ class Task extends WindowContainer<WindowContainer> {
info.userId = isLeafTask() ? mUserId : mCurrentUser;
info.taskId = mTaskId;
info.displayId = getDisplayId();
+ if (tda != null) {
+ info.displayAreaFeatureId = tda.mFeatureId;
+ }
info.isRunning = getTopNonFinishingActivity() != null;
final Intent baseIntent = getBaseIntent();
// Make a copy of base intent because this is like a snapshot info.
@@ -4125,6 +3469,7 @@ class Task extends WindowContainer<WindowContainer> {
: INVALID_TASK_ID;
info.isFocused = isFocused();
info.isVisible = hasVisibleChildren();
+ info.isSleeping = shouldSleepActivities();
ActivityRecord topRecord = getTopNonFinishingActivity();
info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null;
}
@@ -4135,9 +3480,9 @@ class Task extends WindowContainer<WindowContainer> {
private @Nullable PictureInPictureParams getPictureInPictureParams(Task top) {
if (top == null) return null;
- final ActivityRecord topVisibleActivity = top.getTopVisibleActivity();
- return (topVisibleActivity == null || topVisibleActivity.pictureInPictureArgs.empty())
- ? null : new PictureInPictureParams(topVisibleActivity.pictureInPictureArgs);
+ final ActivityRecord topMostActivity = top.getTopMostActivity();
+ return (topMostActivity == null || topMostActivity.pictureInPictureArgs.empty())
+ ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs);
}
Rect getDisplayCutoutInsets() {
@@ -4204,184 +3549,6 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- boolean shouldBeVisible(ActivityRecord starting) {
- return getVisibility(starting) != TASK_VISIBILITY_INVISIBLE;
- }
-
- /**
- * Returns true if the task should be visible.
- *
- * @param starting The currently starting activity or null if there is none.
- */
- @TaskVisibility
- int getVisibility(ActivityRecord starting) {
- if (!isAttached() || isForceHidden()) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- if (isTopActivityLaunchedBehind()) {
- return TASK_VISIBILITY_VISIBLE;
- }
-
- boolean gotRootSplitScreenTask = false;
- boolean gotOpaqueSplitScreenPrimary = false;
- boolean gotOpaqueSplitScreenSecondary = false;
- boolean gotTranslucentFullscreen = false;
- boolean gotTranslucentSplitScreenPrimary = false;
- boolean gotTranslucentSplitScreenSecondary = false;
- boolean shouldBeVisible = true;
-
- // This root task is only considered visible if all its parent root tasks are considered
- // visible, so check the visibility of all ancestor root task first.
- final WindowContainer parent = getParent();
- if (parent.asTask() != null) {
- final int parentVisibility = parent.asTask().getVisibility(starting);
- if (parentVisibility == TASK_VISIBILITY_INVISIBLE) {
- // Can't be visible if parent isn't visible
- return TASK_VISIBILITY_INVISIBLE;
- } else if (parentVisibility == TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
- // Parent is behind a translucent container so the highest visibility this container
- // can get is that.
- gotTranslucentFullscreen = true;
- }
- }
-
- final List<Task> adjacentTasks = new ArrayList<>();
- final int windowingMode = getWindowingMode();
- final boolean isAssistantType = isActivityTypeAssistant();
- for (int i = parent.getChildCount() - 1; i >= 0; --i) {
- final WindowContainer wc = parent.getChildAt(i);
- final Task other = wc.asTask();
- if (other == null) continue;
-
- final boolean hasRunningActivities = other.topRunningActivity() != null;
- if (other == this) {
- // Should be visible if there is no other stack occluding it, unless it doesn't
- // have any running activities, not starting one and not home stack.
- shouldBeVisible = hasRunningActivities || isInTask(starting) != null
- || isActivityTypeHome();
- break;
- }
-
- if (!hasRunningActivities) {
- continue;
- }
-
- final int otherWindowingMode = other.getWindowingMode();
-
- if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent fullscreen stack.
- gotTranslucentFullscreen = true;
- continue;
- }
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
- && other.matchParentBounds()) {
- if (other.isTranslucent(starting)) {
- // Can be visible behind a translucent task.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Multi-window task that matches parent bounds would occlude other children.
- return TASK_VISIBILITY_INVISIBLE;
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && !gotOpaqueSplitScreenPrimary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
- gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && gotOpaqueSplitScreenPrimary) {
- // Can not be visible behind another opaque stack in split-screen-primary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && !gotOpaqueSplitScreenSecondary) {
- gotRootSplitScreenTask = true;
- gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
- gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && gotOpaqueSplitScreenSecondary) {
- // Can not be visible behind another opaque stack in split-screen-secondary mode.
- return TASK_VISIBILITY_INVISIBLE;
- }
- }
- if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
- // Can not be visible if we are in split-screen windowing mode and both halves of
- // the screen are opaque.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (isAssistantType && gotRootSplitScreenTask) {
- // Assistant stack can't be visible behind split-screen. In addition to this not
- // making sense, it also works around an issue here we boost the z-order of the
- // assistant window surfaces in window manager whenever it is visible.
- return TASK_VISIBILITY_INVISIBLE;
- }
- if (other.mAdjacentTask != null) {
- if (adjacentTasks.contains(other.mAdjacentTask)) {
- if (other.isTranslucent(starting)
- || other.mAdjacentTask.isTranslucent(starting)) {
- // Can be visible behind a translucent adjacent tasks.
- gotTranslucentFullscreen = true;
- continue;
- }
- // Can not be visible behind adjacent tasks.
- return TASK_VISIBILITY_INVISIBLE;
- } else {
- adjacentTasks.add(other);
- }
- }
- }
-
- if (!shouldBeVisible) {
- return TASK_VISIBILITY_INVISIBLE;
- }
-
- // Handle cases when there can be a translucent split-screen stack on top.
- switch (windowingMode) {
- case WINDOWING_MODE_FULLSCREEN:
- if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
- // At least one of the split-screen stacks that covers this one is translucent.
- // When in split mode, home task will be reparented to the secondary split while
- // leaving tasks not supporting split below. Due to
- // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
- // the bottom, this makes sure tasks not in split roots won't occlude home task
- // unexpectedly.
- return TASK_VISIBILITY_INVISIBLE;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
- if (gotTranslucentSplitScreenPrimary) {
- // Covered by translucent primary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
- if (gotTranslucentSplitScreenSecondary) {
- // Covered by translucent secondary split-screen on top.
- return TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
- }
- break;
- }
-
- // Lastly - check if there is a translucent fullscreen stack on top.
- return gotTranslucentFullscreen ? TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
- : TASK_VISIBILITY_VISIBLE;
- }
-
- private boolean isTopActivityLaunchedBehind() {
- final ActivityRecord top = topRunningActivity();
- if (top != null && top.mLaunchTaskBehind) {
- return true;
- }
- return false;
- }
-
ActivityRecord isInTask(ActivityRecord r) {
if (r == null) {
return null;
@@ -4492,9 +3659,6 @@ class Task extends WindowContainer<WindowContainer> {
pw.print(" isResizeable="); pw.println(isResizeable());
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
- if (mForceNotOrganized) {
- pw.print(prefix); pw.println("mForceNotOrganized=true");
- }
}
@Override
@@ -4511,6 +3675,8 @@ class Task extends WindowContainer<WindowContainer> {
}
sb.append(" visible=");
sb.append(shouldBeVisible(null /* starting */));
+ sb.append(" visibleRequested=");
+ sb.append(isVisibleRequested());
sb.append(" mode=");
sb.append(windowingModeToString(getWindowingMode()));
sb.append(" translucent=");
@@ -4564,7 +3730,7 @@ class Task extends WindowContainer<WindowContainer> {
// Increment the total number of non-finishing activities
numActivities++;
- if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+ if (top == null || (top.isState(INITIALIZING))) {
top = r;
// Reset the number of running activities until we hit the first non-initializing
// activity
@@ -4968,10 +4134,6 @@ class Task extends WindowContainer<WindowContainer> {
}
private boolean canBeOrganized() {
- if (mForceNotOrganized || !mAtmService.mTaskOrganizerController
- .isSupportedWindowingMode(getWindowingMode())) {
- return false;
- }
// All root tasks can be organized
if (isRootTask()) {
return true;
@@ -5124,9 +4286,8 @@ class Task extends WindowContainer<WindowContainer> {
return setTaskOrganizer(null);
}
- final int windowingMode = getWindowingMode();
final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController;
- final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
+ final ITaskOrganizer organizer = controller.getTaskOrganizer();
if (!forceUpdate && mTaskOrganizer == organizer) {
return false;
}
@@ -5145,10 +4306,10 @@ class Task extends WindowContainer<WindowContainer> {
* @return true if the task is currently focused.
*/
private boolean isFocused() {
- if (mDisplayContent == null || mDisplayContent.mCurrentFocus == null) {
+ if (mDisplayContent == null || mDisplayContent.mFocusedApp == null) {
return false;
}
- return mDisplayContent.mCurrentFocus.getTask() == this;
+ return mDisplayContent.mFocusedApp.getTask() == this;
}
/**
@@ -5205,10 +4366,9 @@ class Task extends WindowContainer<WindowContainer> {
* Called on the task of a window which gained or lost focus.
* @param hasFocus
*/
- void onWindowFocusChanged(boolean hasFocus) {
+ void onAppFocusChanged(boolean hasFocus) {
updateShadowsRadius(hasFocus, getSyncTransaction());
- // TODO(b/180525887): Un-comment once there is resolution on the bug.
- // dispatchTaskInfoChangedIfNeeded(false /* force */);
+ dispatchTaskInfoChangedIfNeeded(false /* force */);
}
void onPictureInPictureParamsChanged() {
@@ -5307,9 +4467,7 @@ class Task extends WindowContainer<WindowContainer> {
return super.isAlwaysOnTop();
}
- /**
- * Returns whether this task is currently forced to be hidden for any reason.
- */
+ @Override
protected boolean isForceHidden() {
return mForceHiddenFlags != 0;
}
@@ -5454,7 +4612,8 @@ class Task extends WindowContainer<WindowContainer> {
// From fullscreen to PiP.
if (topActivity != null && currentMode == WINDOWING_MODE_FULLSCREEN
- && windowingMode == WINDOWING_MODE_PINNED) {
+ && windowingMode == WINDOWING_MODE_PINNED
+ && !mAtmService.getTransitionController().isShellTransitionsEnabled()) {
mDisplayContent.mPinnedTaskController
.deferOrientationChangeForEnteringPipFromFullScreenIfNeeded();
}
@@ -5462,8 +4621,10 @@ class Task extends WindowContainer<WindowContainer> {
mAtmService.continueWindowLayout();
}
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- mRootWindowContainer.resumeFocusedTasksTopActivities();
+ if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
+ mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
}
void resumeNextFocusAfterReparent() {
@@ -5595,19 +4756,6 @@ class Task extends WindowContainer<WindowContainer> {
r.completeResumeLocked();
}
- void awakeFromSleepingLocked() {
- if (!isLeafTask()) {
- forAllLeafTasks((task) -> task.awakeFromSleepingLocked(),
- true /* traverseTopToBottom */);
- return;
- }
-
- if (mPausingActivity != null) {
- Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
- mPausingActivity.activityPaused(true);
- }
- }
-
void checkReadyForSleep() {
if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
mTaskSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -5626,302 +4774,13 @@ class Task extends WindowContainer<WindowContainer> {
* the process of going to sleep (checkReadyForSleep will be called when that process finishes).
*/
boolean goToSleepIfPossible(boolean shuttingDown) {
- if (!isLeafTask()) {
- final int[] sleepInProgress = {0};
- forAllLeafTasks((t) -> {
- if (!t.goToSleepIfPossible(shuttingDown)) {
- sleepInProgress[0]++;
- }
- }, true);
- return sleepInProgress[0] == 0;
- }
-
- boolean shouldSleep = true;
- if (mResumedActivity != null) {
- // Still have something resumed; can't sleep until it is paused.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
- if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
- "Sleep => pause with userLeaving=false");
-
- startPausingLocked(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
- "sleep");
- shouldSleep = false ;
- } else if (mPausingActivity != null) {
- // Still waiting for something to pause; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
- shouldSleep = false;
- }
-
- if (!shuttingDown) {
- if (containsActivityFromRootTask(mTaskSupervisor.mStoppingActivities)) {
- // Still need to tell some activities to stop; can't sleep yet.
- ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
- mTaskSupervisor.mStoppingActivities.size());
-
- mTaskSupervisor.scheduleIdle();
- shouldSleep = false;
- }
- }
-
- if (shouldSleep) {
- ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- !PRESERVE_WINDOWS);
- }
-
- return shouldSleep;
- }
-
- private boolean containsActivityFromRootTask(List<ActivityRecord> rs) {
- for (ActivityRecord r : rs) {
- if (r.getRootTask() == this) {
- return true;
- }
- }
- return false;
- }
-
- final boolean startPausingLocked(boolean uiSleeping, ActivityRecord resuming, String reason) {
- return startPausingLocked(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
- }
-
- /**
- * Start pausing the currently resumed activity. It is an error to call this if there
- * is already an activity being paused or there is no resumed activity.
- *
- * @param userLeaving True if this should result in an onUserLeaving to the current activity.
- * @param uiSleeping True if this is happening with the user interface going to sleep (the
- * screen turning off).
- * @param resuming The activity we are currently trying to resume or null if this is not being
- * called as part of resuming the top activity, so we shouldn't try to instigate
- * a resume here if not null.
- * @param reason The reason of pausing the activity.
- * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
- * it to tell us when it is done.
- */
- final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
- ActivityRecord resuming, String reason) {
- if (!isLeafTask()) {
- final int[] pausing = {0};
- forAllLeafTasks((t) -> {
- if (t.startPausingLocked(userLeaving, uiSleeping, resuming, reason)) {
- pausing[0]++;
- }
- }, true /* traverseTopToBottom */);
- return pausing[0] > 0;
- }
-
- if (mPausingActivity != null) {
- Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
- + " state=" + mPausingActivity.getState());
- if (!shouldSleepActivities()) {
- // Avoid recursion among check for sleep and complete pause during sleeping.
- // Because activity will be paused immediately after resume, just let pause
- // be completed by the order of activity paused from clients.
- completePauseLocked(false, resuming);
- }
- }
- ActivityRecord prev = mResumedActivity;
-
- if (prev == null) {
- if (resuming == null) {
- Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- return false;
- }
-
- if (prev == resuming) {
- Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
- return false;
- }
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
- mPausingActivity = prev;
- mLastPausedActivity = prev;
- if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
- mTaskSupervisor.mNoHistoryActivities.add(prev);
- }
- prev.setState(PAUSING, "startPausingLocked");
- prev.getTask().touchActiveTime();
-
- mAtmService.updateCpuStats();
-
- boolean pauseImmediately = false;
- boolean shouldAutoPip = false;
- if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
- // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
- // activity to be paused, while at the same time resuming the new resume activity
- // only if the previous activity can't go into Pip since we want to give Pip
- // activities a chance to enter Pip before resuming the next activity.
- final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
- "shouldResumeWhilePausing", userLeaving);
- if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
- shouldAutoPip = true;
- } else if (!lastResumedCanPip) {
- pauseImmediately = true;
- } else {
- // The previous activity may still enter PIP even though it did not allow auto-PIP.
- }
- }
-
- boolean didAutoPip = false;
- if (prev.attachedToProcess()) {
- if (shouldAutoPip) {
- ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
- + "directly: %s", prev);
-
- didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
- mPausingActivity = null;
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
- }
- } else {
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
-
- // If we are not going to sleep, we want to ensure the device is
- // awake until the next activity is started.
- if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
- mTaskSupervisor.acquireLaunchWakelock();
- }
-
- // 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
- // key dispatch; the same activity will pick it up again on wakeup.
- if (!uiSleeping) {
- prev.pauseKeyDispatchingLocked();
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
- }
-
- if (pauseImmediately) {
- // If the caller said they don't want to wait for the pause, then complete
- // the pause now.
- completePauseLocked(false, resuming);
- return false;
-
- } else {
- prev.schedulePauseTimeout();
- return true;
- }
-
- } else {
- // 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();
+ final int[] sleepInProgress = {0};
+ forAllLeafTasksAndLeafTaskFragments(taskFragment -> {
+ if (!taskFragment.sleepIfPossible(shuttingDown)) {
+ sleepInProgress[0]++;
}
- return false;
- }
- }
-
- @VisibleForTesting
- void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
- // Complete the pausing process of a pausing activity, so it doesn't make sense to
- // operate on non-leaf tasks.
- warnForNonLeafTask("completePauseLocked");
-
- ActivityRecord prev = mPausingActivity;
- ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
-
- if (prev != null) {
- prev.setWillCloseOrEnterPip(false);
- final boolean wasStopping = prev.isState(STOPPING);
- prev.setState(PAUSED, "completePausedLocked");
- if (prev.finishing) {
- // We will update the activity visibility later, no need to do in
- // completeFinishing(). Updating visibility here might also making the next
- // activities to be resumed, and could result in wrong app transition due to
- // lack of previous activity information.
- ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
- prev = prev.completeFinishing(false /* updateVisibility */,
- "completePausedLocked");
- } else if (prev.hasProcess()) {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
- + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
- prev.mVisibleRequested);
- if (prev.deferRelaunchUntilPaused) {
- // Complete the deferred relaunch that was waiting for pause to complete.
- ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
- prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
- } else if (wasStopping) {
- // We are also stopping, the stop request must have gone soon after the pause.
- // We can't clobber it, because the stop confirmation will not be handled.
- // We don't need to schedule another stop, we only need to let it happen.
- prev.setState(STOPPING, "completePausedLocked");
- } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
- // Clear out any deferred client hide we might currently have.
- prev.setDeferHidingClient(false);
- // If we were visible then resumeTopActivities will release resources before
- // stopping.
- prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
- "completePauseLocked");
- }
- } else {
- ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
- prev = null;
- }
- // It is possible the activity was freezing the screen before it was paused.
- // In that case go ahead and remove the freeze this activity has on the screen
- // since it is no longer visible.
- if (prev != null) {
- prev.stopFreezingScreenLocked(true /*force*/);
- }
- mPausingActivity = null;
- }
-
- if (resumeNext) {
- final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
- if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
- mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev, null);
- } else {
- checkReadyForSleep();
- final ActivityRecord top =
- topRootTask != null ? topRootTask.topRunningActivity() : null;
- if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run, do resume anyway to start
- // something. Also if the top activity on the root task is not the just paused
- // activity, we need to go ahead and resume it to ensure we complete an
- // in-flight app switch.
- mRootWindowContainer.resumeFocusedTasksTopActivities();
- }
- }
- }
-
- if (prev != null) {
- prev.resumeKeyDispatchingLocked();
- }
-
- mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
-
- // Notify when the task stack has changed, but only if visibilities changed (not just
- // focus). Also if there is an active root pinned task - we always want to notify it about
- // task stack changes, because its positioning may depend on it.
- if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
- || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
- mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
- mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
- }
+ }, true /* traverseTopToBottom */);
+ return sleepInProgress[0] == 0;
}
boolean isTopRootTaskInDisplayArea() {
@@ -5944,10 +4803,10 @@ class Task extends WindowContainer<WindowContainer> {
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*
*/
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
@@ -5963,21 +4822,22 @@ class Task extends WindowContainer<WindowContainer> {
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param notifyClients Flag indicating whether the visibility updates should be sent to the
- * clients in {@link mEnsureActivitiesVisibleHelper}.
+ * clients in {@link EnsureActivitiesVisibleHelper}.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
- * configuration in {@link mEnsureActivitiesVisibleHelper}.
+ * configuration in {@link EnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
- * {@link mEnsureActivitiesVisibleHelper}.
+ * {@link EnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a root task in most cases.
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mTaskSupervisor.beginActivityVisibilityUpdate();
try {
- forAllLeafTasks(task -> task.mEnsureActivitiesVisibleHelper.process(
- starting, configChanges, preserveWindows, notifyClients),
- true /* traverseTopToBottom */);
+ forAllLeafTasks(task -> {
+ task.updateActivityVisibilities(starting, configChanges, preserveWindows,
+ notifyClients);
+ }, true /* traverseTopToBottom */);
// Notify WM shell that task visibilities may have changed
forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
@@ -6052,25 +4912,6 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /** @see ActivityRecord#cancelInitializing() */
- void cancelInitializingActivities() {
- // We don't want to clear starting window for activities that aren't behind fullscreen
- // activities as we need to display their starting window until they are done initializing.
- checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
- }
-
- /**
- * If an activity {@param toCheck} is given, this method returns {@code true} if the activity
- * is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
- * function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
- * activities to the function.
- */
- boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
- Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- return mCheckBehindFullscreenActivityHelper.process(
- toCheck, handleBehindFullscreenActivity);
- }
-
/**
* Ensure that the top activity in the root task is resumed.
*
@@ -6111,7 +4952,8 @@ class Task extends WindowContainer<WindowContainer> {
if (!child.isTopActivityFocusable()) {
continue;
}
- if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
+ if (child.getVisibility(null /* starting */)
+ != TASK_FRAGMENT_VISIBILITY_VISIBLE) {
break;
}
@@ -6158,383 +5000,26 @@ class Task extends WindowContainer<WindowContainer> {
return false;
}
- // Find the next top-most activity to resume in this root task that is not finishing and is
- // focusable. If it is not focusable, we will fall into the case below to resume the
- // top activity in the next focusable task.
- ActivityRecord next = topRunningActivity(true /* focusableOnly */);
-
- final boolean hasRunningActivity = next != null;
-
- // TODO: Maybe this entire condition can get removed?
- if (hasRunningActivity && !isAttached()) {
- return false;
- }
-
- mRootWindowContainer.cancelInitializingActivities();
-
- if (!hasRunningActivity) {
- // There are no activities left in the root task, let's look somewhere else.
+ final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */);
+ if (topActivity == null) {
+ // There are no activities left in this task, let's look somewhere else.
return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
}
- next.delayedResume = false;
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
- // If the top activity is the resumed one, nothing to do.
- if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
- // we still want to check if the visibility of other windows have changed (e.g. bringing
- // a fullscreen window forward to cover another freeform activity.)
- if (taskDisplayArea.inMultiWindowMode()) {
- taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
- false /* preserveWindows */, true /* notifyClients */);
- }
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity "
- + "resumed %s", next);
- return false;
- }
-
- if (!next.canResumeByCompat()) {
- return false;
- }
-
- // If we are currently pausing an activity, then don't do anything until that is done.
- final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
- if (!allPausedComplete) {
- ProtoLog.v(WM_DEBUG_STATES,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- return false;
- }
-
- // If we are sleeping, and there is no resumed activity, and the top activity is paused,
- // well that is the state we want.
- if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Going to sleep and"
- + " all paused");
- return false;
- }
-
- // Make sure that the user who owns this activity is started. If not,
- // we will just leave it as is because someone should be bringing
- // another user's activities to the top of the stack.
- if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
- Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.mUserId + " is stopped");
- return false;
- }
-
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mTaskSupervisor.mStoppingActivities.remove(next);
-
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
-
- mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
-
- ActivityRecord lastResumed = null;
- final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
- if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTask()) {
- // So, why aren't we using prev here??? See the param comment on the method. prev
- // doesn't represent the last resumed activity. However, the last focus stack does if
- // it isn't null.
- lastResumed = lastFocusedRootTask.getResumedActivity();
- }
-
- boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
- if (mResumedActivity != null) {
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity);
- pausing |= startPausingLocked(false /* uiSleeping */, next,
- "resumeTopActivityInnerLocked");
- }
- if (pausing) {
- ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivityLocked: Skip resume: need to"
- + " start pausing");
- // At this point we want to put the upcoming activity's process
- // at the top of the LRU list, since we know we will be needing it
- // very soon and it would be a waste to let it get killed if it
- // happens to be sitting towards the end.
- if (next.attachedToProcess()) {
- next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
- true /* activityChange */, false /* updateOomAdj */,
- false /* addPendingTopUid */);
- } else if (!next.isProcessRunning()) {
- // Since the start-process is asynchronous, if we already know the process of next
- // activity isn't running, we can start the process earlier to save the time to wait
- // for the current activity to be paused.
- final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
- mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
- isTop ? "pre-top-activity" : "pre-activity");
- }
- if (lastResumed != null) {
- lastResumed.setWillCloseOrEnterPip(true);
- }
- return true;
- } else if (mResumedActivity == next && next.isState(RESUMED)
- && taskDisplayArea.allResumedActivitiesComplete()) {
- // It is possible for the activity to be resumed when we paused back stacks above if the
- // next activity doesn't have to wait for pause to complete.
- // So, nothing else to-do except:
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- executeAppTransition(options);
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Top activity resumed "
- + "(dontWaitForPause) %s", next);
- return true;
- }
-
- // If the most recent activity was noHistory but was only stopped rather
- // than stopped+finished because the device went to sleep, we need to make
- // sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities()) {
- mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
- }
-
- if (prev != null && prev != next && next.nowVisible) {
-
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- prev.setVisibility(false);
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Not waiting for visible to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Previous already visible but still waiting to hide: " + prev
- + ", nowVisible=" + next.nowVisible);
- }
-
- }
-
- // Launching this app's activity, make sure the app is no longer
- // considered stopped.
- try {
- mTaskSupervisor.getActivityMetricsLogger()
- .notifyBeforePackageUnstopped(next.packageName);
- mAtmService.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
- } catch (RemoteException e1) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + next.packageName + ": " + e);
- }
-
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- boolean anim = true;
- final DisplayContent dc = taskDisplayArea.mDisplayContent;
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare close transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_CLOSE);
- }
- prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
- "Prepare open transition: prev=" + prev);
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN,
- next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
- }
- }
-
- if (anim) {
- next.applyOptionsAnimation();
- } else {
- next.abortAndClearOptionsAnimation();
- }
-
- mTaskSupervisor.mNoAnimActivities.clear();
-
- if (next.attachedToProcess()) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next
- + " stopped=" + next.stopped
- + " visibleRequested=" + next.mVisibleRequested);
-
- // If the previous activity is translucent, force a visibility update of
- // the next activity, so that it's added to WM's opening app list, and
- // transition animation can be set up properly.
- // For example, pressing Home button with a translucent activity in focus.
- // Launcher is already visible in this case. If we don't add it to opening
- // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
- // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
- final boolean lastActivityTranslucent = lastFocusedRootTask != null
- && (lastFocusedRootTask.inMultiWindowMode()
- || (lastFocusedRootTask.mLastPausedActivity != null
- && !lastFocusedRootTask.mLastPausedActivity.occludesParent()));
-
- // This activity is now becoming visible.
- if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
- next.setVisibility(true);
- }
-
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
-
- ActivityRecord lastResumedActivity =
- lastFocusedRootTask == null ? null : lastFocusedRootTask.getResumedActivity();
- final ActivityState lastState = next.getState();
-
- mAtmService.updateCpuStats();
-
- ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
-
- next.setState(RESUMED, "resumeTopActivityInnerLocked");
-
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
-
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
- // We have special rotation behavior when here is some active activity that
- // requests specific orientation or Keyguard is locked. Make sure all activity
- // visibilities are set correctly as well as the transition is updated if needed
- // to get the correct rotation behavior. Otherwise the following call to update
- // the orientation may cause incorrect configurations delivered to client as a
- // result of invisible window resize.
- // TODO: Remove this once visibilities are set correctly immediately when
- // starting an activity.
- notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
-
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivity();
- ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
- + "%s, new next: %s", next, nextNext);
- if (nextNext != next) {
- // Do over!
- mTaskSupervisor.scheduleResumeTopActivities();
- }
- if (!next.mVisibleRequested || next.stopped) {
- next.setVisibility(true);
- }
- next.completeResumeLocked();
- return true;
- }
-
- try {
- final ClientTransaction transaction =
- ClientTransaction.obtain(next.app.getThread(), next.appToken);
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > 0) {
- if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
- "Delivering results to " + next + ": " + a);
- transaction.addCallback(ActivityResultItem.obtain(a));
- }
- }
-
- if (next.newIntents != null) {
- transaction.addCallback(
- NewIntentItem.obtain(next.newIntents, true /* resume */));
- }
-
- // Well the app will no longer be stopped.
- // Clear app token stopped state in window manager if needed.
- next.notifyAppResumed(next.stopped);
-
- EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
- next.getTask().mTaskId, next.shortComponentName);
-
- mAtmService.getAppWarningsLocked().onResumeActivity(next);
- next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
- next.abortAndClearOptionsAnimation();
- transaction.setLifecycleStateRequest(
- ResumeActivityItem.obtain(next.app.getReportedProcState(),
- dc.isNextTransitionForward()));
- mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
- + "%s", lastState, next);
- next.setState(lastState, "resumeTopActivityInnerLocked");
-
- // lastResumedActivity being non-null implies there is a lastStack present.
- if (lastResumedActivity != null) {
- lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
- }
-
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
- && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
- next.showStartingWindow(false /* taskSwitch */);
- }
- mTaskSupervisor.startSpecificActivity(next, true, false);
- return true;
- }
-
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.completeResumeLocked();
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- next.finishIfPossible("resume-exception", true /* oomAdj */);
- return true;
+ final boolean[] resumed = new boolean[1];
+ final TaskFragment topFragment = topActivity.getTaskFragment();
+ resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
+ forAllLeafTaskFragments(f -> {
+ if (topFragment == f) {
+ return;
}
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW) {
- next.showStartingWindow(false /* taskSwich */);
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+ if (!f.isFocusableAndVisible()) {
+ // No need to resume activity in TaskFragment that is not visible.
+ return;
}
- ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Restarting %s", next);
- mTaskSupervisor.startSpecificActivity(next, true, true);
- }
-
- return true;
+ resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
+ }, true);
+ return resumed[0];
}
/**
@@ -6568,7 +5053,7 @@ class Task extends WindowContainer<WindowContainer> {
}
void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
- boolean newTask, boolean keepCurTransition, ActivityOptions options,
+ boolean newTask, boolean isTaskSwitch, ActivityOptions options,
@Nullable ActivityRecord sourceRecord) {
Task rTask = r.getTask();
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
@@ -6614,7 +5099,6 @@ class Task extends WindowContainer<WindowContainer> {
// Slot the activity into the history root task and proceed
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+ "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
- task.positionChildAtTop(r);
// The transition animation and starting window are not needed if {@code allowMoveToFront}
// is false, because the activity won't be visible.
@@ -6675,21 +5159,18 @@ class Task extends WindowContainer<WindowContainer> {
// "has the same starting icon" as the next one. This allows the
// window manager to keep the previous window it had previously
// created, if it still had one.
- Task prevTask = r.getTask();
- ActivityRecord prev = prevTask.topActivityWithStartingWindow();
- if (prev != null) {
- // We don't want to reuse the previous starting preview if:
- // (1) The current activity is in a different task.
- if (prev.getTask() != prevTask) {
- prev = null;
- }
- // (2) The current activity is already displayed.
- else if (prev.nowVisible) {
- prev = null;
- }
+ Task baseTask = r.getTask();
+ if (baseTask.isEmbedded()) {
+ // If the task is embedded in a task fragment, there may have an existing
+ // starting window in the parent task. This allows the embedded activities
+ // to share the starting window and make sure that the window can have top
+ // z-order by transferring to the top activity.
+ baseTask = baseTask.getParent().asTaskFragment().getTask();
}
- r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),
+ final ActivityRecord prev = baseTask.getActivity(
+ a -> a.mStartingData != null && a.okToShowLocked());
+ r.showStartingWindow(prev, newTask, isTaskSwitch,
true /* startActivity */, sourceRecord);
}
} else {
@@ -6723,10 +5204,6 @@ class Task extends WindowContainer<WindowContainer> {
return true;
}
- private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
- return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
- }
-
/**
* Reset the task by reparenting the activities that have same affinity to the task or
* reparenting the activities that have different affinityies out of the task, while these
@@ -6784,7 +5261,6 @@ class Task extends WindowContainer<WindowContainer> {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
- mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
r.finishIfPossible(reason, false /* oomAdj */);
@@ -6842,18 +5318,6 @@ class Task extends WindowContainer<WindowContainer> {
return true;
}
- /** Finish all activities in the root task without waiting. */
- void finishAllActivitiesImmediately() {
- if (!hasChild()) {
- removeIfPossible("finishAllActivitiesImmediately");
- return;
- }
- forAllActivities((r) -> {
- Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
- r.destroyIfPossible("finishAllActivitiesImmediately");
- });
- }
-
/** @return true if the root task behind this one is a standard activity type. */
private boolean inFrontOfStandardRootTask() {
final TaskDisplayArea taskDisplayArea = getDisplayArea();
@@ -7164,7 +5628,6 @@ class Task extends WindowContainer<WindowContainer> {
// Skip the transition for pinned task.
if (!inPinnedWindowingMode()) {
- mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
}
moveToBack("moveTaskToBackLocked", tr);
@@ -7190,13 +5653,6 @@ class Task extends WindowContainer<WindowContainer> {
return true;
}
- /**
- * Ensures all visible activities at or below the input activity have the right configuration.
- */
- void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
- mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
- }
-
// TODO: Can only be called from special methods in ActivityTaskSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
@@ -7250,114 +5706,32 @@ class Task extends WindowContainer<WindowContainer> {
}
}
- /**
- * Reset local parameters because an app's activity died.
- * @param app The app of the activity that died.
- * @return {@code true} if the process of the pausing activity is died.
- */
- boolean handleAppDied(WindowProcessController app) {
- warnForNonLeafTask("handleAppDied");
- boolean isPausingDied = false;
- if (mPausingActivity != null && mPausingActivity.app == app) {
- ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
- mPausingActivity);
- mPausingActivity = null;
- isPausingDied = true;
- }
- if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
- if (mLastPausedActivity.isNoHistory()) {
- mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
- }
- mLastPausedActivity = null;
- }
- return isPausingDied;
- }
-
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
String dumpPackage, final boolean needSep) {
- Runnable headerPrinter = () -> {
- if (needSep) {
- pw.println();
- }
- pw.println(" RootTask #" + getRootTaskId()
- + ": type=" + activityTypeToString(getActivityType())
- + " mode=" + windowingModeToString(getWindowingMode()));
- pw.println(" isSleeping=" + shouldSleepActivities());
- pw.println(" mBounds=" + getRequestedOverrideBounds());
- pw.println(" mCreatedByOrganizer=" + mCreatedByOrganizer);
- };
-
- boolean printed = false;
+ return dump(" ", fd, pw, dumpAll, dumpClient, dumpPackage, needSep, null /* header */);
+ }
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no tasks/activities inside.
- headerPrinter.run();
- headerPrinter = null;
- printed = true;
+ @Override
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ super.dumpInner(prefix, pw, dumpAll, dumpPackage);
+ if (mCreatedByOrganizer) {
+ pw.println(prefix + " mCreatedByOrganizer=true");
}
-
- printed |= printThisActivity(pw, getPausingActivity(), dumpPackage, false,
- " mPausingActivity: ", null);
- printed |= printThisActivity(pw, getResumedActivity(), dumpPackage, false,
- " mResumedActivity: ", null);
- if (dumpAll) {
- printed |= printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
- " mLastPausedActivity: ", null);
+ if (mLastNonFullscreenBounds != null) {
+ pw.print(prefix); pw.print(" mLastNonFullscreenBounds=");
+ pw.println(mLastNonFullscreenBounds);
}
-
- printed |= dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, false, headerPrinter);
-
- return printed;
- }
-
- private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
- boolean dumpClient, String dumpPackage, boolean needSep, Runnable header) {
- if (!hasChild()) {
- return false;
+ if (isLeafTask()) {
+ pw.println(prefix + " isSleeping=" + shouldSleepActivities());
+ printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
+ prefix + " topPausingActivity=", null);
+ printThisActivity(pw, getTopResumedActivity(), dumpPackage, false,
+ prefix + " topResumedActivity=", null);
+ if (mMinWidth != INVALID_MIN_SIZE || mMinHeight != INVALID_MIN_SIZE) {
+ pw.print(prefix); pw.print(" mMinWidth="); pw.print(mMinWidth);
+ pw.print(" mMinHeight="); pw.println(mMinHeight);
+ }
}
- final AtomicBoolean printedHeader = new AtomicBoolean(false);
- final AtomicBoolean printed = new AtomicBoolean(false);
- forAllLeafTasks((task) -> {
- final String prefix = " ";
- Runnable headerPrinter = () -> {
- printed.set(true);
- if (!printedHeader.get()) {
- if (needSep) {
- pw.println("");
- }
- if (header != null) {
- header.run();
- }
- printedHeader.set(true);
- }
- pw.print(prefix); pw.print("* "); pw.println(task);
- pw.print(prefix); pw.print(" mBounds=");
- pw.println(task.getRequestedOverrideBounds());
- pw.print(prefix); pw.print(" mMinWidth="); pw.print(task.mMinWidth);
- pw.print(" mMinHeight="); pw.println(task.mMinHeight);
- if (mLastNonFullscreenBounds != null) {
- pw.print(prefix);
- pw.print(" mLastNonFullscreenBounds=");
- pw.println(task.mLastNonFullscreenBounds);
- }
- task.dump(pw, prefix + " ");
- };
- if (dumpPackage == null) {
- // If we are not filtering by package, we want to print absolutely everything,
- // so always print the header even if there are no activities inside.
- headerPrinter.run();
- headerPrinter = null;
- }
- final ArrayList<ActivityRecord> activities = new ArrayList<>();
- // Add activities by traversing the hierarchy from bottom to top, since activities
- // are dumped in reverse order in {@link ActivityTaskSupervisor#dumpHistoryList()}.
- task.forAllActivities((Consumer<ActivityRecord>) activities::add,
- false /* traverseTopToBottom */);
- dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
- dumpPackage, false, headerPrinter, task);
- }, true /* traverseTopToBottom */);
- return printed.get();
}
ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -7743,15 +6117,6 @@ class Task extends WindowContainer<WindowContainer> {
getDisplayContent().getPinnedTaskController().setActions(actions);
}
- /** Returns true if a removal action is still being deferred. */
- boolean handleCompleteDeferredRemoval() {
- if (isAnimating(TRANSITION | CHILDREN)) {
- return true;
- }
-
- return super.handleCompleteDeferredRemoval();
- }
-
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
@@ -7760,6 +6125,7 @@ class Task extends WindowContainer<WindowContainer> {
return mAnimatingActivityRegistry;
}
+ @Override
void executeAppTransition(ActivityOptions options) {
mDisplayContent.executeAppTransition();
ActivityOptions.abort(options);
@@ -7782,10 +6148,6 @@ class Task extends WindowContainer<WindowContainer> {
return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
}
- boolean shouldSleepOrShutDownActivities() {
- return shouldSleepActivities() || mAtmService.mShuttingDown;
- }
-
private Rect getRawBounds() {
return super.getBounds();
}
@@ -7804,14 +6166,12 @@ class Task extends WindowContainer<WindowContainer> {
}
final long token = proto.start(fieldId);
- super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(TaskProto.ID, mTaskId);
- proto.write(DISPLAY_ID, getDisplayId());
proto.write(ROOT_TASK_ID, getRootTaskId());
- if (mResumedActivity != null) {
- mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ if (getTopResumedActivity() != null) {
+ getTopResumedActivity().writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
if (realActivity != null) {
proto.write(REAL_ACTIVITY, realActivity.flattenToShortString());
@@ -7819,11 +6179,7 @@ class Task extends WindowContainer<WindowContainer> {
if (origActivity != null) {
proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString());
}
- proto.write(ACTIVITY_TYPE, getActivityType());
proto.write(RESIZE_MODE, mResizeMode);
- proto.write(MIN_WIDTH, mMinWidth);
- proto.write(MIN_HEIGHT, mMinHeight);
-
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().dumpDebug(proto, BOUNDS);
@@ -7840,6 +6196,8 @@ class Task extends WindowContainer<WindowContainer> {
proto.write(AFFINITY, affinity);
proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
+ super.dumpDebug(proto, TASK_FRAGMENT, logLevel);
+
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index d450dbffe4a1..c7ca180bfa14 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -32,13 +32,13 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -113,7 +113,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
- private int mTmpLayerForSplitScreenDividerAnchor;
private int mTmpLayerForAnimationLayer;
private ArrayList<Task> mTmpTasks = new ArrayList<>();
@@ -469,7 +468,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Update the top resumed activity because the preferred top focusable task may be changed.
mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded();
- final ActivityRecord r = child.getResumedActivity();
+ final ActivityRecord r = child.getTopResumedActivity();
if (r != null && r == mRootWindowContainer.getTopResumedActivity()) {
mAtmService.setResumedActivityUncheckLocked(r, "positionChildAt");
}
@@ -781,10 +780,12 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
return SCREEN_ORIENTATION_UNSPECIFIED;
} else {
- // Apps and their containers are not allowed to specify an orientation of full screen
- // tasks created by organizer. The organizer handles the orientation instead.
- final Task task = getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- if (task != null && task.isVisible() && task.mCreatedByOrganizer) {
+ // Apps and their containers are not allowed to specify an orientation of non floating
+ // visible tasks created by organizer. The organizer handles the orientation instead.
+ final Task nonFloatingTopTask =
+ getRootTask(t -> !t.getWindowConfiguration().tasksAreFloating());
+ if (nonFloatingTopTask != null && nonFloatingTopTask.mCreatedByOrganizer
+ && nonFloatingTopTask.isVisible()) {
return SCREEN_ORIENTATION_UNSPECIFIED;
}
}
@@ -854,26 +855,23 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer, false /* normalRootTasks */);
// The home animation layer is between the root home tasks and the normal root tasks.
final int layerForHomeAnimationLayer = layer++;
- mTmpLayerForSplitScreenDividerAnchor = layer++;
mTmpLayerForAnimationLayer = layer++;
layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer, true /* normalRootTasks */);
// The boosted animation layer is between the normal root tasks and the always on top
// root tasks.
final int layerForBoostedAnimationLayer = layer++;
+ // Always on top tasks layer should higher than split divider layer so set it as start.
+ layer = SPLIT_DIVIDER_LAYER + 1;
adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer, false /* normalRootTasks */);
t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
- t.setLayer(mSplitScreenDividerAnchor, mTmpLayerForSplitScreenDividerAnchor);
+ t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
}
private int adjustNormalRootTaskLayer(WindowContainer child, int layer) {
- if (child.asTask() != null && child.inSplitScreenWindowingMode()) {
- // The split screen divider anchor is located above the split screen window.
- mTmpLayerForSplitScreenDividerAnchor = layer++;
- }
if ((child.asTask() != null && child.asTask().isAnimatingByRecents())
|| child.isAppTransitioning()) {
// The animation layer is located above the highest animating root task and no
@@ -1231,7 +1229,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
+ adjacentFlagRootTask);
}
- if (adjacentFlagRootTask.mAdjacentTask == null) {
+ if (adjacentFlagRootTask.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Can't set non-adjacent root as launch adjacent flag root tr="
+ adjacentFlagRootTask);
@@ -1269,8 +1267,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// If the adjacent launch is coming from the same root, launch to adjacent root instead.
if (sourceTask != null
&& sourceTask.getRootTask().mTaskId == mLaunchAdjacentFlagRootTask.mTaskId
- && mLaunchAdjacentFlagRootTask.mAdjacentTask != null) {
- return mLaunchAdjacentFlagRootTask.mAdjacentTask;
+ && mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment() != null) {
+ return mLaunchAdjacentFlagRootTask.getAdjacentTaskFragment().asTask();
} else {
return mLaunchAdjacentFlagRootTask;
}
@@ -1280,8 +1278,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
// Return the focusable root task for improving the UX with staged split screen.
- final Task adjacentRootTask = launchRootTask != null
- ? launchRootTask.mAdjacentTask : null;
+ final TaskFragment adjacentTaskFragment = launchRootTask != null
+ ? launchRootTask.getAdjacentTaskFragment() : null;
+ final Task adjacentRootTask =
+ adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
return adjacentRootTask;
} else {
@@ -1373,11 +1373,11 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
}
// TODO(b/111541062): Move this into Task#getResumedActivity()
// Check if the focused root task has the resumed activity
- ActivityRecord resumedActivity = focusedRootTask.getResumedActivity();
+ ActivityRecord resumedActivity = focusedRootTask.getTopResumedActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If there is no registered resumed activity in the root task or it is not running -
// try to use previously resumed one.
- resumedActivity = focusedRootTask.getPausingActivity();
+ resumedActivity = focusedRootTask.getTopPausingActivity();
if (resumedActivity == null || resumedActivity.app == null) {
// If previously resumed activity doesn't work either - find the topmost running
// activity that can be focused.
@@ -1404,7 +1404,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Clear last paused activity if focused root task changed while sleeping, so that the
// top activity of current focused task can be resumed.
if (mDisplayContent.isSleeping()) {
- currentFocusedTask.mLastPausedActivity = null;
+ currentFocusedTask.clearLastPausedActivity();
}
mLastFocusedRootTask = prevFocusedTask;
@@ -1425,7 +1425,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
continue;
}
- final ActivityRecord r = mChildren.get(i).asTask().getResumedActivity();
+ final ActivityRecord r = mChildren.get(i).asTask().getTopResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
}
@@ -1451,18 +1451,30 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
*/
boolean pauseBackTasks(ActivityRecord resuming) {
final int[] someActivityPaused = {0};
- forAllLeafTasks((task) -> {
- final ActivityRecord resumedActivity = task.getResumedActivity();
- if (resumedActivity != null
- && (task.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE
- || !task.isTopActivityFocusable())) {
- ProtoLog.d(WM_DEBUG_STATES, "pauseBackTasks: task=%s "
- + "mResumedActivity=%s", task, resumedActivity);
- if (task.startPausingLocked(false /* uiSleeping*/,
- resuming, "pauseBackTasks")) {
- someActivityPaused[0]++;
+ forAllLeafTasks(leafTask -> {
+ // Check if the direct child resumed activity in the leaf task needed to be paused if
+ // the leaf task is not a leaf task fragment.
+ if (!leafTask.isLeafTaskFragment()) {
+ final ActivityRecord top = topRunningActivity();
+ final ActivityRecord resumedActivity = leafTask.getResumedActivity();
+ if (resumedActivity != null && top.getTaskFragment() != leafTask) {
+ // Pausing the resumed activity because it is occluded by other task fragment.
+ if (leafTask.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
+ }
}
}
+
+ leafTask.forAllLeafTaskFragments((taskFrag) -> {
+ final ActivityRecord resumedActivity = taskFrag.getResumedActivity();
+ if (resumedActivity != null
+ && (taskFrag.getVisibility(resuming) != TASK_FRAGMENT_VISIBILITY_VISIBLE
+ || !taskFrag.isTopActivityFocusable())) {
+ if (taskFrag.startPausing(false /* uiSleeping*/, resuming, "pauseBackTasks")) {
+ someActivityPaused[0]++;
+ }
+ }
+ }, true /* traverseTopToBottom */);
}, true /* traverseTopToBottom */);
return someActivityPaused[0] > 0;
}
@@ -2089,7 +2101,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
if (destroyContentOnRemoval
|| !task.isActivityTypeStandardOrUndefined()
|| task.mCreatedByOrganizer) {
- task.finishAllActivitiesImmediately();
+ task.remove(false /* withTransition */, "removeTaskDisplayArea");
} else {
// Reparent task to corresponding launch root or display area.
final WindowContainer launchRoot =
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
new file mode 100644
index 000000000000..70b0ccd76474
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -0,0 +1,2221 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.os.UserHandle.USER_NULL;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskSupervisor.printThisActivity;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.IdentifierProto.USER_ID;
+import static com.android.server.wm.TaskFragmentProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskFragmentProto.DISPLAY_ID;
+import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
+import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.ResultInfo;
+import android.app.WindowConfiguration;
+import android.app.servertransaction.ActivityResultItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.NewIntentItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.DisplayInfo;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizerToken;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A basic container that can be used to contain activities or other {@link TaskFragment}, which
+ * also able to manage the activity lifecycle and updates the visibilities of the activities in it.
+ */
+class TaskFragment extends WindowContainer<WindowContainer> {
+ @IntDef(prefix = {"TASK_FRAGMENT_VISIBILITY"}, value = {
+ TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ })
+ @interface TaskFragmentVisibility {}
+
+ /**
+ * TaskFragment is visible. No other TaskFragment(s) on top that fully or partially occlude it.
+ */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE = 0;
+
+ /** TaskFragment is partially occluded by other translucent TaskFragment(s) on top of it. */
+ static final int TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT = 1;
+
+ /** TaskFragment is completely invisible. */
+ static final int TASK_FRAGMENT_VISIBILITY_INVISIBLE = 2;
+
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskFragment" : TAG_ATM;
+ private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+ private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+
+ /** Set to false to disable the preview that is shown while a new activity is being started. */
+ static final boolean SHOW_APP_STARTING_PREVIEW = true;
+
+ /**
+ * Indicate that the minimal width/height should use the default value.
+ *
+ * @see #mMinWidth
+ * @see #mMinHeight
+ */
+ static final int INVALID_MIN_SIZE = -1;
+
+ final ActivityTaskManagerService mAtmService;
+ final ActivityTaskSupervisor mTaskSupervisor;
+ final RootWindowContainer mRootWindowContainer;
+ private final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
+
+ /**
+ * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal width.
+ */
+ int mMinWidth;
+
+ /**
+ * Minimal height of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it
+ * should use the default minimal height.
+ */
+ int mMinHeight;
+
+ /** This task fragment will be removed when the cleanup of its children are done. */
+ private boolean mIsRemovalRequested;
+
+ /** The TaskFragment that is adjacent to this one. */
+ @Nullable
+ private TaskFragment mAdjacentTaskFragment;
+
+ /**
+ * Prevents duplicate calls to onTaskAppeared.
+ */
+ boolean mTaskFragmentAppearedSent;
+
+ /**
+ * When we are in the process of pausing an activity, before starting the
+ * next one, this variable holds the activity that is currently being paused.
+ *
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mPausingActivity = null;
+
+ /**
+ * This is the last activity that we put into the paused state. This is
+ * used to determine if we need to do an activity transition while sleeping,
+ * when we normally hold the top activity paused.
+ */
+ ActivityRecord mLastPausedActivity = null;
+
+ /**
+ * Current activity that is resumed, or null if there is none.
+ * Only set at leaf task fragments.
+ */
+ @Nullable
+ private ActivityRecord mResumedActivity = null;
+
+ /**
+ * This TaskFragment was created by an organizer which has the following implementations.
+ * <ul>
+ * <li>The TaskFragment won't be removed when it is empty. Removal has to be an explicit
+ * request from the organizer.</li>
+ * <li>If this fragment is a Task object then unlike other non-root tasks, it's direct
+ * children are visible to the organizer for ordering purposes.</li>
+ * <li>A TaskFragment can be created by {@link android.window.TaskFragmentOrganizer}, and
+ * a Task can be created by {@link android.window.TaskOrganizer}.</li>
+ * </ul>
+ */
+ @VisibleForTesting
+ boolean mCreatedByOrganizer;
+
+ /** Whether this TaskFragment is embedded in a task. */
+ private final boolean mIsEmbedded;
+
+ /** Organizer that organizing this TaskFragment. */
+ @Nullable
+ private ITaskFragmentOrganizer mTaskFragmentOrganizer;
+
+ /** Client assigned unique token for this TaskFragment if this is created by an organizer. */
+ @Nullable
+ private IBinder mFragmentToken;
+
+ /**
+ * The PID of the organizer that created this TaskFragment. It should be the same as the PID
+ * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
+ * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
+ */
+ private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
+
+ private final Rect mTmpInsets = new Rect();
+ private final Rect mTmpBounds = new Rect();
+ private final Rect mTmpFullBounds = new Rect();
+ private final Rect mTmpStableBounds = new Rect();
+ private final Rect mTmpNonDecorBounds = new Rect();
+
+ private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
+ new EnsureActivitiesVisibleHelper(this);
+ private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+ new EnsureVisibleActivitiesConfigHelper();
+ private class EnsureVisibleActivitiesConfigHelper {
+ private boolean mUpdateConfig;
+ private boolean mPreserveWindow;
+ private boolean mBehindFullscreen;
+
+ void reset(boolean preserveWindow) {
+ mPreserveWindow = preserveWindow;
+ mUpdateConfig = false;
+ mBehindFullscreen = false;
+ }
+
+ void process(ActivityRecord start, boolean preserveWindow) {
+ if (start == null || !start.mVisibleRequested) {
+ return;
+ }
+ reset(preserveWindow);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ EnsureVisibleActivitiesConfigHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(f, start, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+ f.recycle();
+
+ if (mUpdateConfig) {
+ // Ensure the resumed state of the focus activity if we updated the configuration of
+ // any activity.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+
+ boolean processActivity(ActivityRecord r) {
+ mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+ mBehindFullscreen |= r.occludesParent();
+ return mBehindFullscreen;
+ }
+ }
+
+ /** Creates an embedded task fragment. */
+ TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
+ boolean createdByOrganizer) {
+ this(atmService, fragmentToken, createdByOrganizer, true /* isEmbedded */);
+ }
+
+ TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
+ boolean createdByOrganizer, boolean isEmbedded) {
+ super(atmService.mWindowManager);
+
+ mAtmService = atmService;
+ mTaskSupervisor = mAtmService.mTaskSupervisor;
+ mRootWindowContainer = mAtmService.mRootWindowContainer;
+ mCreatedByOrganizer = createdByOrganizer;
+ mIsEmbedded = isEmbedded;
+ mTaskFragmentOrganizerController =
+ mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mFragmentToken = fragmentToken;
+ mRemoteToken = new RemoteToken(this);
+ }
+
+ @NonNull
+ static TaskFragment fromTaskFragmentToken(@Nullable IBinder token,
+ @NonNull ActivityTaskManagerService service) {
+ if (token == null) return null;
+ return service.mWindowOrganizerController.getTaskFragment(token);
+ }
+
+ void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
+ if (mAdjacentTaskFragment == taskFragment) {
+ return;
+ }
+ resetAdjacentTaskFragment();
+ if (taskFragment != null) {
+ mAdjacentTaskFragment = taskFragment;
+ taskFragment.setAdjacentTaskFragment(this);
+ }
+ }
+
+ private void resetAdjacentTaskFragment() {
+ // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
+ if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
+ mAdjacentTaskFragment.mAdjacentTaskFragment = null;
+ }
+ mAdjacentTaskFragment = null;
+ }
+
+ void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
+ mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
+ mTaskFragmentOrganizerPid = pid;
+ }
+
+ /** Whether this TaskFragment is organized by the given {@code organizer}. */
+ boolean hasTaskFragmentOrganizer(ITaskFragmentOrganizer organizer) {
+ return organizer != null && mTaskFragmentOrganizer != null
+ && organizer.asBinder().equals(mTaskFragmentOrganizer.asBinder());
+ }
+
+ TaskFragment getAdjacentTaskFragment() {
+ return mAdjacentTaskFragment;
+ }
+
+ /** @return the currently resumed activity. */
+ ActivityRecord getResumedActivity() {
+ return mResumedActivity;
+ }
+
+ void setResumedActivity(ActivityRecord r, String reason) {
+ warnForNonLeafTaskFragment("setResumedActivity");
+ if (mResumedActivity == r) {
+ return;
+ }
+
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.d(TAG, "setResumedActivity taskFrag:" + this + " + from: "
+ + mResumedActivity + " to:" + r + " reason:" + reason);
+ }
+ mResumedActivity = r;
+ mTaskSupervisor.updateTopResumedActivityIfNeeded();
+ }
+
+ @VisibleForTesting
+ void setPausingActivity(ActivityRecord pausing) {
+ mPausingActivity = pausing;
+ }
+
+ ActivityRecord getPausingActivity() {
+ return mPausingActivity;
+ }
+
+ int getDisplayId() {
+ final DisplayContent dc = getDisplayContent();
+ return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
+ }
+
+ @Nullable
+ Task getTask() {
+ if (asTask() != null) {
+ return asTask();
+ }
+
+ TaskFragment parent = getParent() != null ? getParent().asTaskFragment() : null;
+ return parent != null ? parent.getTask() : null;
+ }
+
+ @Override
+ @Nullable
+ TaskDisplayArea getDisplayArea() {
+ return (TaskDisplayArea) super.getDisplayArea();
+ }
+
+ @Override
+ public boolean isAttached() {
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+ return taskDisplayArea != null && !taskDisplayArea.isRemoved();
+ }
+
+ /**
+ * Returns the root {@link TaskFragment}, which is usually also a {@link Task}.
+ */
+ @NonNull
+ TaskFragment getRootTaskFragment() {
+ final WindowContainer parent = getParent();
+ if (parent == null) return this;
+
+ final TaskFragment parentTaskFragment = parent.asTaskFragment();
+ return parentTaskFragment == null ? this : parentTaskFragment.getRootTaskFragment();
+ }
+
+ @Override
+ TaskFragment asTaskFragment() {
+ return this;
+ }
+
+ /** Returns {@code true} if this is a container for embedded activities or tasks. */
+ boolean isEmbedded() {
+ if (mIsEmbedded) {
+ return true;
+ }
+ final WindowContainer<?> parent = getParent();
+ if (parent != null) {
+ final TaskFragment taskFragment = parent.asTaskFragment();
+ return taskFragment != null && taskFragment.isEmbedded();
+ }
+ return false;
+ }
+
+ /**
+ * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
+ */
+ private void warnForNonLeafTaskFragment(String func) {
+ if (!isLeafTaskFragment()) {
+ Slog.w(TAG, func + " on non-leaf task fragment " + this);
+ }
+ }
+
+ boolean hasDirectChildActivities() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asActivityRecord() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void cleanUpActivityReferences(@NonNull ActivityRecord r) {
+ if (mPausingActivity != null && mPausingActivity == r) {
+ mPausingActivity = null;
+ }
+
+ if (mResumedActivity != null && mResumedActivity == r) {
+ setResumedActivity(null, "cleanUpActivityReferences");
+ }
+ r.removeTimeouts();
+ }
+
+ /**
+ * Returns whether this TaskFragment is currently forced to be hidden for any reason.
+ */
+ protected boolean isForceHidden() {
+ return false;
+ }
+
+ boolean isLeafTaskFragment() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).asTaskFragment() != null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This should be called when an child activity changes state. This should only
+ * be called from
+ * {@link ActivityRecord#setState(ActivityRecord.State, String)} .
+ * @param record The {@link ActivityRecord} whose state has changed.
+ * @param state The new state.
+ * @param reason The reason for the change.
+ */
+ void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
+ String reason) {
+ warnForNonLeafTaskFragment("onActivityStateChanged");
+ if (record == mResumedActivity && state != RESUMED) {
+ setResumedActivity(null, reason + " - onActivityStateChanged");
+ }
+
+ if (state == RESUMED) {
+ if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) {
+ Slog.v(TAG, "set resumed activity to:" + record + " reason:" + reason);
+ }
+ setResumedActivity(record, reason + " - onActivityStateChanged");
+ if (record == mRootWindowContainer.getTopResumedActivity()) {
+ mAtmService.setResumedActivityUncheckLocked(record, reason);
+ }
+ mTaskSupervisor.mRecentTasks.add(record.getTask());
+ }
+ }
+
+ /**
+ * Resets local parameters because an app's activity died.
+ * @param app The app of the activity that died.
+ * @return {@code true} if the process of the pausing activity is died.
+ */
+ boolean handleAppDied(WindowProcessController app) {
+ warnForNonLeafTaskFragment("handleAppDied");
+ boolean isPausingDied = false;
+ if (mPausingActivity != null && mPausingActivity.app == app) {
+ ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
+ mPausingActivity);
+ mPausingActivity = null;
+ isPausingDied = true;
+ }
+ if (mLastPausedActivity != null && mLastPausedActivity.app == app) {
+ if (mLastPausedActivity.isNoHistory()) {
+ mTaskSupervisor.mNoHistoryActivities.remove(mLastPausedActivity);
+ }
+ mLastPausedActivity = null;
+ }
+ return isPausingDied;
+ }
+
+ void awakeFromSleeping() {
+ if (mPausingActivity != null) {
+ Slog.d(TAG, "awakeFromSleeping: previously pausing activity didn't pause");
+ mPausingActivity.activityPaused(true);
+ }
+ }
+
+ /**
+ * Tries to put the activities in the task fragment to sleep.
+ *
+ * If the task fragment is not in a state where its activities can be put to sleep, this
+ * function will start any necessary actions to move the task fragment into such a state.
+ * It is expected that this function get called again when those actions complete.
+ *
+ * @param shuttingDown {@code true} when the called because the device is shutting down.
+ * @return true if the root task finished going to sleep, false if the root task only started
+ * the process of going to sleep (checkReadyForSleep will be called when that process finishes).
+ */
+ boolean sleepIfPossible(boolean shuttingDown) {
+ boolean shouldSleep = true;
+ if (mResumedActivity != null) {
+ // Still have something resumed; can't sleep until it is paused.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep needs to pause %s", mResumedActivity);
+ startPausing(false /* userLeaving */, true /* uiSleeping */, null /* resuming */,
+ "sleep");
+ shouldSleep = false;
+ } else if (mPausingActivity != null) {
+ // Still waiting for something to pause; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still waiting to pause %s", mPausingActivity);
+ shouldSleep = false;
+ }
+
+ if (!shuttingDown) {
+ if (containsStoppingActivity()) {
+ // Still need to tell some activities to stop; can't sleep yet.
+ ProtoLog.v(WM_DEBUG_STATES, "Sleep still need to stop %d activities",
+ mTaskSupervisor.mStoppingActivities.size());
+
+ mTaskSupervisor.scheduleIdle();
+ shouldSleep = false;
+ }
+ }
+
+ if (shouldSleep) {
+ updateActivityVisibilities(null /* starting */, 0 /* configChanges */,
+ !PRESERVE_WINDOWS, true /* notifyClients */);
+ }
+
+ return shouldSleep;
+ }
+
+ private boolean containsStoppingActivity() {
+ for (int i = mTaskSupervisor.mStoppingActivities.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mTaskSupervisor.mStoppingActivities.get(i);
+ if (r.getTaskFragment() == this) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the TaskFragment is translucent and can have other contents visible behind
+ * it if needed. A TaskFragment is considered translucent if it don't contain a visible or
+ * starting (about to be visible) activity that is fullscreen (opaque).
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @VisibleForTesting
+ boolean isTranslucent(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return true;
+ }
+ final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
+ PooledLambda.__(ActivityRecord.class), starting);
+ final ActivityRecord opaque = getActivity(p);
+ p.recycle();
+ return opaque == null;
+ }
+
+ private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+ if (r.finishing) {
+ // We don't factor in finishing activities when determining translucency since
+ // they will be gone soon.
+ return false;
+ }
+
+ if (!r.visibleIgnoringKeyguard && r != starting) {
+ // Also ignore invisible activities that are not the currently starting
+ // activity (about to be visible).
+ return false;
+ }
+
+ if (r.occludesParent()) {
+ // Root task isn't translucent if it has at least one fullscreen activity
+ // that is visible.
+ return true;
+ }
+ return false;
+ }
+
+ ActivityRecord getTopNonFinishingActivity() {
+ return getTopNonFinishingActivity(true /* includeOverlays */);
+ }
+
+ ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
+ return getTopNonFinishingActivity(includeOverlays, true /* includingEmbeddedTask */);
+ }
+
+ /**
+ * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
+ * the current user.
+ * @param includeOverlays whether the task overlay activity should be included.
+ * @param includingEmbeddedTask whether the activity in a task that being embedded from this
+ * one should be included.
+ * @see #topRunningActivity(boolean, boolean)
+ * @see ActivityRecord#okToShowLocked()
+ */
+ ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
+ boolean includingEmbeddedTask) {
+ // Split into 4 to avoid object creation due to variable capture.
+ if (includeOverlays) {
+ if (includingEmbeddedTask) {
+ return getActivity((r) -> !r.finishing);
+ }
+ return getActivity((r) -> !r.finishing && r.getTask() == this.getTask());
+ }
+
+ if (includingEmbeddedTask) {
+ return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
+ }
+ return getActivity(
+ (r) -> !r.finishing && !r.isTaskOverlay() && r.getTask() == this.getTask());
+ }
+
+ ActivityRecord topRunningActivity() {
+ return topRunningActivity(false /* focusableOnly */);
+ }
+
+ ActivityRecord topRunningActivity(boolean focusableOnly) {
+ return topRunningActivity(focusableOnly, true /* includingEmbeddedTask */);
+ }
+
+ /**
+ * Returns the top-most running activity, which the activity is non-finishing and ok to show
+ * to the current user.
+ *
+ * @see ActivityRecord#canBeTopRunning()
+ */
+ ActivityRecord topRunningActivity(boolean focusableOnly, boolean includingEmbeddedTask) {
+ // Split into 4 to avoid object creation due to variable capture.
+ if (focusableOnly) {
+ if (includingEmbeddedTask) {
+ return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+ }
+ return getActivity(
+ (r) -> r.canBeTopRunning() && r.isFocusable() && r.getTask() == this.getTask());
+ }
+
+ if (includingEmbeddedTask) {
+ return getActivity(ActivityRecord::canBeTopRunning);
+ }
+ return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask());
+ }
+
+ boolean isTopActivityFocusable() {
+ final ActivityRecord r = topRunningActivity();
+ return r != null ? r.isFocusable()
+ : (isFocusable() && getWindowConfiguration().canReceiveKeys());
+ }
+
+ /**
+ * Returns the visibility state of this TaskFragment.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ @TaskFragmentVisibility
+ int getVisibility(ActivityRecord starting) {
+ if (!isAttached() || isForceHidden()) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ if (isTopActivityLaunchedBehind()) {
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ boolean gotRootSplitScreenFragment = false;
+ boolean gotOpaqueSplitScreenPrimary = false;
+ boolean gotOpaqueSplitScreenSecondary = false;
+ boolean gotTranslucentFullscreen = false;
+ boolean gotTranslucentSplitScreenPrimary = false;
+ boolean gotTranslucentSplitScreenSecondary = false;
+ boolean shouldBeVisible = true;
+
+ // This TaskFragment is only considered visible if all its parent TaskFragments are
+ // considered visible, so check the visibility of all ancestor TaskFragment first.
+ final WindowContainer parent = getParent();
+ if (parent.asTaskFragment() != null) {
+ final int parentVisibility = parent.asTaskFragment().getVisibility(starting);
+ if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) {
+ // Can't be visible if parent isn't visible
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (parentVisibility == TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
+ // Parent is behind a translucent container so the highest visibility this container
+ // can get is that.
+ gotTranslucentFullscreen = true;
+ }
+ }
+
+ final List<TaskFragment> adjacentTaskFragments = new ArrayList<>();
+ final int windowingMode = getWindowingMode();
+ final boolean isAssistantType = isActivityTypeAssistant();
+ for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer other = parent.getChildAt(i);
+ if (other == null) continue;
+
+ final boolean hasRunningActivities = hasRunningActivity(other);
+ if (other == this) {
+ // Should be visible if there is no other fragment occluding it, unless it doesn't
+ // have any running activities, not starting one and not home stack.
+ shouldBeVisible = hasRunningActivities
+ || (starting != null && starting.isDescendantOf(this))
+ || isActivityTypeHome();
+ break;
+ }
+
+ if (!hasRunningActivities) {
+ continue;
+ }
+
+ final int otherWindowingMode = other.getWindowingMode();
+ if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ if (isTranslucent(other, starting)) {
+ // Can be visible behind a translucent fullscreen TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
+ && other.matchParentBounds()) {
+ if (isTranslucent(other, starting)) {
+ // Can be visible behind a translucent TaskFragment.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Multi-window TaskFragment that matches parent bounds would occlude other children
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && !gotOpaqueSplitScreenPrimary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenPrimary = isTranslucent(other, starting);
+ gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && gotOpaqueSplitScreenPrimary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-primary.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ } else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && !gotOpaqueSplitScreenSecondary) {
+ gotRootSplitScreenFragment = true;
+ gotTranslucentSplitScreenSecondary = isTranslucent(other, starting);
+ gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && gotOpaqueSplitScreenSecondary) {
+ // Can't be visible behind another opaque TaskFragment in split-screen-secondary
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ }
+ if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
+ // Can not be visible if we are in split-screen windowing mode and both halves of
+ // the screen are opaque.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ if (isAssistantType && gotRootSplitScreenFragment) {
+ // Assistant TaskFragment can't be visible behind split-screen. In addition to
+ // this not making sense, it also works around an issue here we boost the z-order
+ // of the assistant window surfaces in window manager whenever it is visible.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ final TaskFragment otherTaskFrag = other.asTaskFragment();
+ if (otherTaskFrag != null && otherTaskFrag.mAdjacentTaskFragment != null) {
+ if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
+ if (otherTaskFrag.isTranslucent(starting)
+ || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
+ // Can be visible behind a translucent adjacent TaskFragments.
+ gotTranslucentFullscreen = true;
+ continue;
+ }
+ // Can not be visible behind adjacent TaskFragments.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ } else {
+ adjacentTaskFragments.add(otherTaskFrag);
+ }
+ }
+
+ }
+
+ if (!shouldBeVisible) {
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ // Handle cases when there can be a translucent split-screen TaskFragment on top.
+ switch (windowingMode) {
+ case WINDOWING_MODE_FULLSCREEN:
+ if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
+ // At least one of the split-screen TaskFragment that covers this one is
+ // translucent.
+ // When in split mode, home will be reparented to the secondary split while
+ // leaving TaskFragments not supporting split below. Due to
+ // TaskDisplayArea#assignRootTaskOrdering always adjusts home surface layer to
+ // the bottom, this makes sure TaskFragments not in split roots won't occlude
+ // home task unexpectedly.
+ return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+ if (gotTranslucentSplitScreenPrimary) {
+ // Covered by translucent primary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+ if (gotTranslucentSplitScreenSecondary) {
+ // Covered by translucent secondary split-screen on top.
+ return TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+ }
+ break;
+ }
+
+ // Lastly - check if there is a translucent fullscreen TaskFragment on top.
+ return gotTranslucentFullscreen
+ ? TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
+ : TASK_FRAGMENT_VISIBILITY_VISIBLE;
+ }
+
+ private static boolean hasRunningActivity(WindowContainer wc) {
+ if (wc.asTaskFragment() != null) {
+ return wc.asTaskFragment().topRunningActivity() != null;
+ }
+ return wc.asActivityRecord() != null && !wc.asActivityRecord().finishing;
+ }
+
+ private static boolean isTranslucent(WindowContainer wc, ActivityRecord starting) {
+ if (wc.asTaskFragment() != null) {
+ return wc.asTaskFragment().isTranslucent(starting);
+ } else if (wc.asActivityRecord() != null) {
+ return !wc.asActivityRecord().occludesParent();
+ }
+ return false;
+ }
+
+
+ private boolean isTopActivityLaunchedBehind() {
+ final ActivityRecord top = topRunningActivity();
+ return top != null && top.mLaunchTaskBehind;
+ }
+
+ final void updateActivityVisibilities(@Nullable ActivityRecord starting, int configChanges,
+ boolean preserveWindows, boolean notifyClients) {
+ mTaskSupervisor.beginActivityVisibilityUpdate();
+ try {
+ mEnsureActivitiesVisibleHelper.process(
+ starting, configChanges, preserveWindows, notifyClients);
+ } finally {
+ mTaskSupervisor.endActivityVisibilityUpdate();
+ }
+ }
+
+ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
+ boolean deferPause) {
+ ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+ if (next == null || !next.canResumeByCompat()) {
+ return false;
+ }
+
+ next.delayedResume = false;
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
+
+ // If the top activity is the resumed one, nothing to do.
+ if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
+ // we still want to check if the visibility of other windows have changed (e.g. bringing
+ // a fullscreen window forward to cover another freeform activity.)
+ if (taskDisplayArea.inMultiWindowMode()) {
+ taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */, true /* notifyClients */);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ + "resumed %s", next);
+ return false;
+ }
+
+ // If we are currently pausing an activity, then don't do anything until that is done.
+ final boolean allPausedComplete = mRootWindowContainer.allPausedActivitiesComplete();
+ if (!allPausedComplete) {
+ ProtoLog.v(WM_DEBUG_STATES,
+ "resumeTopActivity: Skip resume: some activity pausing.");
+ return false;
+ }
+
+ // If we are sleeping, and there is no resumed activity, and the top activity is paused,
+ // well that is the state we want.
+ if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and"
+ + " all paused");
+ return false;
+ }
+
+ // Make sure that the user who owns this activity is started. If not,
+ // we will just leave it as is because someone should be bringing
+ // another user's activities to the top of the stack.
+ if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
+ Slog.w(TAG, "Skipping resume of top activity " + next
+ + ": user " + next.mUserId + " is stopped");
+ return false;
+ }
+
+ // The activity may be waiting for stop, but that is no longer
+ // appropriate for it.
+ mTaskSupervisor.mStoppingActivities.remove(next);
+
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+
+ mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+
+ ActivityRecord lastResumed = null;
+ final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
+ if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {
+ // So, why aren't we using prev here??? See the param comment on the method. prev
+ // doesn't represent the last resumed activity. However, the last focus stack does if
+ // it isn't null.
+ lastResumed = lastFocusedRootTask.getTopResumedActivity();
+ }
+
+ boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
+ if (mResumedActivity != null) {
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
+ pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
+ next, "resumeTopActivity");
+ }
+ if (pausing) {
+ ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"
+ + " start pausing");
+ // At this point we want to put the upcoming activity's process
+ // at the top of the LRU list, since we know we will be needing it
+ // very soon and it would be a waste to let it get killed if it
+ // happens to be sitting towards the end.
+ if (next.attachedToProcess()) {
+ next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, false /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ } else if (!next.isProcessRunning()) {
+ // Since the start-process is asynchronous, if we already know the process of next
+ // activity isn't running, we can start the process earlier to save the time to wait
+ // for the current activity to be paused.
+ final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
+ mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+ isTop ? "pre-top-activity" : "pre-activity");
+ }
+ if (lastResumed != null) {
+ lastResumed.setWillCloseOrEnterPip(true);
+ }
+ return true;
+ } else if (mResumedActivity == next && next.isState(RESUMED)
+ && taskDisplayArea.allResumedActivitiesComplete()) {
+ // It is possible for the activity to be resumed when we paused back stacks above if the
+ // next activity doesn't have to wait for pause to complete.
+ // So, nothing else to-do except:
+ // Make sure we have executed any pending transitions, since there
+ // should be nothing left to do at this point.
+ executeAppTransition(options);
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed "
+ + "(dontWaitForPause) %s", next);
+ return true;
+ }
+
+ // If the most recent activity was noHistory but was only stopped rather
+ // than stopped+finished because the device went to sleep, we need to make
+ // sure to finish it as we're making a new activity topmost.
+ if (shouldSleepActivities()) {
+ mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
+ }
+
+ if (prev != null && prev != next && next.nowVisible) {
+ // The next activity is already visible, so hide the previous
+ // activity's windows right now so we can show the new one ASAP.
+ // We only do this if the previous is finishing, which should mean
+ // it is on top of the one being resumed so hiding it quickly
+ // is good. Otherwise, we want to do the normal route of allowing
+ // the resumed activity to be shown so we can decide if the
+ // previous should actually be hidden depending on whether the
+ // new one is found to be full-screen or not.
+ if (prev.finishing) {
+ prev.setVisibility(false);
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ } else {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev
+ + ", nowVisible=" + next.nowVisible);
+ }
+ }
+ }
+
+ // Launching this app's activity, make sure the app is no longer
+ // considered stopped.
+ try {
+ mTaskSupervisor.getActivityMetricsLogger()
+ .notifyBeforePackageUnstopped(next.packageName);
+ mAtmService.getPackageManager().setPackageStoppedState(
+ next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
+ } catch (RemoteException e1) {
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Failed trying to unstop package "
+ + next.packageName + ": " + e);
+ }
+
+ // We are starting up the next activity, so tell the window manager
+ // that the previous one will be hidden soon. This way it can know
+ // to ignore it when computing the desired screen orientation.
+ boolean anim = true;
+ final DisplayContent dc = taskDisplayArea.mDisplayContent;
+ if (prev != null) {
+ if (prev.finishing) {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_CLOSE);
+ }
+ prev.setVisibility(false);
+ } else {
+ if (DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
+ }
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN,
+ next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
+ }
+ }
+ } else {
+ if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
+ if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
+ dc.prepareAppTransition(TRANSIT_NONE);
+ } else {
+ dc.prepareAppTransition(TRANSIT_OPEN);
+ }
+ }
+
+ if (anim) {
+ next.applyOptionsAnimation();
+ } else {
+ next.abortAndClearOptionsAnimation();
+ }
+
+ mTaskSupervisor.mNoAnimActivities.clear();
+
+ if (next.attachedToProcess()) {
+ if (DEBUG_SWITCH) {
+ Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped
+ + " visibleRequested=" + next.mVisibleRequested);
+ }
+
+ // If the previous activity is translucent, force a visibility update of
+ // the next activity, so that it's added to WM's opening app list, and
+ // transition animation can be set up properly.
+ // For example, pressing Home button with a translucent activity in focus.
+ // Launcher is already visible in this case. If we don't add it to opening
+ // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
+ // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
+ final boolean lastActivityTranslucent = inMultiWindowMode()
+ || mLastPausedActivity != null && !mLastPausedActivity.occludesParent();
+
+ // This activity is now becoming visible.
+ if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) {
+ next.setVisibility(true);
+ }
+
+ // schedule launch ticks to collect information about slow apps.
+ next.startLaunchTickingLocked();
+
+ ActivityRecord lastResumedActivity =
+ lastFocusedRootTask == null ? null
+ : lastFocusedRootTask.getTopResumedActivity();
+ final ActivityRecord.State lastState = next.getState();
+
+ mAtmService.updateCpuStats();
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
+
+ next.setState(RESUMED, "resumeTopActivity");
+
+ // Have the window manager re-evaluate the orientation of
+ // the screen based on the new activity order.
+ boolean notUpdated = true;
+
+ // Activity should also be visible if set mLaunchTaskBehind to true (see
+ // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
+ if (shouldBeVisible(next)) {
+ // We have special rotation behavior when here is some active activity that
+ // requests specific orientation or Keyguard is locked. Make sure all activity
+ // visibilities are set correctly as well as the transition is updated if needed
+ // to get the correct rotation behavior. Otherwise the following call to update
+ // the orientation may cause incorrect configurations delivered to client as a
+ // result of invisible window resize.
+ // TODO: Remove this once visibilities are set correctly immediately when
+ // starting an activity.
+ notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+
+ if (notUpdated) {
+ // The configuration update wasn't able to keep the existing
+ // instance of the activity, and instead started a new one.
+ // We should be all done, but let's just make sure our activity
+ // is still at the top and schedule another run if something
+ // weird happened.
+ ActivityRecord nextNext = topRunningActivity();
+ ProtoLog.i(WM_DEBUG_STATES, "Activity config changed during resume: "
+ + "%s, new next: %s", next, nextNext);
+ if (nextNext != next) {
+ // Do over!
+ mTaskSupervisor.scheduleResumeTopActivities();
+ }
+ if (!next.mVisibleRequested || next.stopped) {
+ next.setVisibility(true);
+ }
+ next.completeResumeLocked();
+ return true;
+ }
+
+ try {
+ final ClientTransaction transaction =
+ ClientTransaction.obtain(next.app.getThread(), next.appToken);
+ // Deliver all pending results.
+ ArrayList<ResultInfo> a = next.results;
+ if (a != null) {
+ final int size = a.size();
+ if (!next.finishing && size > 0) {
+ if (DEBUG_RESULTS) {
+ Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
+ }
+ transaction.addCallback(ActivityResultItem.obtain(a));
+ }
+ }
+
+ if (next.newIntents != null) {
+ transaction.addCallback(
+ NewIntentItem.obtain(next.newIntents, true /* resume */));
+ }
+
+ // Well the app will no longer be stopped.
+ // Clear app token stopped state in window manager if needed.
+ next.notifyAppResumed(next.stopped);
+
+ EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
+ next.getTask().mTaskId, next.shortComponentName);
+
+ mAtmService.getAppWarningsLocked().onResumeActivity(next);
+ next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+ next.abortAndClearOptionsAnimation();
+ transaction.setLifecycleStateRequest(
+ ResumeActivityItem.obtain(next.app.getReportedProcState(),
+ dc.isNextTransitionForward()));
+ mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
+ } catch (Exception e) {
+ // Whoops, need to restart this activity!
+ ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
+ + "%s", lastState, next);
+ next.setState(lastState, "resumeTopActivityInnerLocked");
+
+ // lastResumedActivity being non-null implies there is a lastStack present.
+ if (lastResumedActivity != null) {
+ lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
+ }
+
+ Slog.i(TAG, "Restarting because process died: " + next);
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
+ && lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
+ next.showStartingWindow(false /* taskSwitch */);
+ }
+ mTaskSupervisor.startSpecificActivity(next, true, false);
+ return true;
+ }
+
+ // From this point on, if something goes wrong there is no way
+ // to recover the activity.
+ try {
+ next.completeResumeLocked();
+ } catch (Exception e) {
+ // If any exception gets thrown, toss away this
+ // activity and try the next one.
+ Slog.w(TAG, "Exception thrown during resume of " + next, e);
+ next.finishIfPossible("resume-exception", true /* oomAdj */);
+ return true;
+ }
+ } else {
+ // Whoops, need to restart this activity!
+ if (!next.hasBeenLaunched) {
+ next.hasBeenLaunched = true;
+ } else {
+ if (SHOW_APP_STARTING_PREVIEW) {
+ next.showStartingWindow(false /* taskSwich */);
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
+ }
+ ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
+ mTaskSupervisor.startSpecificActivity(next, true, true);
+ }
+
+ return true;
+ }
+
+ boolean shouldSleepOrShutDownActivities() {
+ return shouldSleepActivities() || mAtmService.mShuttingDown;
+ }
+
+ /**
+ * Returns true if the TaskFragment should be visible.
+ *
+ * @param starting The currently starting activity or null if there is none.
+ */
+ boolean shouldBeVisible(ActivityRecord starting) {
+ return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+ }
+
+ boolean isFocusableAndVisible() {
+ return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
+ }
+
+ final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) {
+ return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
+ }
+
+ /**
+ * Start pausing the currently resumed activity. It is an error to call this if there
+ * is already an activity being paused or there is no resumed activity.
+ *
+ * @param userLeaving True if this should result in an onUserLeaving to the current activity.
+ * @param uiSleeping True if this is happening with the user interface going to sleep (the
+ * screen turning off).
+ * @param resuming The activity we are currently trying to resume or null if this is not being
+ * called as part of resuming the top activity, so we shouldn't try to instigate
+ * a resume here if not null.
+ * @param reason The reason of pausing the activity.
+ * @return Returns true if an activity now is in the PAUSING state, and we are waiting for
+ * it to tell us when it is done.
+ */
+ boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
+ String reason) {
+ if (!hasDirectChildActivities()) {
+ return false;
+ }
+
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
+ mResumedActivity);
+
+ if (mPausingActivity != null) {
+ Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ + " state=" + mPausingActivity.getState());
+ if (!shouldSleepActivities()) {
+ // Avoid recursion among check for sleep and complete pause during sleeping.
+ // Because activity will be paused immediately after resume, just let pause
+ // be completed by the order of activity paused from clients.
+ completePause(false, resuming);
+ }
+ }
+ ActivityRecord prev = mResumedActivity;
+
+ if (prev == null) {
+ if (resuming == null) {
+ Slog.wtf(TAG, "Trying to pause when nothing is resumed");
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ return false;
+ }
+
+ if (prev == resuming) {
+ Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+ return false;
+ }
+
+ ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
+ mPausingActivity = prev;
+ mLastPausedActivity = prev;
+ if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+ mTaskSupervisor.mNoHistoryActivities.add(prev);
+ }
+ prev.setState(PAUSING, "startPausingLocked");
+ prev.getTask().touchActiveTime();
+
+ mAtmService.updateCpuStats();
+
+ boolean pauseImmediately = false;
+ boolean shouldAutoPip = false;
+ if (resuming != null && (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0) {
+ // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
+ // activity to be paused, while at the same time resuming the new resume activity
+ // only if the previous activity can't go into Pip since we want to give Pip
+ // activities a chance to enter Pip before resuming the next activity.
+ final boolean lastResumedCanPip = prev != null && prev.checkEnterPictureInPictureState(
+ "shouldResumeWhilePausing", userLeaving);
+ if (lastResumedCanPip && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
+ shouldAutoPip = true;
+ } else if (!lastResumedCanPip) {
+ pauseImmediately = true;
+ } else {
+ // The previous activity may still enter PIP even though it did not allow auto-PIP.
+ }
+ }
+
+ boolean didAutoPip = false;
+ if (prev.attachedToProcess()) {
+ if (shouldAutoPip) {
+ ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ + "directly: %s", prev);
+
+ didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
+ mPausingActivity = null;
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+ }
+ } else {
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+
+ // If we are not going to sleep, we want to ensure the device is
+ // awake until the next activity is started.
+ if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
+ mTaskSupervisor.acquireLaunchWakelock();
+ }
+
+ // 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
+ // key dispatch; the same activity will pick it up again on wakeup.
+ if (!uiSleeping) {
+ prev.pauseKeyDispatchingLocked();
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
+ }
+
+ if (pauseImmediately) {
+ // If the caller said they don't want to wait for the pause, then complete
+ // the pause now.
+ completePause(false, resuming);
+ return false;
+
+ } else {
+ prev.schedulePauseTimeout();
+ // Unset readiness since we now need to wait until this pause is complete.
+ mAtmService.getTransitionController().setReady(this, false /* ready */);
+ return true;
+ }
+
+ } else {
+ // 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();
+ }
+ return false;
+ }
+ }
+
+ @VisibleForTesting
+ void completePause(boolean resumeNext, ActivityRecord resuming) {
+ // Complete the pausing process of a pausing activity, so it doesn't make sense to
+ // operate on non-leaf tasks.
+ // warnForNonLeafTask("completePauseLocked");
+
+ ActivityRecord prev = mPausingActivity;
+ ProtoLog.v(WM_DEBUG_STATES, "Complete pause: %s", prev);
+
+ if (prev != null) {
+ prev.setWillCloseOrEnterPip(false);
+ final boolean wasStopping = prev.isState(STOPPING);
+ prev.setState(PAUSED, "completePausedLocked");
+ if (prev.finishing) {
+ // We will update the activity visibility later, no need to do in
+ // completeFinishing(). Updating visibility here might also making the next
+ // activities to be resumed, and could result in wrong app transition due to
+ // lack of previous activity information.
+ ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
+ prev = prev.completeFinishing(false /* updateVisibility */,
+ "completePausedLocked");
+ } else if (prev.hasProcess()) {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ + "wasStopping=%b visibleRequested=%b", prev, wasStopping,
+ prev.mVisibleRequested);
+ if (prev.deferRelaunchUntilPaused) {
+ // Complete the deferred relaunch that was waiting for pause to complete.
+ ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev);
+ prev.relaunchActivityLocked(prev.preserveWindowOnDeferredRelaunch);
+ } else if (wasStopping) {
+ // We are also stopping, the stop request must have gone soon after the pause.
+ // We can't clobber it, because the stop confirmation will not be handled.
+ // We don't need to schedule another stop, we only need to let it happen.
+ prev.setState(STOPPING, "completePausedLocked");
+ } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) {
+ // Clear out any deferred client hide we might currently have.
+ prev.setDeferHidingClient(false);
+ // If we were visible then resumeTopActivities will release resources before
+ // stopping.
+ prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
+ "completePauseLocked");
+ }
+ } else {
+ ProtoLog.v(WM_DEBUG_STATES, "App died during pause, not stopping: %s", prev);
+ prev = null;
+ }
+ // It is possible the activity was freezing the screen before it was paused.
+ // In that case go ahead and remove the freeze this activity has on the screen
+ // since it is no longer visible.
+ if (prev != null) {
+ prev.stopFreezingScreenLocked(true /*force*/);
+ }
+ mPausingActivity = null;
+ }
+
+ if (resumeNext) {
+ final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
+ if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
+ mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
+ null /* targetOptions */);
+ } else {
+ // checkReadyForSleep();
+ final ActivityRecord top =
+ topRootTask != null ? topRootTask.topRunningActivity() : null;
+ if (top == null || (prev != null && top != prev)) {
+ // If there are no more activities available to run, do resume anyway to start
+ // something. Also if the top activity on the root task is not the just paused
+ // activity, we need to go ahead and resume it to ensure we complete an
+ // in-flight app switch.
+ mRootWindowContainer.resumeFocusedTasksTopActivities();
+ }
+ }
+ }
+
+ if (prev != null) {
+ prev.resumeKeyDispatchingLocked();
+ }
+
+ mRootWindowContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
+
+ // Notify when the task stack has changed, but only if visibilities changed (not just
+ // focus). Also if there is an active root pinned task - we always want to notify it about
+ // task stack changes, because its positioning may depend on it.
+ if (mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause
+ || (getDisplayArea() != null && getDisplayArea().hasPinnedTask())) {
+ mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
+ mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
+ }
+ }
+
+ @Override
+ void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ super.forAllTaskFragments(callback, traverseTopToBottom);
+ callback.accept(this);
+ }
+
+ @Override
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ boolean isLeafTaskFrag = true;
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ child.forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+ if (isLeafTaskFrag) callback.accept(this);
+ }
+
+ @Override
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ boolean isLeafTaskFrag = true;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final TaskFragment child = mChildren.get(i).asTaskFragment();
+ if (child != null) {
+ isLeafTaskFrag = false;
+ if (child.forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ }
+ if (isLeafTaskFrag) {
+ return callback.apply(this);
+ }
+ return false;
+ }
+
+ void addChild(ActivityRecord r) {
+ addChild(r, POSITION_TOP);
+ }
+
+ @Override
+ void addChild(WindowContainer child, int index) {
+ boolean isAddingActivity = child.asActivityRecord() != null;
+ final Task task = isAddingActivity ? getTask() : null;
+
+ // If this task had any child before we added this one.
+ boolean taskHadChild = task != null && task.hasChild();
+ // getActivityType() looks at the top child, so we need to read the type before adding
+ // a new child in case the new child is on top and UNDEFINED.
+ final int activityType = task != null ? task.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+
+ super.addChild(child, index);
+
+ if (isAddingActivity && task != null) {
+ child.asActivityRecord().inHistory = true;
+ task.onDescendantActivityAdded(taskHadChild, activityType, child.asActivityRecord());
+ }
+ }
+
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+
+ sendTaskFragmentInfoChanged();
+ }
+
+ void executeAppTransition(ActivityOptions options) {
+ // No app transition applied to the task fragment.
+ }
+
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopMostActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
+ @Override
+ boolean canCreateRemoteAnimationTarget() {
+ return true;
+ }
+
+ boolean shouldSleepActivities() {
+ return false;
+ }
+
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+ super.resolveOverrideConfiguration(newParentConfig);
+
+ int windowingMode =
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+
+ // Resolve override windowing mode to fullscreen for home task (even on freeform
+ // display), or split-screen if in split-screen mode.
+ if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = WindowConfiguration.isSplitScreenWindowingMode(parentWindowingMode)
+ ? parentWindowingMode : WINDOWING_MODE_FULLSCREEN;
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ }
+
+ // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+ // pinned windowing mode.
+ if (!supportsMultiWindow()) {
+ final int candidateWindowingMode =
+ windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
+ if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
+ && candidateWindowingMode != WINDOWING_MODE_PINNED) {
+ getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_FULLSCREEN);
+ }
+ }
+
+ final Task thisTask = asTask();
+ if (thisTask != null) {
+ thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
+ mTmpBounds /* previousBounds */);
+ }
+ computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+ }
+
+ boolean supportsMultiWindow() {
+ return supportsMultiWindowInDisplayArea(getDisplayArea());
+ }
+
+ /**
+ * @return whether this task supports multi-window if it is in the given
+ * {@link TaskDisplayArea}.
+ */
+ boolean supportsMultiWindowInDisplayArea(@Nullable TaskDisplayArea tda) {
+ if (!mAtmService.mSupportsMultiWindow) {
+ return false;
+ }
+ final Task task = getTask();
+ if (task == null) {
+ return false;
+ }
+ if (tda == null) {
+ Slog.w(TAG, "Can't find TaskDisplayArea to determine support for multi"
+ + " window. Task id=" + getTaskId() + " attached=" + isAttached());
+ return false;
+ }
+ if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) {
+ // Not support non-resizable in multi window.
+ return false;
+ }
+
+ return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight);
+ }
+
+ private int getTaskId() {
+ return getTask() != null ? getTask().mTaskId : INVALID_TASK_ID;
+ }
+
+ /**
+ * Ensures all visible activities at or below the input activity have the right configuration.
+ */
+ void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+ mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig) {
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
+ if (overrideDisplayInfo != null) {
+ // Make sure the screen related configs can be computed by the provided display info.
+ inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
+ null /* compatInsets */);
+ }
+
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ if (compatInsets != null) {
+ // Make sure the app bounds can be computed by the compat insets.
+ invalidateAppBoundsConfig(inOutConfig);
+ }
+ computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
+ compatInsets);
+ }
+
+ /**
+ * Forces the app bounds related configuration can be computed by
+ * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+ * ActivityRecord.CompatDisplayInsets)}.
+ */
+ private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+ final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (appBounds != null) {
+ appBounds.setEmpty();
+ }
+ inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+ inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+ }
+
+ /**
+ * Calculates configuration values used by the client to get resources. This should be run
+ * using app-facing bounds (bounds unmodified by animations or transient interactions).
+ *
+ * This assumes bounds are non-empty/null. For the null-bounds case, the caller is likely
+ * configuring an "inherit-bounds" window which means that all configuration settings would
+ * just be inherited from the parent configuration.
+ **/
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
+ @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo,
+ @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+ int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = parentConfig.windowConfiguration.getWindowingMode();
+ }
+
+ float density = inOutConfig.densityDpi;
+ if (density == Configuration.DENSITY_DPI_UNDEFINED) {
+ density = parentConfig.densityDpi;
+ }
+ density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+
+ // The bounds may have been overridden at this level. If the parent cannot cover these
+ // bounds, the configuration is still computed according to the override bounds.
+ final boolean insideParentBounds;
+
+ final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
+ final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds();
+ if (resolvedBounds == null || resolvedBounds.isEmpty()) {
+ mTmpFullBounds.set(parentBounds);
+ insideParentBounds = true;
+ } else {
+ mTmpFullBounds.set(resolvedBounds);
+ insideParentBounds = parentBounds.contains(resolvedBounds);
+ }
+
+ // Non-null compatibility insets means the activity prefers to keep its original size, so
+ // out bounds doesn't need to be restricted by the parent or current display
+ final boolean customContainerPolicy = compatInsets != null;
+
+ Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+ if (outAppBounds == null || outAppBounds.isEmpty()) {
+ // App-bounds hasn't been overridden, so calculate a value for it.
+ inOutConfig.windowConfiguration.setAppBounds(mTmpFullBounds);
+ outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
+
+ if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
+ final Rect containingAppBounds;
+ if (insideParentBounds) {
+ containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+ } else {
+ // Restrict appBounds to display non-decor rather than parent because the
+ // override bounds are beyond the parent. Otherwise, it won't match the
+ // overridden bounds.
+ final TaskDisplayArea displayArea = getDisplayArea();
+ containingAppBounds = displayArea != null
+ ? displayArea.getWindowConfiguration().getAppBounds() : null;
+ }
+ if (containingAppBounds != null && !containingAppBounds.isEmpty()) {
+ outAppBounds.intersect(containingAppBounds);
+ }
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
+ || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ } else if (!customContainerPolicy
+ && (overrideDisplayInfo != null || getDisplayContent() != null)) {
+ final DisplayInfo di = overrideDisplayInfo != null
+ ? overrideDisplayInfo
+ : getDisplayContent().getDisplayInfo();
+
+ // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
+ // area, i.e. the screen area without the system bars.
+ // The non decor inset are areas that could never be removed in Honeycomb. See
+ // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
+ calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di);
+ } else {
+ // Apply the given non-decor and stable insets to calculate the corresponding bounds
+ // for screen size of configuration.
+ int rotation = inOutConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED) {
+ rotation = parentConfig.windowConfiguration.getRotation();
+ }
+ if (rotation != ROTATION_UNDEFINED && customContainerPolicy) {
+ mTmpNonDecorBounds.set(mTmpFullBounds);
+ mTmpStableBounds.set(mTmpFullBounds);
+ compatInsets.getBoundsByRotation(mTmpBounds, rotation);
+ intersectWithInsetsIfFits(mTmpNonDecorBounds, mTmpBounds,
+ compatInsets.mNonDecorInsets[rotation]);
+ intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
+ compatInsets.mStableInsets[rotation]);
+ outAppBounds.set(mTmpNonDecorBounds);
+ } else {
+ // Set to app bounds because it excludes decor insets.
+ mTmpNonDecorBounds.set(outAppBounds);
+ mTmpStableBounds.set(outAppBounds);
+ }
+ }
+
+ if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
+ inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
+ : overrideScreenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
+ inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy)
+ ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp)
+ : overrideScreenHeightDp;
+ }
+
+ if (inOutConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ if (WindowConfiguration.isFloating(windowingMode)) {
+ // For floating tasks, calculate the smallest width from the bounds of the task
+ inOutConfig.smallestScreenWidthDp = (int) (
+ Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
+ }
+ // otherwise, it will just inherit
+ }
+ }
+
+ if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
+ inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ }
+ if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
+ // For calculating screen layout, we need to use the non-decor inset screen area for the
+ // calculation for compatibility reasons, i.e. screen area without system bars that
+ // could never go away in Honeycomb.
+ int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+ int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+ // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+ // undefined so it can't be used.
+ if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+ compatScreenWidthDp = inOutConfig.screenWidthDp;
+ }
+ if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+ compatScreenHeightDp = inOutConfig.screenHeightDp;
+ }
+ // Reducing the screen layout starting from its parent config.
+ inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
+ compatScreenWidthDp, compatScreenHeightDp);
+ }
+ }
+
+ /**
+ * Gets bounds with non-decor and stable insets applied respectively.
+ *
+ * If bounds overhangs the display, those edges will not get insets. See
+ * {@link #intersectWithInsetsIfFits}
+ *
+ * @param outNonDecorBounds where to place bounds with non-decor insets applied.
+ * @param outStableBounds where to place bounds with stable insets applied.
+ * @param bounds the bounds to inset.
+ */
+ void calculateInsetFrames(Rect outNonDecorBounds, Rect outStableBounds, Rect bounds,
+ DisplayInfo displayInfo) {
+ outNonDecorBounds.set(bounds);
+ outStableBounds.set(bounds);
+ final Task rootTask = getRootTaskFragment().asTask();
+ if (rootTask == null || rootTask.mDisplayContent == null) {
+ return;
+ }
+ mTmpBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ final DisplayPolicy policy = rootTask.mDisplayContent.getDisplayPolicy();
+ policy.getNonDecorInsetsLw(displayInfo.rotation, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.displayCutout, mTmpInsets);
+ intersectWithInsetsIfFits(outNonDecorBounds, mTmpBounds, mTmpInsets);
+
+ policy.convertNonDecorInsetsToStableInsets(mTmpInsets, displayInfo.rotation);
+ intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
+ }
+
+ /**
+ * Intersects inOutBounds with intersectBounds-intersectInsets. If inOutBounds is larger than
+ * intersectBounds on a side, then the respective side will not be intersected.
+ *
+ * The assumption is that if inOutBounds is initially larger than intersectBounds, then the
+ * inset on that side is no-longer applicable. This scenario happens when a task's minimal
+ * bounds are larger than the provided parent/display bounds.
+ *
+ * @param inOutBounds the bounds to intersect.
+ * @param intersectBounds the bounds to intersect with.
+ * @param intersectInsets insets to apply to intersectBounds before intersecting.
+ */
+ static void intersectWithInsetsIfFits(
+ Rect inOutBounds, Rect intersectBounds, Rect intersectInsets) {
+ if (inOutBounds.right <= intersectBounds.right) {
+ inOutBounds.right =
+ Math.min(intersectBounds.right - intersectInsets.right, inOutBounds.right);
+ }
+ if (inOutBounds.bottom <= intersectBounds.bottom) {
+ inOutBounds.bottom =
+ Math.min(intersectBounds.bottom - intersectInsets.bottom, inOutBounds.bottom);
+ }
+ if (inOutBounds.left >= intersectBounds.left) {
+ inOutBounds.left =
+ Math.max(intersectBounds.left + intersectInsets.left, inOutBounds.left);
+ }
+ if (inOutBounds.top >= intersectBounds.top) {
+ inOutBounds.top =
+ Math.max(intersectBounds.top + intersectInsets.top, inOutBounds.top);
+ }
+ }
+
+ /** Computes LONG, SIZE and COMPAT parts of {@link Configuration#screenLayout}. */
+ static int computeScreenLayoutOverride(int sourceScreenLayout, int screenWidthDp,
+ int screenHeightDp) {
+ sourceScreenLayout = sourceScreenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK);
+ final int longSize = Math.max(screenWidthDp, screenHeightDp);
+ final int shortSize = Math.min(screenWidthDp, screenHeightDp);
+ return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
+ }
+
+ @Override
+ public int getActivityType() {
+ final int applicationType = super.getActivityType();
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
+ return applicationType;
+ }
+ return getTopChild().getActivityType();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ super.onConfigurationChanged(newParentConfig);
+ sendTaskFragmentInfoChanged();
+ }
+
+ @Override
+ void setSurfaceControl(SurfaceControl sc) {
+ super.setSurfaceControl(sc);
+ // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
+ // emit the callbacks now.
+ sendTaskFragmentAppeared();
+ }
+
+ void sendTaskFragmentInfoChanged() {
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizerController
+ .onTaskFragmentInfoChanged(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ private void sendTaskFragmentAppeared() {
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizerController.onTaskFragmentAppeared(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ private void sendTaskFragmentVanished() {
+ if (mTaskFragmentOrganizer != null) {
+ mTaskFragmentOrganizerController.onTaskFragmentVanished(mTaskFragmentOrganizer, this);
+ }
+ }
+
+ int getTaskFragmentOrganizerPid() {
+ return mTaskFragmentOrganizerPid;
+ }
+
+ /**
+ * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be
+ * called from {@link Task}.
+ */
+ TaskFragmentInfo getTaskFragmentInfo() {
+ List<IBinder> childActivities = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ WindowContainer wc = getChildAt(i);
+ if (mTaskFragmentOrganizerPid != ActivityRecord.INVALID_PID
+ && wc.asActivityRecord() != null
+ && wc.asActivityRecord().getPid() == mTaskFragmentOrganizerPid) {
+ // Only includes Activities that belong to the organizer process for security.
+ childActivities.add(wc.asActivityRecord().appToken);
+ }
+ }
+ final Point positionInParent = new Point();
+ getRelativePosition(positionInParent);
+ return new TaskFragmentInfo(
+ mFragmentToken,
+ mRemoteToken.toWindowContainerToken(),
+ getConfiguration(),
+ getChildCount() == 0,
+ hasRunningActivity(this),
+ isVisible(),
+ childActivities,
+ positionInParent);
+ }
+
+ @Nullable
+ IBinder getFragmentToken() {
+ return mFragmentToken;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ ITaskFragmentOrganizer getTaskFragmentOrganizer() {
+ return mTaskFragmentOrganizer;
+ }
+
+ /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
+ void clearLastPausedActivity() {
+ forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
+ }
+
+ /**
+ * Sets {@link #mMinWidth} and {@link #mMinWidth} to this TaskFragment.
+ * It is usually set from the parent {@link Task} when adding the TaskFragment to the window
+ * hierarchy.
+ */
+ void setMinDimensions(int minWidth, int minHeight) {
+ if (asTask() != null) {
+ throw new UnsupportedOperationException("This method must not be used to Task. The "
+ + " minimum dimension of Task should be passed from Task constructor.");
+ }
+ mMinWidth = minWidth;
+ mMinHeight = minHeight;
+ }
+
+ @Override
+ void removeChild(WindowContainer child) {
+ removeChild(child, true /* removeSelfIfPossible */);
+ }
+
+ void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
+ super.removeChild(child);
+ if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
+ removeImmediately("removeLastChild " + child);
+ }
+ }
+
+ /**
+ * Requests to remove this task fragment. If it doesn't have children, it is removed
+ * immediately. Otherwise it will be removed until all activities are destroyed.
+ *
+ * @param withTransition Whether to use transition animation when removing activities. Set to
+ * {@code false} if this is invisible to user, e.g. display removal.
+ */
+ void remove(boolean withTransition, String reason) {
+ if (!hasChild()) {
+ removeImmediately(reason);
+ return;
+ }
+ mIsRemovalRequested = true;
+ forAllActivities(r -> {
+ if (withTransition) {
+ r.finishIfPossible(reason, false /* oomAdj */);
+ } else {
+ r.destroyIfPossible(reason);
+ }
+ });
+ }
+
+ boolean shouldDeferRemoval() {
+ if (!hasChild()) {
+ return false;
+ }
+ return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES)
+ || mAtmService.getTransitionController().inTransition(this);
+ }
+
+ @Override
+ boolean handleCompleteDeferredRemoval() {
+ if (shouldDeferRemoval()) {
+ return true;
+ }
+ return super.handleCompleteDeferredRemoval();
+ }
+
+ /** The overridden method must call {@link #removeImmediately()} instead of super. */
+ void removeImmediately(String reason) {
+ Slog.d(TAG, "Remove task fragment: " + reason);
+ removeImmediately();
+ }
+
+ @Override
+ void removeImmediately() {
+ mIsRemovalRequested = false;
+ resetAdjacentTaskFragment();
+ super.removeImmediately();
+ sendTaskFragmentVanished();
+ }
+
+ boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ boolean dumpClient, String dumpPackage, final boolean needSep, Runnable header) {
+ boolean printed = false;
+ Runnable headerPrinter = () -> {
+ if (needSep) {
+ pw.println();
+ }
+ if (header != null) {
+ header.run();
+ }
+
+ dumpInner(prefix, pw, dumpAll, dumpPackage);
+ };
+
+ if (dumpPackage == null) {
+ // If we are not filtering by package, we want to print absolutely everything,
+ // so always print the header even if there are no tasks/activities inside.
+ headerPrinter.run();
+ headerPrinter = null;
+ printed = true;
+ }
+
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ WindowContainer child = mChildren.get(i);
+ if (child.asTaskFragment() != null) {
+ printed |= child.asTaskFragment().dump(prefix + " ", fd, pw, dumpAll,
+ dumpClient, dumpPackage, needSep, headerPrinter);
+ } else if (child.asActivityRecord() != null) {
+ ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + " ",
+ "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter,
+ getTask());
+ }
+ }
+
+ return printed;
+ }
+
+ void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
+ pw.print(prefix); pw.print("* "); pw.println(this);
+ final Rect bounds = getRequestedOverrideBounds();
+ if (!bounds.isEmpty()) {
+ pw.println(prefix + " mBounds=" + bounds);
+ }
+ if (mIsRemovalRequested) {
+ pw.println(prefix + " mIsRemovalRequested=true");
+ }
+ if (dumpAll) {
+ printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
+ prefix + " mLastPausedActivity: ", null);
+ }
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ super.dump(pw, prefix, dumpAll);
+ pw.println(prefix + "bounds=" + getBounds().toShortString());
+ final String doublePrefix = prefix + " ";
+ for (int i = mChildren.size() - 1; i >= 0; i--) {
+ final WindowContainer<?> child = mChildren.get(i);
+ pw.println(prefix + "* " + child);
+ // Only dump non-activity because full activity info is already printed by
+ // RootWindowContainer#dumpActivities.
+ if (child.asActivityRecord() == null) {
+ child.dump(pw, doublePrefix, dumpAll);
+ }
+ }
+ }
+
+ @Override
+ void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ final ActivityRecord topActivity = topRunningActivity();
+ proto.write(USER_ID, topActivity != null ? topActivity.mUserId : USER_NULL);
+ proto.write(TITLE, topActivity != null ? topActivity.intent.getComponent()
+ .flattenToShortString() : "TaskFragment");
+ proto.end(token);
+ }
+
+ @Override
+ long getProtoFieldId() {
+ return TASK_FRAGMENT;
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+ return;
+ }
+
+ final long token = proto.start(fieldId);
+
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+
+ proto.write(DISPLAY_ID, getDisplayId());
+ proto.write(ACTIVITY_TYPE, getActivityType());
+ proto.write(MIN_WIDTH, mMinWidth);
+ proto.write(MIN_HEIGHT, mMinHeight);
+
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
new file mode 100644
index 000000000000..690f67c25cfe
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.window.TaskFragmentOrganizer.putExceptionInBundle;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.ITaskFragmentOrganizerController;
+import android.window.TaskFragmentAppearedInfo;
+import android.window.TaskFragmentInfo;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Stores and manages the client {@link android.window.TaskFragmentOrganizer}.
+ */
+public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerController.Stub {
+ private static final String TAG = "TaskFragmentOrganizerController";
+
+ private final ActivityTaskManagerService mAtmService;
+ private final WindowManagerGlobalLock mGlobalLock;
+ /**
+ * A Map which manages the relationship between
+ * {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
+ */
+ private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
+ new ArrayMap<>();
+ /**
+ * A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
+ */
+ private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
+ new ArrayList<>();
+
+ TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
+ mAtmService = atm;
+ mGlobalLock = atm.mGlobalLock;
+ }
+
+ /**
+ * A class to manage {@link ITaskFragmentOrganizer} and its organized
+ * {@link TaskFragment TaskFragments}.
+ */
+ private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
+ private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
+ private final ITaskFragmentOrganizer mOrganizer;
+ private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
+ new WeakHashMap<>();
+ private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
+ new WeakHashMap<>();
+
+ TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
+ mOrganizer = organizer;
+ try {
+ mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mGlobalLock) {
+ removeOrganizer(mOrganizer);
+ }
+ }
+
+ /**
+ * @return {@code true} if taskFragment is organized and not sent the appeared event before.
+ */
+ boolean addTaskFragment(TaskFragment taskFragment) {
+ if (taskFragment.mTaskFragmentAppearedSent) {
+ return false;
+ }
+ if (mOrganizedTaskFragments.contains(taskFragment)) {
+ return false;
+ }
+ mOrganizedTaskFragments.add(taskFragment);
+ return true;
+ }
+
+ void removeTaskFragment(TaskFragment taskFragment) {
+ mOrganizedTaskFragments.remove(taskFragment);
+ }
+
+ void dispose() {
+ while (!mOrganizedTaskFragments.isEmpty()) {
+ final TaskFragment taskFragment = mOrganizedTaskFragments.get(0);
+ taskFragment.removeImmediately();
+ mOrganizedTaskFragments.remove(taskFragment);
+ }
+ mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
+ }
+
+ void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
+ final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+ final SurfaceControl outSurfaceControl = new SurfaceControl(tf.getSurfaceControl(),
+ "TaskFragmentOrganizerController.onTaskFragmentInfoAppeared");
+ try {
+ organizer.onTaskFragmentAppeared(
+ new TaskFragmentAppearedInfo(info, outSurfaceControl));
+ mLastSentTaskFragmentInfos.put(tf, info);
+ tf.mTaskFragmentAppearedSent = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+ }
+ onTaskFragmentParentInfoChanged(organizer, tf);
+ }
+
+ void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
+ try {
+ organizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentVanished callback", e);
+ }
+ tf.mTaskFragmentAppearedSent = false;
+ mLastSentTaskFragmentInfos.remove(tf);
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ }
+
+ void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ // Parent config may have changed. The controller will check if there is any important
+ // config change for the organizer.
+ onTaskFragmentParentInfoChanged(organizer, tf);
+
+ // Check if the info is different from the last reported info.
+ final TaskFragmentInfo info = tf.getTaskFragmentInfo();
+ final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
+ if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
+ info.getConfiguration(), lastInfo.getConfiguration())) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
+ tf.getName());
+ try {
+ organizer.onTaskFragmentInfoChanged(tf.getTaskFragmentInfo());
+ mLastSentTaskFragmentInfos.put(tf, info);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+ }
+ }
+
+ void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment tf) {
+ // Check if the parent info is different from the last reported parent info.
+ if (tf.getParent() == null || tf.getParent().asTask() == null) {
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ return;
+ }
+ final Task parent = tf.getParent().asTask();
+ final Configuration parentConfig = parent.getConfiguration();
+ final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
+ if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)) {
+ return;
+ }
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "TaskFragment parent info changed name=%s parentTaskId=%d",
+ tf.getName(), parent.mTaskId);
+ try {
+ organizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
+ mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
+ }
+ }
+
+ void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
+ Throwable exception) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Sending TaskFragment error exception=%s", exception.toString());
+ final Bundle exceptionBundle = putExceptionInBundle(exception);
+ try {
+ organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskFragmentError callback", e);
+ }
+ }
+ }
+
+ @Override
+ public void registerOrganizer(ITaskFragmentOrganizer organizer) {
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Register task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ if (mTaskFragmentOrganizerState.containsKey(organizer.asBinder())) {
+ throw new IllegalStateException(
+ "Replacing existing organizer currently unsupported");
+ }
+ mTaskFragmentOrganizerState.put(organizer.asBinder(),
+ new TaskFragmentOrganizerState(organizer));
+ }
+ }
+
+ @Override
+ public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
+ validateAndGetState(organizer);
+ final int pid = Binder.getCallingPid();
+ final long uid = Binder.getCallingUid();
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+ "Unregister task fragment organizer=%s uid=%d pid=%d",
+ organizer.asBinder(), uid, pid);
+ removeOrganizer(organizer);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+ if (!state.addTaskFragment(taskFragment)) {
+ return;
+ }
+ PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
+ PendingTaskFragmentEvent.EVENT_APPEARED);
+ if (pendingEvent == null) {
+ pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer,
+ PendingTaskFragmentEvent.EVENT_APPEARED);
+ mPendingTaskFragmentEvents.add(pendingEvent);
+ }
+ }
+
+ void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ handleTaskFragmentInfoChanged(organizer, taskFragment,
+ PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
+ }
+
+ void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
+ TaskFragment taskFragment) {
+ handleTaskFragmentInfoChanged(organizer, taskFragment,
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
+ }
+
+ private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
+ TaskFragment taskFragment, int eventType) {
+ validateAndGetState(organizer);
+ if (!taskFragment.mTaskFragmentAppearedSent) {
+ // Skip if TaskFragment still not appeared.
+ return;
+ }
+ PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
+ if (pendingEvent == null) {
+ pendingEvent = new PendingTaskFragmentEvent(taskFragment, organizer, eventType);
+ } else {
+ if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
+ // Skipped the info changed event if vanished event is pending.
+ return;
+ }
+ // Remove and add for re-ordering.
+ mPendingTaskFragmentEvents.remove(pendingEvent);
+ }
+ mPendingTaskFragmentEvents.add(pendingEvent);
+ }
+
+ void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+ for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+ PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+ if (taskFragment == entry.mTaskFragment) {
+ mPendingTaskFragmentEvents.remove(i);
+ if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
+ // If taskFragment appeared callback is pending, ignore the vanished request.
+ return;
+ }
+ }
+ }
+ if (!taskFragment.mTaskFragmentAppearedSent) {
+ return;
+ }
+ PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(taskFragment,
+ organizer, PendingTaskFragmentEvent.EVENT_VANISHED);
+ mPendingTaskFragmentEvents.add(pendingEvent);
+ state.removeTaskFragment(taskFragment);
+ }
+
+ void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
+ Throwable exception) {
+ validateAndGetState(organizer);
+ Slog.w(TAG, "onTaskFragmentError ", exception);
+ PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer,
+ errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR);
+ mPendingTaskFragmentEvents.add(pendingEvent);
+ }
+
+ private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentOrganizerState state = validateAndGetState(organizer);
+ // remove all of the children of the organized TaskFragment
+ state.dispose();
+ mTaskFragmentOrganizerState.remove(organizer.asBinder());
+ }
+
+ /**
+ * Makes sure that the organizer has been correctly registered to prevent any Sidecar
+ * implementation from organizing {@link TaskFragment} without registering first. In such case,
+ * we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
+ * {@link TaskFragment} after the organizer process died.
+ */
+ private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(organizer.asBinder());
+ if (state == null) {
+ throw new IllegalArgumentException(
+ "TaskFragmentOrganizer has not been registered. Organizer=" + organizer);
+ }
+ return state;
+ }
+
+ /**
+ * A class to store {@link ITaskFragmentOrganizer} and its organized
+ * {@link TaskFragment TaskFragments} with different pending event request.
+ */
+ private static class PendingTaskFragmentEvent {
+ static final int EVENT_APPEARED = 0;
+ static final int EVENT_VANISHED = 1;
+ static final int EVENT_INFO_CHANGED = 2;
+ static final int EVENT_PARENT_INFO_CHANGED = 3;
+ static final int EVENT_ERROR = 4;
+
+ @IntDef(prefix = "EVENT_", value = {
+ EVENT_APPEARED,
+ EVENT_VANISHED,
+ EVENT_INFO_CHANGED,
+ EVENT_PARENT_INFO_CHANGED,
+ EVENT_ERROR
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
+ @EventType
+ private final int mEventType;
+ private final ITaskFragmentOrganizer mTaskFragmentOrg;
+ private final TaskFragment mTaskFragment;
+ private final IBinder mErrorCallback;
+ private final Throwable mException;
+
+ private PendingTaskFragmentEvent(TaskFragment taskFragment,
+ ITaskFragmentOrganizer taskFragmentOrg, @EventType int eventType) {
+ this(taskFragment, taskFragmentOrg, null /* errorCallback */,
+ null /* exception */, eventType);
+
+ }
+
+ private PendingTaskFragmentEvent(ITaskFragmentOrganizer taskFragmentOrg,
+ IBinder errorCallback, Throwable exception, @EventType int eventType) {
+ this(null /* taskFragment */, taskFragmentOrg, errorCallback, exception,
+ eventType);
+ }
+
+ private PendingTaskFragmentEvent(TaskFragment taskFragment,
+ ITaskFragmentOrganizer taskFragmentOrg, IBinder errorCallback, Throwable exception,
+ @EventType int eventType) {
+ mTaskFragment = taskFragment;
+ mTaskFragmentOrg = taskFragmentOrg;
+ mErrorCallback = errorCallback;
+ mException = exception;
+ mEventType = eventType;
+ }
+
+ /**
+ * @return {@code true} if the pending event is related with taskFragment created, vanished
+ * and information changed.
+ */
+ boolean isLifecycleEvent() {
+ switch (mEventType) {
+ case EVENT_APPEARED:
+ case EVENT_VANISHED:
+ case EVENT_INFO_CHANGED:
+ case EVENT_PARENT_INFO_CHANGED:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ @Nullable
+ private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
+ for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+ PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+ if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+ int type) {
+ for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
+ PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
+ if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ void dispatchPendingEvents() {
+ if (mAtmService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()
+ || mPendingTaskFragmentEvents.isEmpty()) {
+ return;
+ }
+ for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
+ PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
+ final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
+ final TaskFragment taskFragment = event.mTaskFragment;
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
+ if (state == null) continue;
+ switch (event.mEventType) {
+ case PendingTaskFragmentEvent.EVENT_APPEARED:
+ state.onTaskFragmentAppeared(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_VANISHED:
+ state.onTaskFragmentVanished(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
+ state.onTaskFragmentInfoChanged(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
+ state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_ERROR:
+ state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback,
+ event.mException);
+ }
+ }
+ mPendingTaskFragmentEvents.clear();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 09c5581385dd..fa7b276bc418 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,22 +16,17 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
-import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
+import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
import android.os.Binder;
@@ -69,21 +64,6 @@ import java.util.function.Consumer;
class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
- /**
- * Masks specifying which configurations are important to report back to an organizer when
- * changed.
- */
- private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
- private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
-
- // The set of modes that are currently supports
- // TODO: Remove once the task organizer can support all modes
- @VisibleForTesting
- static final int[] UNSUPPORTED_WINDOWING_MODES = {
- WINDOWING_MODE_UNDEFINED,
- WINDOWING_MODE_FREEFORM
- };
-
private class DeathRecipient implements IBinder.DeathRecipient {
ITaskOrganizer mTaskOrganizer;
@@ -122,109 +102,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return mTaskOrganizer.asBinder();
}
- void addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
- TaskSnapshot taskSnapshot) {
- final StartingWindowInfo info = task.getStartingWindowInfo(activity);
- if (launchTheme != 0) {
- info.splashScreenThemeResId = launchTheme;
- }
- info.mTaskSnapshot = taskSnapshot;
- // make this happen prior than prepare surface
- try {
- mTaskOrganizer.addStartingWindow(info, activity.token);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskStart callback", e);
- }
- }
-
- // Capture the animation surface control for activity's main window
- private class StartingWindowAnimationAdaptor implements AnimationAdapter {
- private SurfaceControl mAnimationLeash;
- @Override
- public boolean getShowWallpaper() {
- return false;
- }
-
- @Override
- public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
- int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
- mAnimationLeash = animationLeash;
- }
-
- @Override
- public void onAnimationCancelled(SurfaceControl animationLeash) {
- if (mAnimationLeash == animationLeash) {
- mAnimationLeash = null;
- }
- }
-
- @Override
- public long getDurationHint() {
- return 0;
- }
-
- @Override
- public long getStatusBarTransitionsStartTime() {
- return 0;
- }
-
- @Override
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
- pw.print(mAnimationLeash);
- pw.println();
- }
-
- @Override
- public void dumpDebug(ProtoOutputStream proto) {
- }
- }
-
- void removeStartingWindow(Task task, boolean prepareAnimation) {
- SurfaceControl windowAnimationLeash = null;
- Rect mainFrame = null;
- final boolean playShiftUpAnimation = !task.inMultiWindowMode();
- if (prepareAnimation && playShiftUpAnimation) {
- final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
- if (topActivity != null) {
- final WindowState mainWindow =
- topActivity.findMainWindow(false/* includeStartingApp */);
- if (mainWindow != null) {
- final StartingWindowAnimationAdaptor adaptor =
- new StartingWindowAnimationAdaptor();
- final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
- mainWindow.startAnimation(t, adaptor, false,
- ANIMATION_TYPE_STARTING_REVEAL);
- windowAnimationLeash = adaptor.mAnimationLeash;
- mainFrame = mainWindow.getRelativeFrame();
- t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
- }
- }
- }
- try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
- mainFrame, prepareAnimation);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
- }
- }
-
- void copySplashScreenView(Task task) {
- try {
- mTaskOrganizer.copySplashScreenView(task.mTaskId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
- }
- }
-
- void onAppSplashScreenViewRemoved(Task task) {
- try {
- mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
- }
- }
-
SurfaceControl prepareLeash(Task task, String reason) {
return new SurfaceControl(task.getSurfaceControl(), reason);
}
@@ -311,23 +188,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mUid = uid;
}
- void addStartingWindow(Task t, ActivityRecord activity, int launchTheme,
- TaskSnapshot taskSnapshot) {
- mOrganizer.addStartingWindow(t, activity, launchTheme, taskSnapshot);
- }
-
- void removeStartingWindow(Task t, boolean prepareAnimation) {
- mOrganizer.removeStartingWindow(t, prepareAnimation);
- }
-
- void copySplashScreenView(Task t) {
- mOrganizer.copySplashScreenView(t);
- }
-
- public void onAppSplashScreenViewRemoved(Task t) {
- mOrganizer.onAppSplashScreenViewRemoved(t);
- }
-
/**
* Register this task with this state, but doesn't trigger the task appeared callback to
* the organizer.
@@ -389,6 +249,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mOrganizer.mTaskOrganizer, t);
}
}
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ // dispose is only called outside of transitions (eg during unregister). Since
+ // we "migrate" surfaces when replacing organizers, visibility gets delegated
+ // to transitions; however, since there is no transition at this point, we have
+ // to manually show the surface here.
+ if (t.mTaskOrganizer != null && t.getSurfaceControl() != null) {
+ t.getSyncTransaction().show(t.getSurfaceControl());
+ }
+ }
}
// Remove organizer state after removing tasks so we get a chance to send
@@ -481,7 +350,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
+ final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
+ final Runnable withGlobalLock = () -> {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
organizer.asBinder(), uid);
if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
@@ -490,24 +360,27 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
new TaskOrganizerState(organizer, uid));
}
- final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>();
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
mService.mRootWindowContainer.forAllTasks((task) -> {
- if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) {
- return;
- }
-
boolean returnTask = !task.mCreatedByOrganizer;
task.updateTaskOrganizerState(true /* forceUpdate */,
returnTask /* skipTaskAppeared */);
if (returnTask) {
SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task,
"TaskOrganizerController.registerTaskOrganizer");
- taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
+ taskInfos.add(
+ new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl));
}
});
- return new ParceledListSlice<>(taskInfos);
+ };
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+ } else {
+ synchronized (mGlobalLock) {
+ withGlobalLock.run();
+ }
}
+ return new ParceledListSlice<>(taskInfos);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -519,7 +392,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
try {
- synchronized (mGlobalLock) {
+ final Runnable withGlobalLock = () -> {
final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
if (state == null) {
return;
@@ -528,6 +401,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
organizer.asBinder(), uid);
state.unlinkDeath();
state.dispose();
+ };
+ if (mService.getTransitionController().isShellTransitionsEnabled()) {
+ mService.getTransitionController().mRunningLock.runWhenIdle(1000, withGlobalLock);
+ } else {
+ synchronized (mGlobalLock) {
+ withGlobalLock.run();
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -537,46 +417,130 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
/**
* @return the task organizer key for a given windowing mode.
*/
- ITaskOrganizer getTaskOrganizer(int windowingMode) {
- return isSupportedWindowingMode(windowingMode)
- ? mTaskOrganizers.peekLast()
- : null;
+ ITaskOrganizer getTaskOrganizer() {
+ return mTaskOrganizers.peekLast();
}
- boolean isSupportedWindowingMode(int winMode) {
- return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
+ // Capture the animation surface control for activity's main window
+ private static class StartingWindowAnimationAdaptor implements AnimationAdapter {
+ private SurfaceControl mAnimationLeash;
+ @Override
+ public boolean getShowWallpaper() {
+ return false;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ mAnimationLeash = animationLeash;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (mAnimationLeash == animationLeash) {
+ mAnimationLeash = null;
+ }
+ }
+
+ @Override
+ public long getDurationHint() {
+ return 0;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return 0;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
+ pw.print(mAnimationLeash);
+ pw.println();
+ }
+
+ @Override
+ public void dumpDebug(ProtoOutputStream proto) {
+ }
}
boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
TaskSnapshot taskSnapshot) {
final Task rootTask = task.getRootTask();
- if (rootTask == null || rootTask.mTaskOrganizer == null || activity.mStartingData == null) {
+ if (rootTask == null || activity.mStartingData == null) {
+ return false;
+ }
+ final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ if (lastOrganizer == null) {
+ return false;
+ }
+ final StartingWindowInfo info = task.getStartingWindowInfo(activity);
+ if (launchTheme != 0) {
+ info.splashScreenThemeResId = launchTheme;
+ }
+ info.mTaskSnapshot = taskSnapshot;
+ // make this happen prior than prepare surface
+ try {
+ lastOrganizer.addStartingWindow(info, activity.token);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskStart callback", e);
return false;
}
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.addStartingWindow(task, activity, launchTheme, taskSnapshot);
return true;
}
void removeStartingWindow(Task task, boolean prepareAnimation) {
final Task rootTask = task.getRootTask();
- if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ if (rootTask == null) {
+ return;
+ }
+ final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ if (lastOrganizer == null) {
return;
}
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.removeStartingWindow(task, prepareAnimation);
+ SurfaceControl windowAnimationLeash = null;
+ Rect mainFrame = null;
+ final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+ if (prepareAnimation && playShiftUpAnimation) {
+ final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
+ if (topActivity != null) {
+ final WindowState mainWindow =
+ topActivity.findMainWindow(false/* includeStartingApp */);
+ if (mainWindow != null) {
+ final StartingWindowAnimationAdaptor adaptor =
+ new StartingWindowAnimationAdaptor();
+ final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
+ mainWindow.startAnimation(t, adaptor, false,
+ ANIMATION_TYPE_STARTING_REVEAL);
+ windowAnimationLeash = adaptor.mAnimationLeash;
+ mainFrame = mainWindow.getRelativeFrame();
+ t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
+ }
+ }
+ }
+ try {
+ lastOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
+ mainFrame, prepareAnimation);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
+ }
}
boolean copySplashScreenView(Task task) {
final Task rootTask = task.getRootTask();
- if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ if (rootTask == null) {
+ return false;
+ }
+ final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ if (lastOrganizer == null) {
+ return false;
+ }
+ try {
+ lastOrganizer.copySplashScreenView(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
return false;
}
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.copySplashScreenView(task);
return true;
}
@@ -588,12 +552,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
*/
public void onAppSplashScreenViewRemoved(Task task) {
final Task rootTask = task.getRootTask();
- if (rootTask == null || rootTask.mTaskOrganizer == null) {
+ if (rootTask == null) {
return;
}
- final TaskOrganizerState state =
- mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.onAppSplashScreenViewRemoved(task);
+ final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ if (lastOrganizer == null) {
+ return;
+ }
+ try {
+ lastOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
+ }
}
void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -688,7 +658,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
task.getDisplayId(), task.getWindowingMode());
- task.removeImmediately("deleteRootTask");
+ task.remove(true /* withTransition */, "deleteRootTask");
return true;
}
} finally {
@@ -766,18 +736,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mTmpTaskInfo.configuration.unset();
task.fillTaskInfo(mTmpTaskInfo);
- boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo);
- if (!changed) {
- int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
- final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
- ? (int) mTmpTaskInfo.configuration.windowConfiguration.diff(
- lastInfo.configuration.windowConfiguration,
- true /* compareUndefined */) : 0;
- if ((winCfgChanges & REPORT_WINDOW_CONFIGS) == 0) {
- cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
- }
- changed = (cfgChanges & REPORT_CONFIGS) != 0;
- }
+ boolean changed = !mTmpTaskInfo.equalsForTaskOrganizer(lastInfo)
+ || !configurationsAreEqualForOrganizer(
+ mTmpTaskInfo.configuration, lastInfo.configuration);
if (!(changed || force)) {
// mTmpTaskInfo will be reused next time.
return;
@@ -1010,9 +971,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
for (int k = 0; k < tasks.size(); k++) {
final Task task = tasks.get(k);
final int mode = task.getWindowingMode();
- if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, mode)) {
- continue;
- }
pw.println(innerPrefix + " ("
+ WindowConfiguration.windowingModeToString(mode) + ") " + task);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e74371036619..54390dc6b58c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -114,9 +114,9 @@ class TaskSnapshotController {
private final boolean mIsRunningOnIoT;
/**
- * Flag indicating whether we are running on an Android Wear device.
+ * Flag indicating if task snapshot is enabled on this device.
*/
- private final boolean mIsRunningOnWear;
+ private boolean mTaskSnapshotEnabled;
TaskSnapshotController(WindowManagerService service) {
mService = service;
@@ -127,10 +127,12 @@ class TaskSnapshotController {
PackageManager.FEATURE_LEANBACK);
mIsRunningOnIoT = mService.mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_EMBEDDED);
- mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WATCH);
mHighResTaskSnapshotScale = mService.mContext.getResources().getFloat(
com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+ mTaskSnapshotEnabled =
+ !mService.mContext
+ .getResources()
+ .getBoolean(com.android.internal.R.bool.config_disableTaskSnapshots);
}
void systemReady() {
@@ -487,8 +489,12 @@ class TaskSnapshotController {
return builder.build();
}
+ void setTaskSnapshotEnabled(boolean enabled) {
+ mTaskSnapshotEnabled = enabled;
+ }
+
boolean shouldDisableSnapshots() {
- return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
+ return mIsRunningOnTv || mIsRunningOnIoT || !mTaskSnapshotEnabled;
}
/**
@@ -688,6 +694,7 @@ class TaskSnapshotController {
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "mHighResTaskSnapshotScale=" + mHighResTaskSnapshotScale);
+ pw.println(prefix + "mTaskSnapshotEnabled=" + mTaskSnapshotEnabled);
mCache.dump(pw, prefix);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index cc4abab01b72..059eb876ad94 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -72,6 +72,7 @@ import android.util.Slog;
import android.view.IWindowSession;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -166,6 +167,7 @@ class TaskSnapshotSurface implements StartingSurface {
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
final Rect taskBounds;
final InsetsState mTmpInsetsState = new InsetsState();
+ final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
final TaskDescription taskDescription = new TaskDescription();
@@ -227,7 +229,8 @@ class TaskSnapshotSurface implements StartingSurface {
int displayId = activity.getDisplayContent().getDisplayId();
try {
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- mTmpInsetsState, null /* outInputChannel */, mTmpInsetsState, mTempControls);
+ mRequestedVisibilities, null /* outInputChannel */, mTmpInsetsState,
+ mTempControls);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return null;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0cd098070401..07f0197f520b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,11 +16,19 @@
package com.android.server.wm;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -31,27 +39,36 @@ import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionFlags;
+import static android.view.WindowManager.TransitionType;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
+import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.view.SurfaceControl;
-import android.view.WindowManager;
import android.view.animation.Animation;
import android.window.IRemoteTransition;
import android.window.TransitionInfo;
@@ -59,6 +76,7 @@ import android.window.TransitionInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -100,9 +118,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
@Retention(RetentionPolicy.SOURCE)
@interface TransitionState {}
- final @WindowManager.TransitionType int mType;
+ final @TransitionType int mType;
private int mSyncId;
- private @WindowManager.TransitionFlags int mFlags;
+ private @TransitionFlags int mFlags;
private final TransitionController mController;
private final BLASTSyncEngine mSyncEngine;
private IRemoteTransition mRemoteTransition = null;
@@ -124,10 +142,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ /**
+ * Set of participating windowtokens (activity/wallpaper) which are visible at the end of
+ * the transition animation.
+ */
+ private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
+
+ /** Custom activity-level animation options and callbacks. */
+ private TransitionInfo.AnimationOptions mOverrideOptions;
+ private IRemoteCallback mClientAnimationStartCallback = null;
+ private IRemoteCallback mClientAnimationFinishCallback = null;
+
private @TransitionState int mState = STATE_COLLECTING;
- private boolean mReadyCalled = false;
+ private final ReadyTracker mReadyTracker = new ReadyTracker();
+
+ // TODO(b/188595497): remove when not needed.
+ /** @see RecentsAnimationController#mNavigationBarAttachedToApp */
+ private boolean mNavBarAttachedToApp = false;
+ private int mRecentsDisplayId = INVALID_DISPLAY;
- Transition(@WindowManager.TransitionType int type, @WindowManager.TransitionFlags int flags,
+ Transition(@TransitionType int type, @TransitionFlags int flags,
TransitionController controller, BLASTSyncEngine syncEngine) {
mType = type;
mFlags = flags;
@@ -136,11 +170,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mSyncId = mSyncEngine.startSyncSet(this);
}
+ void addFlag(int flag) {
+ mFlags |= flag;
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
}
+ @TransitionFlags
+ int getFlags() {
+ return mFlags;
+ }
+
/**
* Formally starts the transition. Participants can be collected before this is started,
* but this won't consider itself ready until started -- even if all the participants have
@@ -153,9 +196,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mState = STATE_STARTED;
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d",
mSyncId);
- if (mReadyCalled) {
- setReady();
- }
+ applyReady();
}
/**
@@ -170,6 +211,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
for (WindowContainer curr = wc.getParent(); curr != null && !mChanges.containsKey(curr);
curr = curr.getParent()) {
mChanges.put(curr, new ChangeInfo(curr));
+ if (isReadyGroup(curr)) {
+ mReadyTracker.addGroup(curr);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
+ + " Transition %d with root=%s", mSyncId, curr);
+ }
}
if (mParticipants.contains(wc)) return;
mSyncEngine.addToSyncSet(mSyncId, wc);
@@ -206,26 +252,60 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mChanges.get(wc).mExistenceChanged = true;
}
+ private void sendRemoteCallback(@Nullable IRemoteCallback callback) {
+ if (callback == null) return;
+ mController.mAtm.mH.sendMessage(PooledLambda.obtainMessage(cb -> {
+ try {
+ cb.sendResult(null);
+ } catch (RemoteException e) { }
+ }, callback));
+ }
+
+ /**
+ * Set animation options for collecting transition by ActivityRecord.
+ * @param options AnimationOptions captured from ActivityOptions
+ */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options,
+ @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
+ if (mSyncId < 0) return;
+ mOverrideOptions = options;
+ sendRemoteCallback(mClientAnimationStartCallback);
+ mClientAnimationStartCallback = startCallback;
+ mClientAnimationFinishCallback = finishCallback;
+ }
+
/**
* Call this when all known changes related to this transition have been applied. Until
* all participants have finished drawing, the transition can still collect participants.
*
* If this is called before the transition is started, it will be deferred until start.
+ *
+ * @param wc A reference point to determine which ready-group to update. For now, each display
+ * has its own ready-group, so this is used to look-up which display to mark ready.
+ * The transition will wait for all groups to be ready.
*/
- void setReady(boolean ready) {
+ void setReady(WindowContainer wc, boolean ready) {
if (mSyncId < 0) return;
- if (mState < STATE_STARTED) {
- mReadyCalled = ready;
- return;
- }
+ mReadyTracker.setReadyFrom(wc, ready);
+ applyReady();
+ }
+
+ private void applyReady() {
+ if (mState < STATE_STARTED) return;
+ final boolean ready = mReadyTracker.allReady();
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Set transition ready=%b %d", ready, mSyncId);
mSyncEngine.setReady(mSyncId, ready);
}
- /** @see #setReady . This calls with parameter true. */
- void setReady() {
- setReady(true);
+ /**
+ * Sets all possible ready groups to ready.
+ * @see ReadyTracker#setAllReady.
+ */
+ void setAllReady() {
+ if (mSyncId < 0) return;
+ mReadyTracker.setAllReady();
+ applyReady();
}
/**
@@ -275,20 +355,73 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Commit all going-invisible containers
+ boolean activitiesWentInvisible = false;
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar != null && !ar.isVisibleRequested()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- " Commit activity becoming invisible: %s", ar);
- ar.commitVisibility(false /* visible */, false /* performLayout */);
+ if (ar != null) {
+ boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar);
+ // We need both the expected visibility AND current requested-visibility to be
+ // false. If it is expected-visible but not currently visible, it means that
+ // another animation is queued-up to animate this to invisibility, so we can't
+ // remove the surfaces yet. If it is currently visible, but not expected-visible,
+ // then doing commitVisibility here would actually be out-of-order and leave the
+ // activity in a bad state.
+ if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) {
+ boolean commitVisibility = true;
+ if (ar.getDeferHidingClient() && ar.getTask() != null) {
+ if (ar.pictureInPictureArgs != null
+ && ar.pictureInPictureArgs.isAutoEnterEnabled()) {
+ mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs);
+ // Avoid commit visibility to false here, or else we will get a sudden
+ // "flash" / surface going invisible for a split second.
+ commitVisibility = false;
+ } else {
+ mController.mAtm.mTaskSupervisor.mUserLeaving = true;
+ ar.getTaskFragment().startPausing(false /* uiSleeping */,
+ null /* resuming */, "finishTransition");
+ mController.mAtm.mTaskSupervisor.mUserLeaving = false;
+ }
+ }
+ if (commitVisibility) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Commit activity becoming invisible: %s", ar);
+ ar.commitVisibility(false /* visible */, false /* performLayout */);
+ activitiesWentInvisible = true;
+ }
+ }
+ if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
+ // Legacy dispatch relies on this (for now).
+ ar.mEnteringAnimation = visibleAtTransitionEnd;
+ }
+ mController.dispatchLegacyAppTransitionFinished(ar);
}
final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
- if (wt != null && !wt.isVisibleRequested()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- " Commit wallpaper becoming invisible: %s", ar);
- wt.commitVisibility(false /* visible */);
+ if (wt != null) {
+ final boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(wt);
+ if (!visibleAtTransitionEnd && !wt.isVisibleRequested()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Commit wallpaper becoming invisible: %s", wt);
+ wt.commitVisibility(false /* visible */);
+ }
}
}
+ if (activitiesWentInvisible) {
+ // Always schedule stop processing when transition finishes because activities don't
+ // stop while they are in a transition thus their stop could still be pending.
+ mController.mAtm.mTaskSupervisor
+ .scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+ }
+
+ sendRemoteCallback(mClientAnimationFinishCallback);
+
+ legacyRestoreNavigationBarFromApp();
+
+ if (mRecentsDisplayId != INVALID_DISPLAY) {
+ // Clean up input monitors (for recents)
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
+ dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+ }
}
void abort() {
@@ -297,6 +430,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (mState != STATE_COLLECTING) {
throw new IllegalStateException("Too late to abort.");
}
+ mController.dispatchLegacyAppTransitionCancelled();
mState = STATE_ABORT;
// Syncengine abort will call through to onTransactionReady()
mSyncEngine.abort(mSyncId);
@@ -327,6 +461,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId)
.getPendingTransaction().merge(transaction);
mSyncId = -1;
+ mOverrideOptions = null;
return;
}
@@ -340,9 +475,18 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Resolve the animating targets from the participants
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
+ info.setAnimationOptions(mOverrideOptions);
+
+ // TODO(b/188669821): Move to animation impl in shell.
+ handleLegacyRecentsStartBehavior(displayId, info);
handleNonAppWindowsInTransition(displayId, mType, mFlags);
+ reportStartReasonsToLogger();
+
+ // The callback is only populated for custom activity-level client animations
+ sendRemoteCallback(mClientAnimationStartCallback);
+
// Manually show any activities that are visibleRequested. This is needed to properly
// support simultaneous animation queueing/merging. Specifically, if transition A makes
// an activity invisible, it's finishTransaction (which is applied *after* the animation)
@@ -354,12 +498,41 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
if (ar == null || !ar.mVisibleRequested) continue;
transaction.show(ar.getSurfaceControl());
+
+ // Also manually show any non-reported parents. This is necessary in a few cases
+ // where a task is NOT organized but had its visibility changed within its direct
+ // parent. An example of this is if an alternate home leaf-task HB is started atop the
+ // normal home leaf-task HA: these are both in the Home root-task HR, so there will be a
+ // transition containing HA and HB where HA surface is hidden. If a standard task SA is
+ // launched on top, then HB finishes, no transition will happen since neither home is
+ // visible. When SA finishes, the transition contains HR rather than HA. Since home
+ // leaf-tasks are NOT organized, HA won't be in the transition and thus its surface
+ // wouldn't be shown. Just show is safe here since all other properties will have
+ // already been reset by the original hiding-transition's finishTransaction (we can't
+ // show in the finishTransaction because by then the activity doesn't hide until
+ // surface placement).
+ for (WindowContainer p = ar.getParent(); p != null && !mTargets.contains(p);
+ p = p.getParent()) {
+ if (p.getSurfaceControl() != null) {
+ transaction.show(p.getSurfaceControl());
+ }
+ }
+ }
+
+ // Record windowtokens (activity/wallpaper) that are expected to be visible after the
+ // transition animation. This will be used in finishTransition to prevent prematurely
+ // committing visibility.
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mParticipants.valueAt(i);
+ if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
+ mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
}
mStartTransaction = transaction;
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
if (mController.getTransitionPlayer() != null) {
+ mController.dispatchLegacyAppTransitionStarting(info);
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Calling onTransitionReady: %s", info);
@@ -375,6 +548,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
cleanUpOnFailure();
}
mSyncId = -1;
+ mOverrideOptions = null;
}
/**
@@ -394,14 +568,149 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
finishTransition();
}
+ /** @see RecentsAnimationController#attachNavigationBarToApp */
+ private void handleLegacyRecentsStartBehavior(int displayId, TransitionInfo info) {
+ if ((mFlags & TRANSIT_FLAG_IS_RECENTS) == 0) {
+ return;
+ }
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null) return;
+ mRecentsDisplayId = displayId;
+
+ // Recents has an input-consumer to grab input from the "live tile" app. Set that up here
+ final InputConsumerImpl recentsAnimationInputConsumer =
+ dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ if (recentsAnimationInputConsumer != null) {
+ // find the top-most going-away activity and the recents activity. The top-most
+ // is used as layer reference while the recents is used for registering the consumer
+ // override.
+ ActivityRecord recentsActivity = null;
+ ActivityRecord topActivity = null;
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getTaskInfo() == null) continue;
+ final Task task = Task.fromWindowContainerToken(
+ info.getChanges().get(i).getTaskInfo().token);
+ if (task == null) continue;
+ final int activityType = change.getTaskInfo().topActivityType;
+ final boolean isRecents = activityType == ACTIVITY_TYPE_HOME
+ || activityType == ACTIVITY_TYPE_RECENTS;
+ if (isRecents && recentsActivity == null) {
+ recentsActivity = task.getTopVisibleActivity();
+ } else if (!isRecents && topActivity == null) {
+ topActivity = task.getTopNonFinishingActivity();
+ }
+ }
+ if (recentsActivity != null && topActivity != null) {
+ recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(
+ topActivity.getBounds());
+ dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity);
+ }
+ }
+
+ // The rest of this function handles nav-bar reparenting
+
+ if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+ // Skip the case where the nav bar is controlled by fade rotation.
+ || dc.getFadeRotationAnimationController() != null) {
+ return;
+ }
+
+ WindowContainer topWC = null;
+ // Find the top-most non-home, closing app.
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change c = info.getChanges().get(i);
+ if (c.getTaskInfo() == null || c.getTaskInfo().displayId != displayId
+ || c.getTaskInfo().getActivityType() != ACTIVITY_TYPE_STANDARD
+ || !(c.getMode() == TRANSIT_CLOSE || c.getMode() == TRANSIT_TO_BACK)) {
+ continue;
+ }
+ topWC = WindowContainer.fromBinder(c.getContainer().asBinder());
+ break;
+ }
+ if (topWC == null || topWC.inMultiWindowMode()) {
+ return;
+ }
+
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null || navWindow.mToken == null) {
+ return;
+ }
+ mNavBarAttachedToApp = true;
+ navWindow.mToken.cancelAnimation();
+ final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+ final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+ t.reparent(navSurfaceControl, topWC.getSurfaceControl());
+ t.show(navSurfaceControl);
+
+ final WindowContainer imeContainer = dc.getImeContainer();
+ if (imeContainer.isVisible()) {
+ t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
+ } else {
+ // Place the nav bar on top of anything else in the top activity.
+ t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
+ }
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(displayId, false);
+ }
+ }
+
+ /** @see RecentsAnimationController#restoreNavigationBarFromApp */
+ void legacyRestoreNavigationBarFromApp() {
+ if (!mNavBarAttachedToApp) return;
+ mNavBarAttachedToApp = false;
+
+ if (mRecentsDisplayId == INVALID_DISPLAY) {
+ Slog.e(TAG, "Reparented navigation bar without a valid display");
+ mRecentsDisplayId = DEFAULT_DISPLAY;
+ }
+
+ if (mController.mStatusBar != null) {
+ mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true);
+ }
+
+ final DisplayContent dc =
+ mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
+ final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
+ if (navWindow == null) return;
+ navWindow.setSurfaceTranslationY(0);
+
+ final WindowToken navToken = navWindow.mToken;
+ if (navToken == null) return;
+ final SurfaceControl.Transaction t = dc.getPendingTransaction();
+ final WindowContainer parent = navToken.getParent();
+ t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
+
+ boolean animate = false;
+ // Search for the home task. If it is supposed to be visible, then the navbar is not at
+ // the bottom of the screen, so we need to animate it.
+ for (int i = 0; i < mTargets.size(); ++i) {
+ final Task task = mTargets.valueAt(i).asTask();
+ if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+ animate = task.isVisibleRequested();
+ break;
+ }
+
+ if (animate) {
+ final NavBarFadeAnimationController controller =
+ new NavBarFadeAnimationController(dc);
+ controller.fadeWindowToken(true);
+ } else {
+ // Reparent the SurfaceControl of nav bar token back.
+ t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+ }
+ }
+
private void handleNonAppWindowsInTransition(int displayId,
- @WindowManager.TransitionType int transit, int flags) {
+ @TransitionType int transit, @TransitionFlags int flags) {
final DisplayContent dc =
mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
if (dc == null) {
return;
}
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY
+ if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
+ || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
&& !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
&& (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
@@ -427,6 +736,23 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
}
+ private void reportStartReasonsToLogger() {
+ // Record transition start in metrics logger. We just assume everything is "DRAWN"
+ // at this point since splash-screen is a presentation (shell) detail.
+ ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
+ if (r == null || !r.mVisibleRequested) continue;
+ // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
+ // ready due to starting-window.
+ reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
+ && !r.mLastAllReadyAtSync)
+ ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN);
+ }
+ mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
+ reasons);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -465,6 +791,22 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return wc.asWallpaperToken() != null;
}
+ private static boolean occludesKeyguard(WindowContainer wc) {
+ final ActivityRecord ar = wc.asActivityRecord();
+ if (ar != null) {
+ return ar.canShowWhenLocked();
+ }
+ final Task t = wc.asTask();
+ if (t != null) {
+ // Get the top activity which was visible (since this is going away, it will remain
+ // client visible until the transition is finished).
+ // skip hidden (or about to hide) apps
+ final ActivityRecord top = t.getActivity(WindowToken::isClientVisible);
+ return top != null && top.canShowWhenLocked();
+ }
+ return false;
+ }
+
/**
* Under some conditions (eg. all visible targets within a parent container are transitioning
* the same way) the transition can be "promoted" to the parent container. This means an
@@ -612,7 +954,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// of participants that should always be reported even if they aren't top.
for (WindowContainer wc : participants) {
// Don't include detached windows.
- if (!wc.isAttached()) continue;
+ if (!wc.isAttached()) {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+ " Rejecting as detached: %s", wc);
+ continue;
+ }
final ChangeInfo changeInfo = changes.get(wc);
@@ -629,6 +975,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (reportIfNotTop(wc)) {
tmpList.add(wc);
}
+ // Wallpaper must be the top (regardless of how nested it is in DisplayAreas).
+ boolean skipIntermediateReports = isWallpaper(wc);
for (WindowContainer p = wc.getParent(); p != null; p = p.getParent()) {
if (!p.isAttached() || !changes.get(p).hasChanged(p)) {
// Again, we're skipping no-ops
@@ -637,7 +985,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (participants.contains(p)) {
topParent = p;
break;
- } else if (reportIfNotTop(p)) {
+ } else if (isWallpaper(p)) {
+ skipIntermediateReports = true;
+ } else if (reportIfNotTop(p) && !skipIntermediateReports) {
tmpList.add(p);
}
}
@@ -707,12 +1057,21 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/**
+ * A ready group is defined by a root window-container where all transitioning windows under
+ * it are expected to animate together as a group. At the moment, this treats each display as
+ * a ready-group to match the existing legacy transition behavior.
+ */
+ private static boolean isReadyGroup(WindowContainer wc) {
+ return wc instanceof DisplayContent;
+ }
+
+ /**
* Construct a TransitionInfo object from a set of targets and changes. Also populates the
* root surface.
*/
@VisibleForTesting
@NonNull
- static TransitionInfo calculateTransitionInfo(int type, int flags,
+ static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
final TransitionInfo out = new TransitionInfo(type, flags);
@@ -723,17 +1082,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
// Find the top-most shared ancestor of app targets
- WindowContainer ancestor = null;
- for (int i = appTargets.size() - 1; i >= 0; --i) {
- final WindowContainer wc = appTargets.valueAt(i);
- ancestor = wc;
- break;
- }
- if (ancestor == null) {
+ if (appTargets.isEmpty()) {
out.setRootLeash(new SurfaceControl(), 0, 0);
return out;
}
- ancestor = ancestor.getParent();
+ WindowContainer ancestor = appTargets.valueAt(appTargets.size() - 1).getParent();
// Go up ancestor parent chain until all targets are descendants.
ancestorLoop:
@@ -798,6 +1151,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
task.fillTaskInfo(tinfo);
change.setTaskInfo(tinfo);
+ change.setRotationAnimation(getTaskRotationAnimation(task));
+ final ActivityRecord topMostActivity = task.getTopMostActivity();
+ change.setAllowEnterPip(topMostActivity != null
+ && topMostActivity.checkEnterPictureInPictureAppOpsState());
}
out.addChange(change);
}
@@ -805,6 +1162,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
return out;
}
+ private static int getTaskRotationAnimation(@NonNull Task task) {
+ final ActivityRecord top = task.getTopVisibleActivity();
+ if (top == null) return ROTATION_ANIMATION_UNSPECIFIED;
+ final WindowState mainWin = top.findMainWindow(false);
+ if (mainWin == null) return ROTATION_ANIMATION_UNSPECIFIED;
+ int anim = mainWin.getRotationAnimationHint();
+ if (anim >= 0) return anim;
+ anim = mainWin.getAttrs().rotationAnimation;
+ if (anim != ROTATION_ANIMATION_SEAMLESS) return anim;
+ if (mainWin != task.mDisplayContent.getDisplayPolicy().getTopFullscreenOpaqueWindow()
+ || !top.matchParentBounds()) {
+ // At the moment, we only support seamless rotation if there is only one window showing.
+ return ROTATION_ANIMATION_UNSPECIFIED;
+ }
+ return mainWin.getAttrs().rotationAnimation;
+ }
+
+ boolean getLegacyIsReady() {
+ return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
+ }
+
static Transition fromBinder(IBinder binder) {
return (Transition) binder;
}
@@ -891,9 +1269,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
flags |= FLAG_IS_VOICE_INTERACTION;
}
}
+ final DisplayContent dc = wc.asDisplayContent();
+ if (dc != null) {
+ flags |= FLAG_IS_DISPLAY;
+ if (dc.hasAlertWindowSurfaces()) {
+ flags |= FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+ }
+ }
if (isWallpaper(wc)) {
flags |= FLAG_IS_WALLPAPER;
}
+ if (occludesKeyguard(wc)) {
+ flags |= FLAG_OCCLUDES_KEYGUARD;
+ }
return flags;
}
@@ -910,4 +1298,95 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mChildren.addAll(wcs);
}
}
+
+ /**
+ * The transition sync mechanism has 2 parts:
+ * 1. Whether all WM operations for a particular transition are "ready" (eg. did the app
+ * launch or stop or get a new configuration?).
+ * 2. Whether all the windows involved have finished drawing their final-state content.
+ *
+ * A transition animation can play once both parts are complete. This ready-tracker keeps track
+ * of part (1). Currently, WM code assumes that "readiness" (part 1) is grouped. This means that
+ * even if the WM operations in one group are ready, the whole transition itself may not be
+ * ready if there are WM operations still pending in another group. This class helps keep track
+ * of readiness across the multiple groups. Currently, we assume that each display is a group
+ * since that is how it has been until now.
+ */
+ private static class ReadyTracker {
+ private final ArrayMap<WindowContainer, Boolean> mReadyGroups = new ArrayMap<>();
+
+ /**
+ * Ensures that this doesn't report as allReady before it has been used. This is needed
+ * in very niche cases where a transition is a no-op (nothing has been collected) but we
+ * still want to be marked ready (via. setAllReady).
+ */
+ private boolean mUsed = false;
+
+ /**
+ * If true, this overrides all ready groups and reports ready. Used by shell-initiated
+ * transitions via {@link #setAllReady()}.
+ */
+ private boolean mReadyOverride = false;
+
+ /**
+ * Adds a ready-group. Any setReady calls in this subtree will be tracked together. For
+ * now these are only DisplayContents.
+ */
+ void addGroup(WindowContainer wc) {
+ if (mReadyGroups.containsKey(wc)) {
+ Slog.e(TAG, "Trying to add a ready-group twice: " + wc);
+ return;
+ }
+ mReadyGroups.put(wc, false);
+ }
+
+ /**
+ * Sets a group's ready state.
+ * @param wc Any container within a group's subtree. Used to identify the ready-group.
+ */
+ void setReadyFrom(WindowContainer wc, boolean ready) {
+ mUsed = true;
+ WindowContainer current = wc;
+ while (current != null) {
+ if (isReadyGroup(current)) {
+ mReadyGroups.put(current, ready);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting Ready-group to"
+ + " %b. group=%s from %s", ready, current, wc);
+ break;
+ }
+ current = current.getParent();
+ }
+ }
+
+ /** Marks this as ready regardless of individual groups. */
+ void setAllReady() {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Setting allReady override");
+ mUsed = true;
+ mReadyOverride = true;
+ }
+
+ /** @return true if all tracked subtrees are ready. */
+ boolean allReady() {
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b "
+ + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString());
+ if (!mUsed) return false;
+ if (mReadyOverride) return true;
+ for (int i = mReadyGroups.size() - 1; i >= 0; --i) {
+ final WindowContainer wc = mReadyGroups.keyAt(i);
+ if (!wc.isAttached() || !wc.isVisibleRequested()) continue;
+ if (!mReadyGroups.valueAt(i)) return false;
+ }
+ return true;
+ }
+
+ private String groupsToString() {
+ StringBuilder b = new StringBuilder();
+ for (int i = 0; i < mReadyGroups.size(); ++i) {
+ if (i != 0) b.append(',');
+ b.append(mReadyGroups.keyAt(i)).append(':')
+ .append(mReadyGroups.valueAt(i));
+ }
+ return b.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cc63c4922c32..419b4c3a706d 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -17,21 +17,30 @@
package com.android.server.wm;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.ITransitionPlayer;
+import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.util.ArrayList;
@@ -41,15 +50,25 @@ import java.util.ArrayList;
class TransitionController {
private static final String TAG = "TransitionController";
+ // State constants to line-up with legacy app-transition proto expectations.
+ private static final int LEGACY_STATE_IDLE = 0;
+ private static final int LEGACY_STATE_READY = 1;
+ private static final int LEGACY_STATE_RUNNING = 2;
+
private ITransitionPlayer mTransitionPlayer;
final ActivityTaskManagerService mAtm;
+ private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
+ new ArrayList<>();
+
/**
* Currently playing transitions (in the order they were started). When finished, records are
* removed from this list.
*/
private final ArrayList<Transition> mPlayingTransitions = new ArrayList<>();
+ final Lock mRunningLock = new Lock();
+
private final IBinder.DeathRecipient mTransitionPlayerDeath = () -> {
// clean-up/finish any playing transitions.
for (int i = 0; i < mPlayingTransitions.size(); ++i) {
@@ -57,13 +76,18 @@ class TransitionController {
}
mPlayingTransitions.clear();
mTransitionPlayer = null;
+ mRunningLock.doNotifyLocked();
};
/** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
+ // TODO(b/188595497): remove when not needed.
+ final StatusBarManagerInternal mStatusBar;
+
TransitionController(ActivityTaskManagerService atm) {
mAtm = atm;
+ mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
}
/** @see #createTransition(int, int) */
@@ -76,7 +100,7 @@ class TransitionController {
* Creates a transition. It can immediately collect participants.
*/
@NonNull
- Transition createTransition(@WindowManager.TransitionType int type,
+ private Transition createTransition(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags) {
if (mTransitionPlayer == null) {
throw new IllegalStateException("Shell Transitions not enabled");
@@ -87,16 +111,22 @@ class TransitionController {
mCollectingTransition = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s",
mCollectingTransition);
+ dispatchLegacyAppTransitionPending();
return mCollectingTransition;
}
void registerTransitionPlayer(@Nullable ITransitionPlayer player) {
try {
+ // Note: asBinder() can be null if player is same process (likely in a test).
if (mTransitionPlayer != null) {
- mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0);
+ if (mTransitionPlayer.asBinder() != null) {
+ mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0);
+ }
mTransitionPlayer = null;
}
- player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
+ if (player.asBinder() != null) {
+ player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
+ }
mTransitionPlayer = player;
} catch (RemoteException e) {
throw new RuntimeException("Unable to set transition player");
@@ -154,13 +184,18 @@ class TransitionController {
return false;
}
+ @WindowManager.TransitionType
+ int getCollectingTransitionType() {
+ return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
+ }
+
/**
* @see #requestTransitionIfNeeded(int, int, WindowContainer, IRemoteTransition)
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
- @Nullable WindowContainer trigger) {
- return requestTransitionIfNeeded(type, 0 /* flags */, trigger);
+ @NonNull WindowContainer trigger) {
+ return requestTransitionIfNeeded(type, 0 /* flags */, trigger, trigger /* readyGroupRef */);
}
/**
@@ -168,8 +203,10 @@ class TransitionController {
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
- @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
- return requestTransitionIfNeeded(type, flags, trigger, null /* remote */);
+ @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
+ @NonNull WindowContainer readyGroupRef) {
+ return requestTransitionIfNeeded(type, flags, trigger, readyGroupRef,
+ null /* remoteTransition */);
}
private static boolean isExistenceType(@WindowManager.TransitionType int type) {
@@ -180,19 +217,24 @@ class TransitionController {
* If a transition isn't requested yet, creates one and asks the TransitionPlayer (Shell) to
* start it. Collection can start immediately.
* @param trigger if non-null, this is the first container that will be collected
+ * @param readyGroupRef Used to identify which ready-group this request is for.
* @return the created transition if created or null otherwise.
*/
@Nullable
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
- @Nullable IRemoteTransition remoteTransition) {
+ @NonNull WindowContainer readyGroupRef, @Nullable IRemoteTransition remoteTransition) {
if (mTransitionPlayer == null) {
return null;
}
Transition newTransition = null;
if (isCollecting()) {
// Make the collecting transition wait until this request is ready.
- mCollectingTransition.setReady(false);
+ mCollectingTransition.setReady(readyGroupRef, false);
+ if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+ // Add keyguard flag to dismiss keyguard
+ mCollectingTransition.addFlag(flags);
+ }
} else {
newTransition = requestStartTransition(createTransition(type, flags),
trigger != null ? trigger.asTask() : null, remoteTransition);
@@ -240,15 +282,22 @@ class TransitionController {
mCollectingTransition.collectExistenceChange(wc);
}
+ /** @see Transition#setOverrideAnimation */
+ void setOverrideAnimation(TransitionInfo.AnimationOptions options,
+ @Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.setOverrideAnimation(options, startCallback, finishCallback);
+ }
+
/** @see Transition#setReady */
- void setReady(boolean ready) {
+ void setReady(WindowContainer wc, boolean ready) {
if (mCollectingTransition == null) return;
- mCollectingTransition.setReady(ready);
+ mCollectingTransition.setReady(wc, ready);
}
/** @see Transition#setReady */
- void setReady() {
- setReady(true);
+ void setReady(WindowContainer wc) {
+ setReady(wc, true);
}
/** @see Transition#finishTransition */
@@ -261,6 +310,7 @@ class TransitionController {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Finish Transition: %s", record);
mPlayingTransitions.remove(record);
record.finishTransition();
+ mRunningLock.doNotifyLocked();
}
void moveToPlaying(Transition transition) {
@@ -279,4 +329,105 @@ class TransitionController {
mCollectingTransition = null;
}
+ /**
+ * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
+ * behaviors.
+ * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ */
+ void setIsLegacyRecents() {
+ if (mCollectingTransition == null) return;
+ mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ }
+
+ void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
+ final Transition transition = Transition.fromBinder(token);
+ if (transition == null || !mPlayingTransitions.contains(transition)) {
+ Slog.e(TAG, "Transition isn't playing: " + token);
+ return;
+ }
+ transition.legacyRestoreNavigationBarFromApp();
+ }
+
+ void registerLegacyListener(WindowManagerInternal.AppTransitionListener listener) {
+ mLegacyListeners.add(listener);
+ }
+
+ void dispatchLegacyAppTransitionPending() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionPendingLocked();
+ }
+ }
+
+ void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
+ final boolean keyguardGoingAway = info.isKeyguardGoingAway();
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+ 0 /* durationHint */, SystemClock.uptimeMillis(),
+ AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
+ }
+ }
+
+ void dispatchLegacyAppTransitionFinished(ActivityRecord ar) {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionFinishedLocked(ar.token);
+ }
+ }
+
+ void dispatchLegacyAppTransitionCancelled() {
+ for (int i = 0; i < mLegacyListeners.size(); ++i) {
+ mLegacyListeners.get(i).onAppTransitionCancelledLocked(
+ false /* keyguardGoingAway */);
+ }
+ }
+
+ void dumpDebugLegacy(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ int state = LEGACY_STATE_IDLE;
+ if (!mPlayingTransitions.isEmpty()) {
+ state = LEGACY_STATE_RUNNING;
+ } else if (mCollectingTransition != null && mCollectingTransition.getLegacyIsReady()) {
+ state = LEGACY_STATE_READY;
+ }
+ proto.write(AppTransitionProto.APP_TRANSITION_STATE, state);
+ proto.end(token);
+ }
+
+ class Lock {
+ private int mTransitionWaiters = 0;
+ void runWhenIdle(long timeout, Runnable r) {
+ synchronized (mAtm.mGlobalLock) {
+ if (!inTransition()) {
+ r.run();
+ return;
+ }
+ mTransitionWaiters += 1;
+ }
+ final long startTime = SystemClock.uptimeMillis();
+ final long endTime = startTime + timeout;
+ while (true) {
+ synchronized (mAtm.mGlobalLock) {
+ if (!inTransition() || SystemClock.uptimeMillis() > endTime) {
+ mTransitionWaiters -= 1;
+ r.run();
+ return;
+ }
+ }
+ synchronized (this) {
+ try {
+ this.wait(timeout);
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ void doNotifyLocked() {
+ synchronized (this) {
+ if (mTransitionWaiters > 0) {
+ this.notifyAll();
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 416b9dfe50b4..25f7269effe8 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -93,7 +93,7 @@ class WallpaperAnimationAdapter implements AnimationAdapter {
RemoteAnimationTarget createRemoteAnimationTarget() {
mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null,
mWallpaperToken.getPrefixOrderIndex(), new Point(), null, null,
- mWallpaperToken.getWindowConfiguration(), true, null, null, null);
+ mWallpaperToken.getWindowConfiguration(), true, null, null, null, false);
return mTarget;
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 4ff6d3c9a5f2..7893612b5725 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.WallpaperManager.COMMAND_FREEZE;
+import static android.app.WallpaperManager.COMMAND_UNFREEZE;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -79,6 +81,8 @@ class WallpaperController {
private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
private final float mMaxWallpaperScale;
+ // Whether COMMAND_FREEZE was dispatched.
+ private boolean mLastFrozen = false;
// This is set when we are waiting for a wallpaper to tell us it is done
// changing its scroll position.
@@ -194,6 +198,7 @@ class WallpaperController {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Win " + w + ": token animating, looking behind.");
}
+ mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
// Found a target! End search.
return true;
}
@@ -424,20 +429,25 @@ class WallpaperController {
Bundle sendWindowWallpaperCommand(
WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
- boolean doWait = sync;
- for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
- }
-
- if (doWait) {
- // TODO: Need to wait for result.
- }
+ sendWindowWallpaperCommand(action, x, y, z, extras, sync);
}
return null;
}
+ private void sendWindowWallpaperCommand(
+ String action, int x, int y, int z, Bundle extras, boolean sync) {
+ boolean doWait = sync;
+ for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
+ token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
+ }
+
+ if (doWait) {
+ // TODO: Need to wait for result.
+ }
+ }
+
private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
WindowState target = mWallpaperTarget;
if (target != null) {
@@ -644,6 +654,13 @@ class WallpaperController {
updateWallpaperTokens(visible);
+ if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
+ mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
+ sendWindowWallpaperCommand(
+ mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE,
+ /* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false);
+ }
+
if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
+ " prev=" + mPrevWallpaperTarget);
}
@@ -841,6 +858,7 @@ class WallpaperController {
boolean useTopWallpaperAsTarget = false;
WindowState wallpaperTarget = null;
boolean resetTopWallpaper = false;
+ boolean isWallpaperTargetForLetterbox = false;
void setTopWallpaper(WindowState win) {
topWallpaper = win;
@@ -854,11 +872,16 @@ class WallpaperController {
useTopWallpaperAsTarget = topWallpaperAsTarget;
}
+ void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) {
+ this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox;
+ }
+
void reset() {
topWallpaper = null;
wallpaperTarget = null;
useTopWallpaperAsTarget = false;
resetTopWallpaper = false;
+ isWallpaperTargetForLetterbox = false;
}
}
}
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 66ab094f0994..9780d3317e11 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -31,6 +31,7 @@ import android.util.TypedValue;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import android.view.WindowManagerPolicyConstants;
/**
* Displays a watermark on top of the window manager's windows.
@@ -117,7 +118,7 @@ class Watermark {
.setFormat(PixelFormat.TRANSLUCENT)
.setCallsite(TITLE)
.build();
- t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
+ t.setLayer(ctrl, WindowManagerPolicyConstants.WATERMARK_LAYER)
.setPosition(ctrl, 0, 0)
.show(ctrl);
// Ensure we aren't considered as obscuring for Input purposes.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index b1c7e196b70c..c48e9d1b4dea 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -851,7 +851,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
boolean isAttached() {
- return getDisplayArea() != null;
+ WindowContainer parent = getParent();
+ return parent != null && parent.isAttached();
}
void setWaitingForDrawnIfResizingChanged() {
@@ -1671,6 +1672,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return false;
}
+ boolean forAllLeafTaskFragments(Function<TaskFragment, Boolean> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).forAllLeafTaskFragments(callback)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -1726,6 +1736,28 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ /**
+ * For all task fragments at or below this container call the callback.
+ *
+ * @param callback Callback to be called for every task.
+ */
+ void forAllTaskFragments(Consumer<TaskFragment> callback) {
+ forAllTaskFragments(callback, true /*traverseTopToBottom*/);
+ }
+
+ void forAllTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).forAllTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+
void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
if (traverseTopToBottom) {
@@ -1739,6 +1771,19 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void forAllLeafTaskFragments(Consumer<TaskFragment> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ mChildren.get(i).forAllLeafTaskFragments(callback, traverseTopToBottom);
+ }
+ }
+ }
+
/**
* For all root tasks at or below this container call the callback.
*
@@ -3075,6 +3120,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/** Cheap way of doing cast and instanceof. */
+ TaskFragment asTaskFragment() {
+ return null;
+ }
+
+ /** Cheap way of doing cast and instanceof. */
WindowToken asWindowToken() {
return null;
}
@@ -3282,6 +3332,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Special helper to check that all windows are synced (vs just top one). This is only
+ * used to differentiate between starting-window vs full-drawn in activity-metrics reporting.
+ */
+ boolean allSyncFinished() {
+ if (!isVisibleRequested()) return true;
+ if (mSyncState != SYNC_STATE_READY) return false;
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (!child.allSyncFinished()) return false;
+ }
+ return true;
+ }
+
+ /**
* Called during reparent to handle sync state when the hierarchy changes.
* If this is in a sync group and gets reparented out, it will cancel syncing.
* If this is not in a sync group and gets parented into one, it will prepare itself.
@@ -3336,6 +3400,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
+ * Forces the receiver container to always use the configuration of the supplier container as
+ * its requested override configuration. It allows to propagate configuration without changing
+ * the relationship between child and parent.
+ */
+ static void overrideConfigurationPropagation(WindowContainer<?> receiver,
+ WindowContainer<?> supplier) {
+ final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
+ @Override
+ public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
+ receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
+ }
+ };
+ supplier.registerConfigurationChangeListener(listener);
+ receiver.registerWindowContainerListener(new WindowContainerListener() {
+ @Override
+ public void onRemoved() {
+ receiver.unregisterWindowContainerListener(this);
+ supplier.unregisterConfigurationChangeListener(listener);
+ }
+ });
+ }
+
+ /**
* Returns the {@link WindowManager.LayoutParams.WindowType}.
*/
@WindowManager.LayoutParams.WindowType int getWindowType() {
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index ffd6d21c1026..baea85439582 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -89,6 +89,11 @@ public class WindowFrames {
final Rect mCompatFrame = new Rect();
/**
+ * {@code true} if the window frame is a simulated frame and attached to a decor window.
+ */
+ boolean mIsSimulatingDecorWindow = false;
+
+ /**
* Whether the parent frame would have been different if there was no display cutout.
*/
private boolean mParentFrameWasClippedByDisplayCutout;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 47087cfbd147..8a242090d1f9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -40,6 +40,7 @@ import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.util.List;
+import java.util.Set;
/**
* Window manager local system service interface.
@@ -54,17 +55,18 @@ public abstract class WindowManagerInternal {
*/
public interface AccessibilityControllerInternal {
/**
- * Enable the accessibility trace logging.
+ * Start tracing for the given logging types.
+ * @param loggingTypeFlags flags of the logging types enabled.
*/
- void startTrace();
+ void startTrace(long loggingTypeFlags);
/**
- * Disable the accessibility trace logging.
+ * Disable accessibility tracing for all logging types.
*/
void stopTrace();
/**
- * Is trace enabled or not.
+ * Is tracing enabled for any logging type.
*/
boolean isAccessibilityTracingEnabled();
@@ -73,20 +75,23 @@ public abstract class WindowManagerInternal {
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
* @param stackTrace The stack trace, null if not needed.
+ * @param ignoreStackEntries The stack entries can be removed
*/
void logTrace(
- String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] stackTrace);
+ String where, long loggingTypeFlags, String callingParams, byte[] a11yDump,
+ int callingUid, StackTraceElement[] stackTrace, Set<String> ignoreStackEntries);
/**
* Add an accessibility trace entry.
*
* @param where A string to identify this log entry, which can be used to filter/search
* through the tracing file.
+ * @param loggingTypeFlags The flags for the logging types this log entry belongs to.
* @param callingParams The parameters for the method to be logged.
* @param a11yDump The proto byte array for a11y state when the entry is generated.
* @param callingUid The calling uid.
@@ -94,9 +99,11 @@ public abstract class WindowManagerInternal {
* @param timeStamp The time when the method to be logged is called.
* @param processId The calling process Id.
* @param threadId The calling thread Id.
+ * @param ignoreStackEntries The stack entries can be removed
*/
- void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid,
- StackTraceElement[] callStack, long timeStamp, int processId, long threadId);
+ void logTrace(String where, long loggingTypeFlags, String callingParams,
+ byte[] a11yDump, int callingUid, StackTraceElement[] callStack, long timeStamp,
+ int processId, long threadId, Set<String> ignoreStackEntries);
}
/**
@@ -143,11 +150,11 @@ public abstract class WindowManagerInternal {
void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
/**
- * Notifies that the rotation changed.
+ * Notifies that the display size is changed when rotation or the
+ * logical display is changed.
*
- * @param rotation The current rotation.
*/
- void onRotationChanged(int rotation);
+ void onDisplaySizeChanged();
/**
* Notifies that the context of the user changed. For example, an application
@@ -281,6 +288,16 @@ public abstract class WindowManagerInternal {
* Called when drag operation was cancelled.
*/
default void postCancelDragAndDrop() {}
+
+ /**
+ * Called when it has entered a View that is willing to accept the drop.
+ */
+ default void dragRecipientEntered(IWindow window) {}
+
+ /**
+ * Called when it has exited a View that is willing to accept the drop.
+ */
+ default void dragRecipientExited(IWindow window) {}
}
/**
@@ -358,9 +375,8 @@ public abstract class WindowManagerInternal {
*
* @param displayId The logical display id.
* @param callback The callback.
- * @return {@code false} if display id is not valid.
*/
- public abstract boolean setWindowsForAccessibilityCallback(int displayId,
+ public abstract void setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback);
/**
@@ -659,24 +675,43 @@ public abstract class WindowManagerInternal {
public abstract String getWindowName(@NonNull IBinder binder);
/**
- * Return the window name of IME Insets control target.
+ * The callback after the request of show/hide input method is sent.
*
+ * @param show Whether to show or hide input method.
+ * @param focusedToken The token of focused window.
+ * @param requestToken The token of window who requests the change.
* @param displayId The ID of the display which input method is currently focused.
- * @return The corresponding {@link WindowState#getName()}
+ * @return The information of the input method target.
*/
- public abstract @Nullable String getImeControlTargetNameForLogging(int displayId);
+ public abstract ImeTargetInfo onToggleImeRequested(boolean show,
+ @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId);
- /**
- * Return the current window name of the input method is on top of.
- *
- * Note that the concept of this window is only reparent the target window behind the input
- * method window, it may different with the window which reported by
- * {@code InputMethodManagerService#reportStartInput} which has input connection.
- *
- * @param displayId The ID of the display which input method is currently focused.
- * @return The corresponding {@link WindowState#getName()}
- */
- public abstract @Nullable String getImeTargetNameForLogging(int displayId);
+ /** The information of input method target when IME is requested to show or hide. */
+ public static class ImeTargetInfo {
+ public final String focusedWindowName;
+ public final String requestWindowName;
+
+ /** The window name of IME Insets control target. */
+ public final String imeControlTargetName;
+
+ /**
+ * The current window name of the input method is on top of.
+ * <p>
+ * Note that the concept of this window is only used to reparent the target window behind
+ * the input method window, it may be different from the window reported by
+ * {@link com.android.server.inputmethod.InputMethodManagerService#reportStartInput} which
+ * has input connection.
+ */
+ public final String imeLayerTargetName;
+
+ public ImeTargetInfo(String focusedWindowName, String requestWindowName,
+ String imeControlTargetName, String imeLayerTargetName) {
+ this.focusedWindowName = focusedWindowName;
+ this.requestWindowName = requestWindowName;
+ this.imeControlTargetName = imeControlTargetName;
+ this.imeLayerTargetName = imeLayerTargetName;
+ }
+ }
/**
* Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1ec9187d7a76..c7af2e80b185 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -47,6 +47,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_P
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -56,6 +57,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
@@ -84,8 +86,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.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
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.window.WindowProviderService.isWindowProviderService;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -248,6 +252,7 @@ import android.view.InputEvent;
import android.view.InputWindowHandle;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
import android.view.MotionEvent;
@@ -337,27 +342,6 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean PROFILE_ORIENTATION = false;
- /** How much to multiply the policy's type layer, to reserve room
- * for multiple windows of the same type and Z-ordering adjustment
- * with TYPE_LAYER_OFFSET. */
- static final int TYPE_LAYER_MULTIPLIER = 10000;
-
- /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
- * or below others in the same layer. */
- static final int TYPE_LAYER_OFFSET = 1000;
-
- /** How much to increment the layer for each window, to reserve room
- * for effect surfaces between them.
- */
- static final int WINDOW_LAYER_MULTIPLIER = 5;
-
- /**
- * Animation thumbnail is as far as possible below the window above
- * the thumbnail (or in other words as far as possible above the window
- * below it).
- */
- static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
-
/** The maximum length we will accept for a loaded animation duration:
* this is 10 seconds.
*/
@@ -450,14 +434,14 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
- public static final boolean sEnableRemoteKeyguardGoingAwayAnimation = !sEnableShellTransitions
- && sEnableRemoteKeyguardAnimation >= 1;
+ public static final boolean sEnableRemoteKeyguardGoingAwayAnimation =
+ sEnableRemoteKeyguardAnimation >= 1;
/**
* @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
*/
- public static final boolean sEnableRemoteKeyguardOccludeAnimation = !sEnableShellTransitions
- && sEnableRemoteKeyguardAnimation >= 2;
+ public static final boolean sEnableRemoteKeyguardOccludeAnimation =
+ sEnableRemoteKeyguardAnimation >= 2;
/**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
@@ -1455,7 +1439,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
- int displayId, int requestUserId, InsetsState requestedVisibility,
+ int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
Arrays.fill(outActiveControls, null);
@@ -1677,7 +1661,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
- win.updateRequestedVisibility(requestedVisibility);
+ win.setRequestedVisibilities(requestedVisibilities);
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
@@ -1728,16 +1712,22 @@ public class WindowManagerService extends IWindowManager.Stub
&& mWindowContextListenerController.hasListener(windowContextToken)) {
final int windowContextType = mWindowContextListenerController
.getWindowType(windowContextToken);
+ final Bundle options = mWindowContextListenerController
+ .getOptions(windowContextToken);
if (type != windowContextType) {
ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
+ " LayoutParams.type should match! Type from LayoutParams is %d,"
+ " but type from WindowContext is %d", type, windowContextType);
- return WindowManagerGlobal.ADD_INVALID_TYPE;
+ // We allow WindowProviderService to add window other than windowContextType,
+ // but the WindowProviderService won't be associated with the window's
+ // WindowToken.
+ if (!isWindowProviderService(options)) {
+ return WindowManagerGlobal.ADD_INVALID_TYPE;
+ }
+ } else {
+ mWindowContextListenerController.registerWindowContainerListener(
+ windowContextToken, token, callingUid, type, options);
}
- final Bundle options = mWindowContextListenerController
- .getOptions(windowContextToken);
- mWindowContextListenerController.registerWindowContainerListener(
- windowContextToken, token, callingUid, type, options);
}
// From now on, no exceptions or errors allowed!
@@ -1767,9 +1757,8 @@ public class WindowManagerService extends IWindowManager.Stub
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
- final ActivityRecord tokenActivity = token.asActivityRecord();
- if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
- tokenActivity.mStartingWindow = win;
+ if (type == TYPE_APPLICATION_STARTING && activity != null) {
+ activity.attachStartingWindow(win);
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
activity, win);
}
@@ -1804,7 +1793,8 @@ public class WindowManagerService extends IWindowManager.Stub
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
- if (activity != null && activity.isVisible()
+ if (mAtmService.getTransitionController().getTransitionPlayer() == null
+ && activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
// so that the unfreeze wait for the apps to draw. This might be needed if
@@ -2478,7 +2468,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
- if (displayPolicy.areSystemBarsForcedShownLw(win)) {
+ if (displayPolicy.areSystemBarsForcedShownLw()) {
result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
}
if (!win.isGoneForLayout()) {
@@ -2524,7 +2514,8 @@ public class WindowManagerService extends IWindowManager.Stub
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (winAnimator.mSurfaceController != null) {
- win.calculateSurfaceBounds(win.getAttrs(), mTmpRect);
+ win.calculateSurfaceBounds(win.getLayoutingAttrs(
+ win.getWindowConfiguration().getRotation()), mTmpRect);
outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
}
getInsetsSourceControls(win, outActiveControls);
@@ -2717,8 +2708,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean attachWindowContextToDisplayArea(IBinder clientToken, int type, int displayId,
- Bundle options) {
+ public Configuration attachWindowContextToDisplayArea(IBinder clientToken, int
+ type, int displayId, Bundle options) {
final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS,
"attachWindowContextToDisplayArea", false /* printLog */);
final int callingUid = Binder.getCallingUid();
@@ -2729,15 +2720,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (dc == null) {
ProtoLog.w(WM_ERROR, "attachWindowContextToDisplayArea: trying to attach"
+ " to a non-existing display:%d", displayId);
- return false;
+ return null;
}
// TODO(b/155340867): Investigate if we still need roundedCornerOverlay after
// the feature b/155340867 is completed.
final DisplayArea da = dc.findAreaForWindowType(type, options,
callerCanManageAppTokens, false /* roundedCornerOverlay */);
+ // TODO(b/190019118): Avoid to send onConfigurationChanged because it has been done
+ // in return value of attachWindowContextToDisplayArea.
mWindowContextListenerController.registerWindowContainerListener(clientToken, da,
callingUid, type, options);
- return true;
+ return da.getConfiguration();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -3044,7 +3037,7 @@ public class WindowManagerService extends IWindowManager.Stub
mSettingsObserver.updateSystemUiSettings(true /* handleChange */);
synchronized (mGlobalLock) {
// force a re-application of focused window sysui visibility on each display.
- mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw);
+ mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemBarAttributes);
}
}
@@ -4153,7 +4146,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void modifyDisplayWindowInsets(int displayId, InsetsState state) {
+ public void updateDisplayWindowRequestedVisibilities(int displayId, InsetsVisibilities vis) {
if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
@@ -4165,7 +4158,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (dc == null || dc.mRemoteInsetsControlTarget == null) {
return;
}
- dc.mRemoteInsetsControlTarget.updateRequestedVisibility(state);
+ dc.mRemoteInsetsControlTarget.setRequestedVisibilities(vis);
dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget);
}
} finally {
@@ -5286,6 +5279,7 @@ public class WindowManagerService extends IWindowManager.Stub
case LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED: {
synchronized (mGlobalLock) {
final DisplayContent displayContent = (DisplayContent) msg.obj;
+ displayContent.mLayoutAndAssignWindowLayersScheduled = false;
displayContent.layoutAndAssignWindowLayersIfNeeded();
}
break;
@@ -5390,6 +5384,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
+ if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.setSandboxDisplayApis(sandboxDisplayApis);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/** The global settings only apply to default display. */
private boolean applyForcedPropertiesForDefaultDisplay() {
boolean changed = false;
@@ -7061,6 +7074,7 @@ public class WindowManagerService extends IWindowManager.Stub
"requestScrollCapture: caught exception dispatching to window."
+ "token=%s", targetWindow.mClient.asBinder());
responseBuilder.setWindowTitle(targetWindow.getName());
+ responseBuilder.setPackageName(targetWindow.getOwningPackage());
responseBuilder.setDescription(String.format("caught exception: %s", e));
listener.onScrollCaptureResponse(responseBuilder.build());
}
@@ -7431,20 +7445,18 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean setWindowsForAccessibilityCallback(int displayId,
+ public void setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
synchronized (mGlobalLock) {
if (mAccessibilityController == null) {
mAccessibilityController = new AccessibilityController(
WindowManagerService.this);
}
- final boolean result =
- mAccessibilityController.setWindowsForAccessibilityCallback(
+ mAccessibilityController.setWindowsForAccessibilityCallback(
displayId, callback);
if (!mAccessibilityController.hasCallbacks()) {
mAccessibilityController = null;
}
- return result;
}
}
@@ -7571,6 +7583,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void registerAppTransitionListener(AppTransitionListener listener) {
synchronized (mGlobalLock) {
getDefaultDisplayContentLocked().mAppTransition.registerListenerLocked(listener);
+ mAtmService.getTransitionController().registerLegacyListener(listener);
}
}
@@ -7840,7 +7853,10 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public boolean isTouchOrFaketouchDevice() {
synchronized (mGlobalLock) {
- // All touchable devices are also faketouchable.
+ if (mIsTouchDevice && !mIsFakeTouchDevice) {
+ throw new IllegalStateException(
+ "touchscreen supported device must report faketouch.");
+ }
return mIsFakeTouchDevice;
}
}
@@ -7873,30 +7889,37 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public String getImeControlTargetNameForLogging(int displayId) {
- synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- return null;
- }
- final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_CONTROL);
- if (target == null) {
- return null;
- }
- final WindowState win = target.getWindow();
- return win != null ? win.getName() : target.toString();
- }
- }
-
- @Override
- public String getImeTargetNameForLogging(int displayId) {
+ public ImeTargetInfo onToggleImeRequested(boolean show, IBinder focusedToken,
+ IBinder requestToken, int displayId) {
+ final String focusedWindowName;
+ final String requestWindowName;
+ final String imeControlTargetName;
+ final String imeLayerTargetName;
synchronized (mGlobalLock) {
+ final WindowState focusedWin = mWindowMap.get(focusedToken);
+ focusedWindowName = focusedWin != null ? focusedWin.getName() : "null";
+ final WindowState requestWin = mWindowMap.get(requestToken);
+ requestWindowName = requestWin != null ? requestWin.getName() : "null";
final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null || dc.getImeTarget(IME_TARGET_LAYERING) == null) {
- return null;
+ if (dc != null) {
+ final InsetsControlTarget controlTarget = dc.getImeTarget(IME_TARGET_CONTROL);
+ if (controlTarget != null) {
+ final WindowState w = InsetsControlTarget.asWindowOrNull(controlTarget);
+ imeControlTargetName = w != null ? w.getName() : controlTarget.toString();
+ } else {
+ imeControlTargetName = "null";
+ }
+ final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING);
+ imeLayerTargetName = target != null ? target.getWindow().getName() : "null";
+ if (show) {
+ dc.onShowImeRequested();
+ }
+ } else {
+ imeControlTargetName = imeLayerTargetName = "no-display";
}
- return dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getName();
}
+ return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName,
+ imeLayerTargetName);
}
@Override
@@ -8094,11 +8117,21 @@ public class WindowManagerService extends IWindowManager.Stub
// This could prevent if there is no container animation, we still have to apply the
// pending transaction and exit waiting.
mAnimator.mNotifyWhenNoAnimation = true;
+ boolean animateStarting = false;
while (timeoutRemaining > 0) {
+ // Waiting until all starting windows has finished animating.
+ animateStarting = mRoot.forAllActivities(a -> {
+ return a.hasStartingWindow();
+ });
boolean isAnimating = mAnimator.isAnimationScheduled()
- || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL);
+ || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
+ || animateStarting;
if (!isAnimating) {
- break;
+ // isAnimating is a legacy transition query and will be removed, so also add
+ // a check for whether this is in a shell-transition when not using legacy.
+ if (!mAtmService.getTransitionController().inTransition()) {
+ break;
+ }
}
long startTime = System.currentTimeMillis();
try {
@@ -8112,13 +8145,14 @@ public class WindowManagerService extends IWindowManager.Stub
WindowContainer animatingContainer;
animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
ANIMATION_TYPE_ALL);
- if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
+ if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) {
Slog.w(TAG, "Timed out waiting for animations to complete,"
+ " animatingContainer=" + animatingContainer
+ " animationType=" + SurfaceAnimator.animationTypeToString(
animatingContainer != null
? animatingContainer.mSurfaceAnimator.getAnimationType()
- : SurfaceAnimator.ANIMATION_TYPE_NONE));
+ : SurfaceAnimator.ANIMATION_TYPE_NONE)
+ + " animateStarting=" + animateStarting);
}
}
}
@@ -8160,11 +8194,11 @@ public class WindowManagerService extends IWindowManager.Stub
displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent,
true /* includingParents */);
}
- handleTaskFocusChange(touchedWindow.getTask());
+ handleTaskFocusChange(touchedWindow.getTask(), touchedWindow.mActivityRecord);
}
@VisibleForTesting
- void handleTaskFocusChange(Task task) {
+ void handleTaskFocusChange(Task task, ActivityRecord touchedActivity) {
if (task == null) {
return;
}
@@ -8183,7 +8217,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- mAtmService.setFocusedTask(task.mTaskId);
+ mAtmService.setFocusedTask(task.mTaskId, touchedActivity);
}
/**
@@ -8211,7 +8245,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
- name, applicationHandle, flags, privateFlags, type, null /* region */);
+ name, applicationHandle, flags, privateFlags, type, null /* region */, window);
clientChannel.copyTo(outInputChannel);
}
@@ -8219,13 +8253,16 @@ public class WindowManagerService extends IWindowManager.Stub
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
int displayId, SurfaceControl surface, String name,
InputApplicationHandle applicationHandle, int flags,
- int privateFlags, int type, Region region) {
+ int privateFlags, int type, Region region, IWindow window) {
InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
h.token = channelToken;
+ h.setWindowToken(window);
h.name = name;
+ flags = DisplayPolicy.sanitizeFlagSlippery(flags, privateFlags, name);
+
final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
- | LayoutParams.FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
+ | FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE);
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
h.layoutParamsType = type;
h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -8277,7 +8314,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
- applicationHandle, flags, privateFlags, win.mWindowType, region);
+ applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient);
}
/** Return whether layer tracing is enabled */
@@ -8556,7 +8593,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (win.mActivityRecord == null || !win.mActivityRecord.isState(
- Task.ActivityState.RESUMED)) {
+ ActivityRecord.State.RESUMED)) {
mDisplayHashController.sendDisplayHashError(callback,
DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
@@ -8606,9 +8643,34 @@ public class WindowManagerService extends IWindowManager.Stub
if (imeTargetWindowTask == null) {
return false;
}
- final TaskSnapshot snapshot = mAtmService.getTaskSnapshot(imeTargetWindowTask.mTaskId,
- false /* isLowResolution */);
+ final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+ imeTargetWindowTask.mUserId, false /* isLowResolution */,
+ false /* restoreFromDisk */);
return snapshot != null && snapshot.hasImeSurface();
}
}
+
+ @Override
+ public int getImeDisplayId() {
+ // TODO(b/189805422): Add a toast to notify users that IMS may get extra
+ // onConfigurationChanged callback when perDisplayFocus is enabled.
+ // Enabling perDisplayFocus means that we track focus on each display, so we don't have
+ // the "top focus" display and getTopFocusedDisplayContent returns the default display
+ // as the fallback. It leads to InputMethodService receives an extra onConfiguration
+ // callback when InputMethodService move from a secondary display to another display
+ // with the same display metrics because InputMethodService will always associate with
+ // the ImeContainer on the default display in onCreate and receive a configuration update
+ // to match default display ImeContainer and then receive another configuration update
+ // from attachToWindowToken.
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getTopFocusedDisplayContent();
+ return dc.getImePolicy() == DISPLAY_IME_POLICY_LOCAL ? dc.getDisplayId()
+ : DEFAULT_DISPLAY;
+ }
+ }
+
+ @Override
+ public void setTaskSnapshotEnabled(boolean enabled) {
+ mTaskSnapshotController.setTaskSnapshotEnabled(enabled);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a94fd074ff2e..d59654949a27 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,6 +19,12 @@ package com.android.server.wm;
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -36,6 +42,7 @@ import com.android.internal.os.ByteTransferPipe;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -58,10 +65,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
+ private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
+ mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -113,6 +122,14 @@ public class WindowManagerShellCommand extends ShellCommand {
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
+ case "set-letterbox-style":
+ return runSetLetterboxStyle(pw);
+ case "get-letterbox-style":
+ return runGetLetterboxStyle(pw);
+ case "reset-letterbox-style":
+ return runResetLetterboxStyle(pw);
+ case "set-sandbox-display-apis":
+ return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -331,6 +348,37 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ /**
+ * Override display size and metrics to reflect the DisplayArea of the calling activity.
+ */
+ private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
+ int displayId = Display.DEFAULT_DISPLAY;
+ String arg = getNextArgRequired();
+ if ("-d".equals(arg)) {
+ displayId = Integer.parseInt(getNextArgRequired());
+ arg = getNextArgRequired();
+ }
+
+ final boolean sandboxDisplayApis;
+ switch (arg) {
+ case "true":
+ case "1":
+ sandboxDisplayApis = true;
+ break;
+ case "false":
+ case "0":
+ sandboxDisplayApis = false;
+ break;
+ default:
+ getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
+ + "get " + arg);
+ return -1;
+ }
+
+ mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
+ return 0;
+ }
+
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -548,6 +596,231 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
+ final float aspectRatio;
+ try {
+ String arg = getNextArgRequired();
+ aspectRatio = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad aspect ratio format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or aspect ratio should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+ final int cornersRadius;
+ try {
+ String arg = getNextArgRequired();
+ cornersRadius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad corners radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or corners radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
+ @LetterboxBackgroundType final int backgroundType;
+ try {
+ String arg = getNextArgRequired();
+ switch (arg) {
+ case "solid_color":
+ backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
+ break;
+ case "app_color_background":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+ break;
+ case "app_color_background_floating":
+ backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+ break;
+ case "wallpaper":
+ backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument");
+ return -1;
+ }
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset', 'solid_color', 'app_color_background' or "
+ + "'wallpaper' should be provided as an argument" + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
+ final Color color;
+ try {
+ String arg = getNextArgRequired();
+ color = Color.valueOf(Color.parseColor(arg));
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or color in #RRGGBB format should be provided as "
+ + "an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundColor(color);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
+ throws RemoteException {
+ final int radius;
+ try {
+ String arg = getNextArgRequired();
+ radius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: blur radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or blur radius should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
+ throws RemoteException {
+ final float alpha;
+ try {
+ String arg = getNextArgRequired();
+ alpha = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad alpha format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or alpha should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
+ }
+ return 0;
+ }
+
+ private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
+ final float multiplier;
+ try {
+ String arg = getNextArgRequired();
+ multiplier = Float.parseFloat(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad multiplier format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or multiplier should be provided as an argument " + e);
+ return -1;
+ }
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
+ }
+ return 0;
+ }
+
+ private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ getErrPrintWriter().println("Error: No arguments provided.");
+ }
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "--aspectRatio":
+ runSetFixedOrientationLetterboxAspectRatio(pw);
+ break;
+ case "--cornerRadius":
+ runSetLetterboxActivityCornersRadius(pw);
+ break;
+ case "--backgroundType":
+ runSetLetterboxBackgroundType(pw);
+ break;
+ case "--backgroundColor":
+ runSetLetterboxBackgroundColor(pw);
+ break;
+ case "--wallpaperBlurRadius":
+ runSetLetterboxBackgroundWallpaperBlurRadius(pw);
+ break;
+ case "--wallpaperDarkScrimAlpha":
+ runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
+ break;
+ case "--horizontalPositionMultiplier":
+ runSeLetterboxHorizontalPositionMultiplier(pw);
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ if (peekNextArg() == null) {
+ resetLetterboxStyle();
+ }
+ synchronized (mInternal.mGlobalLock) {
+ while (peekNextArg() != null) {
+ String arg = getNextArg();
+ switch (arg) {
+ case "aspectRatio":
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ break;
+ case "cornerRadius":
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ break;
+ case "backgroundType":
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ break;
+ case "backgroundColor":
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ break;
+ case "wallpaperBlurRadius":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ break;
+ case "wallpaperDarkScrimAlpha":
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ break;
+ case "horizontalPositionMultiplier":
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: Unrecognized letterbox style option: " + arg);
+ return -1;
+ }
+ }
+ }
+ return 0;
+ }
+
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -622,6 +895,40 @@ public class WindowManagerShellCommand extends ShellCommand {
return 0;
}
+ private void resetLetterboxStyle() {
+ synchronized (mInternal.mGlobalLock) {
+ mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
+ mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundType();
+ mLetterboxConfiguration.resetLetterboxBackgroundColor();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
+ mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
+ mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
+ }
+ }
+
+ private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
+ synchronized (mInternal.mGlobalLock) {
+ pw.println("Corner radius: "
+ + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
+ pw.println("Horizontal position multiplier: "
+ + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
+ pw.println("Aspect ratio: "
+ + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
+
+ pw.println("Background type: "
+ + LetterboxConfiguration.letterboxBackgroundTypeToString(
+ mLetterboxConfiguration.getLetterboxBackgroundType()));
+ pw.println(" Background color: " + Integer.toHexString(
+ mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
+ pw.println(" Wallpaper blur radius: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
+ pw.println(" Wallpaper dark scrim alpha: "
+ + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
+ }
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -646,6 +953,12 @@ public class WindowManagerShellCommand extends ShellCommand {
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
+ // set-letterbox-style
+ resetLetterboxStyle();
+
+ // set-sandbox-display-apis
+ mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
+
// set-multi-window-config
runResetMultiWindowConfig();
@@ -680,7 +993,12 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
+ pw.println(" set-sandbox-display-apis [true|1|false|0]");
+ pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
+ pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
+ pw.println(" Size Compat Mode.");
+ printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -693,6 +1011,49 @@ public class WindowManagerShellCommand extends ShellCommand {
}
}
+ private void printLetterboxHelp(PrintWriter pw) {
+ pw.println(" set-letterbox-style");
+ pw.println(" Sets letterbox style using the following options:");
+ pw.println(" --aspectRatio aspectRatio");
+ pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
+ + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+ pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
+ pw.println(" be ignored and framework implementation will determine aspect ratio.");
+ pw.println(" --cornerRadius radius");
+ pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
+ pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
+ pw.println(" ignored and corners of the activity won't be rounded.");
+ pw.println(" --backgroundType [reset|solid_color|app_color_background");
+ pw.println(" |app_color_background_floating|wallpaper]");
+ pw.println(" Type of background used in the letterbox mode.");
+ pw.println(" --backgroundColor color");
+ pw.println(" Color of letterbox which is be used when letterbox background type");
+ pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
+ pw.println(" letterbox background type. See Color#parseColor for allowed color");
+ pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
+ pw.println(" --wallpaperBlurRadius radius");
+ pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
+ pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
+ pw.println(" are ignored and 0 is used.");
+ pw.println(" --wallpaperDarkScrimAlpha alpha");
+ pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
+ pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
+ pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
+ pw.println(" and 0.0 (transparent) is used instead.");
+ pw.println(" --horizontalPositionMultiplier multiplier");
+ pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
+ pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
+ pw.println(" are ignored and central position (0.5) is used.");
+ pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
+ pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
+ pw.println(" |horizontalPositionMultiplier]");
+ pw.println(" Resets overrides to default values for specified properties separated");
+ pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
+ pw.println(" If no arguments provided, all values will be reset.");
+ pw.println(" get-letterbox-style");
+ pw.println(" Prints letterbox style configuration.");
+ }
+
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a82a478f2a4f..aa147c4c8712 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -16,13 +16,23 @@
package com.android.server.wm;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.isStartResultSuccessful;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -33,7 +43,11 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.WindowConfiguration;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -42,14 +56,19 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
+import android.window.ITaskFragmentOrganizer;
+import android.window.ITaskFragmentOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
+import android.window.TaskFragmentCreationParams;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,14 +114,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final TaskOrganizerController mTaskOrganizerController;
final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
+ final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
final TransitionController mTransitionController;
+ /**
+ * A Map which manages the relationship between
+ * {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
+ */
+ private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
WindowOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
+ mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
mTransitionController = new TransitionController(atm);
}
@@ -122,14 +148,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@Override
public void applyTransaction(WindowContainerTransaction t) {
- enforceTaskPermission("applyTransaction()");
if (t == null) {
- throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
+ throw new IllegalArgumentException("Null transaction passed to applyTransaction");
}
+ enforceTaskPermission("applyTransaction()", t);
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- applyTransaction(t, -1 /*syncId*/, null /*transition*/);
+ applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -139,10 +166,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
@Override
public int applySyncTransaction(WindowContainerTransaction t,
IWindowContainerTransactionCallback callback) {
- enforceTaskPermission("applySyncTransaction()");
if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
}
+ enforceTaskPermission("applySyncTransaction()", t);
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -162,7 +190,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (callback != null) {
syncId = startSyncWithOrganizer(callback);
}
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
if (syncId >= 0) {
setSyncReady(syncId);
}
@@ -177,6 +205,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
public IBinder startTransition(int type, @Nullable IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
enforceTaskPermission("startTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -196,7 +225,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("Can't use legacy transitions in"
+ " compatibility mode with no WCT.");
}
- applyTransaction(t, -1 /* syncId */, null);
+ applyTransaction(t, -1 /* syncId */, null, caller);
return null;
}
transition = mTransitionController.createTransition(type);
@@ -205,9 +234,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) {
t = new WindowContainerTransaction();
}
- applyTransaction(t, -1 /*syncId*/, transition);
+ applyTransaction(t, -1 /*syncId*/, transition, caller);
if (needsSetReady) {
- transition.setReady();
+ transition.setAllReady();
}
return transition;
}
@@ -217,10 +246,49 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
@Override
+ public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter,
+ @NonNull IWindowContainerTransactionCallback callback,
+ @NonNull WindowContainerTransaction t) {
+ enforceTaskPermission("startLegacyTransition()");
+ final CallerInfo caller = new CallerInfo();
+ final long ident = Binder.clearCallingIdentity();
+ int syncId;
+ try {
+ synchronized (mGlobalLock) {
+ if (type < 0) {
+ throw new IllegalArgumentException("Can't create transition with no type");
+ }
+ if (mTransitionController.getTransitionPlayer() != null) {
+ throw new IllegalArgumentException("Can't use legacy transitions in"
+ + " when shell transitions are enabled.");
+ }
+ final DisplayContent dc =
+ mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+ if (dc.mAppTransition.isTransitionSet()) {
+ // a transition already exists, so the callback probably won't be called.
+ return -1;
+ }
+ adapter.setCallingPidUid(caller.mPid, caller.mUid);
+ dc.prepareAppTransition(type);
+ dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */);
+ syncId = startSyncWithOrganizer(callback);
+ applyTransaction(t, syncId, null /* transition */, caller);
+ setSyncReady(syncId);
+ mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY)
+ .executeAppTransition();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return syncId;
+ }
+
+ @Override
public int finishTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t,
@Nullable IWindowContainerTransactionCallback callback) {
enforceTaskPermission("finishTransition()");
+ final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -231,7 +299,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// apply the incoming transaction before finish in case it alters the visibility
// of the participants.
if (t != null) {
- applyTransaction(t, syncId, null /*transition*/);
+ applyTransaction(t, syncId, null /*transition*/, caller);
}
getTransitionController().finishTransition(transitionToken);
if (syncId >= 0) {
@@ -247,12 +315,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
/**
* @param syncId If non-null, this will be a sync-transaction.
* @param transition A transition to collect changes into.
+ * @param caller Info about the calling process.
*/
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
- @Nullable Transition transition) {
+ @Nullable Transition transition, @Nullable CallerInfo caller) {
int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
if (transition != null) {
// First check if we have a display rotation transition and if so, update it.
@@ -303,7 +373,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final boolean isInLockTaskMode = mService.isInLockTaskMode();
for (int i = 0; i < hopSize; ++i) {
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
- isInLockTaskMode);
+ isInLockTaskMode, caller, t.getErrorCallbackToken(),
+ t.getTaskFragmentOrganizer());
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -341,6 +412,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.setMainWindowSizeChangeTransaction(sft);
}
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
// Already calls ensureActivityConfig
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -362,6 +434,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
}
} finally {
+ mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
}
@@ -402,7 +475,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new UnsupportedOperationException("Not supported to set multi-window"
+ " windowing mode during locked task mode.");
}
+
+ final int prevMode = container.getWindowingMode();
container.setWindowingMode(windowingMode);
+ if (prevMode != container.getWindowingMode()) {
+ // The activity in the container may become focusable or non-focusable due to
+ // windowing modes changes (such as entering or leaving pinned windowing mode),
+ // so also apply the lifecycle effects to this transaction.
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
}
return effects;
}
@@ -458,7 +539,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
- int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
+ int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
+ @Nullable CallerInfo caller, @Nullable IBinder errorCallbackToken,
+ @Nullable ITaskFragmentOrganizer organizer) {
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
@@ -480,7 +563,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.mAdjacentTask == null) {
+ } else if (task.getAdjacentTaskFragment() == null) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
@@ -501,13 +584,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return effects;
}
+ final WindowContainer wc;
+ final IBinder fragmentToken;
switch (type) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
break;
case HIERARCHY_OP_TYPE_REORDER:
case HIERARCHY_OP_TYPE_REPARENT:
- final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+ wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
break;
@@ -537,11 +622,125 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
+ mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "launchTask HierarchyOp");
final Bundle launchOpts = hop.getLaunchOptions();
final int taskId = launchOpts.getInt(
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
- mService.startActivityFromRecents(taskId, launchOpts);
+ final SafeActivityOptions safeOptions = caller != null
+ ? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
+ : SafeActivityOptions.fromBundle(launchOpts);
+ mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
+ taskId, safeOptions);
+ break;
+ case HIERARCHY_OP_TYPE_PENDING_INTENT:
+ String resolvedType = hop.getActivityIntent() != null
+ ? hop.getActivityIntent().resolveTypeIfNeeded(
+ mService.mContext.getContentResolver())
+ : null;
+
+ Bundle options = null;
+ if (hop.getPendingIntent().isActivity()) {
+ // Set the context display id as preferred for this activity launches, so that
+ // it can land on caller's display. Or just brought the task to front at the
+ // display where it was on since it has higher preference.
+ ActivityOptions activityOptions = hop.getLaunchOptions() != null
+ ? new ActivityOptions(hop.getLaunchOptions())
+ : ActivityOptions.makeBasic();
+ activityOptions.setCallerDisplayId(DEFAULT_DISPLAY);
+ options = activityOptions.toBundle();
+ }
+
+ int res = mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(),
+ hop.getPendingIntent().getWhitelistToken(), 0 /* code */,
+ hop.getActivityIntent(), resolvedType, null /* finishReceiver */,
+ null /* requiredPermission */, options);
+ if (res != ActivityManager.START_SUCCESS
+ && res != ActivityManager.START_TASK_TO_FRONT) {
+ if (!mTransitionController.isShellTransitionsEnabled()) {
+ final DisplayContent dc =
+ mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+ dc.cancelAppTransition();
+ }
+ }
+ break;
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ final TaskFragmentCreationParams taskFragmentCreationOptions =
+ hop.getTaskFragmentCreationOptions();
+ createTaskFragment(taskFragmentCreationOptions, errorCallbackToken);
+ break;
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+ wc = WindowContainer.fromBinder(hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: " + wc);
+ break;
+ }
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment == null || taskFragment.asTask() != null) {
+ throw new IllegalArgumentException(
+ "Can only delete organized TaskFragment, but not Task.");
+ }
+ deleteTaskFragment(taskFragment, errorCallbackToken);
+ break;
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ fragmentToken = hop.getContainer();
+ if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ final Intent activityIntent = hop.getActivityIntent();
+ final Bundle activityOptions = hop.getLaunchOptions();
+ final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
+ final int result = mService.getActivityStartController()
+ .startActivityInTaskFragment(tf, activityIntent, activityOptions,
+ hop.getCallingActivity());
+ if (!isStartResultSuccessful(result)) {
+ final Throwable exception =
+ new ActivityNotFoundException("start activity in taskFragment failed");
+ sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
+ errorCallbackToken, exception);
+ }
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ fragmentToken = hop.getNewParent();
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+ if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to operate with invalid fragment token or activity.");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
+ final WindowContainer newParent = hop.getNewParent() != null
+ ? WindowContainer.fromBinder(hop.getNewParent())
+ : null;
+ if (oldParent == null || !oldParent.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + oldParent);
+ break;
+ }
+ reparentTaskFragment(oldParent, newParent, errorCallbackToken);
+ break;
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
+ fragmentToken = hop.getContainer();
+ final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
+ final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
+ final TaskFragment tf2 = adjacentFragmentToken != null
+ ? mLaunchTaskFragments.get(adjacentFragmentToken)
+ : null;
+ if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to set adjacent on invalid fragment tokens");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ break;
+ }
+ tf1.setAdjacentTaskFragment(tf2);
break;
}
return effects;
@@ -704,19 +903,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
- final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask();
- final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask();
+ final TaskFragment root1 = WindowContainer.fromBinder(hop.getContainer()).asTaskFragment();
+ final TaskFragment root2 =
+ WindowContainer.fromBinder(hop.getAdjacentRoot()).asTaskFragment();
if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- root1.setAdjacentTask(root2);
+ root1.setAdjacentTaskFragment(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
private void sanitizeWindowContainer(WindowContainer wc) {
- if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
- throw new RuntimeException("Invalid token in task or displayArea transaction");
+ if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) {
+ throw new RuntimeException("Invalid token in task fragment or displayArea transaction");
}
}
@@ -747,6 +947,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return mDisplayAreaOrganizerController;
}
+ @Override
+ public ITaskFragmentOrganizerController getTaskFragmentOrganizerController() {
+ return mTaskFragmentOrganizerController;
+ }
+
@VisibleForTesting
int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
@@ -795,7 +1000,212 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
+ /** Whether the configuration changes are important to report back to an organizer. */
+ static boolean configurationsAreEqualForOrganizer(
+ Configuration newConfig, @Nullable Configuration oldConfig) {
+ if (oldConfig == null) {
+ return false;
+ }
+ int cfgChanges = newConfig.diff(oldConfig);
+ final int winCfgChanges = (cfgChanges & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0
+ ? (int) newConfig.windowConfiguration.diff(oldConfig.windowConfiguration,
+ true /* compareUndefined */) : 0;
+ if ((winCfgChanges & CONTROLLABLE_WINDOW_CONFIGS) == 0) {
+ cfgChanges &= ~ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+ }
+ return (cfgChanges & CONTROLLABLE_CONFIGS) == 0;
+ }
+
private void enforceTaskPermission(String func) {
mService.enforceTaskPermission(func);
}
+
+ private void enforceTaskPermission(String func, WindowContainerTransaction t) {
+ if (t == null || t.getTaskFragmentOrganizer() == null) {
+ enforceTaskPermission(func);
+ return;
+ }
+
+ // Apps may not have the permission to manage Tasks, but we are allowing apps to manage
+ // TaskFragments belonging to their own Task.
+ enforceOperationsAllowedForTaskFragmentOrganizer(func, t);
+ }
+
+ /**
+ * Makes sure that the transaction only contains operations that are allowed for the
+ * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
+ */
+ private void enforceOperationsAllowedForTaskFragmentOrganizer(
+ String func, WindowContainerTransaction t) {
+ final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
+
+ // Configuration changes
+ final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+ t.getChanges().entrySet().iterator();
+ while (entries.hasNext()) {
+ final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
+ // Only allow to apply changes to TaskFragment that is created by this organizer.
+ enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
+ organizer);
+ }
+
+ // Hierarchy changes
+ final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+ for (int i = hops.size() - 1; i >= 0; i--) {
+ final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+ final int type = hop.getType();
+ // Check for each type of the operations that are allowed for TaskFragmentOrganizer.
+ switch (type) {
+ case HIERARCHY_OP_TYPE_REORDER:
+ case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
+ enforceTaskFragmentOrganized(func,
+ WindowContainer.fromBinder(hop.getContainer()), organizer);
+ break;
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+ enforceTaskFragmentOrganized(func,
+ WindowContainer.fromBinder(hop.getContainer()), organizer);
+ enforceTaskFragmentOrganized(func,
+ WindowContainer.fromBinder(hop.getAdjacentRoot()),
+ organizer);
+ break;
+ case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
+ // We are allowing organizer to create TaskFragment. We will check the
+ // ownerToken in #createTaskFragment, and trigger error callback if that is not
+ // valid.
+ case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
+ case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS:
+ // We are allowing organizer to start/reparent activity to a TaskFragment it
+ // created, or set two TaskFragments adjacent to each other. Nothing to check
+ // here because the TaskFragment may not be created yet, but will be created in
+ // the same transaction.
+ break;
+ case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
+ enforceTaskFragmentOrganized(func,
+ WindowContainer.fromBinder(hop.getContainer()), organizer);
+ if (hop.getNewParent() != null) {
+ enforceTaskFragmentOrganized(func,
+ WindowContainer.fromBinder(hop.getNewParent()),
+ organizer);
+ }
+ break;
+ default:
+ // Other types of hierarchy changes are not allowed.
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " trying to apply a hierarchy change that is not allowed for"
+ + " TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+ }
+
+ private void enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc,
+ ITaskFragmentOrganizer organizer) {
+ if (wc == null) {
+ Slog.e(TAG, "Attempt to operate on window that no longer exists");
+ return;
+ }
+
+ final TaskFragment tf = wc.asTaskFragment();
+ if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
+ String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid() + " trying to modify window container not"
+ + " belonging to the TaskFragmentOrganizer=" + organizer;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ }
+
+ void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
+ @Nullable IBinder errorCallbackToken) {
+ final ActivityRecord ownerActivity =
+ ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
+ final ITaskFragmentOrganizer organizer = ITaskFragmentOrganizer.Stub.asInterface(
+ creationParams.getOrganizer().asBinder());
+
+ if (ownerActivity == null || ownerActivity.getTask() == null) {
+ final Throwable exception =
+ new IllegalArgumentException("Not allowed to operate with invalid ownerToken");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ return;
+ }
+ // The ownerActivity has to belong to the same app as the root Activity of the target Task.
+ final ActivityRecord rootActivity = ownerActivity.getTask().getRootActivity();
+ if (rootActivity.getUid() != ownerActivity.getUid()) {
+ final Throwable exception =
+ new IllegalArgumentException("Not allowed to operate with the ownerToken while "
+ + "the root activity of the target task belong to the different app");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
+ return;
+ }
+ final TaskFragment taskFragment = new TaskFragment(mService,
+ creationParams.getFragmentToken(), true /* createdByOrganizer */);
+ // Set task fragment organizer immediately, since it might have to be notified about further
+ // actions.
+ taskFragment.setTaskFragmentOrganizer(
+ creationParams.getOrganizer(), ownerActivity.getPid());
+ ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
+ taskFragment.setWindowingMode(creationParams.getWindowingMode());
+ taskFragment.setBounds(creationParams.getInitialBounds());
+ mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
+ }
+
+ void reparentTaskFragment(@NonNull WindowContainer oldParent,
+ @Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) {
+ WindowContainer parent = newParent;
+ if (parent == null && oldParent.asTaskFragment() != null) {
+ parent = oldParent.asTaskFragment().getTask();
+ }
+ if (parent == null) {
+ final Throwable exception =
+ new IllegalArgumentException("Not allowed to operate with invalid container");
+ sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(),
+ errorCallbackToken, exception);
+ return;
+ }
+ while (oldParent.hasChild()) {
+ oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
+ }
+ }
+
+ void deleteTaskFragment(@NonNull TaskFragment taskFragment,
+ @Nullable IBinder errorCallbackToken) {
+ final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
+ if (index < 0) {
+ final Throwable exception =
+ new IllegalArgumentException("Not allowed to operate with invalid "
+ + "taskFragment");
+ sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
+ errorCallbackToken, exception);
+ return;
+ }
+ mLaunchTaskFragments.removeAt(index);
+ taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
+ }
+
+ @Nullable
+ TaskFragment getTaskFragment(IBinder tfToken) {
+ return mLaunchTaskFragments.get(tfToken);
+ }
+
+ static class CallerInfo {
+ final int mPid;
+ final int mUid;
+
+ CallerInfo() {
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
+ }
+ }
+
+ void sendTaskFragmentOperationFailure(@NonNull ITaskFragmentOrganizer organizer,
+ @Nullable IBinder errorCallbackToken, @NonNull Throwable exception) {
+ if (organizer == null) {
+ throw new IllegalArgumentException("Not allowed to operate with invalid organizer");
+ }
+ mService.mTaskFragmentOrganizerController
+ .onTaskFragmentError(organizer, errorCallbackToken, exception);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 1364c72e6275..056e17d94b11 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -25,6 +25,13 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ActivityManagerService.MY_PID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -32,13 +39,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import android.Manifest;
import android.annotation.NonNull;
@@ -75,6 +75,7 @@ import com.android.server.wm.ActivityTaskManagerService.HotPath;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -219,6 +220,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
private boolean mRunningRemoteAnimation;
+ /** List of "chained" processes that are running remote animations for this process */
+ private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
+ new ArrayList<>();
+
// The bits used for mActivityStateFlags.
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
@@ -725,20 +730,23 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
canUpdate = true;
}
- // Compare the z-order of ActivityStacks if both activities landed on same display.
- if (display == topDisplay
- && mPreQTopResumedActivity.getRootTask().compareTo(
- activity.getRootTask()) <= 0) {
- canUpdate = true;
+ // Update the topmost activity if the activity has higher z-order than the current
+ // top-resumed activity.
+ if (!canUpdate) {
+ final ActivityRecord ar = topDisplay.getActivity(r -> r == activity,
+ true /* traverseTopToBottom */, mPreQTopResumedActivity);
+ if (ar != null && ar != mPreQTopResumedActivity) {
+ canUpdate = true;
+ }
}
if (canUpdate) {
// Make sure the previous top activity in the process no longer be resumed.
if (mPreQTopResumedActivity != null && mPreQTopResumedActivity.isState(RESUMED)) {
- final Task task = mPreQTopResumedActivity.getTask();
- if (task != null) {
- boolean userLeaving = task.shouldBeVisible(null);
- task.startPausingLocked(userLeaving, false /* uiSleeping */,
+ final TaskFragment taskFrag = mPreQTopResumedActivity.getTaskFragment();
+ if (taskFrag != null) {
+ boolean userLeaving = taskFrag.shouldBeVisible(null);
+ taskFrag.startPausing(userLeaving, false /* uiSleeping */,
activity, "top-resumed-changed");
}
}
@@ -940,7 +948,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
final int displayId = r.getDisplayId();
final Context c = root.getDisplayUiContext(displayId);
- if (r.mVisibleRequested && !displayContexts.contains(c)) {
+ if (c != null && r.mVisibleRequested && !displayContexts.contains(c)) {
displayContexts.add(c);
}
}
@@ -991,7 +999,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
- Task.ActivityState bestInvisibleState = DESTROYED;
+ ActivityRecord.State bestInvisibleState = DESTROYED;
boolean allStoppingFinishing = true;
boolean visible = false;
int minTaskLayer = Integer.MAX_VALUE;
@@ -1215,12 +1223,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
hasVisibleActivities = true;
}
- final Task task = r.getTask();
- if (task != null) {
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null) {
// There may be a pausing activity that hasn't shown any window and was requested
// to be hidden. But pausing is also a visible state, it should be regarded as
// visible, so the caller can know the next activity should be resumed.
- hasVisibleActivities |= task.handleAppDied(this);
+ hasVisibleActivities |= taskFragment.handleAppDied(this);
}
r.handleAppDied();
}
@@ -1596,11 +1604,38 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
updateRunningRemoteOrRecentsAnimation();
}
+ /**
+ * Marks another process as a "delegate" animator. This means that process is doing some part
+ * of a remote animation on behalf of this process.
+ */
+ void addRemoteAnimationDelegate(WindowProcessController delegate) {
+ if (!isRunningRemoteTransition()) {
+ throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
+ + " running a remote animation");
+ }
+ mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
+ }
+
void updateRunningRemoteOrRecentsAnimation() {
+ if (!isRunningRemoteTransition()) {
+ // Clean-up any delegates
+ for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
+ final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
+ if (delegate == null) continue;
+ delegate.setRunningRemoteAnimation(false);
+ delegate.setRunningRecentsAnimation(false);
+ }
+ mRemoteAnimationDelegates.clear();
+ }
+
// Posting on handler so WM lock isn't held when we call into AM.
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
WindowProcessListener::setRunningRemoteAnimation, mListener,
- mRunningRecentsAnimation || mRunningRemoteAnimation));
+ isRunningRemoteTransition()));
+ }
+
+ boolean isRunningRemoteTransition() {
+ return mRunningRecentsAnimation || mRunningRemoteAnimation;
}
/** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3fc99554bcc..1ca0c7eb8aba 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -34,6 +34,7 @@ import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.SurfaceControl.Transaction;
import static android.view.SurfaceControl.getGlobalTransaction;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
@@ -106,6 +107,8 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -152,8 +155,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
@@ -192,6 +193,7 @@ import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyCache;
import android.app.compat.CompatChanges;
@@ -202,6 +204,7 @@ import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.gui.TouchOcclusionMode;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -211,7 +214,6 @@ import android.os.PowerManager.WakeReason;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.TouchOcclusionMode;
import android.os.Trace;
import android.os.WorkSource;
import android.provider.Settings;
@@ -237,6 +239,7 @@ import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -747,7 +750,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private boolean mIsDimming = false;
private @Nullable InsetsSourceProvider mControllableInsetProvider;
- private final InsetsState mRequestedInsetsState = new InsetsState();
+ private final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
/**
* Freeze the insets state in some cases that not necessarily keeps up-to-date to the client.
@@ -870,18 +873,23 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
@Override
public boolean getRequestedVisibility(@InternalInsetsType int type) {
- return mRequestedInsetsState.getSourceOrDefaultVisibility(type);
+ return mRequestedVisibilities.getVisibility(type);
+ }
+
+ /**
+ * Returns all the requested visibilities.
+ *
+ * @return an {@link InsetsVisibilities} as the requested visibilities.
+ */
+ InsetsVisibilities getRequestedVisibilities() {
+ return mRequestedVisibilities;
}
/**
* @see #getRequestedVisibility(int)
*/
- void updateRequestedVisibility(InsetsState state) {
- for (int i = 0; i < InsetsState.SIZE; i++) {
- final InsetsSource source = state.peekSource(i);
- if (source == null) continue;
- mRequestedInsetsState.addSource(source);
- }
+ void setRequestedVisibilities(InsetsVisibilities visibilities) {
+ mRequestedVisibilities.set(visibilities);
}
/**
@@ -1162,7 +1170,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
return TouchOcclusionMode.USE_OPACITY;
}
- if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL)) {
+ if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL)
+ || mWmService.mAtmService.getTransitionController().inTransition(this)) {
return TouchOcclusionMode.USE_OPACITY;
}
return TouchOcclusionMode.BLOCK_UNTRUSTED;
@@ -1259,8 +1268,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
frame.inset(left, top, right, bottom);
}
- void computeFrameAndUpdateSourceFrame() {
- computeFrame();
+ void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) {
+ computeFrame(displayFrames);
// Update the source frame to provide insets to other windows during layout. If the
// simulated frames exist, then this is not computing a stable result so just skip.
if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {
@@ -1271,7 +1280,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/**
* Perform standard frame computation. The result can be obtained with getFrame() if so desired.
*/
- void computeFrame() {
+ void computeFrame(DisplayFrames displayFrames) {
if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -1384,7 +1393,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int fw = windowFrames.mFrame.width();
final int fh = windowFrames.mFrame.height();
- applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame);
+ applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame,
+ displayFrames);
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
@@ -1432,14 +1442,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
- // TODO: Look into whether this override is still necessary.
@Override
public Rect getBounds() {
- if (mActivityRecord != null) {
- return mActivityRecord.getBounds();
- } else {
- return super.getBounds();
- }
+ // The window bounds are used for layout in screen coordinates. If the token has bounds for
+ // size compatibility mode, its configuration bounds are app based coordinates which should
+ // not be used for layout.
+ return mToken.hasSizeCompatBounds() ? mToken.getBounds() : super.getBounds();
}
/** Retrieves the current frame of the window that the application sees. */
@@ -1477,6 +1485,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mAttrs;
}
+ WindowManager.LayoutParams getLayoutingAttrs(int rotation) {
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mAttrs;
+ }
+ final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation;
+ if (paramsForRotation == null || paramsForRotation.length != 4
+ || paramsForRotation[rotation] == null) {
+ return mAttrs;
+ }
+ return paramsForRotation[rotation];
+ }
+
/** Retrieves the flags used to disable system UI functions. */
int getDisableFlags() {
return mDisableFlags;
@@ -1715,6 +1735,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
+ @Nullable TaskFragment getTaskFragment() {
+ return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null;
+ }
+
@Nullable Task getRootTask() {
final Task task = getTask();
if (task != null) {
@@ -1842,9 +1866,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return super.hasContentToDisplay();
}
- @Override
- boolean isVisible() {
- return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy()
+ private boolean isVisibleByPolicyOrInsets() {
+ return isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
// insets, so nobody can hide it over the inset APIs.
&& (mControllableInsetProvider == null
@@ -1852,11 +1875,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
@Override
+ boolean isVisible() {
+ return wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+ }
+
+ @Override
boolean isVisibleRequested() {
- if (shouldCheckTokenVisibleRequested()) {
- return isVisible() && mToken.isVisibleRequested();
+ final boolean localVisibleRequested =
+ wouldBeVisibleRequestedIfPolicyIgnored() && isVisibleByPolicyOrInsets();
+ if (localVisibleRequested && shouldCheckTokenVisibleRequested()) {
+ return mToken.isVisibleRequested();
}
- return isVisible();
+ return localVisibleRequested;
}
/**
@@ -1903,6 +1933,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return !isWallpaper || mToken.isVisible();
}
+ private boolean wouldBeVisibleRequestedIfPolicyIgnored() {
+ final WindowState parent = getParentWindow();
+ final boolean isParentHiddenRequested = parent != null && !parent.isVisibleRequested();
+ if (isParentHiddenRequested || mAnimatingExit || mDestroying) {
+ return false;
+ }
+ final boolean isWallpaper = mToken.asWallpaperToken() != null;
+ return !isWallpaper || mToken.isVisibleRequested();
+ }
+
/**
* Is this window visible, ignoring its app token? It is not visible if there is no surface,
* or we are in the process of running an exit animation that will remove the surface.
@@ -2360,6 +2400,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
@Override
void removeImmediately() {
+ if (!mRemoved) {
+ // Destroy surface before super call. The general pattern is that the children need
+ // to be removed before the parent (so that the sync-engine tracking works). Since
+ // WindowStateAnimator is a "virtual" child, we have to do it manually here.
+ mWinAnimator.destroySurfaceLocked(getSyncTransaction());
+ }
super.removeImmediately();
if (mRemoved) {
@@ -2401,8 +2447,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
disposeInputChannel();
- mWinAnimator.destroySurfaceLocked(mTmpTransaction);
- mTmpTransaction.apply();
mSession.windowRemovedLocked();
try {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
@@ -2878,9 +2922,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// means we need to intercept touches outside of that window. The dim layer
// user associated with the window (task or root task) will give us the good
// bounds, as they would be used to display the dim layer.
- final Task task = getTask();
- if (task != null) {
- task.getDimBounds(mTmpRect);
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
+ final Task task = taskFragment.asTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
+ } else {
+ mTmpRect.set(taskFragment.getBounds());
+ }
} else if (getRootTask() != null) {
getRootTask().getDimBounds(mTmpRect);
}
@@ -3863,7 +3912,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged();
final DisplayContent displayContent = getDisplayContent();
final boolean alwaysConsumeSystemBars =
- displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(this);
+ displayContent.getDisplayPolicy().areSystemBarsForcedShownLw();
final int displayId = displayContent.getDisplayId();
markRedrawForSyncReported();
@@ -4362,9 +4411,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
}
if (dumpAll) {
- final String visibilityString = mRequestedInsetsState.toSourceVisibilityString();
+ final String visibilityString = mRequestedVisibilities.toString();
if (!visibilityString.isEmpty()) {
- pw.println(prefix + "Requested visibility: " + visibilityString);
+ pw.println(prefix + "Requested visibilities: " + visibilityString);
}
}
}
@@ -4397,12 +4446,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame,
- Rect displayFrame) {
+ Rect displayFrame, DisplayFrames displayFrames) {
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds();
- final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
+ final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation);
+ final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
// We need to fit it to the display if either
// a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen
@@ -4412,49 +4462,54 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// screen, but SurfaceViews want to be always at a specific location so we don't fit it to
// the display.
final boolean fitToDisplay = (task == null || !inNonFullscreenContainer)
- || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits);
+ || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
float x, y;
int w,h;
final boolean hasCompatScale = hasCompatScale();
- if ((mAttrs.flags & FLAG_SCALED) != 0) {
- if (mAttrs.width < 0) {
+ if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) {
+ // For the window with different layout attrs for different rotations, we need to avoid
+ // using requested size. Otherwise, when finishing a simulated rotation, the information
+ // coming from WindowManagerServices to the ViewRootImpl may not contain the correct
+ // value for the new rotation, and there will be a quick flash of wrong layout when the
+ // simulated activity faded out.
+ if (attrs.width < 0) {
w = pw;
} else if (hasCompatScale) {
- w = (int)(mAttrs.width * mGlobalScale + .5f);
+ w = (int) (attrs.width * mGlobalScale + .5f);
} else {
- w = mAttrs.width;
+ w = attrs.width;
}
- if (mAttrs.height < 0) {
+ if (attrs.height < 0) {
h = ph;
} else if (hasCompatScale) {
- h = (int)(mAttrs.height * mGlobalScale + .5f);
+ h = (int) (attrs.height * mGlobalScale + .5f);
} else {
- h = mAttrs.height;
+ h = attrs.height;
}
} else {
- if (mAttrs.width == MATCH_PARENT) {
+ if (attrs.width == MATCH_PARENT) {
w = pw;
} else if (hasCompatScale) {
- w = (int)(mRequestedWidth * mGlobalScale + .5f);
+ w = (int) (mRequestedWidth * mGlobalScale + .5f);
} else {
w = mRequestedWidth;
}
- if (mAttrs.height == MATCH_PARENT) {
+ if (attrs.height == MATCH_PARENT) {
h = ph;
} else if (hasCompatScale) {
- h = (int)(mRequestedHeight * mGlobalScale + .5f);
+ h = (int) (mRequestedHeight * mGlobalScale + .5f);
} else {
h = mRequestedHeight;
}
}
if (hasCompatScale) {
- x = mAttrs.x * mGlobalScale;
- y = mAttrs.y * mGlobalScale;
+ x = attrs.x * mGlobalScale;
+ y = attrs.y * mGlobalScale;
} else {
- x = mAttrs.x;
- y = mAttrs.y;
+ x = attrs.x;
+ y = attrs.y;
}
if (inNonFullscreenContainer && !layoutInParentFrame()) {
@@ -4481,13 +4536,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
// Set mFrame
- Gravity.apply(mAttrs.gravity, w, h, containingFrame,
- (int) (x + mAttrs.horizontalMargin * pw),
- (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame);
-
+ Gravity.apply(attrs.gravity, w, h, containingFrame,
+ (int) (x + attrs.horizontalMargin * pw),
+ (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame);
// Now make sure the window fits in the overall display frame.
if (fitToDisplay) {
- Gravity.applyDisplay(mAttrs.gravity, displayFrame, windowFrames.mFrame);
+ Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame);
}
// We need to make sure we update the CompatFrame as it is used for
@@ -4683,7 +4737,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
- mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
+ mActivityRecord.onFirstWindowDrawn(this);
} else {
mActivityRecord.onStartingWindowDrawn();
}
@@ -4771,6 +4825,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowInfo.focused = isFocused();
Task task = getTask();
windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
+ windowInfo.taskId = task == null ? ActivityTaskManager.INVALID_TASK_ID : task.mTaskId;
windowInfo.hasFlagWatchOutsideTouch =
(mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
@@ -5919,7 +5974,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// since a generic WindowContainer only needs to wait for its
// children to finish and is immediately ready from its own
// perspective but at the WindowState level we need to wait for ourselves
- // to draw even if the children draw first our don't need to sync, so we start
+ // to draw even if the children draw first or don't need to sync, so we start
// in WAITING state rather than READY.
mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
requestRedrawForSync();
@@ -5930,6 +5985,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return true;
}
+ @Override
+ boolean isSyncFinished() {
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility == View.GONE) {
+ // Don't wait for GONE windows. However, we don't alter the state in case the window
+ // becomes un-gone while the syncset is still active.
+ return true;
+ }
+ return super.isSyncFinished();
+ }
+
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
if (mOrientationChangeRedrawRequestTime > 0) {
final long duration =
@@ -5949,6 +6014,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
+ if (mActivityRecord != null
+ && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && mAttrs.type == TYPE_APPLICATION_STARTING) {
+ mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
+ .notifyStartingWindowDrawn(mActivityRecord);
+ }
+
if (postDrawTransaction != null) {
mSyncTransaction.merge(postDrawTransaction);
}
@@ -5991,8 +6063,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
boolean hasWallpaper() {
- return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
- || (mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox());
+ return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
+ }
+
+ boolean hasWallpaperForLetterboxBackground() {
+ return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox();
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 80941961cc5a..f25706a97fb6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,7 +41,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
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;
@@ -71,7 +70,6 @@ import java.io.PrintWriter;
**/
class WindowStateAnimator {
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
- static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
static final int PRESERVED_SURFACE_LAYER = 1;
/**
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fbfa400ba852..fa32be363ffe 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,9 +16,11 @@
package com.android.server.wm;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -229,6 +231,11 @@ class WindowToken extends WindowContainer<WindowState> {
ProtoLog.w(WM_DEBUG_WINDOW_MOVEMENT,
"removeAllWindowsIfPossible: removing win=%s", win);
win.removeIfPossible();
+ if (i > mChildren.size()) {
+ // It's possible for removeIfPossible to delete siblings (for example if it is a
+ // starting window, it will perform operations on the ActivityRecord).
+ i = mChildren.size();
+ }
}
}
@@ -451,9 +458,24 @@ class WindowToken extends WindowContainer<WindowState> {
}
Rect getFixedRotationBarContentFrame(int windowType) {
- return isFixedRotationTransforming()
- ? mFixedRotationTransformState.mBarContentFrames.get(windowType)
- : null;
+ if (!isFixedRotationTransforming()) {
+ return null;
+ }
+ if (!INSETS_LAYOUT_GENERALIZATION) {
+ return mFixedRotationTransformState.mBarContentFrames.get(windowType);
+ }
+ final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames;
+ final Rect tmpRect = new Rect();
+ if (windowType == TYPE_NAVIGATION_BAR) {
+ tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR)
+ .getFrame());
+ }
+ if (windowType == TYPE_STATUS_BAR) {
+ tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR)
+ .getFrame());
+ }
+ tmpRect.intersect(displayFrames.mDisplayCutoutSafe);
+ return tmpRect;
}
InsetsState getFixedRotationTransformInsetsState() {
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 0bb97f560a1c..6204824d70a9 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -54,7 +54,8 @@ class WindowTracing {
private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
- private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
+ static final String WINSCOPE_EXT = ".winscope";
+ private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace" + WINSCOPE_EXT;
private static final String TAG = "WindowTracing";
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 53401fd47178..8c933773f1fd 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -30,10 +30,6 @@ cc_library_static {
"BroadcastRadio/TunerCallback.cpp",
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
- "gnss/GnssConfiguration.cpp",
- "gnss/GnssMeasurement.cpp",
- "gnss/GnssMeasurementCallback.cpp",
- "gnss/Utils.cpp",
"stats/SurfaceFlingerPuller.cpp",
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
@@ -122,6 +118,7 @@ cc_defaults {
"libinputflinger",
"libinputflinger_base",
"libinputservice",
+ "libservices.core-gnss",
"libstatshidl",
"libstatspull",
"libstatssocket",
@@ -160,12 +157,12 @@ cc_defaults {
"android.hardware.input.classifier@1.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
- "android.hardware.memtrack-V1-ndk_platform",
+ "android.hardware.memtrack-V1-ndk",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power-V2-cpp",
"android.hardware.power.stats@1.0",
- "android.hardware.power.stats-V1-ndk_platform",
+ "android.hardware.power.stats-V1-ndk",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
"android.hardware.vibrator-V2-cpp",
@@ -178,7 +175,7 @@ cc_defaults {
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
- "android.frameworks.stats-V1-ndk_platform",
+ "android.frameworks.stats-V1-ndk",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend@1.0",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index a87b513491f6..1639d82f7875 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -40,6 +40,7 @@
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include "gnss/GnssAntennaInfoCallback.h"
#include "gnss/GnssConfiguration.h"
#include "gnss/GnssMeasurement.h"
#include "gnss/Utils.h"
@@ -60,12 +61,7 @@
static jclass class_location;
static jclass class_gnssNavigationMessage;
-static jclass class_gnssAntennaInfoBuilder;
static jclass class_gnssPowerStats;
-static jclass class_phaseCenterOffset;
-static jclass class_sphericalCorrections;
-static jclass class_arrayList;
-static jclass class_doubleArray;
jobject android::mCallbacksObj = nullptr;
@@ -89,7 +85,6 @@ static jmethodID method_reportGeofenceAddStatus;
static jmethodID method_reportGeofenceRemoveStatus;
static jmethodID method_reportGeofencePauseStatus;
static jmethodID method_reportGeofenceResumeStatus;
-static jmethodID method_reportAntennaInfo;
static jmethodID method_reportNavigationMessages;
static jmethodID method_reportLocationBatch;
static jmethodID method_reportGnssServiceDied;
@@ -123,25 +118,9 @@ static jmethodID method_reportNfwNotification;
static jmethodID method_isInEmergencySession;
static jmethodID method_locationCtor;
static jmethodID method_gnssNavigationMessageCtor;
-static jmethodID method_gnssAntennaInfoBuilderCtor;
static jmethodID method_gnssPowerStatsCtor;
-static jmethodID method_phaseCenterOffsetCtor;
-static jmethodID method_sphericalCorrectionsCtor;
-static jmethodID method_arrayListCtor;
-static jmethodID method_arrayListAdd;
-static jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
-static jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
-static jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
-static jmethodID method_gnssAntennaInfoBuilderBuild;
static jmethodID method_setSubHalPowerIndicationCapabilities;
-/*
- * Save a pointer to JavaVm to attach/detach threads executing
- * callback methods that need to make JNI calls.
- */
-JavaVM* android::ScopedJniThreadAttach::sJvm;
-
using android::OK;
using android::sp;
using android::status_t;
@@ -193,7 +172,6 @@ using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback;
using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
-using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil;
using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil;
using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss;
@@ -954,208 +932,6 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
}
/*
- * GnssAntennaInfoCallback implements the callback methods required for the
- * GnssAntennaInfo interface.
- */
-struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
- // Methods from V2_1::GnssAntennaInfoCallback follow.
- Return<void> gnssAntennaInfoCb(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
-
-private:
- jobject translateAllGnssAntennaInfos(
- JNIEnv* env,
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
- jobject translateSingleGnssAntennaInfo(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translatePhaseCenterOffset(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translatePhaseCenterVariationCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobject translateSignalGainCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
- jobjectArray translate2dDoubleArray(JNIEnv* env,
- const hidl_vec<IGnssAntennaInfoCallback::Row>& array);
- void translateAndReportGnssAntennaInfo(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
- void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
-};
-
-Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- translateAndReportGnssAntennaInfo(gnssAntennaInfos);
- return Void();
-}
-
-jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
- JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
- jsize numRows = array.size();
- if (numRows == 0) {
- // Empty array
- return NULL;
- }
- jsize numCols = array[0].row.size();
- if (numCols <= 1) {
- // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
- return NULL;
- }
-
- // Allocate array of double arrays
- jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
-
- // Create each double array
- for (uint8_t i = 0; i < numRows; i++) {
- jdoubleArray doubleArray = env->NewDoubleArray(numCols);
- env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
- env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
- env->DeleteLocalRef(doubleArray);
- }
- return returnArray;
-}
-
-jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
- JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- jobject arrayList = env->NewObject(class_arrayList,
- method_arrayListCtor); // Create new ArrayList instance
-
- for (auto gnssAntennaInfo : gnssAntennaInfos) {
- jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
-
- env->CallBooleanMethod(arrayList, method_arrayListAdd,
- gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
-
- // Delete Local Refs
- env->DeleteLocalRef(gnssAntennaInfoObject);
- }
- return arrayList;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- jobject phaseCenterOffset =
- env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
- gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
-
- return phaseCenterOffset;
-}
-
-jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
- gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
- return NULL;
- }
-
- jobjectArray phaseCenterVariationCorrectionsArray =
- translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
- jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
- translate2dDoubleArray(env,
- gnssAntennaInfo
- .phaseCenterVariationCorrectionUncertaintyMillimeters);
-
- if (phaseCenterVariationCorrectionsArray == NULL ||
- phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
- return NULL;
- }
-
- jobject phaseCenterVariationCorrections =
- env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
- phaseCenterVariationCorrectionsArray,
- phaseCenterVariationCorrectionsUncertaintiesArray);
-
- env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
- env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
-
- return phaseCenterVariationCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
- gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
- return NULL;
- }
- jobjectArray signalGainCorrectionsArray =
- translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
- jobjectArray signalGainCorrectionsUncertaintiesArray =
- translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
-
- if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
- return NULL;
- }
-
- jobject signalGainCorrections =
- env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
- signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
-
- env->DeleteLocalRef(signalGainCorrectionsArray);
- env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
-
- return signalGainCorrections;
-}
-
-jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
- JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
- jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
-
- // Nullable
- jobject phaseCenterVariationCorrections =
- translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
-
- // Nullable
- jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
-
- // Get builder
- jobject gnssAntennaInfoBuilderObject =
- env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
-
- // Set fields
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
- gnssAntennaInfo.carrierFrequencyMHz);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
- phaseCenterVariationCorrections);
- env->CallObjectMethod(gnssAntennaInfoBuilderObject,
- method_gnssAntennaInfoBuilderSetSignalGainCorrections,
- signalGainCorrections);
-
- // build
- jobject gnssAntennaInfoObject =
- env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
-
- // Delete Local Refs
- env->DeleteLocalRef(phaseCenterOffset);
- env->DeleteLocalRef(phaseCenterVariationCorrections);
- env->DeleteLocalRef(signalGainCorrections);
-
- return gnssAntennaInfoObject;
-}
-
-void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
- const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
- JNIEnv* env = getJniEnv();
-
- jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
-
- reportAntennaInfo(env, arrayList);
-
- env->DeleteLocalRef(arrayList);
-}
-
-void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
- env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-/*
* MeasurementCorrectionsCallback implements callback methods of interface
* IMeasurementCorrectionsCallback.hal.
*/
@@ -1515,7 +1291,6 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
"(II)V");
method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
"(II)V");
- method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
method_reportNavigationMessages = env->GetMethodID(
clazz,
"reportNavigationMessage",
@@ -1589,39 +1364,6 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D");
method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D");
- jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
- class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
- method_gnssAntennaInfoBuilderCtor =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
- method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
- "(D)Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
- "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
- "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderSetSignalGainCorrections =
- env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
- "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
- "Landroid/location/GnssAntennaInfo$Builder;");
- method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
- "()Landroid/location/GnssAntennaInfo;");
-
- jclass phaseCenterOffsetClass =
- env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
- class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
- method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
-
- jclass sphericalCorrectionsClass =
- env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
- class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
- method_sphericalCorrectionsCtor =
- env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
-
jclass gnssPowerStatsClass = env->FindClass("com/android/server/location/gnss/GnssPowerStats");
class_gnssPowerStats = (jclass)env->NewGlobalRef(gnssPowerStatsClass);
method_gnssPowerStatsCtor = env->GetMethodID(class_gnssPowerStats, "<init>", "(IJDDDDDD[D)V");
@@ -1634,16 +1376,9 @@ static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jc
class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass);
method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "<init>", "()V");
- jclass arrayListClass = env->FindClass("java/util/ArrayList");
- class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
- method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
- method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
-
- jclass doubleArrayClass = env->FindClass("[D");
- class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
-
gnss::GnssConfiguration_class_init_once(env);
gnss::GnssMeasurement_class_init_once(env, clazz);
+ gnss::GnssAntennaInfo_class_init_once(env, clazz);
}
/* Initialization needed at system boot and whenever GNSS service dies. */
@@ -2626,7 +2361,7 @@ static jboolean android_location_gnss_hal_GnssNative_start_antenna_info_listenin
return JNI_FALSE;
}
- sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback();
+ sp<gnss::GnssAntennaInfoCallback> cbIface = new gnss::GnssAntennaInfoCallback(mCallbacksObj);
auto result = gnssAntennaInfoIface->setCallback(cbIface);
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
new file mode 100644
index 000000000000..444cb8b7e955
--- /dev/null
+++ b/services/core/jni/gnss/Android.bp
@@ -0,0 +1,51 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the '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.core-gnss",
+ defaults: ["libservices.core-gnss-libs"],
+
+ cpp_std: "c++2a",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wthread-safety",
+
+ "-DEGL_EGLEXT_PROTOTYPES",
+ "-DGL_GLEXT_PROTOTYPES",
+ ],
+
+ srcs: [
+ "GnssAntennaInfoCallback.cpp",
+ "GnssConfiguration.cpp",
+ "GnssMeasurement.cpp",
+ "GnssMeasurementCallback.cpp",
+ "Utils.cpp",
+ ],
+}
+
+cc_defaults {
+ name: "libservices.core-gnss-libs",
+ shared_libs: [
+ "libandroid_runtime",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.gnss-V1-cpp",
+ "android.hardware.gnss@1.0",
+ "android.hardware.gnss@1.1",
+ "android.hardware.gnss@2.0",
+ "android.hardware.gnss@2.1",
+ "android.hardware.gnss.measurement_corrections@1.0",
+ "android.hardware.gnss.visibility_control@1.0",
+ ],
+}
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.cpp b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
new file mode 100644
index 000000000000..fbc000b25d26
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssAntInfoCbJni"
+
+#include "GnssAntennaInfoCallback.h"
+#include "Utils.h"
+
+namespace android::gnss {
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+
+using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
+
+namespace {
+jclass class_gnssAntennaInfoBuilder;
+jclass class_phaseCenterOffset;
+jclass class_sphericalCorrections;
+jclass class_arrayList;
+jclass class_doubleArray;
+
+jmethodID method_reportAntennaInfo;
+jmethodID method_gnssAntennaInfoBuilderCtor;
+jmethodID method_phaseCenterOffsetCtor;
+jmethodID method_sphericalCorrectionsCtor;
+jmethodID method_arrayListCtor;
+jmethodID method_arrayListAdd;
+jmethodID method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterOffset;
+jmethodID method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections;
+jmethodID method_gnssAntennaInfoBuilderSetSignalGainCorrections;
+jmethodID method_gnssAntennaInfoBuilderBuild;
+} // anonymous namespace
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz) {
+ method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
+ jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder");
+ class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder);
+ method_gnssAntennaInfoBuilderCtor =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "<init>", "()V");
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setCarrierFrequencyMHz",
+ "(D)Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterOffset",
+ "(Landroid/location/GnssAntennaInfo$PhaseCenterOffset;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setPhaseCenterVariationCorrections",
+ "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections =
+ env->GetMethodID(class_gnssAntennaInfoBuilder, "setSignalGainCorrections",
+ "(Landroid/location/GnssAntennaInfo$SphericalCorrections;)"
+ "Landroid/location/GnssAntennaInfo$Builder;");
+ method_gnssAntennaInfoBuilderBuild = env->GetMethodID(class_gnssAntennaInfoBuilder, "build",
+ "()Landroid/location/GnssAntennaInfo;");
+
+ jclass phaseCenterOffsetClass =
+ env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffset");
+ class_phaseCenterOffset = (jclass)env->NewGlobalRef(phaseCenterOffsetClass);
+ method_phaseCenterOffsetCtor = env->GetMethodID(class_phaseCenterOffset, "<init>", "(DDDDDD)V");
+
+ jclass sphericalCorrectionsClass =
+ env->FindClass("android/location/GnssAntennaInfo$SphericalCorrections");
+ class_sphericalCorrections = (jclass)env->NewGlobalRef(sphericalCorrectionsClass);
+ method_sphericalCorrectionsCtor =
+ env->GetMethodID(class_sphericalCorrections, "<init>", "([[D[[D)V");
+
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+ method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+ method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+ jclass doubleArrayClass = env->FindClass("[D");
+ class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
+}
+
+Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ translateAndReportGnssAntennaInfo(gnssAntennaInfos);
+ return Void();
+}
+
+jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
+ JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
+ jsize numRows = array.size();
+ if (numRows == 0) {
+ // Empty array
+ return NULL;
+ }
+ jsize numCols = array[0].row.size();
+ if (numCols <= 1) {
+ // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
+ return NULL;
+ }
+
+ // Allocate array of double arrays
+ jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
+
+ // Create each double array
+ for (uint8_t i = 0; i < numRows; i++) {
+ jdoubleArray doubleArray = env->NewDoubleArray(numCols);
+ env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
+ env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
+ env->DeleteLocalRef(doubleArray);
+ }
+ return returnArray;
+}
+
+jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
+ JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ jobject arrayList = env->NewObject(class_arrayList,
+ method_arrayListCtor); // Create new ArrayList instance
+
+ for (auto gnssAntennaInfo : gnssAntennaInfos) {
+ jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
+
+ env->CallBooleanMethod(arrayList, method_arrayListAdd,
+ gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
+
+ // Delete Local Refs
+ env->DeleteLocalRef(gnssAntennaInfoObject);
+ }
+ return arrayList;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterOffset(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ jobject phaseCenterOffset =
+ env->NewObject(class_phaseCenterOffset, method_phaseCenterOffsetCtor,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
+ gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
+
+ return phaseCenterOffset;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
+ gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
+ return NULL;
+ }
+
+ jobjectArray phaseCenterVariationCorrectionsArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
+ jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
+ translate2dDoubleArray(env,
+ gnssAntennaInfo
+ .phaseCenterVariationCorrectionUncertaintyMillimeters);
+
+ if (phaseCenterVariationCorrectionsArray == NULL ||
+ phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
+ return NULL;
+ }
+
+ jobject phaseCenterVariationCorrections =
+ env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+ phaseCenterVariationCorrectionsArray,
+ phaseCenterVariationCorrectionsUncertaintiesArray);
+
+ env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
+ env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
+
+ return phaseCenterVariationCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
+ gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
+ return NULL;
+ }
+ jobjectArray signalGainCorrectionsArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
+ jobjectArray signalGainCorrectionsUncertaintiesArray =
+ translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
+
+ if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
+ return NULL;
+ }
+
+ jobject signalGainCorrections =
+ env->NewObject(class_sphericalCorrections, method_sphericalCorrectionsCtor,
+ signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
+
+ env->DeleteLocalRef(signalGainCorrectionsArray);
+ env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
+
+ return signalGainCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
+ JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+ jobject phaseCenterOffset = translatePhaseCenterOffset(env, gnssAntennaInfo);
+
+ // Nullable
+ jobject phaseCenterVariationCorrections =
+ translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
+
+ // Nullable
+ jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
+
+ // Get builder
+ jobject gnssAntennaInfoBuilderObject =
+ env->NewObject(class_gnssAntennaInfoBuilder, method_gnssAntennaInfoBuilderCtor);
+
+ // Set fields
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetCarrierFrequencyMHz,
+ gnssAntennaInfo.carrierFrequencyMHz);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterOffset, phaseCenterOffset);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetPhaseCenterVariationCorrections,
+ phaseCenterVariationCorrections);
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject,
+ method_gnssAntennaInfoBuilderSetSignalGainCorrections,
+ signalGainCorrections);
+
+ // build
+ jobject gnssAntennaInfoObject =
+ env->CallObjectMethod(gnssAntennaInfoBuilderObject, method_gnssAntennaInfoBuilderBuild);
+
+ // Delete Local Refs
+ env->DeleteLocalRef(phaseCenterOffset);
+ env->DeleteLocalRef(phaseCenterVariationCorrections);
+ env->DeleteLocalRef(signalGainCorrections);
+
+ return gnssAntennaInfoObject;
+}
+
+void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
+ const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+ JNIEnv* env = getJniEnv();
+
+ jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
+
+ reportAntennaInfo(env, arrayList);
+
+ env->DeleteLocalRef(arrayList);
+}
+
+void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
+ env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssAntennaInfoCallback.h b/services/core/jni/gnss/GnssAntennaInfoCallback.h
new file mode 100644
index 000000000000..0fc763336077
--- /dev/null
+++ b/services/core/jni/gnss/GnssAntennaInfoCallback.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
+#include <log/log.h>
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+void GnssAntennaInfo_class_init_once(JNIEnv* env, jclass& clazz);
+
+/*
+ * GnssAntennaInfoCallback implements the callback methods required for the
+ * GnssAntennaInfo interface.
+ */
+struct GnssAntennaInfoCallback : public android::hardware::gnss::V2_1::IGnssAntennaInfoCallback {
+ GnssAntennaInfoCallback(jobject& callbacksObj) : mCallbacksObj(callbacksObj) {}
+ // Methods from V2_1::GnssAntennaInfoCallback follow.
+ hardware::Return<void> gnssAntennaInfoCb(
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+
+private:
+ jobject translateAllGnssAntennaInfos(
+ JNIEnv* env,
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+ jobject translateSingleGnssAntennaInfo(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translatePhaseCenterOffset(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translatePhaseCenterVariationCorrections(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobject translateSignalGainCorrections(
+ JNIEnv* env,
+ const android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo&
+ gnssAntennaInfo);
+ jobjectArray translate2dDoubleArray(
+ JNIEnv* env,
+ const hardware::hidl_vec<android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::Row>&
+ array);
+ void translateAndReportGnssAntennaInfo(
+ const hardware::hidl_vec<
+ android::hardware::gnss::V2_1::IGnssAntennaInfoCallback::GnssAntennaInfo>&
+ gnssAntennaInfos);
+ void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
+
+ jobject& mCallbacksObj;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSANTENNAINFOCALLBACK_H
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp
index 8c6c673fb5b2..34ae4690e0ea 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.cpp
+++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp
@@ -28,6 +28,7 @@ using hardware::gnss::GnssData;
using hardware::gnss::GnssMeasurement;
using hardware::gnss::SatellitePvt;
+namespace {
jclass class_arrayList;
jclass class_clockInfo;
jclass class_correlationVectorBuilder;
@@ -63,6 +64,8 @@ jmethodID method_positionEcef;
jmethodID method_velocityEcef;
jmethodID method_clockInfo;
+} // anonymous namespace
+
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) {
method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData",
"(Landroid/location/GnssMeasurementsEvent;)V");
diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h
index 26f12437a217..32200fdf904e 100644
--- a/services/core/jni/gnss/GnssMeasurementCallback.h
+++ b/services/core/jni/gnss/GnssMeasurementCallback.h
@@ -34,6 +34,7 @@
namespace android::gnss {
+namespace {
extern jclass class_gnssMeasurementsEvent;
extern jclass class_gnssMeasurement;
extern jclass class_gnssClock;
@@ -42,6 +43,7 @@ extern jmethodID method_gnssMeasurementsEventCtor;
extern jmethodID method_gnssClockCtor;
extern jmethodID method_gnssMeasurementCtor;
extern jmethodID method_reportMeasurementData;
+} // anonymous namespace
void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz);
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 20e14a919fce..8cbdfb8d294e 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -18,6 +18,12 @@
#include "Utils.h"
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+JavaVM* android::ScopedJniThreadAttach::sJvm;
+
namespace android {
namespace {
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index d43cf3f59170..6a50d3834355 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -14,7 +14,6 @@ xsd_config {
package_name: "com.android.server.pm.permission.configfile",
}
-
xsd_config {
name: "platform-compat-config",
srcs: ["platform-compat/config/platform-compat-config.xsd"],
@@ -42,6 +41,7 @@ xsd_config {
srcs: ["display-layout-config/display-layout-config.xsd"],
api_dir: "display-layout-config/schema",
package_name: "com.android.server.display.config.layout",
+ boolean_getter: true,
}
xsd_config {
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index c542c0d0c382..e14139a0860a 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -52,6 +52,6 @@
<xs:element name="address" type="xs:nonNegativeInteger"/>
</xs:sequence>
<xs:attribute name="enabled" type="xs:boolean" use="optional" />
- <xs:attribute name="isDefault" type="xs:boolean" use="optional" />
+ <xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" />
</xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 817188509f81..f3915754a1a4 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -4,11 +4,11 @@ package com.android.server.display.config.layout {
public class Display {
ctor public Display();
method public java.math.BigInteger getAddress();
- method public boolean getEnabled();
- method public boolean getIsDefault();
+ method public boolean isDefaultDisplay();
+ method public boolean isEnabled();
method public void setAddress(java.math.BigInteger);
+ method public void setDefaultDisplay(boolean);
method public void setEnabled(boolean);
- method public void setIsDefault(boolean);
}
public class Layout {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 193d92a3b2ff..b98debc1314f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -327,7 +327,6 @@ import com.android.server.PersistentDataBlockManagerInternal;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
-import com.android.server.devicepolicy.Owners.OwnerDto;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.RestrictionsSet;
@@ -649,6 +648,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
+ private final Object mESIDInitilizationLock = new Object();
+ private EnterpriseSpecificIdCalculator mEsidCalculator;
/**
* Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
@@ -1259,17 +1260,37 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// Used by DevicePolicyManagerServiceShellCommand
- List<OwnerDto> listAllOwners() {
+ List<OwnerShellData> listAllOwners() {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
return mInjector.binderWithCleanCallingIdentity(() -> {
- List<OwnerDto> owners = mOwners.listAllOwners();
+ SparseArray<DevicePolicyData> userData;
+
+ // Gets the owners of "full users" first (device owner and profile owners)
+ List<OwnerShellData> owners = mOwners.listAllOwners();
synchronized (getLockObject()) {
for (int i = 0; i < owners.size(); i++) {
- OwnerDto owner = owners.get(i);
+ OwnerShellData owner = owners.get(i);
owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
}
+ userData = mUserData;
+ }
+
+ // Then the owners of profile users (managed profiles)
+ for (int i = 0; i < userData.size(); i++) {
+ DevicePolicyData policyData = mUserData.valueAt(i);
+ int userId = userData.keyAt(i);
+ int parentUserId = mUserManagerInternal.getProfileParentId(userId);
+ boolean isProfile = parentUserId != userId;
+ if (!isProfile) continue;
+ for (int j = 0; j < policyData.mAdminList.size(); j++) {
+ ActiveAdmin admin = policyData.mAdminList.get(j);
+ OwnerShellData owner = OwnerShellData.forManagedProfileOwner(userId,
+ parentUserId, admin.info.getComponent());
+ owners.add(owner);
+ }
}
+
return owners;
});
}
@@ -1454,6 +1475,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return new LockPatternUtils(mContext);
}
+ EnterpriseSpecificIdCalculator newEnterpriseSpecificIdCalculator() {
+ return new EnterpriseSpecificIdCalculator(mContext);
+ }
+
boolean storageManagerIsFileBasedEncryptionEnabled() {
return StorageManager.isFileEncryptedNativeOnly();
}
@@ -3785,17 +3810,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- @Override
- public boolean isSeparateProfileChallengeAllowed(int userHandle) {
- Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
- String.format(NOT_SYSTEM_CALLER_MSG, "query separate challenge support"));
-
- ComponentName profileOwner = getProfileOwnerAsUser(userHandle);
- // Profile challenge is supported on N or newer release.
- return profileOwner != null &&
- getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
- }
-
private boolean canSetPasswordQualityOnParent(String packageName, final CallerIdentity caller) {
return !mInjector.isChangeEnabled(
PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, caller.getUserId())
@@ -4567,7 +4581,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
public PasswordMetrics getPasswordMinimumMetrics(@UserIdInt int userHandle,
boolean deviceWideOnly) {
final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)
+ && (isSystemUid(caller) || hasCallingOrSelfPermission(
+ permission.SET_INITIAL_LOCK)));
return getPasswordMinimumMetricsUnchecked(userHandle, deviceWideOnly);
}
@@ -11575,14 +11591,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Preconditions.checkCallAuthorization(
isProfileOwner(caller) || isDeviceOwner(caller));
}
- long id = mInjector.binderClearCallingIdentity();
try {
return mIPackageManager.getBlockUninstallForUser(packageName, userId);
} catch (RemoteException re) {
// Shouldn't happen.
Slogf.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
}
}
return false;
@@ -16912,6 +16925,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slogf.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
+ synchronized (mESIDInitilizationLock) {
+ if (mEsidCalculator == null) {
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ mEsidCalculator = mInjector.newEnterpriseSpecificIdCalculator();
+ });
+ }
+ }
+
final String ownerPackage;
synchronized (getLockObject()) {
final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
@@ -16929,16 +16950,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
"The organization ID has been previously set to a different value and cannot "
+ "be changed");
final String dpcPackage = owner.info.getPackageName();
- mInjector.binderWithCleanCallingIdentity(() -> {
- EnterpriseSpecificIdCalculator esidCalculator =
- new EnterpriseSpecificIdCalculator(mContext);
-
- final String esid = esidCalculator.calculateEnterpriseId(dpcPackage,
- organizationId);
- owner.mOrganizationId = organizationId;
- owner.mEnrollmentSpecificId = esid;
- saveSettingsLocked(userId);
- });
+ final String esid = mEsidCalculator.calculateEnterpriseId(dpcPackage,
+ organizationId);
+ owner.mOrganizationId = organizationId;
+ owner.mEnrollmentSpecificId = esid;
+ saveSettingsLocked(userId);
}
DevicePolicyEventLogger
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index a2db6aaca3df..85fe65ca5563 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -22,8 +22,6 @@ import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
-import com.android.server.devicepolicy.Owners.OwnerDto;
-
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
@@ -205,12 +203,12 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
}
private int runListOwners(PrintWriter pw) {
- List<OwnerDto> owners = mService.listAllOwners();
+ List<OwnerShellData> owners = mService.listAllOwners();
int size = printAndGetSize(pw, owners, "owner");
if (size == 0) return 0;
for (int i = 0; i < size; i++) {
- OwnerDto owner = owners.get(i);
+ OwnerShellData owner = owners.get(i);
pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
if (owner.isDeviceOwner) {
pw.print(",DeviceOwner");
@@ -218,6 +216,9 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
if (owner.isProfileOwner) {
pw.print(",ProfileOwner");
}
+ if (owner.isManagedProfileOwner) {
+ pw.printf(",ManagedProfileOwner(parentUserId=%d)", owner.parentUserId);
+ }
if (owner.isAffiliated) {
pw.print(",Affiliated");
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java
new file mode 100644
index 000000000000..b98c3dc2ac07
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.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 com.android.server.devicepolicy;
+
+import static android.os.UserHandle.USER_NULL;
+
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+ */
+final class OwnerShellData {
+
+ public final @UserIdInt int userId;
+ public final @UserIdInt int parentUserId;
+ public final ComponentName admin;
+ public final boolean isDeviceOwner;
+ public final boolean isProfileOwner;
+ public final boolean isManagedProfileOwner;
+ public boolean isAffiliated;
+
+ // NOTE: class is too simple to require a Builder (not to mention isAffiliated is mutable)
+ private OwnerShellData(@UserIdInt int userId, @UserIdInt int parentUserId, ComponentName admin,
+ boolean isDeviceOwner, boolean isProfileOwner, boolean isManagedProfileOwner) {
+ Preconditions.checkArgument(userId != USER_NULL, "userId cannot be USER_NULL");
+ this.userId = userId;
+ this.parentUserId = parentUserId;
+ this.admin = Objects.requireNonNull(admin, "admin must not be null");
+ this.isDeviceOwner = isDeviceOwner;
+ this.isProfileOwner = isProfileOwner;
+ this.isManagedProfileOwner = isManagedProfileOwner;
+ if (isManagedProfileOwner) {
+ Preconditions.checkArgument(parentUserId != USER_NULL,
+ "parentUserId cannot be USER_NULL for managed profile owner");
+ Preconditions.checkArgument(parentUserId != userId,
+ "cannot be parent of itself (%d)", userId);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(getClass().getSimpleName())
+ .append("[userId=").append(userId)
+ .append(",admin=").append(admin.flattenToShortString());
+ if (isDeviceOwner) {
+ sb.append(",deviceOwner");
+ }
+ if (isProfileOwner) {
+ sb.append(",isProfileOwner");
+ }
+ if (isManagedProfileOwner) {
+ sb.append(",isManagedProfileOwner");
+ }
+ if (parentUserId != USER_NULL) {
+ sb.append(",parentUserId=").append(parentUserId);
+ }
+ if (isAffiliated) {
+ sb.append(",isAffiliated");
+ }
+ return sb.append(']').toString();
+ }
+
+ static OwnerShellData forDeviceOwner(@UserIdInt int userId, ComponentName admin) {
+ return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin,
+ /* isDeviceOwner= */ true, /* isProfileOwner= */ false,
+ /* isManagedProfileOwner= */ false);
+ }
+
+ static OwnerShellData forUserProfileOwner(@UserIdInt int userId, ComponentName admin) {
+ return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin,
+ /* isDeviceOwner= */ false, /* isProfileOwner= */ true,
+ /* isManagedProfileOwner= */ false);
+ }
+
+ static OwnerShellData forManagedProfileOwner(@UserIdInt int userId, @UserIdInt int parentUserId,
+ ComponentName admin) {
+ return new OwnerShellData(userId, parentUserId, admin, /* isDeviceOwner= */ false,
+ /* isProfileOwner= */ false, /* isManagedProfileOwner= */ true);
+ }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index fd09e3f9cfd0..3584728a2e62 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -19,7 +19,6 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManagerInternal;
import android.app.admin.DevicePolicyManager.DeviceOwnerType;
@@ -476,17 +475,16 @@ class Owners {
}
}
- List<OwnerDto> listAllOwners() {
- List<OwnerDto> owners = new ArrayList<>();
+ List<OwnerShellData> listAllOwners() {
+ List<OwnerShellData> owners = new ArrayList<>();
synchronized (mLock) {
if (mDeviceOwner != null) {
- owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
- /* isDeviceOwner= */ true));
+ owners.add(OwnerShellData.forDeviceOwner(mDeviceOwnerUserId, mDeviceOwner.admin));
}
for (int i = 0; i < mProfileOwners.size(); i++) {
int userId = mProfileOwners.keyAt(i);
OwnerInfo info = mProfileOwners.valueAt(i);
- owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+ owners.add(OwnerShellData.forUserProfileOwner(userId, info.admin));
}
}
return owners;
@@ -1236,24 +1234,6 @@ class Owners {
}
}
- /**
- * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
- */
- static final class OwnerDto {
- public final @UserIdInt int userId;
- public final ComponentName admin;
- public final boolean isDeviceOwner;
- public final boolean isProfileOwner;
- public boolean isAffiliated;
-
- private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
- this.userId = userId;
- this.admin = Objects.requireNonNull(admin, "admin must not be null");
- this.isDeviceOwner = isDeviceOwner;
- this.isProfileOwner = !isDeviceOwner;
- }
- }
-
public void dump(IndentingPrintWriter pw) {
boolean needBlank = false;
if (mDeviceOwner != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index c29de905d370..939a3dcfa32d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -389,9 +389,15 @@ class SecurityLogMonitor implements Runnable {
mCriticalLevelLogged = false;
Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
}
- if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
- + " with ids " + mPendingLogs.get(0).getId()
- + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+ if (DEBUG) {
+ if (mPendingLogs.size() > 0) {
+ Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging,"
+ + " with ids " + mPendingLogs.get(0).getId()
+ + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId());
+ } else {
+ Slog.d(TAG, "0 pending events in the buffer after merging");
+ }
+ }
}
@GuardedBy("mLock")
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 91d4f7e2a24d..46adb32ff103 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -130,8 +131,6 @@ import com.android.server.hdmi.HdmiControlService;
import com.android.server.incident.IncidentCompanionService;
import com.android.server.input.InputManagerService;
import com.android.server.inputmethod.InputMethodManagerService;
-import com.android.server.inputmethod.InputMethodSystemProperty;
-import com.android.server.inputmethod.MultiClientInputMethodManagerService;
import com.android.server.integrity.AppIntegrityManagerService;
import com.android.server.lights.LightsService;
import com.android.server.location.LocationManagerService;
@@ -287,6 +286,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.job.JobSchedulerService";
private static final String LOCK_SETTINGS_SERVICE_CLASS =
"com.android.server.locksettings.LockSettingsService$Lifecycle";
+ private static final String RESOURCE_ECONOMY_SERVICE_CLASS =
+ "com.android.server.tare.InternalResourceService";
private static final String STORAGE_MANAGER_SERVICE_CLASS =
"com.android.server.StorageManagerService$Lifecycle";
private static final String STORAGE_STATS_SERVICE_CLASS =
@@ -377,10 +378,14 @@ public final class SystemServer implements Dumpable {
"com.android.server.connectivity.IpConnectivityMetrics";
private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
"com.android.server.media.MediaCommunicationService";
+ private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS =
+ "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle";
private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService";
private static final String GAME_MANAGER_SERVICE_CLASS =
"com.android.server.app.GameManagerService$Lifecycle";
+ private static final String UWB_APEX_SERVICE_JAR_PATH =
+ "/apex/com.android.uwb/javalib/service-uwb.jar";
private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -1464,6 +1469,12 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
+ // TODO(aml-jobscheduler): Think about how to do it properly.
+ t.traceBegin("StartResourceEconomy");
+ mSystemServiceManager.startService(RESOURCE_ECONOMY_SERVICE_CLASS);
+ t.traceEnd();
+
+ // TODO(aml-jobscheduler): Think about how to do it properly.
t.traceBegin("StartAlarmManagerService");
mSystemServiceManager.startService(ALARM_MANAGER_SERVICE_CLASS);
t.traceEnd();
@@ -1584,6 +1595,9 @@ public final class SystemServer implements Dumpable {
// all listeners have the chance to react with special handling.
Settings.Global.putInt(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 1);
+ } else if (context.getResources().getBoolean(R.bool.config_autoResetAirplaneMode)) {
+ Settings.Global.putInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
}
StatusBarManagerService statusBar = null;
@@ -1595,12 +1609,7 @@ public final class SystemServer implements Dumpable {
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
t.traceBegin("StartInputMethodManagerLifecycle");
- if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {
- mSystemServiceManager.startService(
- MultiClientInputMethodManagerService.Lifecycle.class);
- } else {
- mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
- }
+ mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
t.traceEnd();
t.traceBegin("StartAccessibilityManagerService");
@@ -2070,11 +2079,14 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(DockObserver.class);
t.traceEnd();
+ // TODO(b/191495635): re-enable thermal observer after fixing b/191375904.
+ /*
if (isWatch) {
t.traceBegin("StartThermalObserver");
mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
t.traceEnd();
}
+ */
t.traceBegin("StartWiredAccessoryManager");
try {
@@ -2428,7 +2440,9 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
- if (isWatch) {
+ // TODO(b/191495635): Re-enable these services after fixing b/191375904.
+ /*
+ if (isWatch) {
// Must be started before services that depend it, e.g. WearConnectivityService
t.traceBegin("StartWearPowerService");
mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS);
@@ -2456,6 +2470,7 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS);
t.traceEnd();
}
+ */
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
t.traceBegin("StartSliceManagerService");
@@ -2632,9 +2647,13 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("ArtManagerLocal");
+ LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+ t.traceEnd();
+
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) {
t.traceBegin("UwbService");
- mSystemServiceManager.startService(UWB_SERVICE_CLASS);
+ mSystemServiceManager.startServiceFromJar(UWB_SERVICE_CLASS, UWB_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
@@ -2650,6 +2669,10 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS);
t.traceEnd();
+ t.traceBegin("AppCompatOverridesService");
+ mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS);
+ t.traceEnd();
+
ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
START_BLOB_STORE_SERVICE);
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index aca48b647efa..ee5a534fe005 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -19,8 +19,8 @@ import android.app.backup.FullBackupDataOutput;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.Environment;
diff --git a/core/tests/uwbtests/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp
index 31f446f7a60d..35d754b4adc5 100644
--- a/core/tests/uwbtests/Android.bp
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 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.
@@ -22,17 +22,18 @@ package {
}
android_test {
- name: "UwbManagerTests",
+ name: "PackageInstallerTests",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
- "androidx.test.ext.junit",
"androidx.test.rules",
- "mockito-target-minus-junit4",
- ],
- libs: [
- "android.test.runner",
+ "androidx.test.runner",
+ "junit",
+ "kotlin-test",
+ "truth-prebuilt",
],
- srcs: ["src/**/*.java"],
platform_apis: true,
- certificate: "platform",
test_suites: ["device-tests"],
}
diff --git a/services/tests/PackageManager/packageinstaller/AndroidManifest.xml b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
new file mode 100644
index 000000000000..d7062587c4bc
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.packageinstaller.test">
+
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.packageinstaller.test"
+ />
+
+</manifest>
+
diff --git a/services/tests/PackageManager/packageinstaller/AndroidTest.xml b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
new file mode 100644
index 000000000000..c39285ffca38
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration description="Test module config for PackageInstallerTests">
+ <option name="test-tag" value="PackageInstallerTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="PackageInstallerTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.packageinstaller.test" />
+ </test>
+</configuration>
diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
new file mode 100644
index 000000000000..d7d2726c1583
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.packageinstaller.test
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import androidx.test.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+
+class ExportedComponentTest {
+
+ private val context: Context = InstrumentationRegistry.getContext()
+
+ @Test
+ fun verifyNoExportedReceivers() {
+ val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
+ data = Uri.parse("content://mockForTest")
+ }
+ val packageInstallers = context.packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY or PackageManager.MATCH_DISABLED_COMPONENTS)
+ .map { it.activityInfo.packageName }
+ .distinct()
+ .map { context.packageManager.getPackageInfo(it, PackageManager.GET_RECEIVERS) }
+
+ assertThat(packageInstallers).isNotEmpty()
+
+ packageInstallers.forEach {
+ val exported = it.receivers.filter { it.exported }
+ assertWithMessage("Receivers should not be exported").that(exported).isEmpty()
+ }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
index 6f168a3888b3..67efa142aac5 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -32,6 +32,8 @@
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
+ <option name="push" value="AppEnumerationHasAppOpPermissionTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationHasAppOpPermissionTestApp.apk" />
+ <option name="push" value="AppEnumerationSharedUserTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSharedUserTestApp.apk" />
</target_preparer>
<option name="test-tag" value="AppEnumerationInternalTest" />
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
index 933784560410..ab004bea553e 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.AppGlobals;
import android.content.pm.IPackageManager;
import android.content.pm.ProviderInfo;
+import android.os.Process;
import androidx.test.runner.AndroidJUnit4;
@@ -43,8 +44,20 @@ public class AppEnumerationInternalTests {
private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/";
private static final String SYNC_PROVIDER_APK_PATH =
TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk";
- private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider";
- private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME;
+ private static final String HAS_APPOP_PERMISSION_APK_PATH =
+ TEST_DATA_PATH + "AppEnumerationHasAppOpPermissionTestApp.apk";
+ private static final String SHARED_USER_APK_PATH =
+ TEST_DATA_PATH + "AppEnumerationSharedUserTestApp.apk";
+
+ private static final String TARGET_SYNC_PROVIDER = "com.android.appenumeration.syncprovider";
+ private static final String TARGET_HAS_APPOP_PERMISSION =
+ "com.android.appenumeration.hasappoppermission";
+ private static final String TARGET_SHARED_USER = "com.android.appenumeration.shareduid";
+
+ private static final String SYNC_PROVIDER_AUTHORITY = TARGET_SYNC_PROVIDER;
+ private static final String PERMISSION_REQUEST_INSTALL_PACKAGES =
+ "android.permission.REQUEST_INSTALL_PACKAGES";
+ private static final String SHARED_USER_NAME = "com.android.appenumeration.shareduid";
private IPackageManager mIPackageManager;
@@ -55,7 +68,9 @@ public class AppEnumerationInternalTests {
@After
public void tearDown() throws Exception {
- uninstallPackage(SYNC_PROVIDER_PKG_NAME);
+ uninstallPackage(TARGET_SYNC_PROVIDER);
+ uninstallPackage(TARGET_HAS_APPOP_PERMISSION);
+ uninstallPackage(TARGET_SHARED_USER);
}
@Test
@@ -67,7 +82,7 @@ public class AppEnumerationInternalTests {
assertThat(names).contains(SYNC_PROVIDER_AUTHORITY);
assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
- .contains(SYNC_PROVIDER_PKG_NAME);
+ .contains(TARGET_SYNC_PROVIDER);
}
@Test
@@ -79,7 +94,43 @@ public class AppEnumerationInternalTests {
assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY);
assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
- .doesNotContain(SYNC_PROVIDER_PKG_NAME);
+ .doesNotContain(TARGET_SYNC_PROVIDER);
+ }
+
+ @Test
+ public void getAppOpPermissionPackages_canSeeForceQueryable() throws Exception {
+ installPackage(HAS_APPOP_PERMISSION_APK_PATH, true /* forceQueryable */);
+
+ final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+ PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+ assertThat(packageNames).asList().contains(TARGET_HAS_APPOP_PERMISSION);
+ }
+
+ @Test
+ public void getAppOpPermissionPackages_cannotSeeHasAppOpPermission() throws Exception {
+ installPackage(HAS_APPOP_PERMISSION_APK_PATH, false /* forceQueryable */);
+
+ final String[] packageNames = mIPackageManager.getAppOpPermissionPackages(
+ PERMISSION_REQUEST_INSTALL_PACKAGES);
+
+ assertThat(packageNames).asList().doesNotContain(TARGET_HAS_APPOP_PERMISSION);
+ }
+
+ @Test
+ public void getUidForSharedUser_canSeeForceQueryable() throws Exception {
+ installPackage(SHARED_USER_APK_PATH, true /* forceQueryable */);
+
+ final int uid = mIPackageManager.getUidForSharedUser(SHARED_USER_NAME);
+ assertThat(uid).isGreaterThan(Process.FIRST_APPLICATION_UID);
+ }
+
+ @Test
+ public void getUidForSharedUser_cannotSeeSharedUser() throws Exception {
+ installPackage(SHARED_USER_APK_PATH, false /* forceQueryable */);
+
+ final int uid = mIPackageManager.getUidForSharedUser(SHARED_USER_NAME);
+ assertThat(uid).isEqualTo(Process.INVALID_UID);
}
private static void installPackage(String apkPath, boolean forceQueryable) {
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
index 64239b4c6b2d..e0f83272f7a8 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
@@ -34,3 +34,31 @@ android_test_helper_app {
test_suites: ["device-tests"],
platform_apis: true,
}
+
+android_test_helper_app {
+ name: "AppEnumerationHasAppOpPermissionTestApp",
+ srcs: ["src/**/*.java"],
+ manifest: "AndroidManifest-hasAppOpPermission.xml",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+}
+
+android_test_helper_app {
+ name: "AppEnumerationSharedUserTestApp",
+ srcs: ["src/**/*.java"],
+ manifest: "AndroidManifest-sharedUser.xml",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.xml
new file mode 100644
index 000000000000..be8a6dc8709c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-hasAppOpPermission.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.appenumeration.hasappoppermission">
+
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-sharedUser.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-sharedUser.xml
new file mode 100644
index 000000000000..b424298e0ae0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-sharedUser.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.appenumeration.shareduid"
+ android:sharedUserId="com.android.appenumeration.shareduid">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest> \ No newline at end of file
diff --git a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
index dc8c8113fac2..f584599e8100 100644
--- a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
@@ -20,6 +20,13 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command"
+ value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.HostTest">
<option name="jar" value="PackageManagerServiceHostTests.jar" />
</test>
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt
new file mode 100644
index 000000000000..86571abcbb38
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/PackageInstallerSessionTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import kotlin.jvm.JvmField
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class PackageInstallerSessionTest : BaseHostJUnit4Test() {
+ companion object {
+ private const val DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk"
+ }
+
+ @Rule
+ @JvmField
+ val tempFolder = TemporaryFolder()
+
+ @Test
+ fun verify_parentSessionFail_childSessionFiles_shouldBeDestroyed() {
+ runDeviceTest("com.android.server.pm.PackageInstallerSessionTest",
+ "verify_parentSessionFail_childSessionFiles_shouldBeDestroyed")
+ }
+
+ /**
+ * Run a device side test from com.android.server.pm.test.deviceside.DeviceSide
+ *
+ * @param method the method to run
+ */
+ fun runDeviceTest(testClassName: String, method: String) {
+ val deviceSideFile = HostUtils.copyResourceToHostFile(DEVICE_SIDE, tempFolder.newFile())
+ Truth.assertThat(device.installPackage(deviceSideFile, true)).isNull()
+ runDeviceTests(device, "com.android.server.pm.test.deviceside",
+ testClassName, method)
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index 7e4f0e72b62d..53adc2fb00e6 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -32,6 +32,8 @@ android_test_helper_app {
],
static_libs: [
"androidx.annotation_annotation",
+ "commands-helper",
+ "cts-install-lib",
"junit",
"junit-params",
"androidx.test.ext.junit",
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml
index 286ad56435fd..dd6dea78d08b 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/AndroidManifest.xml
@@ -19,6 +19,8 @@
package="com.android.server.pm.test.deviceside">
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/PackageInstallerSessionTest.kt
new file mode 100644
index 000000000000..bdef8f188c53
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -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 com.android.server.pm
+
+import android.Manifest
+import android.app.Instrumentation
+import android.content.Context
+import android.system.helpers.CommandsHelper
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.compatibility.common.util.AdoptShellPermissionsRule
+import com.android.compatibility.common.util.SystemUtil
+import com.android.cts.install.lib.Install
+import com.android.cts.install.lib.InstallUtils
+import com.android.cts.install.lib.LocalIntentSender
+import com.android.cts.install.lib.TestApp
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import java.io.IOException
+import kotlin.jvm.JvmField
+
+class PackageInstallerSessionTest {
+ @Rule
+ @JvmField
+ val mAdoptShellPermissionsRule = AdoptShellPermissionsRule(
+ getInstrumentation().getUiAutomation(),
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES)
+
+ private val mInstrumentation: Instrumentation = getInstrumentation()
+ private var mChildSessionStageDirInfo: String? = null
+
+ /**
+ * To get all of child session IDs.
+ *
+ * @param parentSessionId the parent session id
+ * @return the array of child session IDs
+ * @throws IOException caused by opening parent session fail.
+ */
+ @Throws(IOException::class)
+ private fun getChildSessionIds(parentSessionId: Int): IntArray {
+ InstallUtils.openPackageInstallerSession(parentSessionId).use {
+ parentSession -> return parentSession.childSessionIds
+ }
+ }
+
+ private fun getSessionStageDir(sessionId: Int): String? {
+ val commandsHelper: CommandsHelper = CommandsHelper.getInstance(mInstrumentation)
+ val dumpsysForPackage: MutableList<String>? = commandsHelper
+ .executeShellCommandAndSplitOutput("dumpsys package", "\\n")
+ val pattern = Regex(" stageDir=(\\S+${sessionId}\\S+) ")
+ val matchStageDirs: ArrayList<String> = ArrayList()
+ dumpsysForPackage?.forEach { line ->
+ val matchResult: MatchResult? = pattern.find(line)
+ if (matchResult != null) {
+ val (stageDir: String) = matchResult.destructured
+ matchStageDirs.add(stageDir)
+ }
+ }
+
+ if (matchStageDirs.size > 0) {
+ return matchStageDirs[0]
+ }
+ return null
+ }
+
+ private fun getSessionStageDirInfo(sessionId: Int): String? {
+ SystemUtil.runWithShellPermissionIdentity {
+ val sessionStageDir =
+ getSessionStageDir(sessionId) ?: return@runWithShellPermissionIdentity
+ val command = "su root ls $sessionStageDir"
+ val lines: List<String> = CommandsHelper.getInstance(mInstrumentation)
+ .executeShellCommandAndSplitOutput(command, "\\n")
+ val sessionIdStr = sessionId.toString()
+ for (line in lines) {
+ if (line.contains(sessionIdStr)) {
+ mChildSessionStageDirInfo = line
+ }
+ }
+ }
+ return mChildSessionStageDirInfo
+ }
+
+ @Test
+ @Throws(Exception::class)
+ fun verify_parentSessionFail_childSessionFiles_shouldBeDestroyed() {
+ val context: Context = mInstrumentation.targetContext
+ Install.single(TestApp.A3).commit()
+ val parentSessionId: Int = Install.multi(TestApp.A1).createSession()
+ val childSessionIds: IntArray = getChildSessionIds(parentSessionId)
+ val firstChildSessionId = childSessionIds[0]
+
+ val sender = LocalIntentSender()
+ try {
+ InstallUtils.openPackageInstallerSession(parentSessionId).use { session ->
+ session.commit(sender.intentSender)
+ val result = sender.result
+ InstallUtils.assertStatusFailure(result)
+ }
+ } finally {
+ context.unregisterReceiver(sender)
+ }
+
+ Truth.assertThat(getSessionStageDirInfo(firstChildSessionId)).isNull()
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 988c02bfb3db..1bcc3d1f70ad 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -32,6 +32,7 @@ android_test {
"androidx.test.runner",
"junit",
"kotlin-test",
+ "kotlin-reflect",
"services.core",
"servicestests-utils",
"truth-prebuilt",
diff --git a/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING b/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING
new file mode 100644
index 000000000000..cacfcf0dbebc
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "PackageManagerServiceUnitTests"
+ }
+ ]
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
new file mode 100644
index 000000000000..a7644ec439da
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.ConfigurationInfo
+import android.content.pm.FeatureGroupInfo
+import android.content.pm.FeatureInfo
+import android.content.pm.PackageManager
+import android.content.pm.SigningDetails
+import android.content.pm.parsing.ParsingPackage
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedAttribution
+import android.content.pm.parsing.component.ParsedComponent
+import android.content.pm.parsing.component.ParsedInstrumentation
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.parsing.component.ParsedPermission
+import android.content.pm.parsing.component.ParsedPermissionGroup
+import android.content.pm.parsing.component.ParsedProcess
+import android.content.pm.parsing.component.ParsedProvider
+import android.content.pm.parsing.component.ParsedService
+import android.content.pm.parsing.component.ParsedUsesPermission
+import android.net.Uri
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.ArraySet
+import android.util.SparseArray
+import android.util.SparseIntArray
+import com.android.internal.R
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import java.security.KeyPairGenerator
+import java.security.PublicKey
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, PackageImpl::class) {
+
+ override val defaultImpl = PackageImpl.forTesting("com.example.test")
+ override val creator = PackageImpl.CREATOR
+
+ override val excludedMethods = listOf(
+ // Internal methods
+ "toAppInfoToString",
+ "toAppInfoWithoutState",
+ "toAppInfoWithoutStateWithoutFlags",
+ "assignDerivedFields",
+ "buildFakeForDeletion",
+ "capPermissionPriorities",
+ "forParsing",
+ "forTesting",
+ "getBaseAppDataCredentialProtectedDirForSystemUser",
+ "getBaseAppDataDeviceProtectedDirForSystemUser",
+ "getBoolean",
+ "setBoolean",
+ "hideAsFinal",
+ "hideAsParsed",
+ "markNotActivitiesAsNotExportedIfSingleUser",
+ "sortActivities",
+ "sortReceivers",
+ "sortServices",
+ "setAllComponentsDirectBootAware",
+ // Tested through setting minor/major manually
+ "setLongVersionCode",
+ "getLongVersionCode",
+ // Tested through constructor
+ "getManifestPackageName",
+ "setManifestPackageName",
+ // Utility methods
+ "getStorageUuid",
+ // Removal not tested, irrelevant for parcelling concerns
+ "removeUsesOptionalLibrary",
+ "clearAdoptPermissions",
+ "clearOriginalPackages",
+ "clearProtectedBroadcasts",
+ "removePermission",
+ "removeUsesLibrary",
+ "removeUsesOptionalNativeLibrary",
+ // Tested manually
+ "getMimeGroups",
+ "getRequestedPermissions",
+ // Tested through asSplit
+ "asSplit",
+ "getSplitNames",
+ "getSplitCodePaths",
+ "getSplitRevisionCodes",
+ "getSplitFlags",
+ "getSplitClassLoaderNames",
+ "getSplitDependencies",
+ "setSplitCodePaths",
+ "setSplitClassLoaderName",
+ "setSplitHasCode",
+ )
+
+ override val baseParams = listOf(
+ AndroidPackage::getAppComponentFactory,
+ AndroidPackage::getAutoRevokePermissions,
+ AndroidPackage::getBackupAgentName,
+ AndroidPackage::getBanner,
+ AndroidPackage::getBaseApkPath,
+ AndroidPackage::getBaseRevisionCode,
+ AndroidPackage::getCategory,
+ AndroidPackage::getClassLoaderName,
+ AndroidPackage::getClassName,
+ AndroidPackage::getCompatibleWidthLimitDp,
+ AndroidPackage::getCompileSdkVersion,
+ AndroidPackage::getCompileSdkVersionCodeName,
+ AndroidPackage::getDataExtractionRules,
+ AndroidPackage::getDescriptionRes,
+ AndroidPackage::getFullBackupContent,
+ AndroidPackage::getGwpAsanMode,
+ AndroidPackage::getIconRes,
+ AndroidPackage::getInstallLocation,
+ AndroidPackage::getLabelRes,
+ AndroidPackage::getLargestWidthLimitDp,
+ AndroidPackage::getLogo,
+ AndroidPackage::getManageSpaceActivityName,
+ AndroidPackage::getMemtagMode,
+ AndroidPackage::getMinSdkVersion,
+ AndroidPackage::getNativeHeapZeroInitialized,
+ AndroidPackage::getNativeLibraryDir,
+ AndroidPackage::getNativeLibraryRootDir,
+ AndroidPackage::getNetworkSecurityConfigRes,
+ AndroidPackage::getNonLocalizedLabel,
+ AndroidPackage::getOverlayCategory,
+ AndroidPackage::getOverlayPriority,
+ AndroidPackage::getOverlayTarget,
+ AndroidPackage::getOverlayTargetName,
+ AndroidPackage::getPackageName,
+ AndroidPackage::getPath,
+ AndroidPackage::getPermission,
+ AndroidPackage::getPrimaryCpuAbi,
+ AndroidPackage::getProcessName,
+ AndroidPackage::getRealPackage,
+ AndroidPackage::getRequiredAccountType,
+ AndroidPackage::getRequiresSmallestWidthDp,
+ AndroidPackage::getResizeableActivity,
+ AndroidPackage::getRestrictedAccountType,
+ AndroidPackage::getRoundIconRes,
+ AndroidPackage::getSeInfo,
+ AndroidPackage::getSeInfoUser,
+ AndroidPackage::getSecondaryCpuAbi,
+ AndroidPackage::getSecondaryNativeLibraryDir,
+ AndroidPackage::getSharedUserId,
+ AndroidPackage::getSharedUserLabel,
+ AndroidPackage::getStaticSharedLibName,
+ AndroidPackage::getStaticSharedLibVersion,
+ AndroidPackage::getTargetSandboxVersion,
+ AndroidPackage::getTargetSdkVersion,
+ AndroidPackage::getTaskAffinity,
+ AndroidPackage::getTheme,
+ AndroidPackage::getUiOptions,
+ AndroidPackage::getUid,
+ AndroidPackage::getVersionName,
+ AndroidPackage::getZygotePreloadName,
+ AndroidPackage::isAllowAudioPlaybackCapture,
+ AndroidPackage::isAllowBackup,
+ AndroidPackage::isAllowClearUserData,
+ AndroidPackage::isAllowClearUserDataOnFailedRestore,
+ AndroidPackage::isAllowNativeHeapPointerTagging,
+ AndroidPackage::isAllowTaskReparenting,
+ AndroidPackage::isBackupInForeground,
+ AndroidPackage::isBaseHardwareAccelerated,
+ AndroidPackage::isCantSaveState,
+ AndroidPackage::isCoreApp,
+ AndroidPackage::isCrossProfile,
+ AndroidPackage::isDebuggable,
+ AndroidPackage::isDefaultToDeviceProtectedStorage,
+ AndroidPackage::isDirectBootAware,
+ AndroidPackage::isEnabled,
+ AndroidPackage::isExternalStorage,
+ AndroidPackage::isExtractNativeLibs,
+ AndroidPackage::isFactoryTest,
+ AndroidPackage::isForceQueryable,
+ AndroidPackage::isFullBackupOnly,
+ AndroidPackage::isGame,
+ AndroidPackage::isHasCode,
+ AndroidPackage::isHasDomainUrls,
+ AndroidPackage::isHasFragileUserData,
+ AndroidPackage::isIsolatedSplitLoading,
+ AndroidPackage::isKillAfterRestore,
+ AndroidPackage::isLargeHeap,
+ AndroidPackage::isMultiArch,
+ AndroidPackage::isNativeLibraryRootRequiresIsa,
+ AndroidPackage::isOdm,
+ AndroidPackage::isOem,
+ AndroidPackage::isOverlay,
+ AndroidPackage::isOverlayIsStatic,
+ AndroidPackage::isPartiallyDirectBootAware,
+ AndroidPackage::isPersistent,
+ AndroidPackage::isPrivileged,
+ AndroidPackage::isProduct,
+ AndroidPackage::isProfileableByShell,
+ AndroidPackage::isRequestLegacyExternalStorage,
+ AndroidPackage::isRequiredForAllUsers,
+ AndroidPackage::isResizeableActivityViaSdkVersion,
+ AndroidPackage::isRestoreAnyVersion,
+ AndroidPackage::isSignedWithPlatformKey,
+ AndroidPackage::isStaticSharedLibrary,
+ AndroidPackage::isStub,
+ AndroidPackage::isSupportsRtl,
+ AndroidPackage::isSystem,
+ AndroidPackage::isSystemExt,
+ AndroidPackage::isTestOnly,
+ AndroidPackage::isUse32BitAbi,
+ AndroidPackage::isUseEmbeddedDex,
+ AndroidPackage::isUsesCleartextTraffic,
+ AndroidPackage::isUsesNonSdkApi,
+ AndroidPackage::isVendor,
+ AndroidPackage::isVisibleToInstantApps,
+ AndroidPackage::isVmSafeMode,
+ AndroidPackage::getMaxAspectRatio,
+ AndroidPackage::getMinAspectRatio,
+ AndroidPackage::hasPreserveLegacyExternalStorage,
+ AndroidPackage::hasRequestForegroundServiceExemption,
+ AndroidPackage::hasRequestRawExternalStorageAccess,
+ )
+
+ override fun extraParams() = listOf(
+ getter(AndroidPackage::getVolumeUuid, "57554103-df3e-4475-ae7a-8feba49353ac"),
+ getter(AndroidPackage::isProfileable, true),
+ getter(AndroidPackage::getVersionCode, 3),
+ getter(AndroidPackage::getVersionCodeMajor, 9),
+ getter(AndroidPackage::getUpgradeKeySets, setOf("testUpgradeKeySet")),
+ getter(AndroidPackage::isAnyDensity, false, 0),
+ getter(AndroidPackage::isResizeable, false, 0),
+ getter(AndroidPackage::isSupportsSmallScreens, false, 0),
+ getter(AndroidPackage::isSupportsNormalScreens, false, 0),
+ getter(AndroidPackage::isSupportsLargeScreens, false, 0),
+ getter(AndroidPackage::isSupportsExtraLargeScreens, false, 0),
+ adder(AndroidPackage::getAdoptPermissions, "test.adopt.PERMISSION"),
+ adder(AndroidPackage::getOriginalPackages, "com.test.original"),
+ adder(AndroidPackage::getImplicitPermissions, "test.implicit.PERMISSION"),
+ adder(AndroidPackage::getLibraryNames, "testLibraryName"),
+ adder(AndroidPackage::getProtectedBroadcasts, "test.protected.BROADCAST"),
+ adder(AndroidPackage::getQueriesPackages, "com.test.package.queries"),
+ adder(AndroidPackage::getQueriesProviders, "com.test.package.queries.provider"),
+ adder(AndroidPackage::getUsesLibraries, "testUsesLibrary"),
+ adder(AndroidPackage::getUsesNativeLibraries, "testUsesNativeLibrary"),
+ adder(AndroidPackage::getUsesOptionalLibraries, "testUsesOptionalLibrary"),
+ adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"),
+ adder(AndroidPackage::getUsesStaticLibraries, "testUsesStaticLibrary"),
+ getSetByValue(
+ AndroidPackage::getUsesStaticLibrariesVersions,
+ PackageImpl::addUsesStaticLibraryVersion,
+ (testCounter++).toLong(),
+ transformGet = { it?.singleOrNull() }
+ ),
+ getSetByValue(
+ AndroidPackage::areAttributionsUserVisible,
+ ParsingPackage::setAttributionsAreUserVisible,
+ true
+ ),
+ getSetByValue2(
+ AndroidPackage::getOverlayables,
+ PackageImpl::addOverlayable,
+ "testOverlayableName" to "testActorName",
+ transformGet = { "testOverlayableName" to it["testOverlayableName"] }
+ ),
+ getSetByValue(
+ AndroidPackage::getMetaData,
+ PackageImpl::setMetaData,
+ "testBundleKey" to "testBundleValue",
+ transformGet = { "testBundleKey" to it?.getString("testBundleKey") },
+ transformSet = { Bundle().apply { putString(it.first, it.second) } }
+ ),
+ getSetByValue(
+ AndroidPackage::getAttributions,
+ PackageImpl::addAttribution,
+ Triple("testTag", 13, listOf("testInherit")),
+ transformGet = { it.singleOrNull()?.let { Triple(it.tag, it.label, it.inheritFrom) } },
+ transformSet = { it?.let { ParsedAttribution(it.first, it.second, it.third) } }
+ ),
+ getSetByValue2(
+ AndroidPackage::getKeySetMapping,
+ PackageImpl::addKeySet,
+ "testKeySetName" to testKey(),
+ transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() },
+ ),
+ getSetByValue(
+ AndroidPackage::getPermissionGroups,
+ PackageImpl::addPermissionGroup,
+ "test.permission.GROUP",
+ transformGet = { it.singleOrNull()?.name },
+ transformSet = { ParsedPermissionGroup().apply { setName(it) } }
+ ),
+ getSetByValue2(
+ AndroidPackage::getPreferredActivityFilters,
+ PackageImpl::addPreferredActivityFilter,
+ "TestClassName" to ParsedIntentInfo().apply {
+ addDataScheme("http")
+ addDataAuthority("test.pm.server.android.com", null)
+ },
+ transformGet = { it.singleOrNull()?.let { it.first to it.second } },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ { it.first },
+ { it.second.schemesIterator().asSequence().singleOrNull() },
+ { it.second.authoritiesIterator().asSequence().singleOrNull()?.host },
+ )
+ }
+ ),
+ getSetByValue(
+ AndroidPackage::getQueriesIntents,
+ PackageImpl::addQueriesIntent,
+ Intent(Intent.ACTION_VIEW, Uri.parse("https://test.pm.server.android.com")),
+ transformGet = { it.singleOrNull() },
+ compare = { first, second -> first?.filterEquals(second) },
+ ),
+ getSetByValue(
+ AndroidPackage::getRestrictUpdateHash,
+ PackageImpl::setRestrictUpdateHash,
+ byteArrayOf(0, 1, 2, 3, 4),
+ compare = ByteArray::contentEquals
+ ),
+ getSetByValue(
+ AndroidPackage::getSigningDetails,
+ PackageImpl::setSigningDetails,
+ testKey(),
+ transformGet = { it.publicKeys?.takeIf { it.size > 0 }?.valueAt(0) },
+ transformSet = {
+ SigningDetails(
+ null,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ ArraySet<PublicKey>().apply { add(it) },
+ null
+ )
+ }
+ ),
+ getSetByValue(
+ AndroidPackage::getUsesStaticLibrariesCertDigests,
+ PackageImpl::addUsesStaticLibraryCertDigests,
+ arrayOf("testCertDigest"),
+ transformGet = { it?.singleOrNull() },
+ compare = Array<String?>?::contentEquals
+ ),
+ getSetByValue(
+ AndroidPackage::getActivities,
+ PackageImpl::addActivity,
+ "TestActivityName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() }
+ ),
+ getSetByValue(
+ AndroidPackage::getReceivers,
+ PackageImpl::addReceiver,
+ "TestReceiverName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedActivity().apply { name = it }.withMimeGroups() }
+ ),
+ getSetByValue(
+ AndroidPackage::getServices,
+ PackageImpl::addService,
+ "TestServiceName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedService().apply { name = it }.withMimeGroups() }
+ ),
+ getSetByValue(
+ AndroidPackage::getProviders,
+ PackageImpl::addProvider,
+ "TestProviderName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedProvider().apply { name = it }.withMimeGroups() }
+ ),
+ getSetByValue(
+ AndroidPackage::getInstrumentations,
+ PackageImpl::addInstrumentation,
+ "TestInstrumentationName",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedInstrumentation().apply { name = it } }
+ ),
+ getSetByValue(
+ AndroidPackage::getConfigPreferences,
+ PackageImpl::addConfigPreference,
+ testCounter++,
+ transformGet = { it.singleOrNull()?.reqGlEsVersion ?: -1 },
+ transformSet = { ConfigurationInfo().apply { reqGlEsVersion = it } }
+ ),
+ getSetByValue(
+ AndroidPackage::getFeatureGroups,
+ PackageImpl::addFeatureGroup,
+ "test.feature.GROUP",
+ transformGet = { it.singleOrNull()?.features?.singleOrNull()?.name.orEmpty() },
+ transformSet = {
+ FeatureGroupInfo().apply {
+ features = arrayOf(FeatureInfo().apply { name = it })
+ }
+ }
+ ),
+ getSetByValue(
+ AndroidPackage::getPermissions,
+ PackageImpl::addPermission,
+ "test.PERMISSION",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { ParsedPermission().apply { name = it } }
+ ),
+ getSetByValue(
+ AndroidPackage::getUsesPermissions,
+ PackageImpl::addUsesPermission,
+ "test.USES_PERMISSION",
+ transformGet = {
+ // Need to strip implicit permission, which calls addUsesPermission when added
+ it.filterNot { it.name == "test.implicit.PERMISSION" }
+ .singleOrNull()?.name.orEmpty()
+ },
+ transformSet = { ParsedUsesPermission(it, 0) }
+ ),
+ getSetByValue(
+ AndroidPackage::getReqFeatures,
+ PackageImpl::addReqFeature,
+ "test.feature.INFO",
+ transformGet = { it.singleOrNull()?.name.orEmpty() },
+ transformSet = { FeatureInfo().apply { name = it } }
+ ),
+ getSetByValue(
+ AndroidPackage::getMinExtensionVersions,
+ PackageImpl::setMinExtensionVersions,
+ SparseIntArray().apply { put(testCounter++, testCounter++) },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ { it.size() },
+ { it.keyAt(0) },
+ { it.valueAt(0) },
+ )
+ }
+ ),
+ getSetByValue(
+ AndroidPackage::getProcesses,
+ PackageImpl::setProcesses,
+ mapOf("testProcess" to ParsedProcess().apply { name = "testProcessName" }),
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ { it["testProcess"]?.name },
+ )
+ }
+ ),
+ getSetByValue(
+ AndroidPackage::getProperties,
+ PackageImpl::addProperty,
+ PackageManager.Property(
+ "testPropertyName",
+ "testPropertyValue",
+ "testPropertyClassName",
+ "testPropertyPackageName"
+ ),
+ transformGet = { it["testPropertyName"] },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ PackageManager.Property::getName,
+ PackageManager.Property::getClassName,
+ PackageManager.Property::getPackageName,
+ PackageManager.Property::getString,
+ )
+ }
+ ),
+ )
+
+ override fun initialObject() = PackageImpl.forParsing(
+ "com.example.test",
+ "/test/test/base.apk",
+ "/test/test",
+ mockThrowOnUnmocked {
+ whenever(getInteger(R.styleable.AndroidManifest_revisionCode, 0)) { 4 }
+ whenever(getBoolean(R.styleable.AndroidManifest_isolatedSplits, false)) { true }
+
+ // Return invalid values here so that the getter/setter is tested properly
+ whenever(getInteger(R.styleable.AndroidManifest_versionCode, 0)) { -1 }
+ whenever(getInteger(R.styleable.AndroidManifest_versionCodeMajor, 0)) { -1 }
+ whenever(
+ getNonConfigurationString(
+ R.styleable.AndroidManifest_versionName,
+ 0
+ )
+ ) { "" }
+ whenever(getInteger(R.styleable.AndroidManifest_compileSdkVersion, 0)) { 31 }
+ whenever(
+ getNonConfigurationString(
+ R.styleable.AndroidManifest_compileSdkVersionCodename,
+ 0
+ )
+ ) { "" }
+ },
+ true
+ )
+ .asSplit(
+ arrayOf("testSplitNameZero", "testSplitNameOne"),
+ arrayOf("/test/testSplitZero.apk", "/test/testSplitOne.apk"),
+ intArrayOf(10, 11),
+ SparseArray<IntArray>().apply {
+ put(0, intArrayOf(-1))
+ put(1, intArrayOf(0))
+ }
+ )
+ .setSplitHasCode(0, true)
+ .setSplitHasCode(1, false)
+ .setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
+ .setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
+
+ override fun extraAssertions(before: Parcelable, after: Parcelable) {
+ super.extraAssertions(before, after)
+ after as PackageImpl
+ expect.that(after.manifestPackageName).isEqualTo("com.example.test")
+ expect.that(after.isCoreApp).isTrue()
+ expect.that(after.isIsolatedSplitLoading).isEqualTo(true)
+ expect.that(after.longVersionCode).isEqualTo(38654705667)
+ expect.that(after.requestedPermissions)
+ .containsExactlyElementsIn(after.usesPermissions.map { it.name })
+ .inOrder()
+
+ expect.that(after.mimeGroups).containsExactly(
+ "TestActivityName/mimeGroup",
+ "TestReceiverName/mimeGroup",
+ "TestServiceName/mimeGroup",
+ "TestProviderName/mimeGroup"
+ )
+
+ expect.that(after.splitNames).asList()
+ .containsExactly("testSplitNameZero", "testSplitNameOne")
+ .inOrder()
+ expect.that(after.splitCodePaths).asList()
+ .containsExactly("/test/testSplitZero.apk", "/test/testSplitOne.apk")
+ .inOrder()
+ expect.that(after.splitRevisionCodes).asList()
+ .containsExactly(10, 11)
+ .inOrder()
+ expect.that(after.splitFlags).asList()
+ .containsExactly(ApplicationInfo.FLAG_HAS_CODE, 0)
+ .inOrder()
+ expect.that(after.splitClassLoaderNames).asList()
+ .containsExactly("testSplitClassLoaderNameZero", "testSplitClassLoaderNameOne")
+ .inOrder()
+
+ expect.that(after.splitDependencies).isNotNull()
+ after.splitDependencies?.let {
+ expect.that(it.size()).isEqualTo(2)
+ expect.that(it.get(0)).asList().containsExactly(-1)
+ expect.that(it.get(1)).asList().containsExactly(0)
+ }
+ }
+
+ private fun testKey() = KeyPairGenerator.getInstance("RSA")
+ .generateKeyPair()
+ .public
+
+ private fun <T : ParsedComponent> T.withMimeGroups() = apply {
+ val componentName = name
+ addIntent(ParsedIntentInfo().apply {
+ addMimeGroup("$componentName/mimeGroup")
+ })
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt
new file mode 100644
index 000000000000..e16a1871f8b8
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.os.Parcel
+import android.os.Parcelable
+import com.android.server.pm.test.util.IgnoreableExpect
+import com.google.common.truth.Expect
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+import java.util.Objects
+import kotlin.contracts.ExperimentalContracts
+import kotlin.reflect.KClass
+import kotlin.reflect.KFunction
+import kotlin.reflect.KFunction1
+import kotlin.reflect.KFunction2
+import kotlin.reflect.KFunction3
+import kotlin.reflect.KVisibility
+import kotlin.reflect.full.allSuperclasses
+import kotlin.reflect.full.createInstance
+import kotlin.reflect.full.isSubclassOf
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.full.staticProperties
+import kotlin.reflect.jvm.jvmErasure
+
+
+@ExperimentalContracts
+abstract class ParcelableComponentTest(
+ private val getterType: KClass<*>,
+ private val setterType: KClass<out Parcelable>
+) {
+
+ companion object {
+ private val DEFAULT_EXCLUDED = listOf(
+ // Java
+ "toString",
+ "equals",
+ "hashCode",
+ // Parcelable
+ "getStability",
+ "describeContents",
+ "writeToParcel",
+ // @DataClass
+ "__metadata"
+ )
+ }
+
+ internal val ignoreableExpect = IgnoreableExpect()
+
+ // Hides internal type
+ @get:Rule
+ val ignoreableAsTestRule: TestRule = ignoreableExpect
+
+ val expect: Expect
+ get() = ignoreableExpect.expect
+
+ protected var testCounter = 1
+
+ protected abstract val defaultImpl: Any
+ protected abstract val creator: Parcelable.Creator<out Parcelable>
+
+ protected open val excludedMethods: Collection<String> = emptyList()
+
+ protected abstract val baseParams: Collection<KFunction1<*, Any?>>
+
+ private val getters = getterType.memberFunctions
+ .filterNot { DEFAULT_EXCLUDED.contains(it.name) }
+
+ private val setters = setterType.memberFunctions
+ .filterNot { DEFAULT_EXCLUDED.contains(it.name) }
+
+ constructor(kClass: KClass<out Parcelable>) : this(kClass, kClass)
+
+ @Before
+ fun checkNoPublicFields() {
+ // Fields are not currently testable, and the idea is to enforce interface access for
+ // immutability purposes, so disallow any public fields from existing.
+ expect.that(getterType.memberProperties.filter { it.visibility == KVisibility.PUBLIC }
+ .filterNot { DEFAULT_EXCLUDED.contains(it.name) })
+ .isEmpty()
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <ObjectType, ReturnType> buildParams(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ ): Param? {
+ return buildParams<ObjectType, ReturnType, ReturnType, ReturnType>(
+ getFunction,
+ autoValue(getFunction) as ReturnType ?: return null
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <ObjectType, ReturnType, SetType : Any?, CompareType : Any?> buildParams(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ value: SetType,
+ ): Param? {
+ return getSetByValue<ObjectType, ReturnType, SetType, Any?>(
+ getFunction,
+ findSetFunction(getFunction) ?: return null,
+ value
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <ObjectType, ReturnType> findSetFunction(
+ getFunction: KFunction1<ObjectType, ReturnType>
+ ): KFunction2<ObjectType, ReturnType, Any?>? {
+ val getFunctionName = getFunction.name
+ val prefix = when {
+ getFunctionName.startsWith("get") -> "get"
+ getFunctionName.startsWith("is") -> "is"
+ getFunctionName.startsWith("has") -> "has"
+ else -> throw IllegalArgumentException("Unsupported method name $getFunctionName")
+ }
+ val setFunctionName = "set" + getFunctionName.removePrefix(prefix)
+ val setFunction = setters.filter { it.name == setFunctionName }
+ .minByOrNull { it.parameters.size }
+
+ if (setFunction == null) {
+ expect.withMessage("$getFunctionName does not have corresponding $setFunctionName")
+ .fail()
+ return null
+ }
+
+ return setFunction as KFunction2<ObjectType, ReturnType, Any?>
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun <ObjectType, ReturnType, SetType> findAddFunction(
+ getFunction: KFunction1<ObjectType, ReturnType>
+ ): KFunction2<ObjectType, SetType, Any?>? {
+ val getFunctionName = getFunction.name
+ if (!getFunctionName.startsWith("get")) {
+ throw IllegalArgumentException("Unsupported method name $getFunctionName")
+ }
+
+ val setFunctionName = "add" + getFunctionName.removePrefix("get").run {
+ // Remove plurality
+ when {
+ endsWith("ies") -> "${removeSuffix("ies")}y"
+ endsWith("s") -> removeSuffix("s")
+ else -> this
+ }
+ }
+
+ val setFunction = setters.filter { it.name == setFunctionName }
+ .minByOrNull { it.parameters.size }
+
+ if (setFunction == null) {
+ expect.withMessage("$getFunctionName does not have corresponding $setFunctionName")
+ .fail()
+ return null
+ }
+
+ return setFunction as KFunction2<ObjectType, SetType, Any?>
+ }
+
+ protected fun <ObjectType, ReturnType> getter(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ valueToSet: ReturnType
+ ) = buildParams<ObjectType, ReturnType, ReturnType, ReturnType>(getFunction, valueToSet)
+
+ protected fun <ObjectType, ReturnType, SetType : Any?, CompareType : Any?> getter(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ expectedValue: CompareType,
+ valueToSet: SetType
+ ): Param? {
+ return getSetByValue(
+ getFunction,
+ findSetFunction(getFunction) ?: return null,
+ value = expectedValue,
+ transformSet = { valueToSet }
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ protected fun <ObjectType, ReturnType> adder(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ value: ReturnType,
+ ): Param? {
+ return getSetByValue(
+ getFunction,
+ findAddFunction<ObjectType, Any?, ReturnType>(getFunction) ?: return null,
+ value,
+ transformGet = {
+ // Primitive arrays don't implement Iterable, so cast manually
+ when (it) {
+ is BooleanArray -> it.singleOrNull()
+ is IntArray -> it.singleOrNull()
+ is LongArray -> it.singleOrNull()
+ is Iterable<*> -> it.singleOrNull()
+ else -> null
+ }
+ },
+ )
+ }
+
+ /**
+ * Method to provide custom getter and setter logic for values which are not simple primitives
+ * or cannot be directly compared using [Objects.equals].
+ *
+ * @param getFunction the getter function which will be called and marked as tested
+ * @param setFunction the setter function which will be called and marked as tested
+ * @param value the value for comparison through the parcel-unparcel cycle, which can be
+ * anything, like the [String] ID of an inner object
+ * @param transformGet the function to transform the result of [getFunction] into [value]
+ * @param transformSet the function to transform [value] into an input for [setFunction]
+ * @param compare the function that compares the pre/post-parcel [value] objects
+ */
+ @Suppress("UNCHECKED_CAST")
+ protected fun <ObjectType, ReturnType, SetType : Any?, CompareType : Any?> getSetByValue(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ setFunction: KFunction2<ObjectType, SetType, Any?>,
+ value: CompareType,
+ transformGet: (ReturnType) -> CompareType = { it as CompareType },
+ transformSet: (CompareType) -> SetType = { it as SetType },
+ compare: (CompareType, CompareType) -> Boolean? = Objects::equals
+ ) = Param(
+ getFunction.name,
+ { transformGet(getFunction.call(it as ObjectType)) },
+ setFunction.name,
+ { setFunction.call(it.first() as ObjectType, transformSet(it[1] as CompareType)) },
+ { value },
+ { first, second -> compare(first as CompareType, second as CompareType) == true }
+ )
+
+ /**
+ * Variant of [getSetByValue] that allows specifying a [setFunction] with 2 inputs.
+ */
+ @Suppress("UNCHECKED_CAST")
+ protected fun <ObjectType, ReturnType, SetType1 : Any?, SetType2 : Any?, CompareType : Any?>
+ getSetByValue2(
+ getFunction: KFunction1<ObjectType, ReturnType>,
+ setFunction: KFunction3<ObjectType, SetType1, SetType2, Any>,
+ value: CompareType,
+ transformGet: (ReturnType) -> CompareType = { it as CompareType },
+ transformSet: (CompareType) -> Pair<SetType1, SetType2> =
+ { it as Pair<SetType1, SetType2> },
+ compare: (CompareType, CompareType) -> Boolean = Objects::equals
+ ) = Param(
+ getFunction.name,
+ { transformGet(getFunction.call(it as ObjectType)) },
+ setFunction.name,
+ {
+ val pair = transformSet(it[1] as CompareType)
+ setFunction.call(it.first() as ObjectType, pair.first, pair.second)
+ },
+ { value },
+ { first, second -> compare(first as CompareType, second as CompareType) }
+ )
+
+ protected fun autoValue(getFunction: KFunction<*>) = when (getFunction.returnType.jvmErasure) {
+ Boolean::class -> (getFunction.call(defaultImpl) as Boolean?)?.not() ?: true
+ CharSequence::class,
+ String::class -> getFunction.name + "TEST"
+ Int::class -> testCounter++
+ Long::class -> (testCounter++).toLong()
+ Float::class -> (testCounter++).toFloat()
+ else -> {
+ expect.withMessage("${getFunction.name} needs to provide value").fail()
+ null
+ }
+ }
+
+ /**
+ * Verifies two instances are equivalent via a series of properties. For use when a public API
+ * class has not implemented equals.
+ */
+ @Suppress("UNCHECKED_CAST")
+ protected fun <T : Any> equalBy(
+ first: T?,
+ second: T?,
+ vararg properties: (T) -> Any?
+ ) = properties.all { property ->
+ first?.let { property(it) } == second?.let { property(it) }
+ }
+
+ @Test
+ fun valueComparison() {
+ val params = baseParams.mapNotNull(::buildParams) + extraParams().filterNotNull()
+ val before = initialObject()
+
+ params.forEach { it.setFunction(arrayOf(before, it.value())) }
+
+ val parcel = Parcel.obtain()
+ writeToParcel(parcel, before)
+
+ val dataSize = parcel.dataSize()
+
+ parcel.setDataPosition(0)
+
+ val after = creator.createFromParcel(parcel)
+
+ expect.withMessage("Mismatched write and read data sizes")
+ .that(parcel.dataPosition())
+ .isEqualTo(dataSize)
+
+ parcel.recycle()
+
+ runAssertions(params, before, after)
+ }
+
+ @Test
+ open fun parcellingSize() {
+ val parcelOne = Parcel.obtain()
+ writeToParcel(parcelOne, initialObject())
+
+ val parcelTwo = Parcel.obtain()
+ initialObject().writeToParcel(parcelTwo, 0)
+
+ val superDataSizes = setterType.allSuperclasses
+ .filter { it.isSubclassOf(Parcelable::class) }
+ .mapNotNull { it.memberFunctions.find { it.name == "writeToParcel" } }
+ .filter { it.isFinal }
+ .map {
+ val parcel = Parcel.obtain()
+ initialObject().writeToParcel(parcel, 0)
+ parcel.dataSize().also { parcel.recycle() }
+ }
+
+ if ((superDataSizes + parcelOne.dataSize() + parcelTwo.dataSize()).distinct().size != 1) {
+ listOf(getterType, setterType).distinct().forEach {
+ val creatorProperties = it.staticProperties.filter { it.name == "CREATOR" }
+ if (creatorProperties.size > 1) {
+ expect.withMessage(
+ "Multiple matching CREATOR fields found for" +
+ it.qualifiedName
+ )
+ .that(creatorProperties)
+ .hasSize(1)
+ } else {
+ val creator = creatorProperties.single().get()
+ if (creator !is Parcelable.Creator<*>) {
+ expect.that(creator).isInstanceOf(Parcelable.Creator::class.java)
+ return
+ }
+
+ parcelTwo.setDataPosition(0)
+ val parcelable = creator.createFromParcel(parcelTwo)
+ if (parcelable::class.isSubclassOf(setterType)) {
+ expect.withMessage(
+ "${it.qualifiedName} which does not safely override writeToParcel " +
+ "cannot contain a subclass CREATOR field"
+ )
+ .fail()
+ }
+ }
+ }
+ }
+
+ parcelOne.recycle()
+ parcelTwo.recycle()
+ }
+
+ private fun runAssertions(params: List<Param>, before: Parcelable, after: Parcelable) {
+ params.forEach {
+ val actual = it.getFunction(after)
+ val expected = it.value()
+ val equal = it.compare(actual, expected)
+ expect.withMessage("${it.getFunctionName} was $actual, expected $expected")
+ .that(equal)
+ .isTrue()
+ }
+
+ extraAssertions(before, after)
+
+ // TODO: Handle method overloads?
+ val expectedFunctions = (getters.map { it.name }
+ + setters.map { it.name }
+ - excludedMethods)
+ .distinct()
+
+ val allTestedFunctions = params.flatMap {
+ listOfNotNull(it.getFunctionName, it.setFunctionName)
+ }
+ expect.that(allTestedFunctions).containsExactlyElementsIn(expectedFunctions)
+ }
+
+ open fun extraParams(): Collection<Param?> = emptyList()
+
+ open fun initialObject(): Parcelable = setterType.createInstance()
+
+ open fun extraAssertions(before: Parcelable, after: Parcelable) {}
+
+ open fun writeToParcel(parcel: Parcel, value: Parcelable) = value.writeToParcel(parcel, 0)
+
+ data class Param(
+ val getFunctionName: String,
+ val getFunction: (Any?) -> Any?,
+ val setFunctionName: String?,
+ val setFunction: (Array<Any?>) -> Unit,
+ val value: () -> Any?,
+ val compare: (Any?, Any?) -> Boolean = Objects::equals
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt
new file mode 100644
index 000000000000..d506190b8b09
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorInvalidTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.os.Parcel
+import android.os.Parcelable
+import com.android.server.pm.test.parsing.parcelling.java.TestSubWithCreator
+import com.android.server.pm.test.parsing.parcelling.java.TestSuperClass
+import org.junit.Test
+import kotlin.contracts.ExperimentalContracts
+
+/**
+ * Verifies the failing side of [ParcelableCreatorValidTest]. The sole difference is the addition
+ * of [TestSubWithCreator.CREATOR].
+ */
+@ExperimentalContracts
+class ParcelableCreatorInvalidTest :
+ ParcelableComponentTest(TestSuperClass::class, TestSubWithCreator::class) {
+
+ override val defaultImpl = TestSubWithCreator()
+
+ override val creator = object : Parcelable.Creator<Parcelable> {
+ override fun createFromParcel(source: Parcel) = TestSubWithCreator(source)
+ override fun newArray(size: Int) = Array<TestSubWithCreator?>(size) { null }
+ }
+
+ override val excludedMethods = listOf("writeSubToParcel")
+
+ override val baseParams = listOf(TestSuperClass::getSuperString)
+
+ override fun writeToParcel(parcel: Parcel, value: Parcelable) {
+ (value as TestSubWithCreator).writeSubToParcel(parcel, 0)
+ }
+
+ @Test
+ override fun parcellingSize() {
+ super.parcellingSize()
+ if (expect.hasFailures()) {
+ // This is a hack to ignore an expected failure result. Doing it this way, rather than
+ // adding a switch in the test itself, prevents it from accidentally passing through a
+ // programming error.
+ ignoreableExpect.ignore()
+ }
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt
new file mode 100644
index 000000000000..f1bc7b5a07f4
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableCreatorValidTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.os.Parcel
+import android.os.Parcelable
+import com.android.server.pm.test.parsing.parcelling.java.TestSubWithoutCreator
+import com.android.server.pm.test.parsing.parcelling.java.TestSuperClass
+import kotlin.contracts.ExperimentalContracts
+
+/**
+ * Tests the [Parcelable] CREATOR verification by using a mock object with known differences to
+ * ensure that the method succeeds/fails.
+ */
+@ExperimentalContracts
+class ParcelableCreatorValidTest :
+ ParcelableComponentTest(TestSuperClass::class, TestSubWithoutCreator::class) {
+
+ override val defaultImpl = TestSubWithoutCreator()
+
+ override val creator = object : Parcelable.Creator<Parcelable> {
+ override fun createFromParcel(source: Parcel) = TestSubWithoutCreator(source)
+ override fun newArray(size: Int) = Array<TestSubWithoutCreator?>(size) { null }
+ }
+
+ override val excludedMethods = listOf("writeSubToParcel")
+
+ override val baseParams = listOf(TestSuperClass::getSuperString)
+
+ override fun writeToParcel(parcel: Parcel, value: Parcelable) {
+ (value as TestSubWithoutCreator).writeSubToParcel(parcel, 0)
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
new file mode 100644
index 000000000000..ece600bc446e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.content.pm.ActivityInfo
+import android.content.pm.parsing.component.ParsedActivity
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedActivityTest : ParsedMainComponentTest(ParsedActivity::class) {
+
+ override val defaultImpl = ParsedActivity()
+ override val creator = ParsedActivity.CREATOR
+
+ override val mainComponentSubclassBaseParams = listOf(
+ ParsedActivity::getPermission,
+ ParsedActivity::getColorMode,
+ ParsedActivity::getConfigChanges,
+ ParsedActivity::getDocumentLaunchMode,
+ ParsedActivity::getLaunchMode,
+ ParsedActivity::getLockTaskLaunchMode,
+ ParsedActivity::getMaxAspectRatio,
+ ParsedActivity::getMaxRecents,
+ ParsedActivity::getMinAspectRatio,
+ ParsedActivity::getParentActivityName,
+ ParsedActivity::getPersistableMode,
+ ParsedActivity::getPrivateFlags,
+ ParsedActivity::getRequestedVrComponent,
+ ParsedActivity::getResizeMode,
+ ParsedActivity::getRotationAnimation,
+ ParsedActivity::getScreenOrientation,
+ ParsedActivity::getSoftInputMode,
+ ParsedActivity::getTargetActivity,
+ ParsedActivity::getTaskAffinity,
+ ParsedActivity::getTheme,
+ ParsedActivity::getUiOptions,
+ ParsedActivity::isSupportsSizeChanges,
+ )
+
+ override fun mainComponentSubclassExtraParams() = listOf(
+ getSetByValue(
+ ParsedActivity::getWindowLayout,
+ ParsedActivity::setWindowLayout,
+ ActivityInfo.WindowLayout(1, 1f, 2, 1f, 3, 4, 5),
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ ActivityInfo.WindowLayout::width,
+ ActivityInfo.WindowLayout::widthFraction,
+ ActivityInfo.WindowLayout::height,
+ ActivityInfo.WindowLayout::heightFraction,
+ ActivityInfo.WindowLayout::gravity,
+ ActivityInfo.WindowLayout::minWidth,
+ ActivityInfo.WindowLayout::minHeight
+ )
+ }
+ )
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
new file mode 100644
index 000000000000..e739dc70cdbb
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedAttributionTest.kt
@@ -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.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedAttribution
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedAttributionTest : ParcelableComponentTest(ParsedAttribution::class) {
+
+ override val defaultImpl = ParsedAttribution("", 0, emptyList())
+ override val creator = ParsedAttribution.CREATOR
+
+ override val baseParams = listOf(
+ ParsedAttribution::getTag,
+ ParsedAttribution::getLabel,
+ )
+
+ override fun extraParams() = listOf(
+ getter(ParsedAttribution::getInheritFrom, listOf("testInheritFrom"))
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
new file mode 100644
index 000000000000..0a22f6d301a9
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedComponentTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedComponent
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Bundle
+import android.os.Parcelable
+import kotlin.contracts.ExperimentalContracts
+import kotlin.reflect.KClass
+import kotlin.reflect.KFunction1
+
+@ExperimentalContracts
+abstract class ParsedComponentTest(kClass: KClass<out Parcelable>) :
+ ParcelableComponentTest(kClass) {
+
+ final override val excludedMethods
+ get() = subclassExcludedMethods + listOf(
+ // Method aliases/utilities
+ "getClassName",
+ "getComponentName",
+ "setProperties" // Tested though addProperty
+ )
+
+ open val subclassExcludedMethods: Collection<String> = emptyList()
+
+ final override val baseParams
+ get() = subclassBaseParams + listOf(
+ ParsedComponent::getBanner,
+ ParsedComponent::getDescriptionRes,
+ ParsedComponent::getFlags,
+ ParsedComponent::getIcon,
+ ParsedComponent::getLabelRes,
+ ParsedComponent::getLogo,
+ ParsedComponent::getName,
+ ParsedComponent::getNonLocalizedLabel,
+ ParsedComponent::getPackageName,
+ )
+
+ abstract val subclassBaseParams: Collection<KFunction1<*, Any?>>
+
+ final override fun extraParams() = subclassExtraParams() + listOf(
+ getSetByValue(
+ ParsedComponent::getIntents,
+ ParsedComponent::addIntent,
+ "TestLabel",
+ transformGet = { it.singleOrNull()?.nonLocalizedLabel },
+ transformSet = { ParsedIntentInfo().setNonLocalizedLabel(it) },
+ ),
+ getSetByValue(
+ ParsedComponent::getProperties,
+ ParsedComponent::addProperty,
+ PackageManager.Property(
+ "testPropertyName",
+ "testPropertyValue",
+ "testPropertyClassName",
+ "testPropertyPackageName"
+ ),
+ transformGet = { it["testPropertyName"] },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ PackageManager.Property::getName,
+ PackageManager.Property::getClassName,
+ PackageManager.Property::getPackageName,
+ PackageManager.Property::getString,
+ )
+ }
+ ),
+ getSetByValue(
+ ParsedComponent::getMetaData,
+ ParsedComponent::setMetaData,
+ "testBundleKey" to "testBundleValue",
+ transformGet = { "testBundleKey" to it?.getString("testBundleKey") },
+ transformSet = { Bundle().apply { putString(it.first, it.second) } }
+ ),
+ )
+
+ open fun subclassExtraParams(): Collection<Param?> = emptyList()
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
new file mode 100644
index 000000000000..b7a85cc3d186
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedInstrumentationTest.kt
@@ -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.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedInstrumentation
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedInstrumentationTest : ParsedComponentTest(ParsedInstrumentation::class) {
+
+ override val defaultImpl = ParsedInstrumentation()
+ override val creator = ParsedInstrumentation.CREATOR
+
+ override val subclassBaseParams = listOf(
+ ParsedInstrumentation::getTargetPackage,
+ ParsedInstrumentation::getTargetProcesses,
+ ParsedInstrumentation::isFunctionalTest,
+ ParsedInstrumentation::isHandleProfiling,
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
new file mode 100644
index 000000000000..e27bdf23cfc6
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedIntentInfoTest.kt
@@ -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.server.pm.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Parcel
+import android.os.Parcelable
+import android.os.PatternMatcher
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedIntentInfoTest : ParcelableComponentTest(ParsedIntentInfo::class) {
+
+ override val defaultImpl = ParsedIntentInfo()
+
+ override val creator = object : Parcelable.Creator<ParsedIntentInfo> {
+ override fun createFromParcel(source: Parcel) = ParsedIntentInfo(source)
+ override fun newArray(size: Int) = Array<ParsedIntentInfo?>(size) { null }
+ }
+
+ override val excludedMethods = listOf(
+ // Used to parcel
+ "writeIntentInfoToParcel",
+ // All remaining IntentFilter methods, which are out of scope
+ "hasDataPath",
+ "hasDataSchemeSpecificPart",
+ "matchAction",
+ "matchData",
+ "actionsIterator",
+ "addAction",
+ "addCategory",
+ "addDataAuthority",
+ "addDataPath",
+ "addDataScheme",
+ "addDataSchemeSpecificPart",
+ "addDataType",
+ "addDynamicDataType",
+ "addMimeGroup",
+ "asPredicate",
+ "asPredicateWithTypeResolution",
+ "authoritiesIterator",
+ "categoriesIterator",
+ "clearDynamicDataTypes",
+ "countActions",
+ "countCategories",
+ "countDataAuthorities",
+ "countDataPaths",
+ "countDataSchemeSpecificParts",
+ "countDataSchemes",
+ "countDataTypes",
+ "countMimeGroups",
+ "countStaticDataTypes",
+ "dataTypes",
+ "debugCheck",
+ "dump",
+ "dumpDebug",
+ "getAction",
+ "getAutoVerify",
+ "getCategory",
+ "getDataAuthority",
+ "getDataPath",
+ "getDataScheme",
+ "getDataSchemeSpecificPart",
+ "getDataType",
+ "getHosts",
+ "getHostsList",
+ "getMimeGroup",
+ "getOrder",
+ "getPriority",
+ "getVisibilityToInstantApp",
+ "handleAllWebDataURI",
+ "handlesWebUris",
+ "hasAction",
+ "hasCategory",
+ "hasDataAuthority",
+ "hasDataScheme",
+ "hasDataType",
+ "hasExactDataType",
+ "hasExactDynamicDataType",
+ "hasExactStaticDataType",
+ "hasMimeGroup",
+ "isExplicitlyVisibleToInstantApp",
+ "isImplicitlyVisibleToInstantApp",
+ "isVerified",
+ "isVisibleToInstantApp",
+ "match",
+ "matchCategories",
+ "matchDataAuthority",
+ "mimeGroupsIterator",
+ "needsVerification",
+ "pathsIterator",
+ "readFromXml",
+ "schemeSpecificPartsIterator",
+ "schemesIterator",
+ "setAutoVerify",
+ "setOrder",
+ "setPriority",
+ "setVerified",
+ "setVisibilityToInstantApp",
+ "typesIterator",
+ "writeToXml",
+ )
+
+ override val baseParams = listOf(
+ ParsedIntentInfo::getIcon,
+ ParsedIntentInfo::getLabelRes,
+ ParsedIntentInfo::isHasDefault,
+ ParsedIntentInfo::getNonLocalizedLabel,
+ )
+
+ override fun initialObject() = ParsedIntentInfo().apply {
+ addAction("test.ACTION")
+ addDataAuthority("testAuthority", "404")
+ addCategory("test.CATEGORY")
+ addMimeGroup("testMime")
+ addDataPath("testPath", PatternMatcher.PATTERN_LITERAL)
+ }
+
+ override fun extraAssertions(before: Parcelable, after: Parcelable) {
+ super.extraAssertions(before, after)
+ after as ParsedIntentInfo
+ expect.that(after.actionsIterator().asSequence().singleOrNull())
+ .isEqualTo("test.ACTION")
+
+ val authority = after.authoritiesIterator().asSequence().singleOrNull()
+ expect.that(authority?.host).isEqualTo("testAuthority")
+ expect.that(authority?.port).isEqualTo(404)
+
+ expect.that(after.categoriesIterator().asSequence().singleOrNull())
+ .isEqualTo("test.CATEGORY")
+ expect.that(after.mimeGroupsIterator().asSequence().singleOrNull())
+ .isEqualTo("testMime")
+ expect.that(after.hasDataPath("testPath")).isTrue()
+ }
+
+ override fun writeToParcel(parcel: Parcel, value: Parcelable) =
+ ParsedIntentInfo.PARCELER.parcel(value as ParsedIntentInfo, parcel, 0)
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
new file mode 100644
index 000000000000..411cb0950d18
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedMainComponentTest.kt
@@ -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.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedMainComponent
+import android.content.pm.parsing.component.ParsedService
+import android.os.Parcelable
+import java.util.Arrays
+import kotlin.contracts.ExperimentalContracts
+import kotlin.reflect.KClass
+import kotlin.reflect.KFunction1
+
+@ExperimentalContracts
+abstract class ParsedMainComponentTest(kClass: KClass<out Parcelable>) :
+ ParsedComponentTest(kClass) {
+
+ final override val subclassBaseParams
+ get() = mainComponentSubclassBaseParams + listOf(
+ ParsedMainComponent::getOrder,
+ ParsedMainComponent::getProcessName,
+ ParsedMainComponent::getSplitName,
+ ParsedMainComponent::isDirectBootAware,
+ ParsedMainComponent::isEnabled,
+ ParsedMainComponent::isExported,
+ )
+
+ abstract val mainComponentSubclassBaseParams: Collection<KFunction1<*, Any?>>
+
+ final override fun subclassExtraParams() = mainComponentSubclassExtraParams() + listOf(
+ getSetByValue(
+ ParsedService::getAttributionTags,
+ ParsedService::setAttributionTags,
+ arrayOf("testAttributionTag"),
+ compare = Arrays::equals
+ ),
+ )
+
+ open fun mainComponentSubclassExtraParams(): Collection<Param?> = emptyList()
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
new file mode 100644
index 000000000000..53c862a519b1
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionGroupTest.kt
@@ -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.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedPermissionGroup
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedPermissionGroupTest : ParsedComponentTest(ParsedPermissionGroup::class) {
+
+ override val defaultImpl = ParsedPermissionGroup()
+ override val creator = ParsedPermissionGroup.CREATOR
+
+ override val subclassBaseParams = listOf(
+ ParsedPermissionGroup::getRequestDetailResourceId,
+ ParsedPermissionGroup::getBackgroundRequestDetailResourceId,
+ ParsedPermissionGroup::getBackgroundRequestResourceId,
+ ParsedPermissionGroup::getRequestRes,
+ ParsedPermissionGroup::getPriority,
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
new file mode 100644
index 000000000000..bb63e2e2a61d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedPermissionTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedPermission
+import android.content.pm.parsing.component.ParsedPermissionGroup
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedPermissionTest : ParsedComponentTest(ParsedPermission::class) {
+
+ override val defaultImpl = ParsedPermission()
+ override val creator = ParsedPermission.CREATOR
+
+ override val subclassExcludedMethods = listOf(
+ // Utility methods
+ "isAppOp",
+ "isRuntime",
+ "getProtection",
+ "getProtectionFlags",
+ "calculateFootprint",
+ "setKnownCert", // Tested through setKnownCerts
+ )
+ override val subclassBaseParams = listOf(
+ ParsedPermission::getBackgroundPermission,
+ ParsedPermission::getGroup,
+ ParsedPermission::getRequestRes,
+ ParsedPermission::getProtectionLevel,
+ ParsedPermission::isTree,
+ )
+
+ override fun subclassExtraParams() = listOf(
+ getter(ParsedPermission::getKnownCerts, setOf("testCert")),
+ getSetByValue(
+ ParsedPermission::getParsedPermissionGroup,
+ ParsedPermission::setParsedPermissionGroup,
+ ParsedPermissionGroup().apply { name = "test.permission.group" },
+ compare = { first, second -> equalBy(first, second, ParsedPermissionGroup::getName) }
+ ),
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
new file mode 100644
index 000000000000..34f46f2c0adb
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedProcess
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedProcessTest : ParcelableComponentTest(ParsedProcess::class) {
+
+ override val defaultImpl = ParsedProcess()
+ override val creator = ParsedProcess.CREATOR
+
+ override val excludedMethods = listOf(
+ // Copying method
+ "addStateFrom",
+ )
+
+ override val baseParams = listOf(
+ ParsedProcess::getName,
+ ParsedProcess::getGwpAsanMode,
+ ParsedProcess::getMemtagMode,
+ ParsedProcess::getNativeHeapZeroInitialized,
+ )
+
+ override fun extraParams() = listOf(
+ getter(ParsedProcess::getDeniedPermissions, setOf("testDeniedPermission"))
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
new file mode 100644
index 000000000000..e6d5c0fd258f
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProviderTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.parsing.parcelling
+
+import android.content.pm.PathPermission
+import android.content.pm.parsing.component.ParsedProvider
+import android.os.PatternMatcher
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedProviderTest : ParsedMainComponentTest(ParsedProvider::class) {
+
+ override val defaultImpl = ParsedProvider()
+ override val creator = ParsedProvider.CREATOR
+
+ override val mainComponentSubclassBaseParams = listOf(
+ ParsedProvider::getAuthority,
+ ParsedProvider::isSyncable,
+ ParsedProvider::getReadPermission,
+ ParsedProvider::getWritePermission,
+ ParsedProvider::isGrantUriPermissions,
+ ParsedProvider::isForceUriPermissions,
+ ParsedProvider::isMultiProcess,
+ ParsedProvider::getInitOrder,
+ )
+
+ override fun mainComponentSubclassExtraParams() = listOf(
+ getSetByValue(
+ ParsedProvider::getUriPermissionPatterns,
+ ParsedProvider::setUriPermissionPatterns,
+ PatternMatcher("testPattern", PatternMatcher.PATTERN_LITERAL),
+ transformGet = { it?.singleOrNull() },
+ transformSet = { arrayOf(it) },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ PatternMatcher::getPath,
+ PatternMatcher::getType
+ )
+ }
+ ),
+ getSetByValue(
+ ParsedProvider::getPathPermissions,
+ ParsedProvider::setPathPermissions,
+ PathPermission(
+ "testPermissionPattern",
+ PatternMatcher.PATTERN_LITERAL,
+ "test.READ_PERMISSION",
+ "test.WRITE_PERMISSION"
+ ),
+ transformGet = { it?.singleOrNull() },
+ transformSet = { arrayOf(it) },
+ compare = { first, second ->
+ equalBy(
+ first, second,
+ PatternMatcher::getPath,
+ PatternMatcher::getType,
+ PathPermission::getReadPermission,
+ PathPermission::getWritePermission,
+ )
+ }
+ )
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.kt
new file mode 100644
index 000000000000..553018493f8b
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedServiceTest.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.server.pm.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedService
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedServiceTest : ParsedMainComponentTest(ParsedService::class) {
+
+ override val defaultImpl = ParsedService()
+ override val creator = ParsedService.CREATOR
+
+ override val mainComponentSubclassBaseParams = listOf(
+ ParsedService::getForegroundServiceType,
+ ParsedService::getPermission,
+ )
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
new file mode 100644
index 000000000000..1131c72152ad
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedUsesPermissionTest.kt
@@ -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.test.parsing.parcelling
+
+import android.content.pm.parsing.component.ParsedUsesPermission
+import android.os.Parcelable
+import kotlin.contracts.ExperimentalContracts
+
+@ExperimentalContracts
+class ParsedUsesPermissionTest : ParcelableComponentTest(ParsedUsesPermission::class) {
+
+ override val defaultImpl = ParsedUsesPermission("", 0)
+ override val creator = ParsedUsesPermission.CREATOR
+
+ override val baseParams = listOf(
+ ParsedUsesPermission::getName,
+ ParsedUsesPermission::getUsesPermissionFlags
+ )
+
+ override fun initialObject() = ParsedUsesPermission("", 0)
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java
new file mode 100644
index 000000000000..581d2b2d72b8
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithCreator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.parsing.parcelling.java;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+public class TestSubWithCreator extends TestSuperClass {
+
+ @NonNull
+ public static final Parcelable.Creator<TestSubWithCreator> CREATOR =
+ new Parcelable.Creator<TestSubWithCreator>() {
+ @Override
+ public TestSubWithCreator createFromParcel(Parcel source) {
+ return new TestSubWithCreator(source);
+ }
+
+ @Override
+ public TestSubWithCreator[] newArray(int size) {
+ return new TestSubWithCreator[size];
+ }
+ };
+
+ @Nullable
+ private String subString;
+
+ public TestSubWithCreator() {
+ }
+
+ public TestSubWithCreator(@NonNull Parcel in) {
+ super(in);
+ subString = in.readString();
+ }
+
+ public void writeSubToParcel(@NonNull Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeString(subString);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java
new file mode 100644
index 000000000000..4264a11c03e3
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSubWithoutCreator.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.parsing.parcelling.java;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import androidx.annotation.NonNull;
+
+public class TestSubWithoutCreator extends TestSuperClass {
+
+ @Nullable
+ private String subString;
+
+ public TestSubWithoutCreator() {
+ }
+
+ public TestSubWithoutCreator(@NonNull Parcel in) {
+ super(in);
+ subString = in.readString();
+ }
+
+ public void writeSubToParcel(@NonNull Parcel parcel, int flags) {
+ super.writeToParcel(parcel, flags);
+ parcel.writeString(subString);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java
new file mode 100644
index 000000000000..a009786de352
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.parsing.parcelling.java;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+@DataClass(genGetters = true, genSetters = true, genBuilder = false, genAidl = false,
+ genParcelable = true, genConstructor = false)
+public class TestSuperClass implements Parcelable {
+
+ @Nullable
+ private String superString;
+
+ public TestSuperClass() {
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @Nullable String getSuperString() {
+ return superString;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull TestSuperClass setSuperString(@NonNull String value) {
+ superString = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (superString != null) flg |= 0x1;
+ dest.writeByte(flg);
+ if (superString != null) dest.writeString(superString);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected TestSuperClass(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String _superString = (flg & 0x1) == 0 ? null : in.readString();
+
+ this.superString = _superString;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<TestSuperClass> CREATOR
+ = new Parcelable.Creator<TestSuperClass>() {
+ @Override
+ public TestSuperClass[] newArray(int size) {
+ return new TestSuperClass[size];
+ }
+
+ @Override
+ public TestSuperClass createFromParcel(@NonNull android.os.Parcel in) {
+ return new TestSuperClass(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1624381019144L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/java/TestSuperClass.java",
+ inputSignatures = "private @android.annotation.Nullable java.lang.String superString\nclass TestSuperClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genAidl=false, genParcelable=true, genConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
new file mode 100644
index 000000000000..afb18f5be669
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/util/IgnoreableExpect.kt
@@ -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.
+ */
+
+package com.android.server.pm.test.util
+
+import com.google.common.truth.Expect
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Wrapper for [Expect] which supports ignoring any failures. This should be used with caution, but
+ * it allows a base test to be written which doesn't switch success/failure in the test itself,
+ * preventing any logic errors from causing the test to accidentally succeed.
+ */
+internal class IgnoreableExpect : TestRule {
+
+ val expect = Expect.create()
+
+ private var ignore = false
+
+ override fun apply(base: Statement?, description: Description?): Statement {
+ return object : Statement() {
+ override fun evaluate() {
+ ignore = false
+ try {
+ expect.apply(base, description).evaluate()
+ } catch (t: Throwable) {
+ if (!ignore) {
+ throw t
+ }
+ }
+ }
+ }
+ }
+
+ fun ignore() {
+ ignore = true
+ }
+}
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 17a5dccb57da..3cab5ecd3de1 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.MANAGE_APPOPS"/>
<uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 607fb4760236..40b36648ccac 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -1003,6 +1003,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2),
+ eq(true));
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1017,6 +1019,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2),
+ eq(false));
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1030,6 +1034,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1047,6 +1053,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
+ verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2),
+ eq(true));
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1063,6 +1071,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1081,6 +1091,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1095,6 +1107,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1111,6 +1125,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1126,6 +1142,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1142,6 +1160,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1158,6 +1178,8 @@ public class AppStateTrackerTest {
verify(l, times(2)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1172,6 +1194,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1188,6 +1212,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1203,6 +1229,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(anyInt());
@@ -1225,6 +1253,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1240,6 +1270,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1255,6 +1287,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1270,6 +1304,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1286,6 +1322,8 @@ public class AppStateTrackerTest {
verify(l, times(1)).updateAllJobs();
verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(1)).updateAllAlarms();
verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1));
@@ -1301,6 +1339,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1316,6 +1356,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1331,6 +1373,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
@@ -1346,6 +1390,8 @@ public class AppStateTrackerTest {
verify(l, times(0)).updateAllJobs();
verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
+ verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(),
+ anyBoolean());
verify(l, times(0)).updateAllAlarms();
verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1));
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
new file mode 100644
index 000000000000..b3dc3eda362c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.am;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+
+import android.app.compat.CompatChanges;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ServiceInfo;
+import android.os.SystemClock;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+public class ActiveServicesTest {
+
+ private static final long DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN = 10 * 1000;
+ private static final long[] DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = {
+ 0,
+ DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN,
+ DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN * 2,
+ DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN * 3
+ };
+
+ private MockitoSession mMockingSession;
+ private ActivityManagerService mService;
+ private ActiveServices mActiveServices;
+ private AppProfiler mProfiler;
+
+ @Before
+ public void setUp() {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .mockStatic(CompatChanges.class)
+ .startMocking();
+ prepareTestRescheduleServiceRestarts();
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testRescheduleServiceRestartsOnChanges() throws Exception {
+ final long now = SystemClock.uptimeMillis();
+ final long btwn = mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
+ final long rd0 = 0;
+ final long rd1 = 1000;
+ final long rd2 = rd1 + btwn;
+ final long rd3 = rd2 + btwn;
+ final long rd4 = rd3 + btwn * 10;
+ final long rd5 = rd4 + btwn;
+ int memFactor = ADJ_MEM_FACTOR_MODERATE;
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+ fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
+
+ // Test enable/disable.
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(false, true, now);
+ long extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ verifyDelays(now, new long[] {rd0, extra, btwn + extra * 2, btwn * 2 + extra * 3, rd4,
+ rd5 + extra});
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(true, false, now);
+ verifyDelays(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
+
+ final long elapsed = 10;
+ final long now2 = now + elapsed;
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(false, true, now2);
+ verifyDelays(now2, new long[] {rd0 - elapsed, extra - elapsed,
+ btwn + extra * 2 - elapsed, btwn * 2 + extra * 3 - elapsed, rd4 - elapsed,
+ rd5 + extra - elapsed});
+
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(true, false, now2);
+ verifyDelays(now2, new long[] {rd0 - elapsed, rd1 - elapsed, rd2 - elapsed, rd3 - elapsed,
+ rd4 - elapsed, rd5 - elapsed});
+
+ // Test memory level changes.
+ memFactor = ADJ_MEM_FACTOR_LOW;
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+ extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ final long elapsed3 = elapsed * 2;
+ final long now3 = now + elapsed3;
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ ADJ_MEM_FACTOR_MODERATE, memFactor, "test", now3);
+ verifyDelays(now3, new long[] {rd0 - elapsed3, extra - elapsed3,
+ btwn + extra * 2 - elapsed3, btwn * 2 + extra * 3 - elapsed3, rd4 - elapsed3,
+ rd5 + extra - elapsed3});
+
+ memFactor = ADJ_MEM_FACTOR_CRITICAL;
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+ extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ final long elapsed4 = elapsed * 3;
+ final long now4 = now + elapsed4;
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ ADJ_MEM_FACTOR_LOW, memFactor, "test", now4);
+ verifyDelays(now4, new long[] {rd0 - elapsed4, extra - elapsed4,
+ btwn + extra * 2 - elapsed4, btwn * 2 + extra * 3 - elapsed4,
+ btwn * 3 + extra * 4 - elapsed4, btwn * 4 + extra * 5 - elapsed4});
+
+ memFactor = ADJ_MEM_FACTOR_MODERATE;
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+ extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ final long elapsed5 = elapsed * 4;
+ final long now5 = now + elapsed5;
+ mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ ADJ_MEM_FACTOR_CRITICAL, memFactor, "test", now5);
+ verifyDelays(now5, new long[] {rd0 - elapsed5, extra - elapsed5,
+ btwn + extra * 2 - elapsed5, btwn * 2 + extra * 3 - elapsed5,
+ rd4 - elapsed5, rd5 + extra - elapsed5});
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testRescheduleServiceRestartsOnOtherChanges() throws Exception {
+ final long now = SystemClock.uptimeMillis();
+ final long btwn = mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
+ final long rd0 = 1000;
+ final long rd1 = 2000;
+ final long rd2 = btwn * 10;
+ final long rd3 = 5000;
+ final long rd4 = btwn * 11 + 5000;
+ final long rd5 = 3000;
+ int memFactor = ADJ_MEM_FACTOR_CRITICAL;
+ long extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+
+ fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
+ setNextRestarts(now, new long[] {extra, btwn + extra * 2, btwn * 2 + extra * 3,
+ btwn * 3 + extra * 4, btwn * 4 + extra * 5, btwn * 5 + extra * 6});
+ mActiveServices.mRestartingServices.remove(1);
+ mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
+ verifyDelays(now, new long[] {extra, rd2, rd2 + btwn + extra,
+ rd2 + (btwn + extra) * 2, rd2 + (btwn + extra) * 3});
+ mActiveServices.mRestartingServices.remove(0);
+ mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
+ verifyDelays(now, new long[] {extra, rd2, rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
+ mActiveServices.mRestartingServices.remove(1);
+ mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
+ verifyDelays(now, new long[] {extra, btwn + extra * 2, rd4});
+
+ fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
+ setNextRestarts(now, new long[] {extra, btwn + extra * 2, btwn * 2 + extra * 3,
+ btwn * 3 + extra * 4, btwn * 4 + extra * 5, btwn * 5 + extra * 6});
+ mActiveServices.mRestartingServices.remove(1);
+ mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
+ memFactor = ADJ_MEM_FACTOR_LOW;
+ extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
+ when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
+ mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
+ verifyDelays(now, new long[] {extra, btwn + extra * 2, rd2,
+ rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
+ }
+
+ private void prepareTestRescheduleServiceRestarts() {
+ mService = mock(ActivityManagerService.class);
+ mService.mConstants = mock(ActivityManagerConstants.class);
+ mService.mConstants.mEnableExtraServiceRestartDelayOnMemPressure = true;
+ mService.mConstants.mExtraServiceRestartDelayOnMemPressure =
+ DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
+ mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN =
+ DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN;
+ mProfiler = mock(AppProfiler.class);
+ setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", mProfiler);
+ when(mProfiler.getLastMemoryLevelLocked()).thenReturn(ADJ_MEM_FACTOR_NORMAL);
+ mActiveServices = mock(ActiveServices.class);
+ setFieldValue(ActiveServices.class, mActiveServices, "mAm", mService);
+ setFieldValue(ActiveServices.class, mActiveServices, "mRestartingServices",
+ new ArrayList<>());
+ setFieldValue(ActiveServices.class, mActiveServices, "mRestartBackoffDisabledPackages",
+ new ArraySet<>());
+ doNothing().when(mActiveServices).performScheduleRestartLocked(any(ServiceRecord.class),
+ any(String.class), any(String.class), anyLong());
+ doCallRealMethod().when(mActiveServices)
+ .rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ anyBoolean(), anyBoolean(), anyLong());
+ doCallRealMethod().when(mActiveServices)
+ .rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
+ anyInt(), anyInt(), any(String.class), anyLong());
+ doCallRealMethod().when(mActiveServices)
+ .rescheduleServiceRestartIfPossibleLocked(
+ anyLong(), anyLong(), any(String.class), anyLong());
+ doCallRealMethod().when(mActiveServices)
+ .performRescheduleServiceRestartOnMemoryPressureLocked(
+ anyLong(), anyLong(), any(String.class), anyLong());
+ doCallRealMethod().when(mActiveServices).getExtraRestartTimeInBetweenLocked();
+ doCallRealMethod().when(mActiveServices)
+ .isServiceRestartBackoffEnabledLocked(any(String.class));
+ }
+
+ private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
+ try {
+ Field field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ Field mfield = Field.class.getDeclaredField("accessFlags");
+ mfield.setAccessible(true);
+ mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
+ field.set(obj, val);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ }
+ }
+
+ private void fillInRestartingServices(long now, long[] delays) {
+ mActiveServices.mRestartingServices.clear();
+ for (int i = 0; i < delays.length; i++) {
+ mActiveServices.mRestartingServices.add(
+ createRestartingService("testpackage" + i, now, delays[i]));
+ }
+ }
+
+ private void setNextRestarts(long now, long[] nextRestartDelays) {
+ for (int i = 0; i < nextRestartDelays.length; i++) {
+ final ServiceRecord r = mActiveServices.mRestartingServices.get(i);
+ r.restartDelay = nextRestartDelays[i];
+ r.nextRestartTime = now + r.restartDelay;
+ }
+ }
+
+ private ServiceRecord createRestartingService(String packageName, long now, long delay) {
+ final ServiceRecord r = mock(ServiceRecord.class);
+ r.appInfo = new ApplicationInfo();
+ r.appInfo.flags = delay == 0 ? ApplicationInfo.FLAG_PERSISTENT : 0;
+ final ServiceInfo si = new ServiceInfo();
+ setFieldValue(ServiceRecord.class, r, "serviceInfo", si);
+ setFieldValue(ServiceRecord.class, r, "packageName", packageName);
+ si.applicationInfo = r.appInfo;
+ r.nextRestartTime = r.mEarliestRestartTime = now + delay;
+ r.mRestartSchedulingTime = now;
+ r.restartDelay = delay;
+ return r;
+ }
+
+ private void verifyDelays(long now, long[] delays) throws Exception {
+ for (int i = 0; i < delays.length; i++) {
+ final ServiceRecord r = mActiveServices.mRestartingServices.get(i);
+ assertEquals("Expected restart delay=" + delays[i],
+ Math.max(0, delays[i]), r.restartDelay);
+ assertEquals("Expected next restart time=" + (now + delays[i]),
+ now + delays[i], r.nextRestartTime);
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index 022fadcc6dd0..15dfd266853e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -21,12 +21,15 @@ import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -48,7 +51,10 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
-import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -62,6 +68,8 @@ import java.util.concurrent.TimeUnit;
*/
@RunWith(MockitoJUnitRunner.class)
public class CacheOomRankerTest {
+ private static final Instant NOW = LocalDate.of(2021, 1, 1).atStartOfDay(
+ ZoneOffset.UTC).toInstant();
@Mock
private AppOpsService mAppOpsService;
@@ -136,6 +144,15 @@ public class CacheOomRankerTest {
mExecutor.init();
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1),
+ false);
+ mExecutor.waitForLatch();
+ assertThat(mCacheOomRanker.mPreserveTopNApps)
+ .isEqualTo(CacheOomRanker.DEFAULT_PRESERVE_TOP_N_APPS + 1);
+
+ mExecutor.init();
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(CacheOomRanker.DEFAULT_OOM_RE_RANKING_LRU_WEIGHT + 0.1f),
false);
@@ -165,6 +182,7 @@ public class CacheOomRankerTest {
@Test
public void reRankLruCachedApps_lruImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 5,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */1.0f);
@@ -172,36 +190,38 @@ public class CacheOomRankerTest {
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord lastUsed40MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(lastUsed40MinutesAgo);
ProcessRecord lastUsed42MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(lastUsed42MinutesAgo);
ProcessRecord lastUsed60MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(lastUsed60MinutesAgo);
ProcessRecord lastUsed15MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(lastUsed15MinutesAgo);
ProcessRecord lastUsed17MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed17MinutesAgo);
// Only re-ranking 5 entries so this should stay in most recent position.
ProcessRecord lastUsed30MinutesAgo = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 20);
processList.add(lastUsed30MinutesAgo);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 5 ordered by least recently used first, then last processes position unchanged.
assertThat(processList).containsExactly(lastUsed60MinutesAgo, lastUsed42MinutesAgo,
lastUsed40MinutesAgo, lastUsed17MinutesAgo, lastUsed15MinutesAgo,
- lastUsed30MinutesAgo);
+ lastUsed30MinutesAgo).inOrder();
}
@Test
public void reRankLruCachedApps_rssImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 0.0f,
/* pssWeight= */ 1.0f,
/* lruWeight= */ 0.0f);
@@ -209,145 +229,328 @@ public class CacheOomRankerTest {
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord rss10k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(rss10k);
ProcessRecord rss20k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(rss20k);
ProcessRecord rss1k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(60).toMillis(), 1024L, 10000);
+ NOW.minus(60, ChronoUnit.MINUTES).toEpochMilli(), 1024L, 10000);
processList.add(rss1k);
ProcessRecord rss100k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(rss100k);
ProcessRecord rss2k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(rss2k);
ProcessRecord rss15k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 20);
processList.add(rss15k);
// Only re-ranking 6 entries so this should stay in most recent position.
ProcessRecord rss16k = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 20);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 20);
processList.add(rss16k);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 6 ordered by largest pss, then last processes position unchanged.
assertThat(processList).containsExactly(rss100k, rss20k, rss15k, rss10k, rss2k, rss1k,
- rss16k);
+ rss16k).inOrder();
}
@Test
public void reRankLruCachedApps_usesImpactsOrdering() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(1);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(used20);
// Only re-ranking 6 entries so last two should stay in most recent position.
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(used500);
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
// First 4 ordered by uses, then last processes position unchanged.
assertThat(processList).containsExactly(used10, used20, used1000, used2000, used500,
- used200);
+ used200).inOrder();
}
@Test
- public void reRankLruCachedApps_notEnoughProcesses() throws InterruptedException {
+ public void reRankLruCachedApps_fewProcesses() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
- /* usesWeight= */ 0.5f,
- /* pssWeight= */ 0.2f,
- /* lruWeight= */ 0.3f);
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
- ProcessRecord unknownAdj1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
- processList.add(unknownAdj1);
- ProcessRecord unknownAdj2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
- processList.add(unknownAdj2);
- ProcessRecord unknownAdj3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
- processList.add(unknownAdj3);
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
ProcessRecord foregroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(foregroundAdj);
ProcessRecord serviceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(serviceAdj);
ProcessRecord systemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(systemAdj);
+ list.setLruProcessServiceStartLSP(processList.size());
- // 6 Processes but only 3 in eligible for cache so no re-ranking.
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
- // All positions unchanged.
- assertThat(processList).containsExactly(unknownAdj1, unknownAdj2, unknownAdj3,
- foregroundAdj, serviceAdj, systemAdj);
+ // 6 processes, only 3 in eligible for cache, so only those are re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000,
+ foregroundAdj, serviceAdj, systemAdj).inOrder();
}
@Test
- public void reRankLruCachedApps_notEnoughNonServiceProcesses() throws InterruptedException {
+ public void reRankLruCachedApps_fewNonServiceProcesses() throws InterruptedException {
setConfig(/* numberToReRank= */ 4,
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord service1 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(service1);
+ ProcessRecord service2 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(service2);
+ ProcessRecord service3 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(service3);
+ list.setLruProcessServiceStartLSP(3);
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Services unchanged, rest re-ranked.
+ assertThat(processList).containsExactly(used10, used1000, used2000, service1, service2,
+ service3).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_manyProcessesThenFew() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 0,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList set1List = new ProcessList();
+ ArrayList<ProcessRecord> set1ProcessList = set1List.getLruProcessesLSP();
+ ProcessRecord set1Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set1ProcessList.add(set1Used1000);
+ ProcessRecord set1Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set1ProcessList.add(set1Used2000);
+ ProcessRecord set1Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set1ProcessList.add(set1Used10);
+ ProcessRecord set1Uses20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set1ProcessList.add(set1Uses20);
+ ProcessRecord set1Uses500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set1ProcessList.add(set1Uses500);
+ ProcessRecord set1Uses200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set1ProcessList.add(set1Uses200);
+ set1List.setLruProcessServiceStartLSP(set1ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set1ProcessList,
+ set1List.getLruProcessServiceStartLOSP());
+ assertThat(set1ProcessList).containsExactly(set1Used10, set1Uses20, set1Uses200,
+ set1Uses500, set1Used1000, set1Used2000).inOrder();
+
+ ProcessList set2List = new ProcessList();
+ ArrayList<ProcessRecord> set2ProcessList = set2List.getLruProcessesLSP();
+ ProcessRecord set2Used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ set2ProcessList.add(set2Used1000);
+ ProcessRecord set2Used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ set2ProcessList.add(set2Used2000);
+ ProcessRecord set2Used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ set2ProcessList.add(set2Used10);
+ ProcessRecord set2ForegroundAdj = nextProcessRecord(ProcessList.FOREGROUND_APP_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ set2ProcessList.add(set2ForegroundAdj);
+ ProcessRecord set2ServiceAdj = nextProcessRecord(ProcessList.SERVICE_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ set2ProcessList.add(set2ServiceAdj);
+ ProcessRecord set2SystemAdj = nextProcessRecord(ProcessList.SYSTEM_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ set2ProcessList.add(set2SystemAdj);
+ set2List.setLruProcessServiceStartLSP(set2ProcessList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(set2ProcessList,
+ set2List.getLruProcessServiceStartLOSP());
+ assertThat(set2ProcessList).containsExactly(set2Used10, set2Used1000, set2Used2000,
+ set2ForegroundAdj, set2ServiceAdj, set2SystemAdj).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps() throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 3,
/* usesWeight= */ 1.0f,
/* pssWeight= */ 0.0f,
/* lruWeight= */ 0.0f);
ProcessList list = new ProcessList();
- list.setLruProcessServiceStartLSP(4);
ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(40).toMillis(), 10 * 1024L, 1000);
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
processList.add(used1000);
ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(42).toMillis(), 20 * 1024L, 2000);
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
processList.add(used2000);
ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(15).toMillis(), 100 * 1024L, 10);
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
processList.add(used10);
+ // Preserving the top 3 processes, so these should not be re-ranked.
ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(17).toMillis(), 2 * 1024L, 20);
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
processList.add(used20);
ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 15 * 1024L, 500);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
processList.add(used500);
ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
- Duration.ofMinutes(30).toMillis(), 16 * 1024L, 200);
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
- // All positions unchanged.
+ // First 3 ordered by uses, then last processes position unchanged.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_allAppsUnchanged()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ 100,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // Nothing reordered, as we preserve the top 100 apps.
assertThat(processList).containsExactly(used1000, used2000, used10, used20, used500,
- used200);
+ used200).inOrder();
}
- private void setConfig(int numberToReRank, float useWeight, float pssWeight, float lruWeight)
+ @Test
+ public void reRankLruCachedApps_preservesTopNApps_negativeReplacedWithDefault()
+ throws InterruptedException {
+ setConfig(/* numberToReRank= */ 6,
+ /* preserveTopNApps= */ -100,
+ /* usesWeight= */ 1.0f,
+ /* pssWeight= */ 0.0f,
+ /* lruWeight= */ 0.0f);
+
+ ProcessList list = new ProcessList();
+ ArrayList<ProcessRecord> processList = list.getLruProcessesLSP();
+ ProcessRecord used1000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(40, ChronoUnit.MINUTES).toEpochMilli(), 10 * 1024L, 1000);
+ processList.add(used1000);
+ ProcessRecord used2000 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(42, ChronoUnit.MINUTES).toEpochMilli(), 20 * 1024L, 2000);
+ processList.add(used2000);
+ ProcessRecord used10 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(15, ChronoUnit.MINUTES).toEpochMilli(), 100 * 1024L, 10);
+ processList.add(used10);
+ // Negative preserveTopNApps interpreted as the default (3), so the last three are unranked.
+ ProcessRecord used20 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(17, ChronoUnit.MINUTES).toEpochMilli(), 2 * 1024L, 20);
+ processList.add(used20);
+ ProcessRecord used500 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 15 * 1024L, 500);
+ processList.add(used500);
+ ProcessRecord used200 = nextProcessRecord(ProcessList.UNKNOWN_ADJ,
+ NOW.minus(30, ChronoUnit.MINUTES).toEpochMilli(), 16 * 1024L, 200);
+ processList.add(used200);
+ list.setLruProcessServiceStartLSP(processList.size());
+
+ mCacheOomRanker.reRankLruCachedAppsLSP(processList, list.getLruProcessServiceStartLOSP());
+
+ // First 3 apps re-ranked, as preserveTopNApps is interpreted as 3.
+ assertThat(processList).containsExactly(used10, used1000, used2000, used20, used500,
+ used200).inOrder();
+ }
+
+ private void setConfig(int numberToReRank, int preserveTopNApps, float usesWeight,
+ float pssWeight, float lruWeight)
throws InterruptedException {
mExecutor.init(4);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -355,6 +558,10 @@ public class CacheOomRankerTest {
Integer.toString(numberToReRank),
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ CacheOomRanker.KEY_OOM_RE_RANKING_PRESERVE_TOP_N_APPS,
+ Integer.toString(preserveTopNApps),
+ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_LRU_WEIGHT,
Float.toString(lruWeight),
false);
@@ -364,12 +571,12 @@ public class CacheOomRankerTest {
false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
CacheOomRanker.KEY_OOM_RE_RANKING_USES_WEIGHT,
- Float.toString(useWeight),
+ Float.toString(usesWeight),
false);
mExecutor.waitForLatch();
assertThat(mCacheOomRanker.getNumberToReRank()).isEqualTo(numberToReRank);
assertThat(mCacheOomRanker.mRssWeight).isEqualTo(pssWeight);
- assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(useWeight);
+ assertThat(mCacheOomRanker.mUsesWeight).isEqualTo(usesWeight);
assertThat(mCacheOomRanker.mLruWeight).isEqualTo(lruWeight);
}
@@ -382,7 +589,7 @@ public class CacheOomRankerTest {
app.info.uid = mNextPackageUid++;
// Exact value does not mater, it can be any state for which compaction is allowed.
app.mState.setSetProcState(PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
- app.mState.setSetAdj(setAdj);
+ app.mState.setCurAdj(setAdj);
app.setLastActivityTime(lastActivityTime);
app.mProfile.setLastRss(lastRss);
app.mState.setCached(false);
@@ -390,6 +597,12 @@ public class CacheOomRankerTest {
app.mState.setCached(false);
app.mState.setCached(true);
}
+ // Sets the thread returned by ProcessRecord#getThread, which we use to check whether the
+ // app is currently launching.
+ ProcessStatsService processStatsService = new ProcessStatsService(
+ mock(ActivityManagerService.class), new File(Environment.getDataSystemCeDirectory(),
+ "procstats"));
+ app.makeActive(mock(IApplicationThread.class), processStatsService);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 18f1267b890f..284a754ed3eb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -2174,7 +2174,7 @@ public class MockingOomAdjusterTests {
ConnectionRecord cr = spy(new ConnectionRecord(binding,
mock(ActivityServiceConnectionsHolder.class),
mock(IServiceConnection.class), bindFlags,
- 0, null, client.uid, client.processName, client.info.packageName));
+ 0, null, client.uid, client.processName, client.info.packageName, null));
doCallRealMethod().when(record).addConnection(any(IBinder.class),
any(ConnectionRecord.class));
record.addConnection(binder, cr);
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 4d86c8716bf1..85ef8f73534c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -182,7 +182,14 @@ public class GameManagerServiceTests {
}
private void mockDeviceConfigPerformance() {
- String configString = "mode=2,downscaleFactor=0.5";
+ String configString = "mode=2,downscaleFactor=0.5,useAngle=false";
+ when(DeviceConfig.getProperty(anyString(), anyString()))
+ .thenReturn(configString);
+ }
+
+ // ANGLE will be disabled for most apps, so treat enabling ANGLE as a special case.
+ private void mockDeviceConfigPerformanceEnableAngle() {
+ String configString = "mode=2,downscaleFactor=0.5,useAngle=true";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
@@ -200,7 +207,7 @@ public class GameManagerServiceTests {
}
private void mockDeviceConfigInvalid() {
- String configString = "mode=2,downscaleFactor=0.55";
+ String configString = "";
when(DeviceConfig.getProperty(anyString(), anyString()))
.thenReturn(configString);
}
@@ -212,7 +219,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInAll() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -224,7 +232,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInPerformance() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_PERFORMANCE_MODE_ENABLE, true);
@@ -234,7 +243,8 @@ public class GameManagerServiceTests {
}
private void mockGameModeOptInBattery() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_BATTERY_MODE_ENABLE, true);
@@ -244,7 +254,8 @@ public class GameManagerServiceTests {
}
private void mockInterventionAllowDownscaleTrue() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, true);
@@ -254,7 +265,8 @@ public class GameManagerServiceTests {
}
private void mockInterventionAllowDownscaleFalse() throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
Bundle metaDataBundle = new Bundle();
metaDataBundle.putBoolean(
GameManagerService.GamePackageConfiguration.METADATA_WM_ALLOW_DOWNSCALE, false);
@@ -263,6 +275,27 @@ public class GameManagerServiceTests {
.thenReturn(applicationInfo);
}
+ private void mockInterventionAllowAngleTrue() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ metaDataBundle.putBoolean(
+ GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, true);
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
+ private void mockInterventionAllowAngleFalse() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ metaDataBundle.putBoolean(
+ GameManagerService.GamePackageConfiguration.METADATA_ANGLE_ALLOW_ANGLE, false);
+ applicationInfo.metaData = metaDataBundle;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ }
+
/**
* By default game mode is not supported.
*/
@@ -340,11 +373,12 @@ public class GameManagerServiceTests {
*/
@Test
public void testSetGameModePermissionDenied() {
+ mockModifyGameModeGranted();
+ mockDeviceConfigAll();
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.onUserStarting(USER_ID_1);
// Update the game mode so we can read back something valid.
- mockModifyGameModeGranted();
gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
@@ -427,6 +461,19 @@ public class GameManagerServiceTests {
assertEquals(config.getGameModeConfiguration(gameMode).getScaling(), scaling);
}
+ private void checkAngleEnabled(GameManagerService gameManagerService, int gameMode,
+ boolean angleEnabled) {
+ gameManagerService.updateConfigsForUser(USER_ID_1, mPackageName);
+
+ // Validate GamePackageConfiguration returns the correct value.
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName);
+ assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
+
+ // Validate GameManagerService.getAngleEnabled() returns the correct value.
+ assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
+ }
+
/**
* Phenotype device config exists, but is only propagating the default value.
*/
@@ -592,6 +639,50 @@ public class GameManagerServiceTests {
}
/**
+ * PERFORMANCE game mode is configured through Phenotype. The app hasn't specified any metadata.
+ */
+ @Test
+ public void testInterventionAllowAngleDefault() throws Exception {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockDeviceConfigPerformance();
+ mockModifyGameModeGranted();
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+ }
+
+ /**
+ * PERFORMANCE game mode is configured through Phenotype. The app has opted-out of ANGLE.
+ */
+ @Test
+ public void testInterventionAllowAngleFalse() throws Exception {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockDeviceConfigPerformanceEnableAngle();
+ mockInterventionAllowAngleFalse();
+ mockModifyGameModeGranted();
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, false);
+ }
+
+ /**
+ * PERFORMANCE game mode is configured through Phenotype. The app has redundantly specified
+ * the ANGLE metadata default value of "true".
+ */
+ @Test
+ public void testInterventionAllowAngleTrue() throws Exception {
+ mockDeviceConfigPerformanceEnableAngle();
+ mockInterventionAllowAngleTrue();
+
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ mockModifyGameModeGranted();
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ checkAngleEnabled(gameManagerService, GameManager.GAME_MODE_PERFORMANCE, true);
+ }
+
+ /**
* PERFORMANCE game mode is configured through Phenotype, but the app has also opted into the
* same mode. No interventions for this game mode should be available in this case.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
new file mode 100644
index 000000000000..f8c3520e9fa8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
new file mode 100644
index 000000000000..bf97042d7b88
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesParserTest.java
@@ -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.
+ */
+
+package com.android.server.compat.overrides;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptySet;
+
+import android.app.compat.PackageOverride;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.compat.overrides.AppCompatOverridesParser.PackageOverrides;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test class for {@link AppCompatOverridesParser}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesParserTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesParserTest {
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+
+ private AppCompatOverridesParser mParser;
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mParser = new AppCompatOverridesParser(mPackageManager);
+ }
+
+ @Test
+ public void parseRemoveOverrides_emptyConfig_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+
+ assertThat(mParser.parseRemoveOverrides("", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcardNoOwnedChangeIds_returnsEmpty() {
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("*", /* ownedChangeIds= */ emptySet())).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasWildcard_returnsAllInstalledPackagesToAllOwnedIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt()))
+ .thenReturn(Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2),
+ createAppInfo(PACKAGE_3)));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides("*", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(123L, 456L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(123L, 456L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidWildcardSymbol_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(123L, 456L));
+ when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
+ Arrays.asList(createAppInfo(PACKAGE_1), createAppInfo(PACKAGE_2)));
+
+ assertThat(mParser.parseRemoveOverrides("**", ownedChangeIds)).isEmpty();
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasSingleEntry_returnsPackageToChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12:34", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L, 34L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasMultipleEntries_returnsPackagesToChangeIds() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L, 56L, 78L));
+
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=*," + PACKAGE_3 + "=12:56:78," + PACKAGE_4
+ + "=", ownedChangeIds);
+
+ assertThat(result).hasSize(3);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 34L, 56L, 78L);
+ assertThat(result.get(PACKAGE_3)).containsExactly(12L, 56L, 78L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasPackageWithWildcardNoOwnedId_returnsWithoutPackage() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=*," + PACKAGE_2 + "=12", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(1);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L);
+ }
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidKeyValueListFormat_returnsEmpty() {
+ Set<Long> ownedChangeIds = new ArraySet<>(Arrays.asList(12L, 34L));
+
+ assertThat(mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + ">34", ownedChangeIds)).isEmpty();
+ }
+
+
+ @Test
+ public void parseRemoveOverrides_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ Map<String, Set<Long>> result = mParser.parseRemoveOverrides(
+ PACKAGE_1 + "=12," + PACKAGE_2 + "=12:56L:78," + PACKAGE_3
+ + "=34L", /* ownedChangeIds= */ emptySet());
+
+ assertThat(result).hasSize(2);
+ assertThat(result.get(PACKAGE_1)).containsExactly(12L);
+ assertThat(result.get(PACKAGE_2)).containsExactly(12L, 78L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_emptyConfig_returnsEmpty() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("")).isEmpty();
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasSingleChangeId_returnsChangeId() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("123")).containsExactly(123L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasMultipleChangeIds_returnsChangeIds() {
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,34,56")).containsExactly(12L,
+ 34L, 56L);
+ }
+
+ @Test
+ public void parseOwnedChangeIds_configHasInvalidChangeIds_returnsWithoutInvalidChangeIds() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ assertThat(AppCompatOverridesParser.parseOwnedChangeIds("12,C34,56")).containsExactly(12L,
+ 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_emptyConfig_returnsEmpty() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "", /* versionCode= */ 0, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithSingleOverride_returnsOverride() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "123:::true", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToAdd_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "910:3:4:false,78:10::false,12:::false,34:1:2:true,34:10::true,56::2:true,"
+ + "56:3:4:false,34:4:8:true,78:6:7:true,910:5::true,1112::5:true,"
+ + "56:6::true,1112:6:7:false", /* versionCode= */
+ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(6);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(false).build());
+ assertThat(result.overridesToAdd.get(34L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(4).setMaxVersionCode(8).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(3).setMaxVersionCode(4).setEnabled(
+ false).build());
+ assertThat(result.overridesToAdd.get(78L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(6).setMaxVersionCode(7).setEnabled(
+ true).build());
+ assertThat(result.overridesToAdd.get(910L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(1112L)).isEqualTo(
+ new PackageOverride.Builder().setMaxVersionCode(5).setEnabled(true).build());
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithMultipleOverridesToRemove_returnsOverrides() {
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::,34:1:2:", /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToRemove).containsExactly(12L, 34L);
+ assertThat(result.overridesToAdd).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_configWithBothOverridesToAddAndRemove_returnsOverrides() {
+ // Note that change 56 is both added and removed, therefore it will only be removed.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "56:::,12:::true,34:::,56:3:7:true", /* versionCode= */ 5, /* changeIdsToSkip= */
+ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(34L, 56L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipSpecified_returnsWithoutChangeIdsToSkip() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(34L);
+ changeIdsToSkip.add(56L);
+ changeIdsToSkip.add(910L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::,56:3:7:true,78:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).hasSize(1);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToRemove).containsExactly(78L);
+ }
+
+ @Test
+ public void parsePackageOverrides_changeIdsToSkipContainsAllIds_returnsEmpty() {
+ ArraySet<Long> changeIdsToSkip = new ArraySet<>();
+ changeIdsToSkip.add(12L);
+ changeIdsToSkip.add(34L);
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::true,34:::", /* versionCode= */ 5, changeIdsToSkip);
+
+ assertThat(result.overridesToAdd).isEmpty();
+ assertThat(result.overridesToRemove).isEmpty();
+ }
+
+ @Test
+ public void parsePackageOverrides_someOverridesAreInvalid_returnsWithoutInvalidOverrides() {
+ // We add a valid entry before and after the invalid ones to make sure they are applied.
+ PackageOverrides result = AppCompatOverridesParser.parsePackageOverrides(/* configStr= */
+ "12:::True,56:1:2:FALSE,56:3:true,78:4:8:true:,C1:::true,910:::no,"
+ + "1112:1:ten:true,1112:one:10:true,,1314:7:3:false,34:one:ten:",
+ /* versionCode= */ 5, /* changeIdsToSkip= */ emptySet());
+
+ assertThat(result.overridesToAdd).hasSize(2);
+ assertThat(result.overridesToAdd.get(12L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(result.overridesToAdd.get(56L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(1).setMaxVersionCode(2).setEnabled(
+ false).build());
+ assertThat(result.overridesToRemove).containsExactly(34L);
+ }
+
+ private static ApplicationInfo createAppInfo(String packageName) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = packageName;
+ return appInfo;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
new file mode 100644
index 000000000000..312927206a80
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/AppCompatOverridesServiceTest.java
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compat.overrides;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_OWNED_CHANGE_IDS;
+import static com.android.server.compat.overrides.AppCompatOverridesParser.FLAG_REMOVE_OVERRIDES;
+
+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.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.compat.PackageOverride;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.compat.CompatibilityOverrideConfig;
+import com.android.internal.compat.CompatibilityOverridesToRemoveConfig;
+import com.android.internal.compat.IPlatformCompat;
+import com.android.server.testables.TestableDeviceConfig.TestableDeviceConfigRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * Test class for {@link AppCompatOverridesService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksMockingServicesTests:AppCompatOverridesServiceTest
+ */
+@RunWith(MockitoJUnitRunner.class)
+@SmallTest
+@Presubmit
+public class AppCompatOverridesServiceTest {
+ private static final String NAMESPACE_1 = "namespace_1";
+ private static final String NAMESPACE_2 = "namespace_2";
+ private static final String NAMESPACE_3 = "namespace_3";
+ private static final List<String> SUPPORTED_NAMESPACES = Arrays.asList(NAMESPACE_1,
+ NAMESPACE_2, NAMESPACE_3);
+
+ private static final String PACKAGE_1 = "com.android.test1";
+ private static final String PACKAGE_2 = "com.android.test2";
+ private static final String PACKAGE_3 = "com.android.test3";
+ private static final String PACKAGE_4 = "com.android.test4";
+ private static final String PACKAGE_5 = "com.android.test5";
+
+ private MockContext mMockContext;
+ private BroadcastReceiver mPackageReceiver;
+ private AppCompatOverridesService mService;
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private IPlatformCompat mPlatformCompat;
+
+ @Captor
+ private ArgumentCaptor<CompatibilityOverrideConfig> mOverridesToAddConfigCaptor;
+ @Captor
+ private ArgumentCaptor<CompatibilityOverridesToRemoveConfig> mOverridesToRemoveConfigCaptor;
+
+ @Rule
+ public TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfigRule();
+
+ class MockContext extends ContextWrapper {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public Executor getMainExecutor() {
+ // Run on current thread
+ return Runnable::run;
+ }
+
+ @Override
+ @Nullable
+ public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
+ @NonNull IntentFilter filter, @Nullable String broadcastPermission,
+ @Nullable Handler scheduler) {
+ mPackageReceiver = receiver;
+ return null;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = new MockContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext());
+ mService = new AppCompatOverridesService(mMockContext, mPlatformCompat,
+ SUPPORTED_NAMESPACES);
+ mService.registerPackageReceiver();
+ assertThat(mPackageReceiver).isNotNull();
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagNotSet_appliesPackageOverrides()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 3);
+ mockGetApplicationInfoNotInstalled(PACKAGE_2);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 10);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 1);
+ mockGetApplicationInfo(PACKAGE_5, /* versionCode= */ 1);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456::1:false,456:2::true")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "123:1:9:true,123:10:11:false,123:11::true,456:::")
+ .setString(PACKAGE_4, "")
+ .setString(PACKAGE_5, "123:::,789:::")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789").build());
+
+ Map<Long, PackageOverride> addedOverrides;
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(2);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setEnabled(true).build());
+ assertThat(addedOverrides.get(456L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build());
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ addedOverrides = mOverridesToAddConfigCaptor.getValue().overrides;
+ assertThat(addedOverrides).hasSize(1);
+ assertThat(addedOverrides.get(123L)).isEqualTo(
+ new PackageOverride.Builder().setMinVersionCode(10).setMaxVersionCode(
+ 11).setEnabled(false).build());
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 4
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ // Package 5
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_5));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_5));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagSetBefore_skipsOverridesToRemove()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456," + PACKAGE_2 + "=123")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_4, "123:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_2, "123:::true")
+ .setString(PACKAGE_3, "456:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(789L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ // Package 4 (not applied because it hasn't changed after the listener was added)
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_4));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagChangedNoPackageOverridesFlags_removesOnly()
+ throws Exception {
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES,
+ PACKAGE_1 + "=123:456," + PACKAGE_2 + "=789").build());
+
+ // Package 1
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L);
+ // Package 2
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_removeOverridesFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=123:456")
+ .setString(PACKAGE_1, "123:::true,456:::,789:::false")
+ .setString(PACKAGE_3, "456:::false,789:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_2 + "=123," + PACKAGE_3 + "=789")
+ .setString(PACKAGE_2, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L,
+ 789L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(456L);
+ // Package 2
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_2));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.size()).isAtLeast(2);
+ assertThat(configs.get(configs.size() - 2).changeIds).containsExactly(123L);
+ assertThat(configs.get(configs.size() - 1).changeIds).containsExactly(456L);
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_3));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(456L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(789L);
+ }
+
+ @Test
+ public void onPropertiesChanged_ownedChangeIdsFlagAndSomePackageOverrideFlagsChanged_ok()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=*")
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456")
+ .setString(PACKAGE_1, "123:::true")
+ .setString(PACKAGE_3, "456:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "123,456,789")
+ .setString(PACKAGE_2, "123:::true").build());
+
+ // Package 1
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(123L, 456L,
+ 789L);
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(mOverridesToAddConfigCaptor.capture(),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(123L);
+ // Package 3
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_3));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ }
+
+ @Test
+ public void onPropertiesChanged_platformCompatThrowsExceptionForSomeCalls_skipsFailedCalls()
+ throws Exception {
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_2, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_3, /* versionCode= */ 0);
+ mockGetApplicationInfo(PACKAGE_4, /* versionCode= */ 0);
+ doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_2));
+ doThrow(new RemoteException()).when(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+
+ mService.registerDeviceConfigListeners();
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "123:::true,456:::")
+ .setString(PACKAGE_2, "123:::true,456:::")
+ .setString(PACKAGE_3, "123:::true,456:::")
+ .setString(PACKAGE_4, "123:::true,456:::").build());
+
+ // Package 1
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ // Package 2
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_2));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_2));
+ // Package 3
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_3));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_3));
+ // Package 4
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(any(CompatibilityOverrideConfig.class),
+ eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_4));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentDataIsNull_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext, new Intent(ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_actionIsNull_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, /* action= */ null));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_unsupportedAction_doesNothing() throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_USER_SWITCHED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentPackageNotInstalled_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentNoOverridesForPackage_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_2, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_3, "201:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntent_appliesOverridesFromAllNamespaces()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_3, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(PACKAGE_1, "301:::true,302:::false")
+ .setString(PACKAGE_2, "302:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(2)).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+ assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+ assertThat(configs.get(1).overrides.keySet()).containsExactly(301L, 302L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+ }
+
+ @Test
+ public void packageReceiver_packageChangedIntent_appliesOverrides()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true,103:::").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_CHANGED));
+
+ verify(mPlatformCompat).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ assertThat(mOverridesToAddConfigCaptor.getValue().overrides.keySet()).containsExactly(101L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(103L);
+ }
+
+ @Test
+ public void packageReceiver_packageAddedIntentRemoveOverridesSetForSomeNamespaces_skipsIds()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=103," + PACKAGE_2 + "=101")
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_REMOVE_OVERRIDES, PACKAGE_1 + "=301," + PACKAGE_3 + "=302")
+ .setString(PACKAGE_1, "301:::true,302:::false,303:::")
+ .setString(PACKAGE_3, "302:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+ mOverridesToAddConfigCaptor.capture(), eq(PACKAGE_1));
+ verify(mPlatformCompat).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverrideConfig> configs = mOverridesToAddConfigCaptor.getAllValues();
+ assertThat(configs.get(0).overrides.keySet()).containsExactly(101L);
+ assertThat(configs.get(1).overrides.keySet()).containsExactly(201L);
+ assertThat(configs.get(2).overrides.keySet()).containsExactly(302L);
+ assertThat(mOverridesToRemoveConfigCaptor.getValue().changeIds).containsExactly(303L);
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentNoOverridesForPackage_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+ .setString(PACKAGE_2, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+ .setString(PACKAGE_3, "201:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentPackageInstalledForAnotherUser_doesNothing()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+ .setString(PACKAGE_1, "101:::true,103:::").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201,202")
+ .setString(PACKAGE_1, "202:::false").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntent_removesOwnedOverridesForNamespacesWithPackage()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102,103")
+ .setString(PACKAGE_1, "101:::true,103:::")
+ .setString(PACKAGE_2, "102:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(FLAG_OWNED_CHANGE_IDS, "201")
+ .setString(PACKAGE_3, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_OWNED_CHANGE_IDS, "301,302")
+ .setString(PACKAGE_1, "302:::")
+ .setString(PACKAGE_2, "301:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.get(0).changeIds).containsExactly(101L, 102L, 103L);
+ assertThat(configs.get(1).changeIds).containsExactly(301L, 302L);
+ }
+
+ @Test
+ public void packageReceiver_packageRemovedIntentNoOwnedIdsForSomeNamespace_skipsNamespace()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(FLAG_OWNED_CHANGE_IDS, "101,102")
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(FLAG_OWNED_CHANGE_IDS, "301")
+ .setString(PACKAGE_1, "301:::true").build());
+ mockGetApplicationInfoNotInstalled(PACKAGE_1);
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_REMOVED));
+
+ verify(mPlatformCompat, never()).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, times(2)).removeOverridesOnReleaseBuilds(
+ mOverridesToRemoveConfigCaptor.capture(), eq(PACKAGE_1));
+ List<CompatibilityOverridesToRemoveConfig> configs =
+ mOverridesToRemoveConfigCaptor.getAllValues();
+ assertThat(configs.get(0).changeIds).containsExactly(101L, 102L);
+ assertThat(configs.get(1).changeIds).containsExactly(301L);
+ }
+
+ @Test
+ public void packageReceiver_platformCompatThrowsExceptionForSomeNamespace_skipsFailedCall()
+ throws Exception {
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_1)
+ .setString(PACKAGE_1, "101:::true").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_2)
+ .setString(PACKAGE_1, "201:::false").build());
+ DeviceConfig.setProperties(new Properties.Builder(NAMESPACE_3)
+ .setString(PACKAGE_1, "301:::true").build());
+ mockGetApplicationInfo(PACKAGE_1, /* versionCode= */ 0);
+ doThrow(new RemoteException()).when(mPlatformCompat).putOverridesOnReleaseBuilds(
+ argThat(config -> config.overrides.containsKey(201L)), eq(PACKAGE_1));
+
+ mPackageReceiver.onReceive(mMockContext,
+ createPackageIntent(PACKAGE_1, ACTION_PACKAGE_ADDED));
+
+ verify(mPlatformCompat, times(3)).putOverridesOnReleaseBuilds(
+ any(CompatibilityOverrideConfig.class), eq(PACKAGE_1));
+ verify(mPlatformCompat, never()).removeOverridesOnReleaseBuilds(
+ any(CompatibilityOverridesToRemoveConfig.class), eq(PACKAGE_1));
+ }
+
+ private void mockGetApplicationInfo(String packageName, long versionCode)
+ throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt())).thenReturn(
+ createAppInfo(versionCode));
+ }
+
+ private void mockGetApplicationInfoNotInstalled(String packageName) throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException());
+ }
+
+ private static ApplicationInfo createAppInfo(long versionCode) {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.longVersionCode = versionCode;
+ return appInfo;
+ }
+
+ private Intent createPackageIntent(String packageName, @Nullable String action) {
+ return new Intent(action, Uri.parse("package:" + packageName));
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
new file mode 100644
index 000000000000..6e8aefc4bc01
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/compat/overrides/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/compat/overrides/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index 589a3497435e..457c8db9fdf3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -48,7 +48,7 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
/**
- * Run it as {@code atest FrameworksMockingCoreTests:FactoryResetterTest}
+ * Run it as {@code atest FrameworksMockingServicesTests:FactoryResetterTest}
*/
@Presubmit
public final class FactoryResetterTest {
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java
new file mode 100644
index 000000000000..dd67d7208034
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.devicepolicy;
+
+import static android.os.UserHandle.USER_NULL;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.expectThrows;
+
+import android.content.ComponentName;
+
+import org.junit.Test;
+
+/**
+ * Run it as {@code atest FrameworksMockingServicesTests:OwnerShellDataTest}
+ */
+public final class OwnerShellDataTest {
+
+ private static final int USER_ID = 007;
+ private static final int PARENT_USER_ID = 'M' + 'I' + 6;
+ private static final ComponentName ADMIN = new ComponentName("Bond", "James");
+
+ @Test
+ public void testForDeviceOwner_noAdmin() {
+ expectThrows(NullPointerException.class,
+ () -> OwnerShellData.forDeviceOwner(USER_ID, /* admin= */ null));
+ }
+
+ @Test
+ public void testForDeviceOwner_invalidUser() {
+ expectThrows(IllegalArgumentException.class,
+ () -> OwnerShellData.forDeviceOwner(USER_NULL, ADMIN));
+ }
+
+ @Test
+ public void testForDeviceOwner() {
+ OwnerShellData dto = OwnerShellData.forDeviceOwner(USER_ID, ADMIN);
+
+ assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+ assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+ .isEqualTo(USER_NULL);
+ assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+ assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isTrue();
+ assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse();
+ assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+ .isFalse();
+ assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+ }
+
+ @Test
+ public void testForUserProfileOwner_noAdmin() {
+ expectThrows(NullPointerException.class,
+ () -> OwnerShellData.forUserProfileOwner(USER_ID, /* admin= */ null));
+ }
+
+ @Test
+ public void testForUserProfileOwner_invalidUser() {
+ expectThrows(IllegalArgumentException.class,
+ () -> OwnerShellData.forUserProfileOwner(USER_NULL, ADMIN));
+ }
+
+ @Test
+ public void testForUserProfileOwner() {
+ OwnerShellData dto = OwnerShellData.forUserProfileOwner(USER_ID, ADMIN);
+
+ assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+ assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+ .isEqualTo(USER_NULL);
+ assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+ assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse();
+ assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isTrue();
+ assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+ .isFalse();
+ assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+ }
+
+ @Test
+ public void testForManagedProfileOwner_noAdmin() {
+ expectThrows(NullPointerException.class,
+ () -> OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, null));
+ }
+
+ @Test
+ public void testForManagedProfileOwner_invalidUser() {
+ expectThrows(IllegalArgumentException.class,
+ () -> OwnerShellData.forManagedProfileOwner(USER_NULL, PARENT_USER_ID, ADMIN));
+ }
+
+ @Test
+ public void testForManagedProfileOwner_invalidParent() {
+ expectThrows(IllegalArgumentException.class,
+ () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_NULL, ADMIN));
+ }
+
+ @Test
+ public void testForManagedProfileOwner_parentOfItself() {
+ expectThrows(IllegalArgumentException.class,
+ () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_ID, ADMIN));
+ }
+
+ @Test
+ public void testForManagedProfileOwner() {
+ OwnerShellData dto = OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, ADMIN);
+
+ assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+ assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+ .isEqualTo(PARENT_USER_ID);
+ assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+ assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse();
+ assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse();
+ assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+ .isTrue();
+ assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+ }
+}
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 0efcc57eeec2..34856e2580ed 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -134,6 +134,14 @@ public class LocalDisplayAdapterTest {
when(mMockedResources.getFloat(com.android.internal.R.dimen
.config_screenBrightnessSettingMaximumFloat))
.thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]);
+ when(mMockedResources.getStringArray(R.array.config_displayUniqueIdArray))
+ .thenReturn(new String[]{});
+ TypedArray mockArray = mock(TypedArray.class);
+ when(mockArray.length()).thenReturn(0);
+ when(mMockedResources.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray))
+ .thenReturn(mockArray);
+ when(mMockedResources.obtainTypedArray(R.array.config_waterfallCutoutArray))
+ .thenReturn(mockArray);
}
@After
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 56bcb834dbfd..a94f0ee554bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
@@ -97,11 +98,6 @@ public class JobSchedulerServiceTest {
super(context);
mAppStateTracker = mock(AppStateTrackerImpl.class);
}
-
- @Override
- public boolean isChainedAttributionEnabled() {
- return false;
- }
}
@Before
@@ -710,7 +706,7 @@ public class JobSchedulerServiceTest {
spyOn(mService);
doNothing().when(mService).evaluateControllerStatesLocked(any());
doNothing().when(mService).noteJobsPending(any());
- doReturn(true).when(mService).isReadyToBeExecutedLocked(any());
+ doReturn(true).when(mService).isReadyToBeExecutedLocked(any(), anyBoolean());
advanceElapsedClock(24 * HOUR_IN_MILLIS);
JobSchedulerService.MaybeReadyJobQueueFunctor maybeQueueFunctor =
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 3dc7bc1154ea..a9853bf94eeb 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
@@ -152,6 +152,11 @@ public class ConnectivityControllerTest {
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
DataUnit.MEBIBYTES.toBytes(1))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ final JobInfo.Builder jobWithMinChunk = createJob()
+ .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1),
+ DataUnit.MEBIBYTES.toBytes(1))
+ .setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -170,18 +175,51 @@ public class ConnectivityControllerTest {
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
.setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow downstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
.setLinkDownstreamBandwidthKbps(1).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1).build(), mConstants));
// Slow upstream
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
.setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertFalse(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ // Medium network is fine for min chunk
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ // Medium downstream
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(5).build(), mConstants));
+ // Medium upstream
+ assertFalse(controller.isSatisfied(createJobStatus(job), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(5)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Fast network looks great
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
.setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
+ assertTrue(controller.isSatisfied(createJobStatus(jobWithMinChunk), net,
+ createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(1024)
+ .setLinkDownstreamBandwidthKbps(1024).build(), mConstants));
// Slow network still good given time
assertTrue(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
@@ -241,7 +279,6 @@ public class ConnectivityControllerTest {
final ConnectivityController controller = new ConnectivityController(mService);
when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
-
// Suspended networks aren't usable.
assertFalse(controller.isSatisfied(createJobStatus(job), net,
createCapabilitiesBuilder().removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -299,8 +336,17 @@ public class ConnectivityControllerTest {
final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
+ job.setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, DataUnit.MEBIBYTES.toBytes(1));
+ final JobStatus latePrefetchUnknownDown = createJobStatus(job, now - 2000, now + 1000);
+ job.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), JobInfo.NETWORK_BYTES_UNKNOWN);
+ final JobStatus latePrefetchUnknownUp = createJobStatus(job, now - 2000, now + 1000);
+
final ConnectivityController controller = new ConnectivityController(mService);
+ when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+ .thenReturn(0L);
+
// Unmetered network is whenever
{
final Network net = mock(Network.class);
@@ -312,9 +358,11 @@ public class ConnectivityControllerTest {
assertTrue(controller.isSatisfied(late, net, caps, mConstants));
assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
- // Metered network is only when prefetching and late
+ // Metered network is only when prefetching, late, and in opportunistic quota
{
final Network net = mock(Network.class);
final NetworkCapabilities caps = createCapabilitiesBuilder()
@@ -323,7 +371,17 @@ public class ConnectivityControllerTest {
assertFalse(controller.isSatisfied(early, net, caps, mConstants));
assertFalse(controller.isSatisfied(late, net, caps, mConstants));
assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
+
+ when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota(
+ any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS)))
+ .thenReturn(9876543210L);
assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants));
+ // Only relax restrictions when we at least know the estimated download bytes.
+ assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants));
+ assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 0fcda819d83c..6a2556035470 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -678,6 +678,7 @@ public class JobStatusTest {
private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) {
job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
+ job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied);
job.setDeviceNotDozingConstraintSatisfied(
sElapsedRealtimeClock.millis(), isSatisfied, false);
job.setBackgroundNotRestrictedConstraintSatisfied(
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 029930abe6b1..473815923056 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
@@ -389,6 +389,8 @@ public class QuotaControllerTest {
/* state */ true, /* allowlisted */false);
js.setBackgroundNotRestrictedConstraintSatisfied(
sElapsedRealtimeClock.millis(), true, false);
+ js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
return js;
}
@@ -3648,7 +3650,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
inOrder.verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
synchronized (mQuotaController.mLock) {
assertEquals(remainingTimeMs / 2,
mQuotaController.getRemainingExecutionTimeLocked(jobBg));
@@ -3660,7 +3662,7 @@ public class QuotaControllerTest {
advanceElapsedClock(remainingTimeMs / 2 + 1);
inOrder.verify(mJobSchedulerService,
timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
// Top job should still be allowed to run.
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3674,7 +3676,7 @@ public class QuotaControllerTest {
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_TOP);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
trackJobs(jobFg, jobTop);
synchronized (mQuotaController.mLock) {
mQuotaController.prepareForExecutionLocked(jobTop);
@@ -3693,7 +3695,7 @@ public class QuotaControllerTest {
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
// App is now in background and out of quota. Fg should now change to out of quota since it
// wasn't started. Top should remain in quota since it started when the app was in TOP.
assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
@@ -3731,7 +3733,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
jobStatus.getWhenStandbyDeferred());
@@ -3775,7 +3777,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
// The job used up the remaining quota, but in that time, the same amount of time in the
// old TimingSession also fell out of the quota window, so it should still have the same
@@ -3797,7 +3799,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(12 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
verify(handler, never()).sendMessageDelayed(any(), anyInt());
}
@@ -5404,7 +5406,7 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
inOrder.verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
synchronized (mQuotaController.mLock) {
assertEquals(remainingTimeMs / 2,
mQuotaController.getRemainingEJExecutionTimeLocked(
@@ -5415,12 +5417,11 @@ public class QuotaControllerTest {
advanceElapsedClock(remainingTimeMs / 2 + 1);
inOrder.verify(mJobSchedulerService,
timeout(remainingTimeMs / 2 + 2 * SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
// Top should still be "in quota" since it started before the app ran on top out of quota.
- assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobTop.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertFalse(
- jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ assertFalse(jobBg.isExpeditedQuotaApproved());
+ assertTrue(jobTop.isExpeditedQuotaApproved());
+ assertFalse(jobUnstarted.isExpeditedQuotaApproved());
synchronized (mQuotaController.mLock) {
assertTrue(
0 >= mQuotaController
@@ -5440,37 +5441,36 @@ public class QuotaControllerTest {
setProcessState(ActivityManager.PROCESS_STATE_TOP);
// Confirm QC recognizes that jobUnstarted has changed from out-of-quota to in-quota.
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
trackJobs(jobTop2, jobFg);
synchronized (mQuotaController.mLock) {
mQuotaController.prepareForExecutionLocked(jobTop2);
}
- assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ assertTrue(jobTop2.isExpeditedQuotaApproved());
+ assertTrue(jobFg.isExpeditedQuotaApproved());
+ assertTrue(jobBg.isExpeditedQuotaApproved());
+ assertTrue(jobUnstarted.isExpeditedQuotaApproved());
// App still in foreground so everything should be in quota.
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertTrue(jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ assertTrue(jobTop2.isExpeditedQuotaApproved());
+ assertTrue(jobFg.isExpeditedQuotaApproved());
+ assertTrue(jobBg.isExpeditedQuotaApproved());
+ assertTrue(jobUnstarted.isExpeditedQuotaApproved());
advanceElapsedClock(20 * SECOND_IN_MILLIS);
setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
inOrder.verify(mJobSchedulerService, timeout(SECOND_IN_MILLIS).times(1))
- .onControllerStateChanged();
+ .onControllerStateChanged(any());
// App is now in background and out of quota. Fg should now change to out of quota since it
// wasn't started. Top should remain in quota since it started when the app was in TOP.
- assertTrue(jobTop2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ assertTrue(jobTop2.isExpeditedQuotaApproved());
+ assertFalse(jobFg.isExpeditedQuotaApproved());
+ assertFalse(jobBg.isExpeditedQuotaApproved());
trackJobs(jobBg2);
- assertFalse(jobBg2.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
- assertFalse(
- jobUnstarted.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ assertFalse(jobBg2.isExpeditedQuotaApproved());
+ assertFalse(jobUnstarted.isExpeditedQuotaApproved());
synchronized (mQuotaController.mLock) {
assertTrue(
0 >= mQuotaController
@@ -5601,8 +5601,8 @@ public class QuotaControllerTest {
// Wait for some extra time to allow for job processing.
verify(mJobSchedulerService,
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(0))
- .onControllerStateChanged();
- assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_EXPEDITED_QUOTA));
+ .onControllerStateChanged(any());
+ assertTrue(jobStatus.isExpeditedQuotaApproved());
// The job used up the remaining quota, but in that time, the same amount of time in the
// old TimingSession also fell out of the quota window, so it should still have the same
// amount of remaining time left its quota.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index db0c3aece023..3c97c95ab340 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -21,13 +21,14 @@ import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.FallbackCategoryProvider
import android.content.pm.FeatureInfo
-import android.content.pm.PackageParser.SigningDetails
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
import android.content.pm.Signature
+import android.content.pm.SigningDetails
import android.content.pm.UserInfo
import android.content.pm.parsing.ParsingPackage
import android.content.pm.parsing.ParsingPackageUtils
+import android.content.pm.parsing.result.ParseTypeImpl
import android.content.res.Resources
import android.hardware.display.DisplayManager
import android.os.Build
@@ -390,8 +391,10 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
val apkPath = File(File(parent, packageName), "base.apk")
val pkg = PackageImpl.forTesting(packageName, apkPath.parentFile.path) as PackageImpl
pkg.signingDetails = signingDetails
- wheneverStatic { ParsingPackageUtils.getSigningDetails(eq(pkg), anyBoolean()) }
- .thenReturn(signingDetails)
+ val result = ParseTypeImpl.forDefaultParsing().success(signingDetails)
+ wheneverStatic { ParsingPackageUtils.getSigningDetails(
+ any(ParseTypeImpl::class.java), eq(pkg), anyBoolean()) }
+ .thenReturn(result)
pkg.versionCode = versionCode.toInt()
pkg.versionCodeMajor = (versionCode shr 32).toInt()
pkg.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT
@@ -498,8 +501,10 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
val apk = File(File(rootDirectory, "framework"), "framework-res.apk")
val frameworkPkg = PackageImpl.forTesting("android",
apk.parentFile.path) as PackageImpl
- wheneverStatic { ParsingPackageUtils.getSigningDetails(frameworkPkg, true) }
- .thenReturn(frameworkSignature)
+ val result = ParseTypeImpl.forDefaultParsing().success(frameworkSignature)
+ wheneverStatic { ParsingPackageUtils.getSigningDetails(
+ any(ParseTypeImpl::class.java), eq(frameworkPkg), eq(true)) }
+ .thenReturn(result)
stageParse(apk, frameworkPkg)
stageSettingInsert("android",
PackageSettingBuilder().setCodePath(apk.path).setName(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
index bd44c360b518..dbbf73b51830 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -18,7 +18,6 @@ package com.android.server.pm
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
import android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
import android.content.pm.PackageManager
-import android.content.pm.PackageParser
import android.os.Build
import android.os.Process
import android.util.Log
@@ -122,7 +121,7 @@ class PackageManagerServiceBootTest {
argThat { path: File -> path.path.contains("a.data.package") },
anyInt(),
anyBoolean()))
- .thenThrow(PackageParser.PackageParserException(
+ .thenThrow(PackageManagerException(
PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
val pm = createPackageManagerService()
verify(rule.mocks().settings, Mockito.never()).insertPackageSettingLPw(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 521be70df633..b60e0c38cf7a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -18,27 +18,36 @@ package com.android.server.pm;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
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.doAnswer;
+import static org.mockito.Mockito.doNothing;
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;
+import android.apex.ApexInfo;
import android.apex.ApexSessionInfo;
+import android.apex.ApexSessionParams;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.ApexStagedEvent;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
+import android.content.pm.StagedApexInfo;
+import android.os.Message;
import android.os.SystemProperties;
import android.os.storage.IStorageManager;
import android.platform.test.annotations.Presubmit;
+import android.util.IntArray;
import android.util.SparseArray;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -53,15 +62,20 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.function.Predicate;
@Presubmit
@@ -123,9 +137,15 @@ public class StagingManagerTest {
mStagingManager.createSession(session2);
// Session1 should not fail in spite of the overlapping packages
mStagingManager.checkNonOverlappingWithStagedSessions(session1);
- // Session2 should fail due to overlapping packages
+ // setSessionFailed() should've been called when doing overlapping checks on session1
+ verify(session2, times(1)).setSessionFailed(anyInt(), anyString());
+
+ // Yet another session with overlapping packages
+ StagingManager.StagedSession session3 = createSession(333, "com.foo", 3);
+ mStagingManager.createSession(session3);
assertThrows(PackageManagerException.class,
- () -> mStagingManager.checkNonOverlappingWithStagedSessions(session2));
+ () -> mStagingManager.checkNonOverlappingWithStagedSessions(session3));
+ verify(session3, never()).setSessionFailed(anyInt(), anyString());
}
@Test
@@ -227,8 +247,8 @@ public class StagingManagerTest {
assertThat(destroyedNonReadySession.isDestroyed()).isTrue();
mStagingManager.onBootCompletedBroadcastReceived();
- assertThat(nonReadyApkSession.hasPreRebootVerificationStarted()).isTrue();
- assertThat(nonReadyApexSession.hasPreRebootVerificationStarted()).isTrue();
+ assertThat(nonReadyApkSession.hasVerificationStarted()).isTrue();
+ assertThat(nonReadyApexSession.hasVerificationStarted()).isTrue();
}
@Test
@@ -519,6 +539,281 @@ public class StagingManagerTest {
assertThat(mStagingManager.getSessionIdByPackageName("com.bar")).isEqualTo(-1);
}
+ @Test
+ public void getStagedApexInfos_validatePreConditions() throws Exception {
+ // Invalid session: null session
+ {
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(null));
+ assertThat(thrown).hasMessageThat().contains("Session is null");
+ }
+ // Invalid session: has parent
+ {
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setParentSessionId(239);
+ session.setSessionReady();
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(session));
+ assertThat(thrown).hasMessageThat().contains("241 session has parent");
+ }
+
+ // Invalid session: does not contain apex
+ {
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setSessionReady();
+ // Call and verify
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+ () -> mStagingManager.getStagedApexInfos(session));
+ assertThat(thrown).hasMessageThat().contains("241 session does not contain apex");
+ }
+ // Invalid session: not ready
+ {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ assertThat(result).isEmpty();
+ }
+ // Invalid session: destroyed
+ {
+ FakeStagedSession session = new FakeStagedSession(240);
+ session.setSessionReady();
+ session.setIsApex(true);
+ session.setDestroyed(true);
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session);
+ assertThat(result).isEmpty();
+ }
+ }
+
+ private ApexInfo[] getFakeApexInfo(List<String> moduleNames) {
+ List<ApexInfo> result = new ArrayList<>();
+ for (String moduleName : moduleNames) {
+ ApexInfo info = new ApexInfo();
+ info.moduleName = moduleName;
+ result.add(info);
+ }
+ return result.toArray(new ApexInfo[0]);
+ }
+
+ @Test
+ public void getStagedApexInfos_nonParentSession() throws Exception {
+ FakeStagedSession validSession = new FakeStagedSession(239);
+ validSession.setIsApex(true);
+ validSession.setSessionReady();
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession);
+ assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]);
+
+ ArgumentCaptor<ApexSessionParams> argumentCaptor =
+ ArgumentCaptor.forClass(ApexSessionParams.class);
+ verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+ ApexSessionParams params = argumentCaptor.getValue();
+ assertThat(params.sessionId).isEqualTo(239);
+ }
+
+ @Test
+ public void getStagedApexInfos_parentSession() throws Exception {
+ FakeStagedSession childSession1 = new FakeStagedSession(201);
+ childSession1.setIsApex(true);
+ FakeStagedSession childSession2 = new FakeStagedSession(202);
+ childSession2.setIsApex(true);
+ FakeStagedSession nonApexChild = new FakeStagedSession(203);
+ FakeStagedSession parentSession = new FakeStagedSession(239,
+ Arrays.asList(childSession1, childSession2, nonApexChild));
+ parentSession.setSessionReady();
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1", "module2"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Call and verify
+ Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession);
+ assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0],
+ fakeApexInfos[1].moduleName, fakeApexInfos[1]);
+
+ ArgumentCaptor<ApexSessionParams> argumentCaptor =
+ ArgumentCaptor.forClass(ApexSessionParams.class);
+ verify(mApexManager, times(1)).getStagedApexInfos(argumentCaptor.capture());
+ ApexSessionParams params = argumentCaptor.getValue();
+ assertThat(params.sessionId).isEqualTo(239);
+ assertThat(params.childSessionIds).asList().containsExactly(201, 202);
+ }
+
+ @Test
+ public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception {
+ FakeStagedSession validSession1 = new FakeStagedSession(239);
+ validSession1.setIsApex(true);
+ validSession1.setSessionReady();
+ mStagingManager.createSession(validSession1);
+
+ FakeStagedSession childSession1 = new FakeStagedSession(123);
+ childSession1.setIsApex(true);
+ FakeStagedSession childSession2 = new FakeStagedSession(124);
+ childSession2.setIsApex(true);
+ FakeStagedSession nonApexChild = new FakeStagedSession(125);
+ FakeStagedSession parentSession = new FakeStagedSession(240,
+ Arrays.asList(childSession1, childSession2, nonApexChild));
+ parentSession.setSessionReady();
+ mStagingManager.createSession(parentSession);
+
+ mockApexManagerGetStagedApexInfoWithSessionId();
+
+ List<String> result = mStagingManager.getStagedApexModuleNames();
+ assertThat(result).containsExactly("239", "123", "124");
+ verify(mApexManager, times(2)).getStagedApexInfos(any());
+ }
+
+ // Make mApexManager return ApexInfo with same module name as the sessionId
+ // of the parameter that was passed into it
+ private void mockApexManagerGetStagedApexInfoWithSessionId() {
+ when(mApexManager.getStagedApexInfos(any())).thenAnswer(new Answer<ApexInfo[]>() {
+ @Override
+ public ApexInfo[] answer(InvocationOnMock invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ ApexSessionParams params = (ApexSessionParams) args[0];
+ IntArray sessionsToProcess = new IntArray();
+ if (params.childSessionIds.length == 0) {
+ sessionsToProcess.add(params.sessionId);
+ } else {
+ sessionsToProcess.addAll(params.childSessionIds);
+ }
+ List<ApexInfo> result = new ArrayList<>();
+ for (int session : sessionsToProcess.toArray()) {
+ ApexInfo info = new ApexInfo();
+ info.moduleName = String.valueOf(session);
+ result.add(info);
+ }
+ return result.toArray(new ApexInfo[0]);
+ }
+ });
+ }
+
+ @Test
+ public void getStagedApexInfo() throws Exception {
+ FakeStagedSession validSession1 = new FakeStagedSession(239);
+ validSession1.setIsApex(true);
+ validSession1.setSessionReady();
+ mStagingManager.createSession(validSession1);
+ ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1"));
+ when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos);
+
+ // Verify null is returned if module name is not found
+ StagedApexInfo result = mStagingManager.getStagedApexInfo("not found");
+ assertThat(result).isNull();
+ verify(mApexManager, times(1)).getStagedApexInfos(any());
+ // Otherwise, the correct object is returned
+ result = mStagingManager.getStagedApexInfo("module1");
+ assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName);
+ assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath);
+ assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode);
+ assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName);
+ verify(mApexManager, times(2)).getStagedApexInfos(any());
+ }
+
+ @Test
+ public void registeredStagedApexObserverIsNotifiedOnPreRebootVerificationCompletion()
+ throws Exception {
+ // Register observer
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Create one staged session and trigger end of pre-reboot verification
+ {
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ mockApexManagerGetStagedApexInfoWithSessionId();
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[]{"239"});
+ }
+
+ // Create another staged session and verify observers are notified of union
+ {
+ Mockito.clearInvocations(observer);
+ FakeStagedSession session = new FakeStagedSession(240);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[]{"239", "240"});
+ }
+
+ // Finally, verify that once unregistered, observer is not notified
+ mStagingManager.unregisterStagedApexObserver(observer);
+ {
+ Mockito.clearInvocations(observer);
+ FakeStagedSession session = new FakeStagedSession(241);
+ session.setIsApex(true);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ verify(observer, never()).onApexStaged(any());
+ }
+ }
+
+ @Test
+ public void registeredStagedApexObserverIsNotifiedOnSessionAbandon() throws Exception {
+ // Register observer
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Create a ready session and abandon it
+ FakeStagedSession session = new FakeStagedSession(239);
+ session.setIsApex(true);
+ session.setSessionReady();
+ session.setDestroyed(true);
+ mStagingManager.createSession(session);
+
+ mStagingManager.abortCommittedSession(session);
+
+ assertThat(session.isSessionReady()).isTrue();
+ ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass(
+ ApexStagedEvent.class);
+ verify(observer, times(1)).onApexStaged(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0);
+ }
+
+ @Test
+ public void stagedApexObserverIsOnlyCalledForApexSessions() throws Exception {
+ IStagedApexObserver observer = Mockito.mock(IStagedApexObserver.class);
+ mStagingManager.registerStagedApexObserver(observer);
+
+ // Trigger end of pre-reboot verification
+ FakeStagedSession session = new FakeStagedSession(239);
+ mStagingManager.createSession(session);
+
+ triggerEndOfPreRebootVerification(session);
+ assertThat(session.isSessionReady()).isTrue();
+ verify(observer, never()).onApexStaged(any());
+ }
+
+ private void triggerEndOfPreRebootVerification(StagingManager.StagedSession session) {
+ StagingManager.PreRebootVerificationHandler handler =
+ mStagingManager.mPreRebootVerificationHandler;
+ Message msg = handler.obtainMessage(
+ handler.MSG_PRE_REBOOT_VERIFICATION_END, session.sessionId(), -1, session);
+ handler.handleMessage(msg);
+ }
+
private StagingManager.StagedSession createSession(int sessionId, String packageName,
long committedMillis) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
@@ -565,6 +860,7 @@ public class StagingManagerTest {
Predicate<StagingManager.StagedSession> filter = invocation.getArgument(0);
return filter.test(stagedSession);
}).when(stagedSession).sessionContains(any());
+ doNothing().when(stagedSession).setSessionFailed(anyInt(), anyString());
return stagedSession;
}
@@ -581,11 +877,17 @@ public class StagingManagerTest {
private int mParentSessionId = -1;
private String mPackageName;
private boolean mIsAbandonded = false;
- private boolean mPreRebootVerificationStarted = false;
- private final List<StagingManager.StagedSession> mChildSessions = new ArrayList<>();
+ private boolean mVerificationStarted = false;
+ private final List<StagingManager.StagedSession> mChildSessions;
private FakeStagedSession(int sessionId) {
mSessionId = sessionId;
+ mChildSessions = new ArrayList<>();
+ }
+
+ private FakeStagedSession(int sessionId, List<StagingManager.StagedSession> childSessions) {
+ mSessionId = sessionId;
+ mChildSessions = childSessions;
}
private void setParentSessionId(int parentSessionId) {
@@ -612,8 +914,8 @@ public class StagingManagerTest {
return mIsAbandonded;
}
- private boolean hasPreRebootVerificationStarted() {
- return mPreRebootVerificationStarted;
+ private boolean hasVerificationStarted() {
+ return mVerificationStarted;
}
private FakeStagedSession addChildSession(FakeStagedSession session) {
@@ -768,20 +1070,11 @@ public class StagingManagerTest {
}
@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();
- }
+ public void notifyEndPreRebootVerification() {}
@Override
public void verifySession() {
- throw new UnsupportedOperationException();
+ mVerificationStarted = true;
}
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
new file mode 100644
index 000000000000..f94377fe51c2
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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.power;
+
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_VR;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
+import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED;
+import static com.android.server.power.ScreenUndimDetector.KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
+import static com.android.server.power.ScreenUndimDetector.KEY_UNDIMS_REQUIRED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.power.ScreenUndimDetector}
+ */
+@RunWith(JUnit4.class)
+public class ScreenUndimDetectorTest {
+ private static final List<Integer> ALL_POLICIES =
+ Arrays.asList(POLICY_OFF,
+ POLICY_DOZE,
+ POLICY_DIM,
+ POLICY_BRIGHT,
+ POLICY_VR);
+
+ @ClassRule
+ public static final TestableContext sContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+ private ScreenUndimDetector mScreenUndimDetector;
+
+ private final TestClock mClock = new TestClock();
+
+ private static class TestClock extends ScreenUndimDetector.InternalClock {
+ long mCurrentTime = 0;
+ @Override
+ public long getCurrentTime() {
+ return mCurrentTime;
+ }
+
+ public void advanceTime(long millisAdvanced) {
+ mCurrentTime += millisAdvanced;
+ }
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(1), false /*makeDefault*/);
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
+ Long.toString(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS),
+ false /*makeDefault*/);
+
+ mScreenUndimDetector = new ScreenUndimDetector(mClock);
+ mScreenUndimDetector.systemReady(sContext);
+ }
+
+ @Test
+ public void recordScreenPolicy_disabledByFlag_noop() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/);
+
+ setup();
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ }
+
+ @Test
+ public void recordScreenPolicy_samePolicy_noop() {
+ for (int policy : ALL_POLICIES) {
+ setup();
+ mScreenUndimDetector.recordScreenPolicy(policy);
+ mScreenUndimDetector.recordScreenPolicy(policy);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ }
+ }
+
+ @Test
+ public void recordScreenPolicy_dimToBright_extends() {
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+ }
+
+ @Test
+ public void recordScreenPolicy_otherTransitions_doesNotExtend() {
+ for (int from : ALL_POLICIES) {
+ for (int to : ALL_POLICIES) {
+ if (from == POLICY_DIM && to == POLICY_BRIGHT) {
+ continue;
+ }
+ setup();
+ mScreenUndimDetector.recordScreenPolicy(from);
+ mScreenUndimDetector.recordScreenPolicy(to);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+ }
+ }
+ }
+
+ @Test
+ public void recordScreenPolicy_dimToBright_twoUndimsNeeded_extends() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(2), false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+ }
+
+ @Test
+ public void recordScreenPolicy_dimBrightDimOff_resetsCounter_doesNotExtend() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(2), false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+ }
+
+ @Test
+ public void recordScreenPolicy_undimToOff_resetsCounter() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(2), false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+ }
+
+ @Test
+ public void recordScreenPolicy_undimOffUndim_doesNotExtend() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(2), false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ // undim
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ // off
+ mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+ // second undim
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
+ }
+
+ @Test
+ public void recordScreenPolicy_dimToBright_tooFarApart_doesNotExtend() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(2), false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ mClock.advanceTime(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS + 5);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
+ }
+
+ @Test
+ public void recordScreenPolicy_dimToNonBright_resets() {
+ for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) {
+ setup();
+ mScreenUndimDetector.mUndimCounter = 1;
+ mScreenUndimDetector.mUndimCounterStartedMillis = 123;
+ mScreenUndimDetector.mWakeLock.acquire();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+ mScreenUndimDetector.recordScreenPolicy(to);
+
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+ assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ }
+
+ }
+
+ @Test
+ public void recordScreenPolicy_brightToNonDim_resets() {
+ for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) {
+ setup();
+ mScreenUndimDetector.mUndimCounter = 1;
+ mScreenUndimDetector.mUndimCounterStartedMillis = 123;
+ mScreenUndimDetector.mWakeLock.acquire();
+
+ mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+ mScreenUndimDetector.recordScreenPolicy(to);
+
+ assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+ assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
+ assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+ }
+ }
+
+ @Test
+ public void recordScreenPolicy_otherTransitions_doesNotReset() {
+ DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+ KEY_UNDIMS_REQUIRED,
+ Integer.toString(3),
+ false /*makeDefault*/);
+ mScreenUndimDetector.readValuesFromDeviceConfig();
+
+ for (int from : ALL_POLICIES) {
+ for (int to : ALL_POLICIES) {
+ if (from == POLICY_DIM && to != POLICY_BRIGHT) {
+ continue;
+ }
+ if (from == POLICY_BRIGHT && to != POLICY_DIM) {
+ continue;
+ }
+ mScreenUndimDetector.mCurrentScreenPolicy = POLICY_OFF;
+ mScreenUndimDetector.mUndimCounter = 1;
+ mScreenUndimDetector.mUndimCounterStartedMillis =
+ SystemClock.currentThreadTimeMillis();
+
+ mScreenUndimDetector.recordScreenPolicy(from);
+ mScreenUndimDetector.recordScreenPolicy(to);
+
+ assertThat(mScreenUndimDetector.mUndimCounter).isNotEqualTo(0);
+ assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isNotEqualTo(0);
+ }
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
index 64b24c12f046..60a7f78c6949 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java
@@ -95,18 +95,34 @@ public final class TestableDeviceConfig implements StaticMockFixture {
String name = invocationOnMock.getArgument(1);
String value = invocationOnMock.getArgument(2);
mKeyValueMap.put(getKey(namespace, name), value);
- for (DeviceConfig.OnPropertiesChangedListener listener :
- mOnPropertiesChangedListenerMap.keySet()) {
- if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
- mOnPropertiesChangedListenerMap.get(listener).second.execute(
- () -> listener.onPropertiesChanged(
- getProperties(namespace, name, value)));
- }
- }
+ invokeListeners(namespace, getProperties(namespace, name, value));
return true;
}
).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean()));
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String namespace = invocationOnMock.getArgument(0);
+ String name = invocationOnMock.getArgument(1);
+ mKeyValueMap.remove(getKey(namespace, name));
+ invokeListeners(namespace, getProperties(namespace, name, null));
+ return true;
+ }
+ ).when(() -> DeviceConfig.deleteProperty(anyString(), anyString()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ Properties properties = invocationOnMock.getArgument(0);
+ String namespace = properties.getNamespace();
+ Map<String, String> keyValues = new ArrayMap<>();
+ for (String name : properties.getKeyset()) {
+ String value = properties.getString(name, /* defaultValue= */ "");
+ mKeyValueMap.put(getKey(namespace, name), value);
+ keyValues.put(name.toLowerCase(), value);
+ }
+ invokeListeners(namespace, getProperties(namespace, keyValues));
+ return true;
+ }
+ ).when(() -> DeviceConfig.setProperties(any(Properties.class)));
+
doAnswer((Answer<String>) invocationOnMock -> {
String namespace = invocationOnMock.getArgument(0);
String name = invocationOnMock.getArgument(1);
@@ -153,6 +169,16 @@ public final class TestableDeviceConfig implements StaticMockFixture {
return Pair.create(values[0], values[1]);
}
+ private void invokeListeners(String namespace, Properties properties) {
+ for (DeviceConfig.OnPropertiesChangedListener listener :
+ mOnPropertiesChangedListenerMap.keySet()) {
+ if (namespace.equals(mOnPropertiesChangedListenerMap.get(listener).first)) {
+ mOnPropertiesChangedListenerMap.get(listener).second.execute(
+ () -> listener.onPropertiesChanged(properties));
+ }
+ }
+ }
+
private Properties getProperties(String namespace, String name, String value) {
return getProperties(namespace, Collections.singletonMap(name.toLowerCase(), value));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
index 0e40669cf870..f9f43876f39a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java
@@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityThread;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.BadConfigException;
import android.provider.DeviceConfig.Properties;
import androidx.test.filters.SmallTest;
@@ -92,6 +93,30 @@ public class TestableDeviceConfigTest {
}
@Test
+ public void setProperties() throws BadConfigException {
+ String newKey = "key2";
+ String newValue = "value2";
+ DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+ sValue).setString(newKey, newValue).build());
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+ assertThat(DeviceConfig.getProperty(sNamespace, newKey)).isEqualTo(newValue);
+ }
+
+ @Test
+ public void deleteProperty() {
+ DeviceConfig.setProperty(sNamespace, sKey, sValue, false);
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isEqualTo(sValue);
+ DeviceConfig.deleteProperty(sNamespace, sKey);
+ assertThat(DeviceConfig.getProperty(sNamespace, sKey)).isNull();
+ String newNamespace = "namespace2";
+ String newValue = "value2";
+ DeviceConfig.setProperty(newNamespace, sKey, newValue, false);
+ assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isEqualTo(newValue);
+ DeviceConfig.deleteProperty(newNamespace, sKey);
+ assertThat(DeviceConfig.getProperty(newNamespace, sKey)).isNull();
+ }
+
+ @Test
public void getProperties_empty() {
String newKey = "key2";
String newValue = "value2";
@@ -131,13 +156,12 @@ public class TestableDeviceConfigTest {
}
@Test
- public void testListener() throws InterruptedException {
+ public void testListener_setProperty() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
OnPropertiesChangedListener changeListener = (properties) -> {
assertThat(properties.getNamespace()).isEqualTo(sNamespace);
- assertThat(properties.getKeyset().size()).isEqualTo(1);
- assertThat(properties.getKeyset()).contains(sKey);
+ assertThat(properties.getKeyset()).containsExactly(sKey);
assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
countDownLatch.countDown();
@@ -153,6 +177,53 @@ public class TestableDeviceConfigTest {
}
}
+ @Test
+ public void testListener_setProperties() throws BadConfigException, InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ String newKey = "key2";
+ String newValue = "value2";
+
+ OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+ assertThat(properties.getKeyset()).containsExactly(sKey, newKey);
+ assertThat(properties.getString(sKey, "bogus_value")).isEqualTo(sValue);
+ assertThat(properties.getString(newKey, "bogus_value")).isEqualTo(newValue);
+ assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+ countDownLatch.countDown();
+ };
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.setProperties(new Properties.Builder(sNamespace).setString(sKey,
+ sValue).setString(newKey, newValue).build());
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
+
+ @Test
+ public void testListener_deleteProperty() throws InterruptedException {
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ OnPropertiesChangedListener changeListener = (properties) -> {
+ assertThat(properties.getNamespace()).isEqualTo(sNamespace);
+ assertThat(properties.getKeyset()).containsExactly(sKey);
+ assertThat(properties.getString(sKey, "bogus_value")).isEqualTo("bogus_value");
+ assertThat(properties.getString("bogus_key", "bogus_value")).isEqualTo("bogus_value");
+ countDownLatch.countDown();
+ };
+ try {
+ DeviceConfig.addOnPropertiesChangedListener(sNamespace,
+ ActivityThread.currentApplication().getMainExecutor(), changeListener);
+ DeviceConfig.deleteProperty(sNamespace, sKey);
+ assertThat(countDownLatch.await(
+ WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue();
+ } finally {
+ DeviceConfig.removeOnPropertiesChangedListener(changeListener);
+ }
+ }
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 339a5f916b4b..c19155f51743 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -37,8 +37,8 @@ android_test {
"services.net",
"services.people",
"services.usage",
- "services.uwb",
"guava",
+ "guava-android-testlib",
"androidx.test.core",
"androidx.test.ext.truth",
"androidx.test.runner",
@@ -117,6 +117,7 @@ android_test {
":PackageParserTestApp2",
":PackageParserTestApp3",
":PackageParserTestApp4",
+ ":PackageParserTestApp5",
":apex.test",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index bcb2cf8fa0c3..68b84693f0ef 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -71,6 +71,7 @@
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+ <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"/>
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.HARDWARE_TEST"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index d6c11a549dfa..e612d121b093 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -65,6 +65,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index 80e81d6e7cb9..554f0a4265be 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -29,6 +29,7 @@ import static com.android.server.accessibility.AccessibilityInputFilter.FLAG_FEA
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -176,7 +177,7 @@ public class AccessibilityInputFilterTest {
}
@Test
- public void testEventHandler_shouldChangeAfterOnDisplayChanged() {
+ public void testEventHandler_shouldIncreaseAndHaveCorrectOrderAfterOnDisplayAdded() {
prepareLooper();
// Check if there is only one mEventHandler when there is one default display.
@@ -184,13 +185,51 @@ public class AccessibilityInputFilterTest {
assertEquals(1, mEventHandler.size());
// Check if it has correct numbers of mEventHandler for corresponding displays.
- setDisplayCount(4);
- mA11yInputFilter.onDisplayChanged();
- assertEquals(4, mEventHandler.size());
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mEventHandler.size());
+
+ EventStreamTransformation next = mEventHandler.get(SECOND_DISPLAY);
+ assertNotNull(next);
+
+ // Start from index 1 because KeyboardInterceptor only exists in EventHandler for
+ // DEFAULT_DISPLAY.
+ for (int i = 1; next != null; i++) {
+ assertEquals(next.getClass(), mExpectedEventHandlerTypes[i]);
+ next = next.getNext();
+ }
+ }
+
+ @Test
+ public void testEventHandler_shouldDecreaseAfterOnDisplayRemoved() {
+ prepareLooper();
setDisplayCount(2);
- mA11yInputFilter.onDisplayChanged();
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
assertEquals(2, mEventHandler.size());
+
+ // Check if it has correct numbers of mEventHandler for corresponding displays.
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(1, mEventHandler.size());
+
+ EventStreamTransformation eventHandler = mEventHandler.get(SECOND_DISPLAY);
+ assertNull(eventHandler);
+ }
+
+ @Test
+ public void testEventHandler_shouldNoChangedInOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ EventStreamTransformation eventHandlerBeforeDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ EventStreamTransformation eventHandlerAfterDisplayRemoved =
+ mEventHandler.get(DEFAULT_DISPLAY);
+
+ assertEquals(eventHandlerBeforeDisplayRemoved, eventHandlerAfterDisplayRemoved);
}
@Test
@@ -240,7 +279,7 @@ public class AccessibilityInputFilterTest {
}
@Test
- public void testInputEvent_shouldClearEventsForAllEventHandlers() {
+ public void testInputEvent_shouldClearEventsForDisplayEventHandlers() {
prepareLooper();
mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
@@ -253,13 +292,71 @@ public class AccessibilityInputFilterTest {
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
assertEquals(2, mCaptor1.mEvents.size());
- // InputEvent with different input source should trigger clearEvents() for each
- // EventStreamTransformation in EventHandler.
+ // InputEvent with different input source to the same display should trigger
+ // clearEvents() for the EventHandler in this display.
send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_MOUSE));
assertEquals(1, mCaptor1.mEvents.size());
}
@Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayEventHandlers() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ assertEquals(2, mEventHandler.size());
+
+ mCaptor1 = new EventCaptor();
+ mCaptor2 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+ mEventHandler.put(SECOND_DISPLAY, mCaptor2);
+
+ // InputEvent with different displayId should be dispatched to corresponding EventHandler.
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+
+ // InputEvent with different input source should not trigger clearEvents() for
+ // the EventHandler in the other display.
+ send(downEvent(SECOND_DISPLAY, InputDevice.SOURCE_MOUSE));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayAdded() {
+ prepareLooper();
+
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ setDisplayCount(2);
+ mA11yInputFilter.onDisplayAdded(mDisplayList.get(SECOND_DISPLAY));
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
+ public void testInputEvent_shouldNotClearEventsForOtherDisplayAfterOnDisplayRemoved() {
+ prepareLooper();
+
+ setDisplayCount(2);
+ mA11yInputFilter.setUserAndEnabledFeatures(0, mFeatures);
+ mCaptor1 = new EventCaptor();
+ mEventHandler.put(DEFAULT_DISPLAY, mCaptor1);
+
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ send(downEvent(DEFAULT_DISPLAY, InputDevice.SOURCE_TOUCHSCREEN));
+ assertEquals(2, mCaptor1.mEvents.size());
+
+ mA11yInputFilter.onDisplayRemoved(SECOND_DISPLAY);
+ assertEquals(2, mCaptor1.mEvents.size());
+ }
+
+ @Test
public void testEnabledFeatures_windowMagnificationMode_expectedMagnificationGestureHandler() {
prepareLooper();
doReturn(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW).when(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index ee00cb24a991..b403033280fc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static junit.framework.Assert.assertFalse;
@@ -46,6 +48,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SigningInfo;
@@ -59,6 +62,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.R;
+import com.android.server.LocalServices;
import org.junit.Before;
import org.junit.Rule;
@@ -84,10 +88,8 @@ public class AccessibilitySecurityPolicyTest {
private static final int APP_PID = 2000;
private static final int SYSTEM_PID = 558;
private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
- private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
- TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
- private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
+ "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
private static final int[] ALWAYS_DISPATCH_EVENTS = {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -158,6 +160,8 @@ public class AccessibilitySecurityPolicyTest {
private PackageInfo mMockSourcePackageInfo;
@Mock
private PolicyWarningUIController mPolicyWarningUIController;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -167,20 +171,6 @@ public class AccessibilitySecurityPolicyTest {
mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
mContext.getOrCreateTestableResources().addOverride(
R.dimen.accessibility_focus_highlight_stroke_width, 1);
- mContext.getOrCreateTestableResources().addOverride(R.array
- .config_accessibility_allowed_install_source,
- new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
-
- when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
- when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
- when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
- when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
- mMockSourcePackageInfo);
-
- mMockResolveInfo.serviceInfo = mMockServiceInfo;
- mMockServiceInfo.applicationInfo = mMockApplicationInfo;
- mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
- mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -261,8 +251,8 @@ public class AccessibilitySecurityPolicyTest {
@Test
public void resolveValidReportedPackage_uidAndPkgNameMatched_returnPkgName()
throws PackageManager.NameNotFoundException {
- when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(APP_UID);
+ when(mMockPackageManager.getPackageUidAsUser(PACKAGE_NAME,
+ PackageManager.MATCH_ANY_USER, TEST_USER_ID)).thenReturn(APP_UID);
assertEquals(mA11ySecurityPolicy.resolveValidReportedPackageLocked(
PACKAGE_NAME, APP_UID, TEST_USER_ID, APP_PID),
@@ -595,7 +585,7 @@ public class AccessibilitySecurityPolicyTest {
serviceInfo.permission = android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE;
mA11ySecurityPolicy.canRegisterService(serviceInfo);
verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE,
- serviceInfo.applicationInfo.uid, serviceInfo.packageName);
+ serviceInfo.applicationInfo.uid, serviceInfo.packageName, null, null);
}
@Test
@@ -617,96 +607,152 @@ public class AccessibilitySecurityPolicyTest {
mA11ySecurityPolicy.checkAccessibilityAccess(mMockA11yServiceConnection);
verify(mMockAppOpsManager).noteOpNoThrow(AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY,
- APP_UID, PACKAGE_NAME);
+ APP_UID, PACKAGE_NAME, null, null);
}
@Test
- public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
+ public void onBoundServicesChanged_nonA11yTool_invokeAction()
+ throws PackageManager.NameNotFoundException {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
boundServices.add(mMockA11yServiceConnection);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
+ initServiceInfoAndConnection(TEST_COMPONENT_NAME,
+ mMockA11yServiceConnection,
+ /* isAccessibilityTool= */ false);
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
eq(TEST_COMPONENT_NAME));
- }
-
- @Test
- public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID),
eq(TEST_COMPONENT_NAME));
}
@Test
- public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+ public void onBoundServicesChanged_sysA11yTool_noAction()
+ throws PackageManager.NameNotFoundException {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ initServiceInfoAndConnection(TEST_COMPONENT_NAME,
+ mMockA11yServiceConnection,
+ /* isAccessibilityTool= */ true,
+ /* isSystemApp= */true,
+ /* installSourceInfo= */ null);
boundServices.add(mMockA11yServiceConnection);
- when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
- }
-
- @Test
- public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
- onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
any());
}
@Test
- public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+ public void onBoundServicesChanged_nonSysA11yToolFromAllowedInstallerInAllowedList_noAction()
throws PackageManager.NameNotFoundException {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ final String allowedSourcePackageName = "com.allowed.install.package";
+ mContext.getOrCreateTestableResources().addOverride(R.array
+ .config_accessibility_allowed_install_source,
+ new String[]{allowedSourcePackageName});
+ // The allowed Installer should be system app in the allowed list.
+ InstallSourceInfo allowedSource = initInstallSourceInfo(
+ allowedSourcePackageName, /* isSystemApp= */ true);
+ initServiceInfoAndConnection(TEST_COMPONENT_NAME,
+ mMockA11yServiceConnection,
+ /* isAccessibilityTool= */ true,
+ /* isSystemApp= */ false,
+ allowedSource);
boundServices.add(mMockA11yServiceConnection);
- when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
- final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
- ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
- ALLOWED_INSTALL_PACKAGE_NAME);
- when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
- installSourceInfo);
- when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
}
@Test
- public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+ public void onBoundServicesChanged_nonSysA11yToolFromValidInstallerWithoutAllowedList_noAction()
throws PackageManager.NameNotFoundException {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ final String validInstallerPackageName = "com.valid.install.package";
+ final String defaultInstallerPackageName = "com.default.install.package";
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ when(mPackageManagerInternal.getKnownPackageNames(PACKAGE_INSTALLER,
+ TEST_USER_ID)).thenReturn(new String[]{defaultInstallerPackageName});
+ mContext.getOrCreateTestableResources().addOverride(R.array
+ .config_accessibility_allowed_install_source,
+ new String[]{});
+ // The valid Installer should be system app and not the default installer.
+ InstallSourceInfo validSource = initInstallSourceInfo(
+ validInstallerPackageName, /* isSystemApp= */ true);
+ initServiceInfoAndConnection(TEST_COMPONENT_NAME,
+ mMockA11yServiceConnection, /* isAccessibilityTool= */ true,
+ /* isSystemApp= */ false,
+ validSource);
boundServices.add(mMockA11yServiceConnection);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
- final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
- when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
- installSourceInfo);
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
- verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
- eq(TEST_COMPONENT_NAME));
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
}
@Test
- public void onSwitchUser_differentUser_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
+ public void onSwitchUser_oldUserHadAction_invokeActionForOldUser()
+ throws PackageManager.NameNotFoundException {
+ final int newUserId = 2;
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ initServiceInfoAndConnection(TEST_COMPONENT_NAME,
+ mMockA11yServiceConnection,
+ /* isAccessibilityTool= */ false);
+ boundServices.add(mMockA11yServiceConnection);
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
- mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
+ mA11ySecurityPolicy.onSwitchUserLocked(newUserId, new HashSet<>());
- verify(mPolicyWarningUIController).onSwitchUserLocked(eq(2), eq(new HashSet<>()));
+ verify(mPolicyWarningUIController).onSwitchUserLocked(eq(newUserId), eq(new HashSet<>()));
verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID),
eq(TEST_COMPONENT_NAME));
}
+
+ private void initServiceInfoAndConnection(ComponentName componentName,
+ AccessibilityServiceConnection connection,
+ boolean isAccessibilityTool) throws PackageManager.NameNotFoundException {
+ initServiceInfoAndConnection(componentName, connection, isAccessibilityTool, false, null);
+ }
+
+ private void initServiceInfoAndConnection(ComponentName componentName,
+ AccessibilityServiceConnection connection,
+ boolean isAccessibilityTool, boolean isSystemApp, InstallSourceInfo installSourceInfo)
+ throws PackageManager.NameNotFoundException {
+ when(connection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+ when(mMockA11yServiceInfo.getComponentName()).thenReturn(componentName);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(isAccessibilityTool);
+ when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
+ mMockResolveInfo.serviceInfo = mMockServiceInfo;
+ mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+ mMockServiceInfo.packageName = componentName.getPackageName();
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(isSystemApp);
+ when(mMockPackageManager.getInstallSourceInfo(componentName.getPackageName())).thenReturn(
+ installSourceInfo);
+ }
+
+ private InstallSourceInfo initInstallSourceInfo(String packageName, boolean isSystemApp)
+ throws PackageManager.NameNotFoundException {
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+ packageName, new SigningInfo(), null,
+ packageName);
+ when(mMockPackageManager.getPackageInfo(packageName, 0)).thenReturn(
+ mMockSourcePackageInfo);
+ mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
+ when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(isSystemApp);
+ return installSourceInfo;
+ }
}
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 00daa5c89fba..432a500a5041 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index e4d51e4374a7..ca9ab4fa84eb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -30,7 +30,6 @@ import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -120,6 +119,7 @@ public class AccessibilityWindowManagerTest {
@Mock private AccessibilityWindowManager.AccessibilityEventSender mMockA11yEventSender;
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock private AccessibilityTraceManager mMockA11yTraceManager;
@Mock private IBinder mMockHostToken;
@Mock private IBinder mMockEmbeddedToken;
@@ -140,7 +140,8 @@ public class AccessibilityWindowManagerTest {
mMockWindowManagerInternal,
mMockA11yEventSender,
mMockA11ySecurityPolicy,
- mMockA11yUserManager);
+ mMockA11yUserManager,
+ mMockA11yTraceManager);
// Starts tracking window of default display and sets the default display
// as top focused display before each testing starts.
startTrackingPerDisplay(Display.DEFAULT_DISPLAY);
@@ -863,8 +864,6 @@ public class AccessibilityWindowManagerTest {
windowInfosForDisplay.get(DEFAULT_FOCUSED_INDEX).focused = true;
}
// Turns on windows tracking, and update window info.
- when(mMockWindowManagerInternal.setWindowsForAccessibilityCallback(eq(displayId), any()))
- .thenReturn(true);
mA11yWindowManager.startTrackingWindows(displayId);
// Puts window lists into array.
mWindowInfos.put(displayId, windowInfosForDisplay);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 78e651b7a3c1..c62cae5e9b6e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -56,11 +56,13 @@ public class KeyboardInterceptorTest {
private MessageCapturingHandler mHandler = new MessageCapturingHandler(
msg -> mInterceptor.handleMessage(msg));
@Mock AccessibilityManagerService mMockAms;
+ @Mock AccessibilityTraceManager mMockTraceManager;
@Mock WindowManagerPolicy mMockPolicy;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mInterceptor = new KeyboardInterceptor(mMockAms, mMockPolicy, mHandler);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index d4908ee78a1d..59b69f9ffebf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -122,6 +122,7 @@ public class MotionEventInjectorTest {
MotionEventInjector mMotionEventInjector;
IAccessibilityServiceClient mServiceInterface;
+ AccessibilityTraceManager mTrace;
List<GestureStep> mLineList = new ArrayList<>();
List<GestureStep> mClickList = new ArrayList<>();
List<GestureStep> mContinuedLineList1 = new ArrayList<>();
@@ -148,7 +149,8 @@ public class MotionEventInjectorTest {
return mMotionEventInjector.handleMessage(msg);
}
});
- mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler);
+ mTrace = mock(AccessibilityTraceManager.class);
+ mMotionEventInjector = new MotionEventInjector(mMessageCapturingHandler, mTrace);
mServiceInterface = mock(IAccessibilityServiceClient.class);
mLineList = createSimpleGestureFromPoints(0, 0, false, LINE_DURATION, LINE_START, LINE_END);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 160308762a58..4ce9ba031b25 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityTrace;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.app.UiAutomation;
import android.content.Context;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 57ee7aa522c2..4a06611f397e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -36,6 +36,7 @@ import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_E
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
@@ -53,6 +54,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -107,6 +109,8 @@ public class TouchExplorerTest {
@Mock
private AccessibilityManagerService mMockAms;
+ @Mock
+ private AccessibilityTraceManager mMockTraceManager;
@Captor
private ArgumentCaptor<AccessibilityGestureEvent> mGestureCaptor;
@@ -143,6 +147,7 @@ public class TouchExplorerTest {
if (Looper.myLooper() == null) {
Looper.prepare();
}
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
mContext = InstrumentationRegistry.getContext();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mCaptor = new EventCaptor();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index 502f64a1e50b..fe4fed9da468 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -93,6 +94,7 @@ public class FullScreenMagnificationControllerTest {
mock(FullScreenMagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
+ final AccessibilityTraceManager mMockTraceManager = mock(AccessibilityTraceManager.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
private final MagnificationAnimationCallback mAnimationCallback = mock(
MagnificationAnimationCallback.class);
@@ -113,9 +115,11 @@ public class FullScreenMagnificationControllerTest {
when(mMockContext.getMainLooper()).thenReturn(looper);
when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+ when(mMockControllerCtx.getTraceManager()).thenReturn(mMockTraceManager);
when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+ when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
initMockWindowManager();
mFullScreenMagnificationController = new FullScreenMagnificationController(
@@ -773,20 +777,20 @@ public class FullScreenMagnificationControllerTest {
}
@Test
- public void testRotation_resetsMagnification() {
+ public void testDisplaySizeChanged_resetsMagnification() {
for (int i = 0; i < DISPLAY_COUNT; i++) {
- rotation_resetsMagnification(i);
+ changeDisplaySize_resetsMagnification(i);
resetMockWindowManager();
}
}
- private void rotation_resetsMagnification(int displayId) {
+ private void changeDisplaySize_resetsMagnification(int displayId) {
register(displayId);
MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
assertTrue(mFullScreenMagnificationController.isMagnifying(displayId));
- callbacks.onRotationChanged(0);
+ callbacks.onDisplaySizeChanged();
mMessageCapturingHandler.sendAllMessages();
assertFalse(mFullScreenMagnificationController.isMagnifying(displayId));
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index f881f04e5b07..b14c353397e2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -52,6 +52,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationInfoChangedCallback;
import com.android.server.testutils.OffsettableClock;
@@ -129,6 +130,10 @@ public class FullScreenMagnificationGestureHandlerTest {
MagnificationInfoChangedCallback mMagnificationInfoChangedCallback;
@Mock
WindowMagnificationPromptController mWindowMagnificationPromptController;
+ @Mock
+ AccessibilityManagerService mMockAccessibilityManagerService;
+ @Mock
+ AccessibilityTraceManager mMockTraceManager;
private OffsettableClock mClock;
private FullScreenMagnificationGestureHandler mMgh;
@@ -144,7 +149,9 @@ public class FullScreenMagnificationGestureHandlerTest {
mock(FullScreenMagnificationController.ControllerContext.class);
final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
when(mockController.getContext()).thenReturn(mContext);
- when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+ when(mockController.getAms()).thenReturn(mMockAccessibilityManagerService);
+ when(mMockAccessibilityManagerService.getTraceManager()).thenReturn(mMockTraceManager);
+ when(mockController.getTraceManager()).thenReturn(mMockTraceManager);
when(mockController.getWindowManager()).thenReturn(mockWindowManager);
when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
@@ -179,7 +186,7 @@ public class FullScreenMagnificationGestureHandlerTest {
private FullScreenMagnificationGestureHandler newInstance(boolean detectTripleTap,
boolean detectShortcutTrigger) {
FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler(
- mContext, mFullScreenMagnificationController, mMockCallback,
+ mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
mHandler = new TestHandler(h.mDetectingState, mClock) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index b7f5f4da9d9d..e82adc8b403b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -54,6 +54,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.AccessibilityTraceManager;
import org.junit.After;
import org.junit.Before;
@@ -84,6 +85,8 @@ public class MagnificationControllerTest {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ private AccessibilityTraceManager mTraceManager;
+ @Mock
private AccessibilityManagerService mService;
@Mock
private MagnificationController.TransitionCallBack mTransitionCallBack;
@@ -112,7 +115,7 @@ public class MagnificationControllerTest {
CURRENT_USER_ID);
mWindowMagnificationManager = Mockito.spy(
new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mock(WindowMagnificationManager.Callback.class)));
+ mock(WindowMagnificationManager.Callback.class), mTraceManager));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mScreenMagnificationControllerStubber = new FullScreenMagnificationControllerStubber(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 514d16a0149c..ef6ed88011ef 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -31,6 +31,8 @@ import android.view.MotionEvent;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +51,8 @@ public class MagnificationGestureHandlerTest {
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock
+ AccessibilityTraceManager mTraceManager;
+ @Mock
MagnificationGestureHandler.Callback mCallback;
@Before
@@ -57,6 +61,7 @@ public class MagnificationGestureHandlerTest {
mMgh = new TestMagnificationGestureHandler(DISPLAY_0,
/* detectTripleTap= */true,
/* detectShortcutTrigger= */true,
+ mTraceManager,
mCallback);
}
@@ -129,8 +134,9 @@ public class MagnificationGestureHandlerTest {
boolean mIsInternalMethodCalled = false;
TestMagnificationGestureHandler(int displayId, boolean detectTripleTap,
- boolean detectShortcutTrigger, @NonNull Callback callback) {
- super(displayId, detectTripleTap, detectShortcutTrigger, callback);
+ boolean detectShortcutTrigger, @NonNull AccessibilityTraceManager trace,
+ @NonNull Callback callback) {
+ super(displayId, detectTripleTap, detectShortcutTrigger, trace, callback);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
index c88bc3b2e15b..1638563e8242 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -29,6 +29,8 @@ import android.view.accessibility.IWindowMagnificationConnection;
import android.view.accessibility.IWindowMagnificationConnectionCallback;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -45,6 +47,8 @@ public class WindowMagnificationConnectionWrapperTest {
private IWindowMagnificationConnection mConnection;
@Mock
+ private AccessibilityTraceManager mTrace;
+ @Mock
private IWindowMagnificationConnectionCallback mCallback;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -57,7 +61,7 @@ public class WindowMagnificationConnectionWrapperTest {
MockitoAnnotations.initMocks(this);
mMockWindowMagnificationConnection = new MockWindowMagnificationConnection();
mConnection = mMockWindowMagnificationConnection.getConnection();
- mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection, mTrace);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index b9498d641ed7..6a5aae672881 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -35,6 +35,7 @@ import android.view.ViewConfiguration;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.accessibility.utils.TouchEventGenerator;
@@ -74,16 +75,18 @@ public class WindowMagnificationGestureHandlerTest {
private WindowMagnificationGestureHandler mWindowMagnificationGestureHandler;
@Mock
MagnificationGestureHandler.Callback mMockCallback;
+ @Mock
+ AccessibilityTraceManager mMockTrace;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, 0,
- mock(WindowMagnificationManager.Callback.class));
+ mock(WindowMagnificationManager.Callback.class), mMockTrace);
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler(
- mContext, mWindowMagnificationManager, mMockCallback,
+ mContext, mWindowMagnificationManager, mMockTrace, mMockCallback,
/** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a20272ab438d..af6d40f2fdf2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -52,6 +52,7 @@ import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
+import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -72,6 +73,8 @@ public class WindowMagnificationManagerTest {
@Mock
private Context mContext;
@Mock
+ private AccessibilityTraceManager mMockTrace;
+ @Mock
private StatusBarManagerInternal mMockStatusBarManagerInternal;
@Mock
private MagnificationAnimationCallback mAnimationCallback;
@@ -88,7 +91,7 @@ public class WindowMagnificationManagerTest {
mResolver = new MockContentResolver();
mMockConnection = new MockWindowMagnificationConnection();
mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID,
- mMockCallback);
+ mMockCallback, mMockTrace);
when(mContext.getContentResolver()).thenReturn(mResolver);
doAnswer((InvocationOnMock invocation) -> {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index b580eae75144..850f8818b516 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -55,6 +55,7 @@ import android.server.wm.settings.SettingsSession;
import android.support.test.uiautomator.UiDevice;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
+import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Pair;
@@ -99,6 +100,8 @@ public class ActivityManagerTest {
private static final String ACTION_FGS_STATS_TEST =
"com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
private static final String EXTRA_MESSENGER = "extra_messenger";
+ private static final String ACTION_RECEIVER_TEST =
+ "com.android.servicestests.apps.simpleservicetestapp.TEST";
private static final String EXTRA_CALLBACK = "callback";
private static final String EXTRA_COMMAND = "command";
@@ -501,6 +504,205 @@ public class ActivityManagerTest {
return false;
}
+ @LargeTest
+ @Test
+ public void testKillAppIfBgRestrictedCachedIdle() throws Exception {
+ final long shortTimeoutMs = 5_000;
+ final long backgroundSettleMs = 10_000;
+ final PackageManager pm = mContext.getPackageManager();
+ final int uid = pm.getPackageUid(TEST_APP1, 0);
+ final MyUidImportanceListener uidListener1 = new MyUidImportanceListener(uid);
+ final MyUidImportanceListener uidListener2 = new MyUidImportanceListener(uid);
+ final MyUidImportanceListener uidListener3 = new MyUidImportanceListener(uid);
+ SettingsSession<String> amConstantsSettings = null;
+ DeviceConfigSession<Boolean> killBgRestrictedAndCachedIdle = null;
+ DeviceConfigSession<Long> killBgRestrictedAndCachedIdleSettleTime = null;
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final H handler = new H(Looper.getMainLooper(), latchHolder);
+ final Messenger messenger = new Messenger(handler);
+ try {
+ am.addOnUidImportanceListener(uidListener1,
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ am.addOnUidImportanceListener(uidListener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ am.addOnUidImportanceListener(uidListener3, RunningAppProcessInfo.IMPORTANCE_CACHED);
+ toggleScreenOn(true);
+
+ killBgRestrictedAndCachedIdle = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityManagerConstants.KEY_KILL_BG_RESTRICTED_CACHED_IDLE,
+ DeviceConfig::getBoolean,
+ ActivityManagerConstants.DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE);
+ killBgRestrictedAndCachedIdle.set(true);
+ killBgRestrictedAndCachedIdleSettleTime = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityManagerConstants.KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME,
+ DeviceConfig::getLong,
+ ActivityManagerConstants.DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS);
+ killBgRestrictedAndCachedIdleSettleTime.set(backgroundSettleMs);
+ amConstantsSettings = new SettingsSession<>(
+ Settings.Global.getUriFor(Settings.Global.ACTIVITY_MANAGER_CONSTANTS),
+ Settings.Global::getString, Settings.Global::putString);
+ final KeyValueListParser parser = new KeyValueListParser(',');
+ long currentBackgroundSettleMs =
+ ActivityManagerConstants.DEFAULT_BACKGROUND_SETTLE_TIME;
+ try {
+ parser.setString(amConstantsSettings.get());
+ currentBackgroundSettleMs = parser.getLong(
+ ActivityManagerConstants.KEY_BACKGROUND_SETTLE_TIME,
+ ActivityManagerConstants.DEFAULT_BACKGROUND_SETTLE_TIME);
+ } catch (IllegalArgumentException e) {
+ }
+ // Drain queue to make sure the existing UID_IDLE_MSG has been processed.
+ Thread.sleep(currentBackgroundSettleMs);
+ amConstantsSettings.set(
+ ActivityManagerConstants.KEY_BACKGROUND_SETTLE_TIME + "=" + backgroundSettleMs);
+
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND allow");
+
+ final Intent intent = new Intent(ACTION_FGS_STATS_TEST);
+ final ComponentName cn = ComponentName.unflattenFromString(
+ TEST_APP1 + "/" + TEST_FGS_CLASS);
+ final Bundle bundle = new Bundle();
+ intent.setComponent(cn);
+ bundle.putBinder(EXTRA_MESSENGER, messenger.getBinder());
+ intent.putExtras(bundle);
+
+ // Start the FGS.
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Stop the FGS, it shouldn't be killed because it's not in FAS state.
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_SERVICE, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Set the FAS state.
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND deny");
+ // Now it should've been killed.
+ assertTrue("Should have been killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Start the FGS.
+ // Temporarily allow RUN_ANY_IN_BACKGROUND to start FGS.
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND allow");
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND deny");
+ // It shouldn't be killed since it's not cached.
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Stop the FGS, it should get killed because it's cached & uid idle & in FAS state.
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_SERVICE, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ assertTrue("Should have been killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Start the FGS.
+ // Temporarily allow RUN_ANY_IN_BACKGROUND to start FGS.
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND allow");
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND deny");
+ // It shouldn't be killed since it's not cached.
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Stop the FGS.
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_SERVICE, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ assertTrue("Should have been in cached state", uidListener3.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_CACHED, shortTimeoutMs));
+ final long now = SystemClock.uptimeMillis();
+ // Sleep a while to let the UID idle ticking.
+ Thread.sleep(shortTimeoutMs);
+ // Now send a broadcast, it should bring the app out of cached for a while.
+ final Intent intent2 = new Intent(ACTION_RECEIVER_TEST);
+ final Bundle extras = new Bundle();
+ final IRemoteCallback callback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ latchHolder[0].countDown();
+ }
+ };
+ extras.putBinder(EXTRA_CALLBACK, callback.asBinder());
+ intent2.putExtras(extras);
+ intent2.setPackage(TEST_APP1);
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.sendBroadcast(intent2);
+ assertTrue("Timed out to wait for receiving broadcast", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ // Try to wait for the killing, it should be killed after backgroundSettleMs
+ // since receiving the broadcast
+ assertFalse("Shouldn't be killed now", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE,
+ backgroundSettleMs + now - SystemClock.uptimeMillis() + 2_000));
+ // Now wait a bit longer, it should be killed.
+ assertTrue("Should have been killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Disable this FAS cached idle kill feature.
+ killBgRestrictedAndCachedIdle.set(false);
+
+ // Start the FGS.
+ // Temporarily allow RUN_ANY_IN_BACKGROUND to start FGS.
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND allow");
+ latchHolder[0] = new CountDownLatch(1);
+ mContext.startForegroundService(intent);
+ assertTrue("Timed out to start fg service", uidListener1.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE, shortTimeoutMs));
+ assertTrue("Timed out to get the remote messenger", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND deny");
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+
+ // Stop the FGS, it shouldn't be killed because the feature has been turned off.
+ latchHolder[0] = new CountDownLatch(1);
+ handler.sendRemoteMessage(H.MSG_STOP_SERVICE, 0, 0, null);
+ assertTrue("Timed out to wait for stop fg", latchHolder[0].await(
+ shortTimeoutMs, TimeUnit.MILLISECONDS));
+ assertFalse("FGS shouldn't be killed", uidListener2.waitFor(
+ RunningAppProcessInfo.IMPORTANCE_GONE, backgroundSettleMs + shortTimeoutMs));
+ } finally {
+ runShellCommand("cmd appops set " + TEST_APP1 + " RUN_ANY_IN_BACKGROUND default");
+ if (amConstantsSettings != null) {
+ amConstantsSettings.close();
+ }
+ if (killBgRestrictedAndCachedIdle != null) {
+ killBgRestrictedAndCachedIdle.close();
+ }
+ if (killBgRestrictedAndCachedIdleSettleTime != null) {
+ killBgRestrictedAndCachedIdleSettleTime.close();
+ }
+ am.removeOnUidImportanceListener(uidListener1);
+ am.removeOnUidImportanceListener(uidListener2);
+ am.removeOnUidImportanceListener(uidListener3);
+ }
+ }
+
@Ignore("Need to disable calling uid check in ActivityManagerService.killPids before this test")
@Test
public void testKillPids() throws Exception {
@@ -717,6 +919,7 @@ public class ActivityManagerTest {
static final int MSG_DONE = 1;
static final int MSG_START_FOREGROUND = 2;
static final int MSG_STOP_FOREGROUND = 3;
+ static final int MSG_STOP_SERVICE = 4;
private Messenger mRemoteMessenger;
private CountDownLatch[] mLatchHolder;
diff --git a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
index 10f4c05eb6d8..9dd413bcdbab 100644
--- a/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ServiceRestarterTest.java
@@ -16,6 +16,10 @@
package com.android.server.am;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -28,6 +32,9 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.server.wm.settings.SettingsSession;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
@@ -36,6 +43,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,6 +67,12 @@ public final class ServiceRestarterTest {
"com.android.servicestests.apps.simpleservicetestapp3";
private static final String TEST_SERVICE_NAME =
"com.android.servicestests.apps.simpleservicetestapp.SimpleService";
+ private static final String[] MEMORY_PRESSURE_MAP = {
+ "NORMAL", "MODERATE", "LOW", "CRITICAL"};
+ private static final String EXTRA_DELAY = "0,2000,4000,8000";
+ private static final int SERVICE_RESTART_DURATION_FACTOR = 2;
+ private static final long BOUND_SERVICE_CRASH_RESTART_DURATION = 1000;
+ private static final long SERVICE_MIN_RESTART_TIME_BETWEEN = 1000;
private static final long WAIT_MS = 5 * 1000;
private static final long WAIT_LONG_MS = 30 * 1000;
@@ -69,6 +83,10 @@ public final class ServiceRestarterTest {
private static final int ACTION_STOPPKG = 8;
private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
+ private SettingsSession<String> mAMConstantsSettings;
+ private DeviceConfigSession<String> mExtraDelaysDeviceConfig;
+ private DeviceConfigSession<Boolean> mEnableExtraDelaysDeviceConfig;
+
private Context mContext;
private Instrumentation mInstrumentation;
private int mTestPackage1Uid;
@@ -85,6 +103,40 @@ public final class ServiceRestarterTest {
mTestPackage2Uid = ai.uid;
ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE3_NAME, 0);
mTestPackage3Uid = ai.uid;
+ final String activityManagerConstants = Settings.Global.ACTIVITY_MANAGER_CONSTANTS;
+ mAMConstantsSettings = new SettingsSession<>(
+ Settings.Global.getUriFor(activityManagerConstants),
+ Settings.Global::getString, Settings.Global::putString);
+ mAMConstantsSettings.set(
+ ActivityManagerConstants.KEY_SERVICE_RESTART_DURATION_FACTOR + "="
+ + SERVICE_RESTART_DURATION_FACTOR + ","
+ + ActivityManagerConstants.KEY_BOUND_SERVICE_CRASH_RESTART_DURATION + "="
+ + BOUND_SERVICE_CRASH_RESTART_DURATION + ","
+ + ActivityManagerConstants.KEY_SERVICE_MIN_RESTART_TIME_BETWEEN + "="
+ + SERVICE_MIN_RESTART_TIME_BETWEEN);
+ mExtraDelaysDeviceConfig = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityManagerConstants.KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
+ DeviceConfig::getString, null);
+ mExtraDelaysDeviceConfig.set(EXTRA_DELAY);
+ mEnableExtraDelaysDeviceConfig = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ ActivityManagerConstants.KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
+ DeviceConfig::getBoolean, false);
+ mEnableExtraDelaysDeviceConfig.set(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mAMConstantsSettings != null) {
+ mAMConstantsSettings.close();
+ }
+ if (mExtraDelaysDeviceConfig != null) {
+ mExtraDelaysDeviceConfig.close();
+ }
+ if (mEnableExtraDelaysDeviceConfig != null) {
+ mEnableExtraDelaysDeviceConfig.close();
+ }
}
@LargeTest
@@ -113,7 +165,7 @@ public final class ServiceRestarterTest {
// Test restarts in normal case
final long[] ts1 = startKillAndRestart(am, ACTION_START | ACTION_KILL | ACTION_WAIT,
uid1Listener1, uid1Listener2, uid2Listener1, uid2Listener2,
- uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ uid3Listener1, uid3Listener2, Long.MAX_VALUE, false);
assertTrue("app1 restart should be before app2", ts1[1] < ts1[2]);
assertTrue("app2 restart should be before app3", ts1[2] < ts1[3]);
@@ -122,7 +174,7 @@ public final class ServiceRestarterTest {
// Test restarts again.
final long[] ts2 = startKillAndRestart(am, ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG,
uid1Listener1, uid1Listener2, uid2Listener1,
- uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE, false);
assertTrue("app2 restart should be before app1", ts2[2] < ts2[1]);
assertTrue("app1 restart should be before app3", ts2[1] < ts2[3]);
assertTrue("app2 should be restart in a very short moment", ts2[2] - ts2[0] < WAIT_MS);
@@ -131,7 +183,8 @@ public final class ServiceRestarterTest {
executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
// Test restarts again.
final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
- uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE);
+ uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE,
+ false);
assertTrue("app1 restart should be before app2", ts3[1] < ts3[2]);
assertTrue("app2 restart should be before app3", ts3[2] < ts3[3]);
@@ -152,11 +205,101 @@ public final class ServiceRestarterTest {
}
}
+ @LargeTest
+ @Test
+ public void testExtraDelaysInServiceRestartOnLowMem() throws Exception {
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(mTestPackage1Uid);
+ final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(mTestPackage1Uid);
+ final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(mTestPackage2Uid);
+ final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(mTestPackage2Uid);
+ final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(mTestPackage3Uid);
+ final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(mTestPackage3Uid);
+ try {
+ am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
+ am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE1_NAME);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE2_NAME);
+ executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE3_NAME);
+
+ // Force the memory pressure to normal.
+ setMemoryPressure(ADJ_MEM_FACTOR_NORMAL);
+
+ // Test restarts in normal condition.
+ final long[] ts1 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
+ uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
+
+ // Mimic a memory pressure event.
+ setMemoryPressure(ADJ_MEM_FACTOR_MODERATE);
+
+ final long[] ts2 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
+ uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
+
+ assertTrue("Expect extra delays in service restarts",
+ (ts2[1] - ts2[0]) > (ts1[1] - ts1[0]));
+ assertTrue("Expect extra delays in service restarts",
+ (ts2[2] - ts2[0]) > (ts1[2] - ts1[0]));
+ assertTrue("Expect extra delays in service restarts",
+ (ts2[3] - ts2[0]) > (ts1[3] - ts1[0]));
+
+ // Increase the memory pressure event.
+ setMemoryPressure(ADJ_MEM_FACTOR_LOW);
+
+ final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
+ uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
+
+ assertTrue("Expect extra delays in service restarts",
+ (ts3[1] - ts3[0]) > (ts2[1] - ts2[0]));
+ assertTrue("Expect extra delays in service restarts",
+ (ts3[2] - ts3[0]) > (ts2[2] - ts2[0]));
+ assertTrue("Expect extra delays in service restarts",
+ (ts3[3] - ts3[0]) > (ts2[3] - ts2[0]));
+
+ // Start and kill them again, but don't wait for the restart.
+ final long now4 = startKillAndRestart(am, ACTION_START | ACTION_KILL, uid1Listener1,
+ uid1Listener2, uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2,
+ WAIT_LONG_MS, true)[0];
+
+ // Set the memory pressure to normal.
+ setMemoryPressure(ADJ_MEM_FACTOR_NORMAL);
+
+ // Now wait for the restarts.
+ final long[] ts4 = startKillAndRestart(am, ACTION_WAIT | ACTION_STOPPKG, uid1Listener1,
+ uid1Listener2, uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2,
+ WAIT_LONG_MS, true);
+
+ assertTrue("Expect less delays in service restarts",
+ (ts4[1] - now4) < (ts2[1] - ts2[0]));
+ assertTrue("Expect less delays in service restarts",
+ (ts4[2] - now4) < (ts2[2] - ts2[0]));
+ assertTrue("Expect less delays in service restarts",
+ (ts4[3] - now4) < (ts2[3] - ts2[0]));
+ } finally {
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE1_NAME);
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE2_NAME);
+ executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE3_NAME);
+ resetMemoryPressure();
+ am.removeOnUidImportanceListener(uid1Listener1);
+ am.removeOnUidImportanceListener(uid1Listener2);
+ am.removeOnUidImportanceListener(uid2Listener1);
+ am.removeOnUidImportanceListener(uid2Listener2);
+ am.removeOnUidImportanceListener(uid3Listener1);
+ am.removeOnUidImportanceListener(uid3Listener2);
+ am.forceStopPackage(TEST_PACKAGE1_NAME);
+ am.forceStopPackage(TEST_PACKAGE2_NAME);
+ am.forceStopPackage(TEST_PACKAGE3_NAME);
+ }
+ }
+
private long[] startKillAndRestart(ActivityManager am, int action,
MyUidImportanceListener uid1Listener1, MyUidImportanceListener uid1Listener2,
MyUidImportanceListener uid2Listener1, MyUidImportanceListener uid2Listener2,
MyUidImportanceListener uid3Listener1, MyUidImportanceListener uid3Listener2,
- long waitDuration) throws Exception {
+ long waitDuration, boolean checkStartSeq) throws Exception {
final long[] res = new long[4];
// Test restarts in normal condition.
if ((action & ACTION_START) != 0) {
@@ -179,9 +322,15 @@ public final class ServiceRestarterTest {
RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
assertTrue("Timed out to restart " + TEST_PACKAGE3_NAME, uid3Listener1.waitFor(
RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
- res[1] = uid1Listener1.mCurrentTimestamp;
- res[2] = uid2Listener1.mCurrentTimestamp;
- res[3] = uid3Listener1.mCurrentTimestamp;
+ final long uid1RestartTime = res[1] = uid1Listener1.mCurrentTimestamp;
+ final long uid2RestartTime = res[2] = uid2Listener1.mCurrentTimestamp;
+ final long uid3RestartTime = res[3] = uid3Listener1.mCurrentTimestamp;
+ if (checkStartSeq) {
+ assertTrue(TEST_PACKAGE1_NAME + " should restart before " + TEST_PACKAGE2_NAME,
+ uid1RestartTime <= uid2RestartTime);
+ assertTrue(TEST_PACKAGE2_NAME + " should restart before " + TEST_PACKAGE3_NAME,
+ uid2RestartTime <= uid3RestartTime);
+ }
}
if ((action & ACTION_STOPPKG) != 0) {
@@ -224,6 +373,14 @@ public final class ServiceRestarterTest {
return result;
}
+ private void setMemoryPressure(int pressure) throws Exception {
+ executeShellCmd("am memory-factor set " + MEMORY_PRESSURE_MAP[pressure]);
+ }
+
+ private void resetMemoryPressure() throws Exception {
+ executeShellCmd("am memory-factor reset");
+ }
+
private static class MyUidImportanceListener implements OnUidImportanceListener {
final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
private final int mExpectedUid;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 54945fbc2dd2..c0ba3c72beb4 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -75,7 +75,7 @@ public class AudioServiceTest {
for (boolean muted : new boolean[] { true, false}) {
testAudioSystem.configureIsMicrophoneMuted(!muted);
mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), null);
Assert.assertEquals("mic mute reporting wrong value",
muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
@@ -100,7 +100,7 @@ public class AudioServiceTest {
for (boolean muted : new boolean[] { true, false}) {
testAudioSystem.configureIsMicrophoneMuted(!muted);
mAudioService.setMicrophoneMute(muted, mContext.getOpPackageName(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), null);
Assert.assertEquals("mic mute reporting wrong value",
!muted, mAudioService.isMicrophoneMuted());
// verify the intent for mic mute changed is supposed to be fired
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
index 7323096121c4..7aea65e78616 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupEligibilityRulesTest.java
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager.OperationType;
@@ -30,8 +29,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.Property;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
@@ -41,7 +40,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.UserBackupManagerService;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -540,9 +538,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -558,9 +556,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -635,9 +633,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -656,9 +654,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -677,9 +675,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {signature1Copy, signature2Copy},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -698,9 +696,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -719,9 +717,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -743,9 +741,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -770,9 +768,9 @@ public class BackupEligibilityRulesTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
new Signature[] {SIGNATURE_1, SIGNATURE_2}));
packageInfo.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5fcce671db1e..e2536f86b0e2 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -39,8 +39,8 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.os.Bundle;
import android.os.Process;
@@ -376,9 +376,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -413,9 +413,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
packageInfo.applicationInfo.backupAgentName = "backup.agent";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -451,9 +451,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
PackageManagerStub.sPackageInfo = packageInfo;
@@ -492,9 +492,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 2;
@@ -536,9 +536,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
@@ -576,9 +576,9 @@ public class TarBackupReaderTest {
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
packageInfo.applicationInfo.backupAgentName = null;
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {FAKE_SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.versionCode = 1;
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 5ba375b922e2..fcfe27397df6 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -67,6 +67,8 @@ import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.collect.ImmutableMap;
+
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -82,6 +84,7 @@ import org.mockito.MockitoAnnotations;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.LongStream;
@@ -223,7 +226,8 @@ public class DisplayManagerServiceTest {
break;
}
case DisplayViewport.VIEWPORT_EXTERNAL: {
- fail("EXTERNAL viewport should not exist.");
+ // External view port is present for auto devices in the form of instrument
+ // cluster.
break;
}
case DisplayViewport.VIEWPORT_VIRTUAL: {
@@ -259,9 +263,14 @@ public class DisplayManagerServiceTest {
final int displayIds[] = bs.getDisplayIds();
final int size = displayIds.length;
assertTrue(size > 0);
+
+ Map<Integer, Integer> expectedDisplayTypeToViewPortTypeMapping = ImmutableMap.of(
+ Display.TYPE_INTERNAL, DisplayViewport.VIEWPORT_INTERNAL,
+ Display.TYPE_EXTERNAL, DisplayViewport.VIEWPORT_EXTERNAL
+ );
for (int i = 0; i < size; i++) {
DisplayInfo info = bs.getDisplayInfo(displayIds[i]);
- assertEquals(info.type, Display.TYPE_INTERNAL);
+ assertTrue(expectedDisplayTypeToViewPortTypeMapping.keySet().contains(info.type));
}
displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
@@ -281,13 +290,13 @@ public class DisplayManagerServiceTest {
// 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);
+ DisplayViewport viewport = viewports.get(i);
+ assertNotNull(viewport);
+ DisplayInfo displayInfo = bs.getDisplayInfo(viewport.displayId);
+ assertTrue(expectedDisplayTypeToViewPortTypeMapping.get(displayInfo.type)
+ == viewport.type);
+ assertTrue(viewport.valid);
+ assertTrue(Arrays.binarySearch(displayIds, viewport.displayId) >= 0);
}
}
@@ -885,6 +894,61 @@ public class DisplayManagerServiceTest {
assertFalse(callback.mDisplayAddedCalled);
}
+
+
+ @Test
+ public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() {
+ Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ // get the first two internal displays
+ Display[] displays = displayManager.getDisplays();
+ Display internalDisplayOne = null;
+ Display internalDisplayTwo = null;
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_INTERNAL) {
+ if (internalDisplayOne == null) {
+ internalDisplayOne = display;
+ } else {
+ internalDisplayTwo = display;
+ break;
+ }
+ }
+ }
+
+ // return if there are fewer than 2 displays on this device
+ if (internalDisplayOne == null || internalDisplayTwo == null) {
+ return;
+ }
+
+ final String uniqueDisplayIdOne = internalDisplayOne.getUniqueId();
+ final String uniqueDisplayIdTwo = internalDisplayTwo.getUniqueId();
+
+ BrightnessConfiguration configOne =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 12345.0f}, new float[]{15.0f, 400.0f})
+ .setDescription("model:1").build();
+ BrightnessConfiguration configTwo =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 6789.0f}, new float[]{12.0f, 300.0f})
+ .setDescription("model:2").build();
+
+ displayManager.setBrightnessConfigurationForDisplay(configOne,
+ uniqueDisplayIdOne);
+ displayManager.setBrightnessConfigurationForDisplay(configTwo,
+ uniqueDisplayIdTwo);
+
+ BrightnessConfiguration configFromOne =
+ displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdOne);
+ BrightnessConfiguration configFromTwo =
+ displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdTwo);
+
+ assertNotNull(configFromOne);
+ assertEquals(configOne, configFromOne);
+ assertEquals(configTwo, configFromTwo);
+
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
throws Exception {
DisplayManagerService displayManager =
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 196454bd32ce..57a9cb278c80 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -17,13 +17,16 @@
package com.android.server.display;
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.content.Context;
import android.hardware.display.BrightnessConfiguration;
import android.util.Pair;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -144,15 +147,93 @@ public class PersistentDataStoreTest {
}
@Test
+ public void testStoreAndReloadOfDisplayBrightnessConfigurations() {
+ final String uniqueDisplayId = "test:123";
+ int userSerial = 0;
+ String packageName = "pdsTestPackage";
+ final float[] lux = { 0f, 10f };
+ final float[] nits = {1f, 100f };
+ final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
+ .setDescription("a description")
+ .build();
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+ userSerial));
+
+ DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return true;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ mDataStore.setBrightnessConfigurationForDisplayLocked(config, testDisplayDevice, userSerial,
+ packageName);
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ mInjector.setWriteStream(baos);
+ mDataStore.saveIfNeeded();
+ assertTrue(mInjector.wasWriteSuccessful());
+ TestInjector newInjector = new TestInjector();
+ PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ newInjector.setReadStream(bais);
+ newDataStore.loadIfNeeded();
+ assertNotNull(newDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+ userSerial));
+ assertEquals(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+ userSerial), newDataStore.getBrightnessConfigurationForDisplayLocked(
+ uniqueDisplayId, userSerial));
+ }
+
+ @Test
+ public void testSetBrightnessConfigurationFailsWithUnstableId() {
+ final String uniqueDisplayId = "test:123";
+ int userSerial = 0;
+ String packageName = "pdsTestPackage";
+ final float[] lux = { 0f, 10f };
+ final float[] nits = {1f, 100f };
+ final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
+ .setDescription("a description")
+ .build();
+ mDataStore.loadIfNeeded();
+ assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+ userSerial));
+
+ DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+ @Override
+ public boolean hasStableUniqueId() {
+ return false;
+ }
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ return null;
+ }
+ };
+
+ assertFalse(mDataStore.setBrightnessConfigurationForDisplayLocked(
+ config, testDisplayDevice, userSerial, packageName));
+ }
+
+ @Test
public void testStoreAndReloadOfBrightnessConfigurations() {
final float[] lux = { 0f, 10f };
final float[] nits = {1f, 100f };
final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
.setDescription("a description")
.build();
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ String packageName = context.getPackageName();
+
mDataStore.loadIfNeeded();
assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
- mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
+ mDataStore.setBrightnessConfigurationForUser(config, 0, packageName);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
mInjector.setWriteStream(baos);
@@ -173,17 +254,18 @@ public class PersistentDataStoreTest {
public void testNullBrightnessConfiguration() {
final float[] lux = { 0f, 10f };
final float[] nits = {1f, 100f };
+ int userSerial = 0;
final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
.setDescription("a description")
.build();
mDataStore.loadIfNeeded();
- assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ assertNull(mDataStore.getBrightnessConfiguration(userSerial));
- mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
- assertNotNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ mDataStore.setBrightnessConfigurationForUser(config, userSerial, "packagename");
+ assertNotNull(mDataStore.getBrightnessConfiguration(userSerial));
- mDataStore.setBrightnessConfigurationForUser(null, 0, "packagename");
- assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+ mDataStore.setBrightnessConfigurationForUser(null, userSerial, "packagename");
+ assertNull(mDataStore.getBrightnessConfiguration(userSerial));
}
public class TestInjector extends PersistentDataStore.Injector {
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index a19b3872949e..363c26b63bae 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -53,9 +52,7 @@ import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -76,25 +73,29 @@ public class ColorDisplayServiceTest {
private int mUserId;
private MockTwilightManager mTwilightManager;
+ private DisplayTransformManager mDisplayTransformManager;
private ColorDisplayService mCds;
private ColorDisplayService.BinderService mBinderService;
private Resources mResourcesSpy;
- @BeforeClass
- public static void setDtm() {
- final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
- LocalServices.addService(DisplayTransformManager.class, dtm);
- }
+ private static final int[] MINIMAL_COLOR_MODES = new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ ColorDisplayManager.COLOR_MODE_BOOSTED,
+ };
@Before
public void setUp() {
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
doReturn(mContext).when(mContext).getApplicationContext();
- mResourcesSpy = Mockito.spy(mContext.getResources());
- when(mContext.getResources()).thenReturn(mResourcesSpy);
+ final Resources res = Mockito.spy(mContext.getResources());
+ doReturn(MINIMAL_COLOR_MODES).when(res).getIntArray(R.array.config_availableColorModes);
+ doReturn(true).when(res).getBoolean(R.bool.config_nightDisplayAvailable);
+ doReturn(true).when(res).getBoolean(R.bool.config_displayWhiteBalanceAvailable);
+ when(mContext.getResources()).thenReturn(res);
+ mResourcesSpy = res;
mUserId = ActivityManager.getCurrentUser();
@@ -108,6 +109,10 @@ public class ColorDisplayServiceTest {
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
+ mDisplayTransformManager = Mockito.mock(DisplayTransformManager.class);
+ doReturn(true).when(mDisplayTransformManager).needsLinearColorMatrix();
+ LocalServices.addService(DisplayTransformManager.class, mDisplayTransformManager);
+
mCds = new ColorDisplayService(mContext);
mBinderService = mCds.new BinderService();
LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
@@ -116,12 +121,18 @@ public class ColorDisplayServiceTest {
@After
public void tearDown() {
- LocalServices.removeServiceForTest(TwilightManager.class);
-
+ /*
+ * Wait for internal {@link Handler} to finish processing pending messages, so that test
+ * code can safelyremove {@link DisplayTransformManager} mock from {@link LocalServices}.
+ */
+ mCds.mHandler.runWithScissors(() -> { /* nop */ }, /* timeout */ 1000);
mCds = null;
+ LocalServices.removeServiceForTest(TwilightManager.class);
mTwilightManager = null;
+ LocalServices.removeServiceForTest(DisplayTransformManager.class);
+
mUserId = UserHandle.USER_NULL;
mContext = null;
@@ -130,11 +141,6 @@ public class ColorDisplayServiceTest {
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
- @AfterClass
- public static void removeDtm() {
- LocalServices.removeServiceForTest(DisplayTransformManager.class);
- }
-
@Test
public void customSchedule_whenStartedAfterNight_ifOffAfterNight_turnsOff() {
setAutoModeCustom(-120 /* startTimeOffset */, -60 /* endTimeOffset */);
@@ -1064,24 +1070,18 @@ public class ColorDisplayServiceTest {
@Test
public void compositionColorSpaces_noResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {});
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
.thenReturn(new int[] {});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_invalidResources() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL,
@@ -1094,15 +1094,12 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_INVALID));
}
@Test
public void compositionColorSpaces_validResources_validColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1113,15 +1110,12 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(),
- eq(Display.COLOR_MODE_SRGB));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), eq(Display.COLOR_MODE_SRGB));
}
@Test
public void compositionColorSpaces_validResources_invalidColorMode() {
- final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
- reset(dtm);
-
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
.thenReturn(new int[] {
ColorDisplayManager.COLOR_MODE_NATURAL
@@ -1132,8 +1126,8 @@ public class ColorDisplayServiceTest {
});
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
- verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(),
- eq(Display.COLOR_MODE_INVALID));
+ verify(mDisplayTransformManager).setColorMode(
+ eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), eq(Display.COLOR_MODE_INVALID));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index 6cc8d471f0f5..ac2756beff75 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -22,16 +22,11 @@ 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.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;
@@ -41,10 +36,9 @@ 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.Collections;
/** Tests for {@link ActiveSourceAction} */
@SmallTest
@@ -54,29 +48,17 @@ public class ActiveSourceActionTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mPhysicalAddress;
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -89,21 +71,11 @@ public class ActiveSourceActionTest {
}
@Override
- void wakeUp() {
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -119,6 +91,8 @@ public class ActiveSourceActionTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
@@ -138,10 +112,14 @@ public class ActiveSourceActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage menuStatus = HdmiCecMessageBuilder.buildReportMenuStatus(
- playbackDevice.mAddress, ADDR_TV, Constants.MENU_STATE_ACTIVATED);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage menuStatus =
+ HdmiCecMessageBuilder.buildReportMenuStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ Constants.MENU_STATE_ACTIVATED);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(menuStatus);
@@ -161,8 +139,8 @@ public class ActiveSourceActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- assertThat(playbackDevice.getActiveSource().logicalAddress).isEqualTo(
- playbackDevice.mAddress);
+ assertThat(playbackDevice.getActiveSource().logicalAddress)
+ .isEqualTo(playbackDevice.getDeviceInfo().getLogicalAddress());
assertThat(playbackDevice.getActiveSource().physicalAddress).isEqualTo(mPhysicalAddress);
assertThat(playbackDevice.isActiveSource()).isTrue();
}
@@ -181,10 +159,14 @@ public class ActiveSourceActionTest {
audioDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(audioDevice.mAddress,
- mPhysicalAddress);
- HdmiCecMessage menuStatus = HdmiCecMessageBuilder.buildReportMenuStatus(
- audioDevice.mAddress, ADDR_TV, Constants.MENU_STATE_ACTIVATED);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ audioDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage menuStatus =
+ HdmiCecMessageBuilder.buildReportMenuStatus(
+ audioDevice.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ Constants.MENU_STATE_ACTIVATED);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(menuStatus);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 97bd066be543..4ff7c6694aae 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -20,17 +20,12 @@ 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.tv.cec.V1_0.SendMessageResult;
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 android.platform.test.annotations.Presubmit;
@@ -45,6 +40,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcInitiationActionFromAvrTest} */
@SmallTest
@@ -55,14 +51,14 @@ public class ArcInitiationActionFromAvrTest {
private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private ArcInitiationActionFromAvr mAction;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
- @Mock private AudioManager mAudioManager;
+ @Mock
+ private AudioManager mAudioManager;
@Before
public void setUp() throws Exception {
@@ -70,32 +66,14 @@ public class ArcInitiationActionFromAvrTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
boolean isPowerStandby() {
return false;
}
@Override
- void wakeUp() {
- }
-
- @Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
- @Override
AudioManager getAudioManager() {
return mAudioManager;
}
@@ -132,6 +110,8 @@ public class ArcInitiationActionFromAvrTest {
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
hdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ hdmiControlService.setPowerManager(mPowerManager);
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 29c9b407d609..c6bb9144b983 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -20,17 +20,12 @@ 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.tv.cec.V1_0.SendMessageResult;
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 android.platform.test.annotations.Presubmit;
@@ -45,6 +40,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link ArcTerminationActionFromAvr} */
@SmallTest
@@ -54,16 +50,15 @@ public class ArcTerminationActionFromAvrTest {
private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private FakePowerManagerWrapper mPowerManager;
private ArcTerminationActionFromAvr mAction;
private FakeNativeWrapper mNativeWrapper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
-
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
- @Mock private AudioManager mAudioManager;
+ @Mock
+ private AudioManager mAudioManager;
@Before
public void setUp() throws Exception {
@@ -71,26 +66,8 @@ public class ArcTerminationActionFromAvrTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
HdmiControlService hdmiControlService =
- new HdmiControlService(mContextSpy) {
- @Override
- void wakeUp() {
- }
-
- @Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
+ new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return mAudioManager;
@@ -126,7 +103,8 @@ public class ArcTerminationActionFromAvrTest {
hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
hdmiControlService.initService();
-
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ hdmiControlService.setPowerManager(mPowerManager);
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
protected void setPreferredAddress(int addr) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 8b23be511ac0..9c99240628a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -34,6 +34,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
@SmallTest
@Presubmit
@@ -53,7 +55,8 @@ public class DetectTvSystemAudioModeSupportActionTest {
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
HdmiControlService hdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void sendCecCommand(
@@ -70,7 +73,9 @@ public class DetectTvSystemAudioModeSupportActionTest {
mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
HdmiCecMessageBuilder.buildFeatureAbortCommand(
Constants.ADDR_TV,
- mHdmiCecLocalDeviceAudioSystem.mAddress,
+ mHdmiCecLocalDeviceAudioSystem
+ .getDeviceInfo()
+ .getLogicalAddress(),
Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
Constants.ABORT_UNRECOGNIZED_OPCODE));
}
@@ -103,6 +108,7 @@ public class DetectTvSystemAudioModeSupportActionTest {
}
};
mHdmiCecLocalDeviceAudioSystem.init();
+ mHdmiCecLocalDeviceAudioSystem.setDeviceInfo(mDeviceInfoForTests);
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
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 650ffe9abd4b..29eb0da23e71 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -24,7 +24,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
@@ -33,11 +32,7 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
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;
@@ -51,6 +46,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
/** Tests for {@link DevicePowerStatusAction} */
@SmallTest
@@ -63,6 +59,7 @@ public class DevicePowerStatusActionTest {
private HdmiControlService mHdmiControlService;
private HdmiCecLocalDevice mPlaybackDevice;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@@ -70,9 +67,8 @@ public class DevicePowerStatusActionTest {
private DevicePowerStatusAction mDevicePowerStatusAction;
- @Mock private IHdmiControlCallback mCallbackMock;
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
+ @Mock
+ private IHdmiControlCallback mCallbackMock;
@Before
public void setUp() throws Exception {
@@ -80,15 +76,7 @@ public class DevicePowerStatusActionTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -101,21 +89,11 @@ public class DevicePowerStatusActionTest {
}
@Override
- void wakeUp() {
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -131,6 +109,8 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mPlaybackDevice = new HdmiCecLocalDevicePlayback(
@@ -149,12 +129,16 @@ public class DevicePowerStatusActionTest {
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
- HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage expected =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
- HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
- ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ HdmiCecMessage response =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV,
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_STANDBY);
mNativeWrapper.onCecMessage(response);
mTestLooper.dispatchAll();
@@ -176,8 +160,9 @@ public class DevicePowerStatusActionTest {
public void queryDisplayStatus_sendsRequest_timeout_retriesSuccessfully() throws Exception {
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
- HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage expected =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
@@ -187,8 +172,11 @@ public class DevicePowerStatusActionTest {
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
- HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
- ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ HdmiCecMessage response =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV,
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_STANDBY);
mNativeWrapper.onCecMessage(response);
mTestLooper.dispatchAll();
@@ -200,8 +188,9 @@ public class DevicePowerStatusActionTest {
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
- HdmiCecMessage expected = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage expected =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
mNativeWrapper.clearResultMessages();
mTestLooper.moveTimeForward(TIMEOUT_MS);
@@ -220,15 +209,20 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
- HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
- ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ HdmiCecMessage response =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV,
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_STANDBY);
mNativeWrapper.onCecMessage(response);
mTestLooper.dispatchAll();
@@ -240,6 +234,7 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
@@ -251,8 +246,9 @@ public class DevicePowerStatusActionTest {
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
verify(mCallbackMock).onComplete(HdmiControlManager.POWER_STATUS_STANDBY);
@@ -263,6 +259,7 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.HDMI_CEC_VERSION_2_0);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
HdmiCecMessage reportPhysicalAddress = HdmiCecMessageBuilder
.buildReportPhysicalAddressCommand(ADDR_TV, 0x0000, HdmiDeviceInfo.DEVICE_TV);
mNativeWrapper.onCecMessage(reportPhysicalAddress);
@@ -274,12 +271,16 @@ public class DevicePowerStatusActionTest {
mPlaybackDevice.addAndStartAction(mDevicePowerStatusAction);
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
- HdmiCecMessage response = HdmiCecMessageBuilder.buildReportPowerStatus(
- ADDR_TV, mPlaybackDevice.mAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ HdmiCecMessage response =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ ADDR_TV,
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_STANDBY);
mNativeWrapper.onCecMessage(response);
mTestLooper.dispatchAll();
@@ -294,8 +295,9 @@ public class DevicePowerStatusActionTest {
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(
- mPlaybackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessage =
+ HdmiCecMessageBuilder.buildStandby(
+ mPlaybackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessage);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index fa5cb67c573d..28ee592e49a0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -23,8 +23,8 @@ import static android.hardware.hdmi.HdmiControlManager.POWER_STATUS_TRANSIENT_TO
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.DeviceSelectAction.STATE_WAIT_FOR_DEVICE_POWER_ON;
-import static com.android.server.hdmi.DeviceSelectAction.STATE_WAIT_FOR_REPORT_POWER_STATUS;
+import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_DEVICE_POWER_ON;
+import static com.android.server.hdmi.DeviceSelectActionFromTv.STATE_WAIT_FOR_REPORT_POWER_STATUS;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -34,11 +34,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
-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;
@@ -50,14 +46,13 @@ 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.Collections;
@SmallTest
@RunWith(JUnit4.class)
-public class DeviceSelectActionTest {
+public class DeviceSelectActionFromTvTest {
private static final int PORT_1 = 1;
private static final int PORT_2 = 1;
@@ -88,34 +83,25 @@ public class DeviceSelectActionTest {
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
- @Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
}
@Override
- void wakeUp() {
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -124,12 +110,6 @@ public class DeviceSelectActionTest {
boolean isPowerStandbyOrTransient() {
return false;
}
-
- @Override
- protected PowerManager getPowerManager() {
- return new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
- }
};
mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
@@ -152,6 +132,8 @@ public class DeviceSelectActionTest {
true, false, false);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mNativeWrapper.setPhysicalAddress(0x0000);
mTestLooper.dispatchAll();
@@ -191,12 +173,12 @@ public class DeviceSelectActionTest {
}
}
- private DeviceSelectAction createDeviceSelectAction(TestActionTimer actionTimer,
+ private DeviceSelectActionFromTv createDeviceSelectAction(TestActionTimer actionTimer,
TestCallback callback,
boolean isCec20) {
HdmiDeviceInfo hdmiDeviceInfo =
mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_1);
- DeviceSelectAction action = new DeviceSelectAction(mHdmiCecLocalDeviceTv,
+ DeviceSelectActionFromTv action = new DeviceSelectActionFromTv(mHdmiCecLocalDeviceTv,
hdmiDeviceInfo, callback, isCec20);
action.setActionTimer(actionTimer);
return action;
@@ -208,7 +190,7 @@ public class DeviceSelectActionTest {
// playback1.
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
@@ -229,7 +211,7 @@ public class DeviceSelectActionTest {
"testDeviceSelect");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
@@ -255,7 +237,7 @@ public class DeviceSelectActionTest {
"testDeviceSelect");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
@@ -282,7 +264,7 @@ public class DeviceSelectActionTest {
"testDeviceSelect");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
@@ -310,7 +292,7 @@ public class DeviceSelectActionTest {
HdmiControlManager.POWER_STATUS_ON);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/true);
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
@@ -326,7 +308,7 @@ public class DeviceSelectActionTest {
HdmiControlManager.POWER_STATUS_UNKNOWN);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/true);
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
@@ -349,7 +331,7 @@ public class DeviceSelectActionTest {
"testDeviceSelect");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
- DeviceSelectAction action = createDeviceSelectAction(actionTimer, callback,
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
/*isCec20=*/true);
action.start();
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 41946fb8ec63..abc1468e53bf 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -69,12 +69,27 @@ final class FakeHdmiCecConfig extends HdmiCecConfig {
R.bool.config_cecHdmiCecVersion20_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecRoutingControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlMode_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_allowed);
- doReturn(true).when(resources).getBoolean(
+ doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeTv_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_allowed);
doReturn(false).when(resources).getBoolean(
R.bool.config_cecPowerControlModeBroadcast_default);
@@ -95,6 +110,17 @@ final class FakeHdmiCecConfig extends HdmiCecConfig {
R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControl_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecSystemAudioControlDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMuting_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
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 1958cb01b510..d630ef6a5cd3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -56,6 +56,7 @@ final class FakeNativeWrapper implements NativeWrapper {
private final Map<Integer, Boolean> mPortConnectionStatus = new HashMap<>();
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
+ private int mVendorId = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
private HdmiCecController.HdmiCecCallback mCallback = null;
private int mCecVersion = HdmiControlManager.HDMI_CEC_VERSION_2_0;
@@ -102,7 +103,7 @@ final class FakeNativeWrapper implements NativeWrapper {
@Override
public int nativeGetVendorId() {
- return 0;
+ return mVendorId;
}
@Override
@@ -181,6 +182,11 @@ final class FakeNativeWrapper implements NativeWrapper {
}
@VisibleForTesting
+ protected void setVendorId(int vendorId) {
+ mVendorId = vendorId;
+ }
+
+ @VisibleForTesting
protected void setPhysicalAddress(int physicalAddress) {
mMyPhysicalAddress = physicalAddress;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
new file mode 100644
index 000000000000..7c8a11ec1cea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 android.annotation.NonNull;
+import android.content.Context;
+
+/**
+ * Fake class which stubs PowerManagerWrapper (useful for testing).
+ */
+final class FakePowerManagerWrapper extends PowerManagerWrapper {
+ private boolean mInteractive;
+
+ FakePowerManagerWrapper(@NonNull Context context) {
+ super(context);
+ mInteractive = true;
+ }
+
+ @Override
+ boolean isInteractive() {
+ return mInteractive;
+ }
+
+ void setInteractive(boolean interactive) {
+ mInteractive = interactive;
+ }
+
+ @Override
+ void wakeUp(long time, int reason, String details) {
+ mInteractive = true;
+ return;
+ }
+
+ @Override
+ void goToSleep(long time, int reason, int flags) {
+ mInteractive = false;
+ return;
+ }
+
+ // Don't stub WakeLock.
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 29f62b58746e..b903b1649fc8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -25,12 +25,12 @@ 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.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
@@ -38,11 +38,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
-import android.os.Handler;
-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;
@@ -57,10 +53,9 @@ 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.Collections;
/**
* Tests for the {@link HdmiCecAtomWriter} class and its usage by the HDMI-CEC framework.
@@ -75,6 +70,7 @@ public class HdmiCecAtomLoggingTest {
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
private HdmiMhlControllerStub mHdmiMhlControllerStub;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private HdmiCecNetwork mHdmiCecNetwork;
private Looper mLooper;
private Context mContextSpy;
@@ -83,13 +79,8 @@ public class HdmiCecAtomLoggingTest {
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private HdmiPortInfo[] mHdmiPortInfo;
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
mHdmiCecAtomWriterSpy = spy(new HdmiCecAtomWriter());
mLooper = mTestLooper.getLooper();
@@ -97,13 +88,7 @@ public class HdmiCecAtomLoggingTest {
mContextSpy = spy(new ContextWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext()));
-
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mLooper)));
- doReturn(true).when(mIPowerManagerMock).isInteractive();
-
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
@@ -143,6 +128,8 @@ public class HdmiCecAtomLoggingTest {
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
mHdmiControlServiceSpy.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlServiceSpy.setPowerManager(mPowerManager);
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiControlServiceSpy.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
@@ -200,7 +187,7 @@ public class HdmiCecAtomLoggingTest {
mTestLooper.dispatchAll();
- verify(mHdmiCecAtomWriterSpy, times(1)).messageReported(
+ verify(mHdmiCecAtomWriterSpy, atLeastOnce()).messageReported(
any(),
anyInt(),
eq(callerUid),
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index a37f94b023fd..a94690e71eb4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -82,8 +82,10 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -103,8 +105,10 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -125,7 +129,9 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getUserSettings())
.containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.CEC_SETTING_NAME_ROUTING_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
@@ -188,6 +194,7 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
}
@@ -199,6 +206,7 @@ public final class HdmiCecConfigTest {
assertThat(hdmiCecConfig.getAllowedStringValues(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
.containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@@ -255,12 +263,12 @@ public final class HdmiCecConfigTest {
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
- .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
}
@Test
public void getDefaultStringValue_WithOverride() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getDefaultStringValue(
@@ -277,7 +285,7 @@ public final class HdmiCecConfigTest {
@Test
public void getDefaultStringValue_NoDefault() {
- setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+ setBooleanResource(R.bool.config_cecPowerControlModeTvAndAudioSystem_default, false);
assertThrows(RuntimeException.class,
() -> new HdmiCecConfig(mContext, mStorageAdapter));
}
@@ -334,7 +342,7 @@ public final class HdmiCecConfigTest {
public void getStringValue_GlobalSetting_BasicSanity() {
when(mStorageAdapter.retrieveGlobalSetting(
Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.POWER_CONTROL_MODE_TV))
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM))
.thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
assertThat(hdmiCecConfig.getStringValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index ee1a85745701..bd6e46d9ee69 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -69,6 +69,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Optional;
/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@@ -99,7 +100,7 @@ public class HdmiCecControllerTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlServiceSpy = spy(new HdmiControlService(
- InstrumentationRegistry.getTargetContext()));
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList()));
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getIoLooper();
doReturn(mMyLooper).when(mHdmiControlServiceSpy).getServiceLooper();
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7911c409d766..17f827da1ae7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -31,11 +31,7 @@ import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.IThermalService;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -49,10 +45,9 @@ import org.junit.Ignore;
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.Collections;
@SmallTest
@Presubmit
@@ -69,6 +64,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
@@ -83,20 +79,15 @@ public class HdmiCecLocalDeviceAudioSystemTest {
private HdmiDeviceInfo mDeviceInfo;
private boolean mArcSupport;
private HdmiPortInfo[] mHdmiPortInfo;
- private boolean mWokenUp;
-
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -154,11 +145,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Override
- void wakeUp() {
- mWokenUp = true;
- }
-
- @Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceInfo = device;
mInvokeDeviceEventState = status;
@@ -178,12 +164,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
return defVal;
}
}
-
- @Override
- protected PowerManager getPowerManager() {
- return new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
- }
};
mHdmiControlService.getHdmiCecConfig().setIntValue(
@@ -210,7 +190,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDevicePlayback);
- mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnables(true);
+ mHdmiCecLocalDeviceAudioSystem.setRoutingControlFeatureEnabled(true);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
new HdmiPortInfo(
@@ -226,6 +206,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
4, HdmiPortInfo.PORT_INPUT, HDMI_3_PHYSICAL_ADDRESS, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -673,16 +655,16 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void doNotWakeUpOnHotPlug_PlugIn() {
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
mHdmiCecLocalDeviceAudioSystem.onHotplug(0, true);
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
public void doNotWakeUpOnHotPlug_PlugOut() {
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
mHdmiCecLocalDeviceAudioSystem.onHotplug(0, false);
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -793,12 +775,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void setActiveSource_localDevice_playback() {
- mHdmiControlService.setActiveSource(mHdmiCecLocalDevicePlayback.mAddress,
+ mHdmiControlService.setActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
SELF_PHYSICAL_ADDRESS,
"HdmiControlServiceTest");
- assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
- mHdmiCecLocalDevicePlayback.mAddress);
+ assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress)
+ .isEqualTo(mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress());
assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(
SELF_PHYSICAL_ADDRESS);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
@@ -807,12 +790,13 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void setActiveSource_localDevice_audio() {
- mHdmiControlService.setActiveSource(mHdmiCecLocalDeviceAudioSystem.mAddress,
+ mHdmiControlService.setActiveSource(
+ mHdmiCecLocalDeviceAudioSystem.getDeviceInfo().getLogicalAddress(),
SELF_PHYSICAL_ADDRESS,
"HdmiControlServiceTest");
- assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress).isEqualTo(
- mHdmiCecLocalDeviceAudioSystem.mAddress);
+ assertThat(mHdmiControlService.getLocalActiveSource().logicalAddress)
+ .isEqualTo(mHdmiCecLocalDeviceAudioSystem.getDeviceInfo().getLogicalAddress());
assertThat(mHdmiControlService.getLocalActiveSource().physicalAddress).isEqualTo(
SELF_PHYSICAL_ADDRESS);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
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 524ad6282397..8a84c6f9c6e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -24,18 +24,12 @@ 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;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
-import android.os.Handler;
-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;
@@ -49,10 +43,9 @@ 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.Collections;
import java.util.concurrent.TimeUnit;
@SmallTest
@@ -74,36 +67,25 @@ public class HdmiCecLocalDevicePlaybackTest {
private FakeNativeWrapper mNativeWrapper;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
+ private FakePowerManagerWrapper mPowerManager;
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mPlaybackPhysicalAddress;
private int mPlaybackLogicalAddress;
private boolean mWokenUp;
- private boolean mStandby;
private boolean mActiveMediaSessionsPaused;
- @Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
-
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
void wakeUp() {
mWokenUp = true;
- }
-
- @Override
- void standby() {
- mStandby = true;
- mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
+ super.wakeUp();
}
@Override
@@ -112,11 +94,6 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Override
- protected boolean isStandbyMessageReceived() {
- return mStandby;
- }
-
- @Override
boolean isControlEnabled() {
return true;
}
@@ -142,9 +119,8 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
+ boolean canGoToStandby() {
+ return true;
}
};
@@ -165,6 +141,8 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPlaybackPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
@@ -181,7 +159,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
HdmiProperties.playback_device_action_on_routing_control_values.NONE;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
@@ -194,7 +172,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(expectedMessage)).isFalse();
}
@@ -203,7 +181,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
HdmiProperties.playback_device_action_on_routing_control_values.NONE;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
@@ -216,7 +194,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(expectedMessage)).isFalse();
}
@@ -225,7 +203,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
HdmiProperties.playback_device_action_on_routing_control_values.WAKE_UP_ONLY;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
@@ -238,7 +216,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(expectedMessage)).isFalse();
}
@@ -247,7 +225,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
HdmiProperties.playback_device_action_on_routing_control_values.WAKE_UP_ONLY;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
@@ -260,7 +238,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(expectedMessage)).isFalse();
}
@@ -271,7 +249,7 @@ public class HdmiCecLocalDevicePlaybackTest {
.playback_device_action_on_routing_control_values
.WAKE_UP_AND_SEND_ACTIVE_SOURCE;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
@@ -284,7 +262,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@@ -295,7 +273,7 @@ public class HdmiCecLocalDevicePlaybackTest {
.playback_device_action_on_routing_control_values
.WAKE_UP_AND_SEND_ACTIVE_SOURCE;
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
@@ -308,7 +286,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@@ -319,7 +297,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
@@ -329,7 +307,7 @@ public class HdmiCecLocalDevicePlaybackTest {
0x5000);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -339,7 +317,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
mPlaybackPhysicalAddress);
@@ -350,7 +328,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
mPlaybackLogicalAddress);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -360,7 +338,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
mPlaybackPhysicalAddress);
@@ -371,7 +349,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -381,13 +359,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -397,13 +375,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -413,14 +391,14 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -478,7 +456,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message = HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
@@ -487,7 +465,7 @@ public class HdmiCecLocalDevicePlaybackTest {
0x5000);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -497,7 +475,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
mPlaybackPhysicalAddress);
@@ -508,7 +486,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
mPlaybackLogicalAddress);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -518,7 +496,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
mPlaybackPhysicalAddress);
@@ -529,7 +507,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -539,13 +517,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -555,13 +533,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -571,14 +549,14 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV,
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -655,7 +633,9 @@ public class HdmiCecLocalDevicePlaybackTest {
// Test should ignore it and still keep the system audio mode on.
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetSystemAudioMode(
- Constants.ADDR_AUDIO_SYSTEM, mHdmiCecLocalDevicePlayback.mAddress, false);
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ false);
assertThat(mHdmiCecLocalDevicePlayback.handleSetSystemAudioMode(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
@@ -667,7 +647,9 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isFalse();
HdmiCecMessage message =
HdmiCecMessageBuilder.buildReportSystemAudioMode(
- Constants.ADDR_AUDIO_SYSTEM, mHdmiCecLocalDevicePlayback.mAddress, true);
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ true);
assertThat(mHdmiCecLocalDevicePlayback.handleSystemAudioModeStatus(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.mService.isSystemAudioActivated()).isTrue();
@@ -675,16 +657,16 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void doNotWakeUpOnHotPlug_PlugIn() {
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
mHdmiCecLocalDevicePlayback.onHotplug(0, true);
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
public void doNotWakeUpOnHotPlug_PlugOut() {
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
mHdmiCecLocalDevicePlayback.onHotplug(0, false);
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -694,20 +676,55 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_NotActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
+ "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -719,20 +736,25 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -744,20 +766,25 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -769,20 +796,55 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
+ HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
+ }
+
+ @Test
+ public void handleOnStandby_ScreenOff_ActiveSource_ToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -794,20 +856,25 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
}
@@ -819,20 +886,25 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(false, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageToAudioSystem =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToAudioSystem);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
}
@@ -844,16 +916,16 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_CONTROL_MODE_TV);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
- HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageToTv =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
@@ -952,13 +1024,13 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress).isEqualTo(
mPlaybackPhysicalAddress);
@@ -971,12 +1043,12 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress).isEqualTo(
0x0000);
@@ -989,13 +1061,13 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
}
@@ -1004,12 +1076,12 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
}
@@ -1068,36 +1140,42 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
- mStandby = false;
+ mPowerManager.setInteractive(true);
// 1. DUT is <AS>.
- HdmiCecMessage message1 = HdmiCecMessageBuilder.buildActiveSource(
- mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage message1 =
+ HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ mPlaybackPhysicalAddress);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message1))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
// 2. DUT loses <AS> and goes to sleep.
HdmiCecMessage message2 = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message2))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
+ mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
// 3. DUT becomes <AS> again.
- mWokenUp = false;
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
// 4. DUT turned off.
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+ HdmiCecMessage standbyMessageBroadcast =
+ HdmiCecMessageBuilder.buildStandby(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_BROADCAST);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
}
@@ -1109,11 +1187,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
@@ -1127,11 +1208,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
@@ -1145,11 +1229,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_MUTE);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_MUTE);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).contains(keyReleased);
@@ -1163,11 +1250,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
@@ -1181,11 +1271,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
@@ -1199,11 +1292,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
mTestLooper.dispatchAll();
- HdmiCecMessage keyPressed = HdmiCecMessageBuilder.buildUserControlPressed(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV,
- HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
- HdmiCecMessage keyReleased = HdmiCecMessageBuilder.buildUserControlReleased(
- mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+ HdmiCecMessage keyPressed =
+ HdmiCecMessageBuilder.buildUserControlPressed(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+ HdmiCecMessage keyReleased =
+ HdmiCecMessageBuilder.buildUserControlReleased(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyPressed);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(keyReleased);
@@ -1298,8 +1394,10 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.dispatchMessage(setStreamPath);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- mHdmiCecLocalDevicePlayback.mAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
}
@@ -1341,8 +1439,7 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void handleSetStreamPath_Dreaming() throws RemoteException {
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
+ mPowerManager.setInteractive(true);
mWokenUp = false;
HdmiCecMessage message =
@@ -1352,6 +1449,7 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mWokenUp).isTrue();
}
@@ -1362,7 +1460,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
@@ -1372,7 +1470,7 @@ public class HdmiCecLocalDevicePlaybackTest {
0x5000);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -1382,13 +1480,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -1398,13 +1496,13 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
mHdmiCecLocalDevicePlayback.setActiveSource(ADDR_TV, 0x0000,
"HdmiCecLocalDevicePlaybackTest");
- mStandby = false;
+ mPowerManager.setInteractive(true);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -1456,7 +1554,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepToTv() {
+ public void oneTouchPlay_PowerControlModeToTv() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_TV);
@@ -1479,7 +1577,30 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepBroadcast() {
+ public void oneTouchPlay_PowerControlModeToTvAndAudioSystem() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+ HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM);
+ mHdmiControlService.oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ }
+ });
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ HdmiCecMessage systemAudioModeRequest = HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, mPlaybackPhysicalAddress, true);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mNativeWrapper.getResultMessages()).contains(systemAudioModeRequest);
+ }
+
+ @Test
+ public void oneTouchPlay_PowerControlModeBroadcast() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -1502,7 +1623,7 @@ public class HdmiCecLocalDevicePlaybackTest {
}
@Test
- public void oneTouchPlay_SendStandbyOnSleepNone() {
+ public void oneTouchPlay_PowerControlModeNone() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_NONE);
@@ -1534,8 +1655,10 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void getActiveSource_localPlaybackIsActiveSource() {
- mHdmiControlService.setActiveSource(mHdmiCecLocalDevicePlayback.mAddress,
- mHdmiControlService.getPhysicalAddress(), "HdmiControlServiceTest");
+ mHdmiControlService.setActiveSource(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ mHdmiControlService.getPhysicalAddress(),
+ "HdmiControlServiceTest");
assertThat(mHdmiControlService.getActiveSource()).isEqualTo(
mHdmiCecLocalDevicePlayback.getDeviceInfo());
@@ -1586,7 +1709,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_TV);
- mStandby = false;
+ mPowerManager.setInteractive(true);
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
@@ -1597,7 +1720,7 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
mPlaybackLogicalAddress, ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -1605,7 +1728,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
- mStandby = false;
+ mPowerManager.setInteractive(true);
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
@@ -1616,12 +1739,12 @@ public class HdmiCecLocalDevicePlaybackTest {
HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
mPlaybackLogicalAddress, ADDR_BROADCAST);
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- assertThat(mStandby).isTrue();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
public void toggleAndFollowTvPower_TvStatusStandby() {
- mStandby = false;
+ mPowerManager.setInteractive(true);
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_STANDBY);
@@ -1635,12 +1758,12 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
public void toggleAndFollowTvPower_TvStatusUnknown() {
- mStandby = false;
+ mPowerManager.setInteractive(true);
mHdmiControlService.toggleAndFollowTvPower();
HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_UNKNOWN);
@@ -1655,31 +1778,31 @@ public class HdmiCecLocalDevicePlaybackTest {
mPlaybackLogicalAddress, Constants.ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed);
assertThat(mNativeWrapper.getResultMessages()).contains(userControlReleased);
- assertThat(mStandby).isFalse();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
public void toggleAndFollowTvPower_isInteractive() throws RemoteException {
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+ mPowerManager.setInteractive(true);
mActiveMediaSessionsPaused = false;
mWokenUp = false;
mHdmiControlService.toggleAndFollowTvPower();
assertThat(mActiveMediaSessionsPaused).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mWokenUp).isFalse();
}
@Test
public void toggleAndFollowTvPower_isNotInteractive() throws RemoteException {
- when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mPowerManager.setInteractive(false);
mActiveMediaSessionsPaused = false;
- mWokenUp = false;
mHdmiControlService.toggleAndFollowTvPower();
assertThat(mActiveMediaSessionsPaused).isFalse();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 6502e4813171..2f72809127ec 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -18,10 +18,8 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
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_TV;
-import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
import static com.android.server.hdmi.Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -40,6 +38,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.Result;
import android.media.AudioManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -47,6 +46,8 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -141,7 +142,7 @@ public class HdmiCecLocalDeviceTest {
Context context = InstrumentationRegistry.getTargetContext();
mHdmiControlService =
- new HdmiControlService(context) {
+ new HdmiControlService(context, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return isControlEnabled;
@@ -205,6 +206,25 @@ public class HdmiCecLocalDeviceTest {
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mNativeWrapper.setPhysicalAddress(0x2000);
mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ }
+
+ @Test
+ public void testEqualsActiveSource() {
+ int logicalAddress = 0;
+ int physicalAddress = 0x0000;
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress),
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress, physicalAddress + 1))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(logicalAddress + 1, physicalAddress))
+ .addEqualityGroup(
+ new HdmiCecLocalDevice.ActiveSource(
+ logicalAddress + 1, physicalAddress + 1))
+ .testEquals();
}
@Test
@@ -221,37 +241,71 @@ public class HdmiCecLocalDeviceTest {
@Test
public void handleGivePhysicalAddress_success() {
- mSrcAddr = ADDR_UNREGISTERED;
- mDesAddr = ADDR_BROADCAST;
- param =
- new byte[] {
- (byte) ((mPhysicalAddr >> 8) & 0xFF),
- (byte) (mPhysicalAddr & 0xFF),
- (byte) (DEVICE_TV & 0xFF)
- };
- callbackResult = -1;
- @Constants.HandleMessageResult int handleResult =
+ mNativeWrapper.setPhysicalAddress(0x0);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_TV, 0, DEVICE_TV);
+ @Constants.HandleMessageResult
+ int handleResult =
mHdmiLocalDevice.handleGivePhysicalAddress(
- (int finalResult) -> callbackResult = finalResult);
+ HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ ADDR_PLAYBACK_1, ADDR_TV));
mTestLooper.dispatchAll();
- /**
- * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
- */
- assertEquals(0, callbackResult);
assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleGivePhysicalAddress_failure() {
+ mNativeWrapper.setPhysicalAddress(Constants.INVALID_PHYSICAL_ADDRESS);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_TV,
+ ADDR_PLAYBACK_1,
+ Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS,
+ Constants.ABORT_UNABLE_TO_DETERMINE);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGivePhysicalAddress(
+ HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ ADDR_PLAYBACK_1, ADDR_TV));
+ mTestLooper.dispatchAll();
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@Test
public void handleGiveDeviceVendorId_success() {
- mSrcAddr = ADDR_UNREGISTERED;
- mDesAddr = ADDR_BROADCAST;
- /** nativeGetVendorId returns 0 */
- param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
- callbackResult = -1;
- mHdmiLocalDevice.handleGiveDeviceVendorId(
- (int finalResult) -> callbackResult = finalResult);
+ /** Set vendor id to 0 */
+ mNativeWrapper.setVendorId(0);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildDeviceVendorIdCommand(ADDR_TV, 0);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGiveDeviceVendorId(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+ ADDR_PLAYBACK_1, ADDR_TV));
+ mTestLooper.dispatchAll();
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleGiveDeviceVendorId_failure() {
+ mNativeWrapper.setVendorId(Result.FAILURE_UNKNOWN);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_TV,
+ ADDR_PLAYBACK_1,
+ Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID,
+ Constants.ABORT_UNABLE_TO_DETERMINE);
+ @Constants.HandleMessageResult
+ int handleResult =
+ mHdmiLocalDevice.handleGiveDeviceVendorId(
+ HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(
+ ADDR_PLAYBACK_1, ADDR_TV));
mTestLooper.dispatchAll();
- assertEquals(0, callbackResult);
+ assertEquals(Constants.HANDLED, handleResult);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@Test
@@ -332,9 +386,13 @@ public class HdmiCecLocalDeviceTest {
@Test
public void handleCecVersion_isHandled() {
- @Constants.HandleMessageResult int result = mHdmiLocalDevice.onMessage(
- HdmiCecMessageBuilder.buildCecVersion(ADDR_PLAYBACK_1, mHdmiLocalDevice.mAddress,
- HdmiControlManager.HDMI_CEC_VERSION_1_4_B));
+ @Constants.HandleMessageResult
+ int result =
+ mHdmiLocalDevice.onMessage(
+ HdmiCecMessageBuilder.buildCecVersion(
+ ADDR_PLAYBACK_1,
+ mHdmiLocalDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.HDMI_CEC_VERSION_1_4_B));
assertEquals(Constants.HANDLED, result);
}
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 59711a62f3f3..7acf9469df84 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -29,7 +29,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
@@ -38,11 +37,7 @@ 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;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import androidx.test.InstrumentationRegistry;
@@ -56,6 +51,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.Collections;
@SmallTest
@RunWith(JUnit4.class)
@@ -67,18 +63,15 @@ public class HdmiCecLocalDeviceTvTest {
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mTvPhysicalAddress;
private int mTvLogicalAddress;
- private boolean mWokenUp;
@Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
- @Mock private AudioManager mAudioManager;
+ private AudioManager mAudioManager;
@Before
public void setUp() {
@@ -88,12 +81,8 @@ public class HdmiCecLocalDeviceTvTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
- @Override
- void wakeUp() {
- mWokenUp = true;
- }
-
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
@@ -115,12 +104,6 @@ public class HdmiCecLocalDeviceTvTest {
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(context, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mMyLooper));
- }
-
- @Override
AudioManager getAudioManager() {
return mAudioManager;
}
@@ -144,6 +127,8 @@ public class HdmiCecLocalDeviceTvTest {
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTvPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
@@ -237,12 +222,12 @@ public class HdmiCecLocalDeviceTvTest {
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
mTestLooper.dispatchAll();
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
mTvLogicalAddress);
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -251,12 +236,12 @@ public class HdmiCecLocalDeviceTvTest {
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
mTestLooper.dispatchAll();
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isTrue();
+ assertThat(mPowerManager.isInteractive()).isTrue();
}
@Test
@@ -265,12 +250,12 @@ public class HdmiCecLocalDeviceTvTest {
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED);
mTestLooper.dispatchAll();
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
mTvLogicalAddress);
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -279,12 +264,12 @@ public class HdmiCecLocalDeviceTvTest {
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED);
mTestLooper.dispatchAll();
- mWokenUp = false;
+ mPowerManager.setInteractive(false);
HdmiCecMessage imageViewOn = new HdmiCecMessage(ADDR_PLAYBACK_1, mTvLogicalAddress,
Constants.MESSAGE_IMAGE_VIEW_ON, HdmiCecMessage.EMPTY_PARAM);
assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(imageViewOn)).isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mWokenUp).isFalse();
+ assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
@@ -513,8 +498,11 @@ public class HdmiCecLocalDeviceTvTest {
HdmiCecFeatureAction systemAudioAutoInitiationAction =
new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(systemAudioAutoInitiationAction);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -534,66 +522,4 @@ public class HdmiCecLocalDeviceTvTest {
verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
}
-
- @Test
- public void handleReportAudioStatus_SamOnArcOn_setStreamVolumeCalled() {
- mNativeWrapper.setPortConnectionStatus(2, true);
- HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
- mNativeWrapper.onCecMessage(hdmiCecMessage);
-
- HdmiCecFeatureAction systemAudioAutoInitiationAction =
- new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM);
- mHdmiCecLocalDeviceTv.addAndStartAction(systemAudioAutoInitiationAction);
-
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
- mHdmiControlService.handleCecCommand(reportSystemAudioMode);
-
- HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc(
- ADDR_AUDIO_SYSTEM,
- ADDR_TV);
- mNativeWrapper.onCecMessage(requestArcInitiation);
-
- mTestLooper.dispatchAll();
-
- // SAM and ARC must be on
- assertTrue(mHdmiCecLocalDeviceTv.isSystemAudioActivated());
- assertTrue(mHdmiCecLocalDeviceTv.isArcEstablished());
-
- HdmiCecMessage reportAudioStatus = HdmiCecMessageBuilder.buildReportAudioStatus(
- ADDR_AUDIO_SYSTEM,
- ADDR_TV,
- 50, // Volume of incoming message does not affect HDMI-CEC logic
- false);
- mNativeWrapper.onCecMessage(reportAudioStatus);
-
- mTestLooper.dispatchAll();
-
- verify(mAudioManager, times(1)).setStreamVolume(anyInt(), anyInt(), anyInt());
- }
-
- @Test
- public void handleReportAudioStatus_SamOff_setStreamVolumeNotCalled() {
- // 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);
-
- mTestLooper.dispatchAll();
-
- assertFalse(mHdmiCecLocalDeviceTv.isSystemAudioActivated());
-
- HdmiCecMessage reportAudioStatus = HdmiCecMessageBuilder.buildReportAudioStatus(
- ADDR_AUDIO_SYSTEM,
- ADDR_TV,
- 50, // Volume of incoming message does not affect HDMI-CEC logic
- false);
- mNativeWrapper.onCecMessage(reportAudioStatus);
-
- mTestLooper.dispatchAll();
-
- verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
new file mode 100755
index 000000000000..036d08412e7c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.testing.EqualsTester;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link HdmiCecMessage} class. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class HdmiCecMessageTest {
+
+ @Test
+ public void testEqualsHdmiCecMessage() {
+ int source = 0;
+ int destination = 1;
+ int opcode = 0x7f;
+ byte[] params1 = {0x00, 0x1a, 0x2b, 0x3c};
+ byte[] params2 = {0x00, 0x1a, 0x2b, 0x3c, 0x4d};
+
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiCecMessage(source, destination, opcode, params1),
+ new HdmiCecMessage(source, destination, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination, opcode, params2))
+ .addEqualityGroup(new HdmiCecMessage(source + 1, destination, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination + 1, opcode, params1))
+ .addEqualityGroup(new HdmiCecMessage(source, destination, opcode + 1, params1))
+ .testEquals();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 2307a85fe9cd..ebd8d77ff5cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -37,6 +37,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */
@SmallTest
@Presubmit
@@ -49,7 +51,7 @@ public class HdmiCecMessageValidatorTest {
@Before
public void setUp() throws Exception {
HdmiControlService mHdmiControlService = new HdmiControlService(
- InstrumentationRegistry.getTargetContext());
+ InstrumentationRegistry.getTargetContext(), Collections.emptyList());
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index b1998f55c72e..1048eb5acd1a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -64,7 +64,7 @@ public class HdmiCecNetworkTest {
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
- mHdmiControlService = new HdmiControlService(mContext) {
+ mHdmiControlService = new HdmiControlService(mContext, Collections.emptyList()) {
@Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListenerStatuses.add(status);
@@ -542,4 +542,13 @@ public class HdmiCecNetworkTest {
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getCecVersion()).isEqualTo(cecVersion);
}
+
+ @Test
+ public void getSafeCecDevicesLocked_addDevice_sizeOne() {
+ HdmiDeviceInfo cecDeviceInfo = new HdmiDeviceInfo();
+
+ mHdmiCecNetwork.addCecDevice(cecDeviceInfo);
+
+ assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).hasSize(1);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 572ffd930bad..bff12968e8cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -20,17 +20,12 @@ 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.HdmiPortInfo;
-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 android.platform.test.annotations.Presubmit;
@@ -43,10 +38,9 @@ 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.Collections;
@SmallTest
@Presubmit
@@ -60,30 +54,18 @@ public class HdmiCecPowerStatusControllerTest {
HdmiControlManager.POWER_STATUS_STANDBY};
private HdmiCecPowerStatusController mHdmiCecPowerStatusController;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
- @Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
private HdmiControlService mHdmiControlService;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(contextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper)));
- when(contextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(contextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper)));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
- mHdmiControlService = new HdmiControlService(contextSpy) {
+
+ mHdmiControlService = new HdmiControlService(contextSpy, Collections.emptyList()) {
@Override
boolean isControlEnabled() {
return true;
@@ -124,6 +106,8 @@ public class HdmiCecPowerStatusControllerTest {
mNativeWrapper.setPortInfo(hdmiPortInfos);
mNativeWrapper.setPortConnectionStatus(1, true);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(contextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.getHdmiCecNetwork().initPortInfo();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mNativeWrapper.setPhysicalAddress(0x2000);
@@ -183,9 +167,11 @@ public class HdmiCecPowerStatusControllerTest {
mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@@ -196,9 +182,11 @@ public class HdmiCecPowerStatusControllerTest {
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@@ -209,9 +197,11 @@ public class HdmiCecPowerStatusControllerTest {
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
@@ -221,9 +211,11 @@ public class HdmiCecPowerStatusControllerTest {
mHdmiCecPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_ON);
assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
}
@@ -234,9 +226,11 @@ public class HdmiCecPowerStatusControllerTest {
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).contains(reportPowerStatus);
}
@@ -247,9 +241,11 @@ public class HdmiCecPowerStatusControllerTest {
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- mHdmiCecLocalDevicePlayback.mAddress, Constants.ADDR_BROADCAST,
- HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_BROADCAST,
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportPowerStatus);
}
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 f53ae525eaba..c525f9beb1ad 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -33,7 +33,8 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.ContextWrapper;
@@ -42,10 +43,7 @@ import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.os.Binder;
-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;
@@ -58,11 +56,11 @@ 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 org.mockito.Mockito;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Optional;
/**
@@ -73,107 +71,6 @@ import java.util.Optional;
@RunWith(JUnit4.class)
public class HdmiControlServiceTest {
- protected static class MockPlaybackDevice extends HdmiCecLocalDevicePlayback {
-
- private boolean mCanGoToStandby;
- private boolean mIsStandby;
- private boolean mIsDisabled;
-
- MockPlaybackDevice(HdmiControlService service) {
- super(service);
- }
-
- @Override
- protected void onAddressAllocated(int logicalAddress, int reason) {}
-
- @Override
- protected int getPreferredAddress() {
- return 0;
- }
-
- @Override
- protected void setPreferredAddress(int addr) {}
-
- @Override
- protected boolean canGoToStandby() {
- return mCanGoToStandby;
- }
-
- @Override
- protected void disableDevice(
- boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
- mIsDisabled = true;
- originalCallback.onCleared(this);
- }
-
- @Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
- mIsStandby = true;
- }
-
- protected boolean isStandby() {
- return mIsStandby;
- }
-
- protected boolean isDisabled() {
- return mIsDisabled;
- }
-
- protected void setCanGoToStandby(boolean canGoToStandby) {
- mCanGoToStandby = canGoToStandby;
- }
- }
- protected static class MockAudioSystemDevice extends HdmiCecLocalDeviceAudioSystem {
-
- private boolean mCanGoToStandby;
- private boolean mIsStandby;
- private boolean mIsDisabled;
-
- MockAudioSystemDevice(HdmiControlService service) {
- super(service);
- }
-
- @Override
- protected void onAddressAllocated(int logicalAddress, int reason) {}
-
- @Override
- protected int getPreferredAddress() {
- return 0;
- }
-
- @Override
- protected void setPreferredAddress(int addr) {}
-
- @Override
- protected boolean canGoToStandby() {
- return mCanGoToStandby;
- }
-
- @Override
- protected void disableDevice(
- boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
- mIsDisabled = true;
- originalCallback.onCleared(this);
- }
-
- @Override
- protected void onStandby(boolean initiatedByCec, int standbyAction) {
- mIsStandby = true;
- }
-
- protected boolean isStandby() {
- return mIsStandby;
- }
-
- protected boolean isDisabled() {
- return mIsDisabled;
- }
-
- protected void setCanGoToStandby(boolean canGoToStandby) {
- mCanGoToStandby = canGoToStandby;
- }
- }
-
private static final String TAG = "HdmiControlServiceTest";
private Context mContextSpy;
private HdmiControlService mHdmiControlServiceSpy;
@@ -181,31 +78,19 @@ public class HdmiControlServiceTest {
private MockAudioSystemDevice mAudioSystemDeviceSpy;
private MockPlaybackDevice mPlaybackDeviceSpy;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private HdmiPortInfo[] mHdmiPortInfo;
- @Mock private IPowerManager mIPowerManagerMock;
- @Mock private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, null));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, null));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
+ mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy, Collections.emptyList()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
@@ -233,15 +118,17 @@ public class HdmiControlServiceTest {
mLocalDevices.add(mPlaybackDeviceSpy);
mHdmiPortInfo = new HdmiPortInfo[4];
mHdmiPortInfo[0] =
- new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x2100, true, false, false);
mHdmiPortInfo[1] =
- new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
+ new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2200, true, false, false);
mHdmiPortInfo[2] =
- new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
+ new HdmiPortInfo(3, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
mHdmiPortInfo[3] =
- new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
+ new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiControlServiceSpy.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlServiceSpy.setPowerManager(mPowerManager);
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
@@ -279,7 +166,7 @@ public class HdmiControlServiceTest {
@Test
public void initialPowerStatus_quiescentBoot_isTransientToStandby() throws RemoteException {
- when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mPowerManager.setInteractive(false);
assertThat(mHdmiControlServiceSpy.getInitialPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
}
@@ -294,7 +181,7 @@ public class HdmiControlServiceTest {
@Test
public void powerStatusAfterBootComplete_quiescentBoot_isStandby() throws RemoteException {
- when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mPowerManager.setInteractive(false);
mHdmiControlServiceSpy.onBootPhase(PHASE_BOOT_COMPLETED);
assertThat(mHdmiControlServiceSpy.getPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_STANDBY);
@@ -321,6 +208,18 @@ public class HdmiControlServiceTest {
}
@Test
+ public void normalBoot_queuedActionsStartedAfterBoot() {
+ Mockito.clearInvocations(mAudioSystemDeviceSpy);
+ Mockito.clearInvocations(mPlaybackDeviceSpy);
+
+ mHdmiControlServiceSpy.onBootPhase(PHASE_BOOT_COMPLETED);
+ mTestLooper.dispatchAll();
+
+ verify(mAudioSystemDeviceSpy, times(1)).startQueuedActions();
+ verify(mPlaybackDeviceSpy, times(1)).startQueuedActions();
+ }
+
+ @Test
public void initialPowerStatus_normalBoot_goToStandby_broadcastsPowerStatus_2_0() {
mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
@@ -734,9 +633,11 @@ public class HdmiControlServiceTest {
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_TV,
- mHdmiControlServiceSpy.playback().mAddress, HdmiControlManager.POWER_STATUS_ON);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_ON);
mNativeWrapper.onCecMessage(reportPowerStatus);
mTestLooper.dispatchAll();
@@ -755,10 +656,11 @@ public class HdmiControlServiceTest {
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_TV,
- mHdmiControlServiceSpy.playback().mAddress,
- HdmiControlManager.POWER_STATUS_STANDBY);
+ HdmiCecMessage reportPowerStatus =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_STANDBY);
mNativeWrapper.onCecMessage(reportPowerStatus);
mTestLooper.dispatchAll();
@@ -766,30 +668,6 @@ public class HdmiControlServiceTest {
assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue();
}
- private static class HdmiControlStatusCallback extends IHdmiControlStatusChangeListener.Stub {
- boolean mCecEnabled = false;
- boolean mCecAvailable = false;
-
- @Override
- public void onStatusChange(int isCecEnabled, boolean isCecAvailable)
- throws RemoteException {
- mCecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
- mCecAvailable = isCecAvailable;
- }
- }
-
- private static class VolumeControlFeatureCallback extends
- IHdmiCecVolumeControlFeatureListener.Stub {
- boolean mCallbackReceived = false;
- int mVolumeControlEnabled = -1;
-
- @Override
- public void onHdmiCecVolumeControlFeature(int enabled) throws RemoteException {
- this.mCallbackReceived = true;
- this.mVolumeControlEnabled = enabled;
- }
- }
-
@Test
public void handleCecCommand_errorParameter_returnsAbortInvalidOperand() {
// Validity ERROR_PARAMETER. Taken from HdmiCecMessageValidatorTest#isValid_menuStatus
@@ -992,4 +870,134 @@ public class HdmiControlServiceTest {
.containsExactly(DEVICE_PLAYBACK, DEVICE_AUDIO_SYSTEM);
}
+ protected static class MockPlaybackDevice extends HdmiCecLocalDevicePlayback {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ MockPlaybackDevice(HdmiControlService service) {
+ super(service);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ protected static class MockAudioSystemDevice extends HdmiCecLocalDeviceAudioSystem {
+
+ private boolean mCanGoToStandby;
+ private boolean mIsStandby;
+ private boolean mIsDisabled;
+
+ MockAudioSystemDevice(HdmiControlService service) {
+ super(service);
+ }
+
+ @Override
+ protected void onAddressAllocated(int logicalAddress, int reason) {
+ }
+
+ @Override
+ protected int getPreferredAddress() {
+ return 0;
+ }
+
+ @Override
+ protected void setPreferredAddress(int addr) {
+ }
+
+ @Override
+ protected boolean canGoToStandby() {
+ return mCanGoToStandby;
+ }
+
+ @Override
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
+ mIsDisabled = true;
+ originalCallback.onCleared(this);
+ }
+
+ @Override
+ protected void onStandby(boolean initiatedByCec, int standbyAction) {
+ mIsStandby = true;
+ }
+
+ protected boolean isStandby() {
+ return mIsStandby;
+ }
+
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
+ protected void setCanGoToStandby(boolean canGoToStandby) {
+ mCanGoToStandby = canGoToStandby;
+ }
+ }
+
+ private static class HdmiControlStatusCallback extends IHdmiControlStatusChangeListener.Stub {
+ boolean mCecEnabled = false;
+ boolean mCecAvailable = false;
+
+ @Override
+ public void onStatusChange(int isCecEnabled, boolean isCecAvailable)
+ throws RemoteException {
+ mCecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+ mCecAvailable = isCecAvailable;
+ }
+ }
+
+ private static class VolumeControlFeatureCallback extends
+ IHdmiCecVolumeControlFeatureListener.Stub {
+ boolean mCallbackReceived = false;
+ int mVolumeControlEnabled = -1;
+
+ @Override
+ public void onHdmiCecVolumeControlFeature(int enabled) throws RemoteException {
+ this.mCallbackReceived = true;
+ this.mVolumeControlEnabled = enabled;
+ }
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index 9f0cdd56ed7f..c89c32a03553 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -31,6 +31,8 @@ import androidx.test.filters.SmallTest;
import com.android.server.hdmi.HdmiUtils.CodecSad;
import com.android.server.hdmi.HdmiUtils.DeviceConfig;
+import com.google.common.testing.EqualsTester;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -116,6 +118,51 @@ public class HdmiUtilsTest {
}
@Test
+ public void testEqualsCodecSad() {
+ byte[] sad = {0x0a, 0x1b, 0x2c};
+ String sadString = "0a1b2c";
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sad),
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString))
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_LPCM, sadString + "01"))
+ .addEqualityGroup(new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString))
+ .addEqualityGroup(
+ new HdmiUtils.CodecSad(Constants.AUDIO_CODEC_DD, sadString + "01"))
+ .testEquals();
+ }
+
+ @Test
+ public void testEqualsDeviceConfig() {
+ String name = "Name";
+
+ CodecSad expectedCodec1 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "011a03");
+ CodecSad expectedCodec2 = new CodecSad(Constants.AUDIO_CODEC_DD, "0d0506");
+ CodecSad expectedCodec3 = new CodecSad(Constants.AUDIO_CODEC_LPCM, "010203");
+ CodecSad expectedCodec4 = new CodecSad(Constants.AUDIO_CODEC_DD, "040506");
+
+ List<CodecSad> list1 = new ArrayList();
+ list1.add(expectedCodec1);
+ list1.add(expectedCodec2);
+ list1.add(expectedCodec3);
+
+ List<CodecSad> list1Duplicate = new ArrayList(list1);
+
+ List<CodecSad> list2 = new ArrayList(list1);
+ list2.add(expectedCodec4);
+
+ new EqualsTester()
+ .addEqualityGroup(
+ new HdmiUtils.DeviceConfig(name, list1),
+ new HdmiUtils.DeviceConfig(name, list1Duplicate))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig(name, list2))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list1))
+ .addEqualityGroup(new HdmiUtils.DeviceConfig("my" + name, list2))
+ .testEquals();
+ }
+
+ @Test
public void parseSampleXML() {
List<DeviceConfig> config = new ArrayList<>();
try {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index b820df3871ce..5074f64a9830 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -23,7 +23,6 @@ import static com.android.server.hdmi.OneTouchPlayAction.STATE_WAITING_FOR_REPOR
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;
@@ -31,11 +30,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.media.AudioManager;
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.IThermalService;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import androidx.test.InstrumentationRegistry;
@@ -46,8 +41,6 @@ import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
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.Collections;
@@ -69,38 +62,22 @@ public class OneTouchPlayActionTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private FakeHdmiCecConfig mHdmiCecConfig;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mPhysicalAddress;
- @Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
-
/**
* Manually called before tests, because some tests require HDMI control to be disabled.
* @param hdmiControlEnabled whether to enable the global setting hdmi_control.
* @throws Exception
*/
public void setUp(boolean hdmiControlEnabled) throws Exception {
- MockitoAnnotations.initMocks(this);
-
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
mHdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
- setHdmiControlEnabled(hdmiControlEnabled);
-
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
@@ -114,21 +91,11 @@ public class OneTouchPlayActionTest {
}
@Override
- void wakeUp() {
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -137,6 +104,7 @@ public class OneTouchPlayActionTest {
Looper looper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(looper);
mHdmiControlService.setHdmiCecConfig(mHdmiCecConfig);
+ setHdmiControlEnabled(hdmiControlEnabled);
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
@@ -144,6 +112,8 @@ public class OneTouchPlayActionTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
@@ -176,20 +146,27 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
- HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ HdmiCecMessage reportPowerStatusOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_ON);
action.processCommand(reportPowerStatusOn);
mTestLooper.dispatchAll();
@@ -219,20 +196,27 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
- HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ HdmiCecMessage reportPowerStatusOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_ON);
action.processCommand(reportPowerStatusOn);
mTestLooper.dispatchAll();
@@ -262,21 +246,27 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
- HdmiCecMessage reportPowerStatusTransientToOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS,
- POWER_TRANSIENT_TO_ON);
+ HdmiCecMessage reportPowerStatusTransientToOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_TRANSIENT_TO_ON);
action.processCommand(reportPowerStatusTransientToOn);
action.handleTimerEvent(STATE_WAITING_FOR_REPORT_POWER_STATUS);
mTestLooper.dispatchAll();
@@ -284,8 +274,12 @@ public class OneTouchPlayActionTest {
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
- HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ HdmiCecMessage reportPowerStatusOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_ON);
action.processCommand(reportPowerStatusOn);
mTestLooper.dispatchAll();
@@ -315,12 +309,15 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
@@ -361,12 +358,15 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
@@ -396,20 +396,27 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
- HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ HdmiCecMessage reportPowerStatusOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_ON);
action.processCommand(reportPowerStatusOn);
mTestLooper.dispatchAll();
@@ -441,20 +448,27 @@ public class OneTouchPlayActionTest {
playbackDevice.addAndStartAction(action);
mTestLooper.dispatchAll();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
- .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
- HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
- ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ HdmiCecMessage reportPowerStatusOn =
+ new HdmiCecMessage(
+ ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REPORT_POWER_STATUS,
+ POWER_ON);
action.processCommand(reportPowerStatusOn);
mTestLooper.dispatchAll();
@@ -488,11 +502,11 @@ public class OneTouchPlayActionTest {
mTestLooper.dispatchAll();
- HdmiCecMessage reportPowerStatusMessage = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_TV,
- playbackDevice.mAddress,
- HdmiControlManager.POWER_STATUS_ON
- );
+ HdmiCecMessage reportPowerStatusMessage =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_ON);
mNativeWrapper.onCecMessage(reportPowerStatusMessage);
mTestLooper.dispatchAll();
@@ -500,10 +514,12 @@ public class OneTouchPlayActionTest {
assertThat(mHdmiControlService.isAddressAllocated()).isTrue();
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
assertThat(playbackDevice.isActiveSource()).isTrue();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
}
@@ -524,21 +540,23 @@ public class OneTouchPlayActionTest {
TestCallback callback = new TestCallback();
mHdmiControlService.oneTouchPlay(callback);
- HdmiCecMessage reportPowerStatusMessage = HdmiCecMessageBuilder.buildReportPowerStatus(
- Constants.ADDR_TV,
- playbackDevice.mAddress,
- HdmiControlManager.POWER_STATUS_ON
- );
+ HdmiCecMessage reportPowerStatusMessage =
+ HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ playbackDevice.getDeviceInfo().getLogicalAddress(),
+ HdmiControlManager.POWER_STATUS_ON);
mNativeWrapper.onCecMessage(reportPowerStatusMessage);
mTestLooper.dispatchAll();
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
assertThat(playbackDevice.isActiveSource()).isTrue();
- HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
- playbackDevice.mAddress, mPhysicalAddress);
- HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
- ADDR_TV);
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), mPhysicalAddress);
+ HdmiCecMessage textViewOn =
+ HdmiCecMessageBuilder.buildTextViewOn(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
}
@@ -575,8 +593,9 @@ public class OneTouchPlayActionTest {
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(
- playbackDevice.mAddress, ADDR_TV);
+ HdmiCecMessage standbyMessage =
+ HdmiCecMessageBuilder.buildStandby(
+ playbackDevice.getDeviceInfo().getLogicalAddress(), ADDR_TV);
assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessage);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index a9880c0502e8..a12aa295bcd6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -24,7 +24,6 @@ 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;
@@ -32,11 +31,7 @@ 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;
@@ -46,8 +41,6 @@ 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.Collections;
@@ -61,31 +54,17 @@ public class PowerStatusMonitorActionTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
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()));
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
mHdmiControlService = new HdmiControlService(mContextSpy,
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV)) {
@Override
@@ -100,21 +79,11 @@ public class PowerStatusMonitorActionTest {
}
@Override
- void wakeUp() {
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -140,6 +109,8 @@ public class PowerStatusMonitorActionTest {
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, false);
mNativeWrapper.setPortInfo(hdmiPortInfo);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -156,9 +127,9 @@ public class PowerStatusMonitorActionTest {
assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_UNKNOWN);
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_1);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
reportPowerStatus(ADDR_PLAYBACK_1, false, HdmiControlManager.POWER_STATUS_ON);
@@ -195,9 +166,9 @@ public class PowerStatusMonitorActionTest {
assertPowerStatus(ADDR_PLAYBACK_1, HdmiControlManager.POWER_STATUS_ON);
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_1);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
@@ -224,14 +195,14 @@ public class PowerStatusMonitorActionTest {
action.start();
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_1);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
- HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_2);
+ HdmiCecMessage giveDevicePowerStatus2 =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus2);
}
@@ -249,15 +220,15 @@ public class PowerStatusMonitorActionTest {
action.start();
mTestLooper.dispatchAll();
- HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_1);
+ HdmiCecMessage giveDevicePowerStatus =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_1);
assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
- HdmiCecMessage giveDevicePowerStatus2 = HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
- mTvDevice.mAddress,
- ADDR_PLAYBACK_2);
+ HdmiCecMessage giveDevicePowerStatus2 =
+ HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mTvDevice.getDeviceInfo().getLogicalAddress(), ADDR_PLAYBACK_2);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus2);
}
@@ -270,7 +241,8 @@ public class PowerStatusMonitorActionTest {
}
private void reportPowerStatus(int logicalAddress, boolean broadcast, int powerStatus) {
- int destination = broadcast ? ADDR_BROADCAST : mTvDevice.mAddress;
+ int destination =
+ broadcast ? ADDR_BROADCAST : mTvDevice.getDeviceInfo().getLogicalAddress();
HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
logicalAddress, destination,
powerStatus);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
new file mode 100644
index 000000000000..9589b73371bf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.RequestSadAction.RequestSadCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RequestSadActionTest {
+
+ private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
+ private static final ArrayList<Integer> CODECS_TO_QUERY_1 = new ArrayList<Integer>(
+ Arrays.asList(Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
+ Constants.AUDIO_CODEC_MPEG1, Constants.AUDIO_CODEC_MP3));
+ private static final ArrayList<Integer> CODECS_TO_QUERY_2 = new ArrayList<Integer>(
+ Arrays.asList(Constants.AUDIO_CODEC_MPEG2, Constants.AUDIO_CODEC_AAC,
+ Constants.AUDIO_CODEC_DTS, Constants.AUDIO_CODEC_ATRAC));
+ private static final ArrayList<Integer> CODECS_TO_QUERY_3 = new ArrayList<Integer>(
+ Arrays.asList(Constants.AUDIO_CODEC_ONEBITAUDIO, Constants.AUDIO_CODEC_DDP,
+ Constants.AUDIO_CODEC_DTSHD, Constants.AUDIO_CODEC_TRUEHD));
+ private static final ArrayList<Integer> CODECS_TO_QUERY_4 = new ArrayList<Integer>(
+ Arrays.asList(Constants.AUDIO_CODEC_DST, Constants.AUDIO_CODEC_WMAPRO,
+ Constants.AUDIO_CODEC_MAX));
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+ private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private List<byte[]> mSupportedSads;
+ private RequestSadCallback mCallback =
+ new RequestSadCallback() {
+ @Override
+ public void onRequestSadDone(
+ List<byte[]> supportedSads) {
+ mSupportedSads = supportedSads;
+ }
+ };
+
+ private static byte[] concatenateSads(List<byte[]> sads) {
+ byte[] concatenatedSads = new byte[sads.size() * 3];
+ for (int i = 0; i < sads.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ concatenatedSads[3 * i + j] = sads.get(i)[j];
+ }
+ }
+ return concatenatedSads;
+ }
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ }
+
+ @Test
+ public void noResponse_queryAgain_emptyResult() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void featureAbort_dontQueryAgain_emptyResult() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+ HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
+ Constants.ABORT_INVALID_OPERAND);
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(featureAbort);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(featureAbort);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(featureAbort);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ action.processCommand(featureAbort);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void allSupported_completeResult() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_1 = new byte[]{
+ 0x01, 0x18, 0x4A,
+ 0x02, 0x64, 0x5A,
+ 0x03, 0x4B, 0x00,
+ 0x04, 0x20, 0x0A};
+ HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response1);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_2 = new byte[]{
+ 0x05, 0x18, 0x4A,
+ 0x06, 0x64, 0x5A,
+ 0x07, 0x4B, 0x00,
+ 0x08, 0x20, 0x0A};
+ HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response2);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_3 = new byte[]{
+ 0x09, 0x18, 0x4A,
+ 0x0A, 0x64, 0x5A,
+ 0x0B, 0x4B, 0x00,
+ 0x0C, 0x20, 0x0A};
+ HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response3);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_4 = new byte[]{
+ 0x0D, 0x18, 0x4A,
+ 0x0E, 0x64, 0x5A,
+ 0x0F, 0x4B, 0x00};
+ HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ action.processCommand(response4);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(15);
+ assertThat(Arrays.equals(sadsToRespond_1,
+ concatenateSads(mSupportedSads.subList(0, 4)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_2,
+ concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_3,
+ concatenateSads(mSupportedSads.subList(8, 12)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_4,
+ concatenateSads(mSupportedSads.subList(12, 15)))).isTrue();
+ }
+
+ @Test
+ public void subsetSupported_subsetResult() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_1 = new byte[]{
+ 0x01, 0x18, 0x4A,
+ 0x03, 0x4B, 0x00,
+ 0x04, 0x20, 0x0A};
+ HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response1);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_2 = new byte[]{
+ 0x08, 0x20, 0x0A};
+ HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response2);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_3 = new byte[]{
+ 0x09, 0x18, 0x4A,
+ 0x0A, 0x64, 0x5A,
+ 0x0B, 0x4B, 0x00,
+ 0x0C, 0x20, 0x0A};
+ HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response3);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_4 = new byte[]{
+ 0x0F, 0x4B, 0x00};
+ HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ action.processCommand(response4);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(9);
+ assertThat(Arrays.equals(sadsToRespond_1,
+ concatenateSads(mSupportedSads.subList(0, 3)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_2,
+ concatenateSads(mSupportedSads.subList(3, 4)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_3,
+ concatenateSads(mSupportedSads.subList(4, 8)))).isTrue();
+ assertThat(Arrays.equals(sadsToRespond_4,
+ concatenateSads(mSupportedSads.subList(8, 9)))).isTrue();
+ }
+
+ @Test
+ public void invalidCodecs_emptyResults() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_1 = new byte[]{
+ 0x20, 0x18, 0x4A,
+ 0x21, 0x64, 0x5A,
+ 0x22, 0x4B, 0x00,
+ 0x23, 0x20, 0x0A};
+ HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response1);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_2 = new byte[]{
+ 0x24, 0x18, 0x4A,
+ 0x25, 0x64, 0x5A,
+ 0x26, 0x4B, 0x00,
+ 0x27, 0x20, 0x0A};
+ HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response2);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_3 = new byte[]{
+ 0x28, 0x18, 0x4A,
+ 0x29, 0x64, 0x5A,
+ 0x2A, 0x4B, 0x00,
+ 0x2B, 0x20, 0x0A};
+ HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response3);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_4 = new byte[]{
+ 0x2C, 0x18, 0x4A,
+ 0x2D, 0x64, 0x5A,
+ 0x2E, 0x4B, 0x00};
+ HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ action.processCommand(response4);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void invalidMessageLength_queryAgain() {
+ RequestSadAction action = new RequestSadAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM,
+ mCallback);
+ action.start();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected1 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_1.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_1 = new byte[]{
+ 0x01, 0x18,
+ 0x02, 0x64, 0x5A,
+ 0x03, 0x4B, 0x00,
+ 0x04, 0x20, 0x0A};
+ HdmiCecMessage response1 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response1);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected1);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected2 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_2.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_2 = new byte[]{
+ 0x05, 0x18, 0x4A,
+ 0x06, 0x64, 0x5A,
+ 0x07,
+ 0x08, 0x20, 0x0A};
+ HdmiCecMessage response2 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response2);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected2);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected3 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_3.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_3 = new byte[0];
+ HdmiCecMessage response3 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_3);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response3);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected3);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expected4 = HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ Constants.ADDR_AUDIO_SYSTEM,
+ CODECS_TO_QUERY_4.stream().mapToInt(i -> i).toArray());
+ byte[] sadsToRespond_4 = new byte[]{
+ 0x0D, 0x18, 0x4A,
+ 0x0E, 0x64, 0x5A,
+ 0x0F, 0x4B};
+ HdmiCecMessage response4 = HdmiCecMessageBuilder.buildReportShortAudioDescriptor(
+ Constants.ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(), sadsToRespond_4);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ mNativeWrapper.clearResultMessages();
+ action.processCommand(response4);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expected4);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mSupportedSads.size()).isEqualTo(0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
new file mode 100644
index 000000000000..0916b1225197
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -0,0 +1,261 @@
+/*
+ * 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.server.hdmi;
+
+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_TUNER_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+import static com.android.server.hdmi.Constants.MESSAGE_ACTIVE_SOURCE;
+import static com.android.server.hdmi.Constants.MESSAGE_ROUTING_INFORMATION;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+import static com.android.server.hdmi.RoutingControlAction.STATE_WAIT_FOR_ROUTING_INFORMATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.hdmi.HdmiCecFeatureAction.ActionTimer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class RoutingControlActionTest {
+ /*
+ * Example connection diagram used in tests. Double-lined paths indicate the currently active
+ * routes.
+ *
+ *
+ * +-----------+
+ * | TV |
+ * | 0.0.0.0 |
+ * +---+-----+-+
+ * | |
+ * <----------+ 1) AVR -> Switch
+ * +----------+ | | +-----------+
+ * | AVR +---------+ +--+ Switch |
+ * | 1.0.0.0 | | 2.0.0.0 |
+ * +--+---++--+ +--++-----+-+ <-------+ 2) Recorder -> Blu-ray
+ * | || || |
+ * | || || +--------+
+ * +-----------+ | || +----------+ +----++----+ |
+ * | XBox +--+ ++--+ Tuner | | Blueray | +-----+----+
+ * | 1.1.0.0 | | 1.2.0.0 | | 2.1.0.0 | | Recorder |
+ * +-----------+ +----++----+ +----------+ | 2.2.0.0 |
+ * || +----------+
+ * ||
+ * +----++----+
+ * | Player |
+ * | 1.2.1.0 |
+ * +----------+
+ *
+ */
+
+ private static final int PHYSICAL_ADDRESS_TV = 0x0000;
+ private static final int PHYSICAL_ADDRESS_AVR = 0x1000;
+ private static final int PHYSICAL_ADDRESS_SWITCH = 0x2000;
+ private static final int PHYSICAL_ADDRESS_TUNER = 0x1200;
+ private static final int PHYSICAL_ADDRESS_PLAYER = 0x1210;
+ private static final int PHYSICAL_ADDRESS_BLUERAY = 0x2100;
+ private static final int PHYSICAL_ADDRESS_RECORDER = 0x2200;
+ private static final int PORT_1 = 1;
+ private static final int PORT_2 = 2;
+ private static final int VENDOR_ID_AVR = 0x11233;
+
+ private static final byte[] TUNER_PARAM =
+ new byte[]{(PHYSICAL_ADDRESS_TUNER >> 8) & 0xFF, PHYSICAL_ADDRESS_TUNER & 0xFF};
+ private static final byte[] PLAYER_PARAM =
+ new byte[]{(PHYSICAL_ADDRESS_PLAYER >> 8) & 0xFF, PHYSICAL_ADDRESS_PLAYER & 0xFF};
+
+ private static final HdmiDeviceInfo DEVICE_INFO_AVR =
+ new HdmiDeviceInfo(ADDR_AUDIO_SYSTEM, PHYSICAL_ADDRESS_AVR, PORT_1,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM, VENDOR_ID_AVR, "Audio");
+ private static final HdmiDeviceInfo DEVICE_INFO_PLAYER =
+ new HdmiDeviceInfo(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYER, PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK, VENDOR_ID_AVR, "Player");
+ private static final HdmiCecMessage ROUTING_INFORMATION_TUNER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, TUNER_PARAM);
+ private static final HdmiCecMessage ROUTING_INFORMATION_PLAYER = new HdmiCecMessage(
+ ADDR_UNREGISTERED, ADDR_BROADCAST, MESSAGE_ROUTING_INFORMATION, PLAYER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_TUNER = new HdmiCecMessage(
+ ADDR_TUNER_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, TUNER_PARAM);
+ private static final HdmiCecMessage ACTIVE_SOURCE_PLAYER = new HdmiCecMessage(
+ ADDR_PLAYBACK_1, ADDR_BROADCAST, MESSAGE_ACTIVE_SOURCE, PLAYER_PARAM);
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
+ private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ private static RoutingControlAction createRoutingControlAction(HdmiCecLocalDeviceTv localDevice,
+ TestInputSelectCallback callback) {
+ return new RoutingControlAction(localDevice, PHYSICAL_ADDRESS_AVR, callback);
+ }
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mMyLooper = mTestLooper.getLooper();
+
+ HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(context);
+
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext(),
+ Collections.emptyList()) {
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+
+ @Override
+ protected void writeStringSystemProperty(String key, String value) {
+ // do nothing
+ }
+
+ @Override
+ boolean isPowerStandbyOrTransient() {
+ return false;
+ }
+
+ @Override
+ protected HdmiCecConfig getHdmiCecConfig() {
+ return hdmiCecConfig;
+ }
+ };
+
+ mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
+ mHdmiCecLocalDeviceTv.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController = HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDeviceTv);
+ HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[1];
+ hdmiPortInfos[0] =
+ new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, PHYSICAL_ADDRESS_AVR,
+ true, false, false);
+ mNativeWrapper.setPortInfo(hdmiPortInfos);
+ mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ mHdmiControlService.setPowerManager(mPowerManager);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+ }
+
+ // Routing control succeeds against the device connected directly to the port. Action
+ // won't get any <Routing Information> in this case. It times out on <Routing Information>,
+ // regards the directly connected one as the new routing path to switch to.
+ @Test
+ public void testRoutingControl_succeedForDirectlyConnectedDevice() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_AVR);
+
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_AVR);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ // Succeeds by receiving a couple of <Routing Information> commands, followed by
+ // <Set Stream Path> going out in the end.
+ @Test
+ public void testRoutingControl_succeedForDeviceBehindSwitch() {
+ TestInputSelectCallback callback = new TestInputSelectCallback();
+ TestActionTimer actionTimer = new TestActionTimer();
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(DEVICE_INFO_PLAYER);
+ RoutingControlAction action = createRoutingControlAction(mHdmiCecLocalDeviceTv, callback);
+ action.setActionTimer(actionTimer);
+ action.start();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_ROUTING_INFORMATION);
+
+ action.processCommand(ROUTING_INFORMATION_TUNER);
+ action.processCommand(ROUTING_INFORMATION_PLAYER);
+
+ action.handleTimerEvent(actionTimer.getState());
+ mTestLooper.dispatchAll();
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ ADDR_TV, PHYSICAL_ADDRESS_PLAYER);
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ private static class TestActionTimer implements ActionTimer {
+ private int mState;
+
+ @Override
+ public void sendTimerMessage(int state, long delayMillis) {
+ mState = state;
+ }
+
+ @Override
+ public void clearTimerMessage() {
+ }
+
+ private int getState() {
+ return mState;
+ }
+ }
+
+ private static class TestInputSelectCallback extends IHdmiControlCallback.Stub {
+ private final List<Integer> mCallbackResult = new ArrayList<Integer>();
+
+ @Override
+ public void onComplete(int result) {
+ mCallbackResult.add(result);
+ }
+
+ private int getResult() {
+ assert (mCallbackResult.size() == 1);
+ return mCallbackResult.get(0);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 2cf4ef1f14cb..949cf9fd8cc1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -24,17 +24,12 @@ import static com.android.server.hdmi.SystemAudioAutoInitiationAction.RETRIES_ON
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiPortInfo;
import android.media.AudioManager;
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.IThermalService;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import androidx.test.InstrumentationRegistry;
@@ -44,10 +39,9 @@ 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.Collections;
/**
* Test for {@link SystemAudioAutoInitiationAction}.
@@ -59,6 +53,7 @@ public class SystemAudioAutoInitiationActionTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
+ private FakePowerManagerWrapper mPowerManager;
private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
@@ -66,27 +61,13 @@ public class SystemAudioAutoInitiationActionTest {
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mPhysicalAddress;
- @Mock
- private IPowerManager mIPowerManagerMock;
- @Mock
- private IThermalService mIThermalServiceMock;
-
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
- when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper)));
- when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
- new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper)));
- when(mIPowerManagerMock.isInteractive()).thenReturn(true);
-
- mHdmiControlService = new HdmiControlService(mContextSpy) {
+
+ mHdmiControlService = new HdmiControlService(mContextSpy, Collections.emptyList()) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@@ -99,21 +80,11 @@ public class SystemAudioAutoInitiationActionTest {
}
@Override
- void wakeUp() {
- }
-
- @Override
boolean isPowerStandby() {
return false;
}
@Override
- protected PowerManager getPowerManager() {
- return new PowerManager(mContextSpy, mIPowerManagerMock,
- mIThermalServiceMock, new Handler(myLooper));
- }
-
- @Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
@@ -136,6 +107,8 @@ public class SystemAudioAutoInitiationActionTest {
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(mContextSpy);
+ mHdmiControlService.setPowerManager(mPowerManager);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
@@ -166,12 +139,16 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -192,12 +169,16 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -216,12 +197,16 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ false);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -243,12 +228,16 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -270,12 +259,16 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
- HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
- ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
+ HdmiCecMessage reportSystemAudioMode =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM,
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ false);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
@@ -295,7 +288,8 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
mNativeWrapper.clearResultMessages();
@@ -320,7 +314,8 @@ public class SystemAudioAutoInitiationActionTest {
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
- mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+ ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
for (int i = 0; i < RETRIES_ON_TIMEOUT; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index e82c788020ed..b40650e767fd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -38,6 +38,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Collections;
+
/** Tests for {@link SystemAudioInitiationActionFromAvr} */
@SmallTest
@Presubmit
@@ -45,6 +47,7 @@ import org.junit.runners.JUnit4;
public class SystemAudioInitiationActionFromAvrTest {
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private FakePowerManagerWrapper mPowerManager;
private TestLooper mTestLooper = new TestLooper();
private boolean mShouldDispatchActiveSource;
@@ -65,7 +68,8 @@ public class SystemAudioInitiationActionFromAvrTest {
Context context = InstrumentationRegistry.getTargetContext();
- HdmiControlService hdmiControlService = new HdmiControlService(context) {
+ HdmiControlService hdmiControlService = new HdmiControlService(context,
+ Collections.emptyList()) {
@Override
void sendCecCommand(
HdmiCecMessage command, @Nullable SendMessageCallback callback) {
@@ -140,9 +144,6 @@ public class SystemAudioInitiationActionFromAvrTest {
}
@Override
- void wakeUp() {}
-
- @Override
int getPhysicalAddress() {
return 0;
}
@@ -172,6 +173,8 @@ public class SystemAudioInitiationActionFromAvrTest {
hdmiControlService, nativeWrapper, hdmiControlService.getAtomWriter());
hdmiControlService.setCecController(hdmiCecController);
hdmiControlService.initService();
+ mPowerManager = new FakePowerManagerWrapper(context);
+ hdmiControlService.setPowerManager(mPowerManager);
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -194,6 +197,7 @@ public class SystemAudioInitiationActionFromAvrTest {
}
};
mHdmiCecLocalDeviceAudioSystem.init();
+ mHdmiCecLocalDeviceAudioSystem.setDeviceInfo(mDeviceInfoForTests);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
deleted file mode 100644
index 9ab762aaa8fa..000000000000
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ /dev/null
@@ -1,92 +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.inputmethod;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Build;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MultiClientInputMethodManagerServiceTest {
-
- @Before
- public void setUp() {
- // MultiClientInputMethodManagerService is only testable if build is debuggable.
- assumeTrue(Build.IS_DEBUGGABLE);
- }
-
- @Test
- public void testQueryInputMethod_noIMEFound() {
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- emptyList())).isNull();
- }
-
- @Test
- public void testQueryInputMethod_multipleIMEsFound() {
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(new ResolveInfo(), new ResolveInfo()))).isNull();
- }
-
- @Test
- public void testQueryInputMethod_IMEFound_invalidPermission() {
- // Arrange
- ResolveInfo imeService = buildResolveInfo(/* permission= */ "",
- ApplicationInfo.FLAG_SYSTEM);
-
- // Act and assert
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(imeService))).isNull();
- }
-
- @Test
- public void testQueryInputMethod_IMEFound() {
- // Arrange
- ResolveInfo imeService = buildResolveInfo(android.Manifest.permission.BIND_INPUT_METHOD,
- ApplicationInfo.FLAG_SYSTEM);
-
- // Act and assert
- assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
- asList(imeService))).isSameInstanceAs(imeService);
- }
-
- private ResolveInfo buildResolveInfo(String permission, int flags) {
- ResolveInfo imeService = new ResolveInfo();
- imeService.serviceInfo = new ServiceInfo();
- imeService.serviceInfo.packageName = "com.android.server.inputmethod";
- imeService.serviceInfo.name = "someIMEService";
- imeService.serviceInfo.permission = permission;
- imeService.serviceInfo.applicationInfo = new ApplicationInfo();
- imeService.serviceInfo.applicationInfo.flags = flags;
- return imeService;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 8b8a7e631caf..2bda120afb9d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -40,6 +40,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -232,6 +233,21 @@ public class ApexManagerTest {
}
@Test
+ public void testGetStagedApexInfos_throwRunTimeException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).getStagedApexInfos(any());
+
+ assertThrows(RuntimeException.class,
+ () -> mApexManager.getStagedApexInfos(testParamsWithChildren()));
+ }
+
+ @Test
+ public void testGetStagedApexInfos_returnsEmptyArrayOnError() throws RemoteException {
+ doThrow(ServiceSpecificException.class).when(mApexService).getStagedApexInfos(any());
+
+ assertThat(mApexManager.getStagedApexInfos(testParamsWithChildren())).hasLength(0);
+ }
+
+ @Test
public void testMarkStagedSessionReady_throwPackageManagerException() throws RemoteException {
doAnswer(invocation -> {
throw new Exception();
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 67dd0556e098..be942d6738af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -30,9 +30,8 @@ import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
@@ -252,8 +251,8 @@ public class AppsFilterTest {
final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
watcher.register();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
watcher.verifyNoChangeReported("prepare");
android.addProtectedBroadcast("TEST_ACTION");
@@ -592,12 +591,12 @@ public class AppsFilterTest {
appsFilter.onSystemReady();
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final Signature otherSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails otherSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{otherSignature}, 1);
+ final SigningDetails otherSigningDetails =
+ new SigningDetails(new Signature[]{otherSignature}, 1);
simulateAddPackage(appsFilter, pkg("android"), 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
@@ -1199,8 +1198,8 @@ public class AppsFilterTest {
private void simulateAddBasicAndroid(AppsFilter appsFilter) throws Exception {
final Signature frameworkSignature = Mockito.mock(Signature.class);
- final PackageParser.SigningDetails frameworkSigningDetails =
- new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
+ final SigningDetails frameworkSigningDetails =
+ new SigningDetails(new Signature[]{frameworkSignature}, 1);
final ParsingPackage android = pkg("android");
simulateAddPackage(appsFilter, android, 1000,
b -> b.setSigningDetails(frameworkSigningDetails));
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index c572dd65245f..4f77afb4969f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -77,12 +77,12 @@ import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -1413,9 +1413,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
pi.applicationInfo.setVersionCode(version);
pi.signatures = null;
pi.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
genSignatures(signatures),
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
return pi;
diff --git a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
index 94ac9be4af2a..0f1b14b7fe89 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CompatibilityModeTest.java
@@ -30,9 +30,9 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -49,7 +49,7 @@ public class CompatibilityModeTest {
@Before
public void setUp() {
- mCompatibilityModeEnabled = PackageParser.sCompatibilityModeEnabled;
+ mCompatibilityModeEnabled = ParsingPackageUtils.sCompatibilityModeEnabled;
mMockAndroidPackage = mock(AndroidPackage.class);
mMockUserState = mock(PackageUserState.class);
mMockUserState.installed = true;
@@ -226,9 +226,9 @@ public class CompatibilityModeTest {
}
private void setGlobalCompatibilityMode(boolean enabled) {
- if (PackageParser.sCompatibilityModeEnabled == enabled) {
+ if (ParsingPackageUtils.sCompatibilityModeEnabled == enabled) {
return;
}
- PackageParser.setCompatibilityModeEnabled(enabled);
+ ParsingPackageUtils.setCompatibilityModeEnabled(enabled);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 1b6bddc158b8..15f57e34c015 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -17,7 +17,8 @@
package com.android.server.pm;
-import android.content.pm.PackageParser;
+import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
+
import android.content.pm.Signature;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
@@ -60,11 +61,11 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
assertEquals(0, aliases.size());
}
- /* test equivalence of PackageManager cert encoding and PackageParser manifest keys */
+ /* test equivalence of PackageManager cert encoding and ParsingPackageUtils manifest keys */
public void testPublicKeyCertReprEquiv() throws CertificateException {
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
- PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyC = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
Signature sigA = new Signature(KeySetStrings.ctsKeySetCertA);
Signature sigB = new Signature(KeySetStrings.ctsKeySetCertB);
@@ -99,9 +100,9 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
new WatchedArrayMap<String, PackageSetting>();
KeySetManagerService ksms = new KeySetManagerService(packagesMap);
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
- PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyC = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
assertEquals(ksms.encodePublicKey(keyA), KeySetStrings.ctsKeySetPublicKeyA);
assertEquals(ksms.encodePublicKey(keyB), KeySetStrings.ctsKeySetPublicKeyB);
@@ -119,7 +120,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
@@ -146,7 +147,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
@@ -176,12 +177,12 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
/* now upgrade with new key */
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
signingKeys.removeAt(0);
signingKeys.add(keyB);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
@@ -213,13 +214,13 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
/* now upgrade with new key */
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
signingKeys.removeAt(0);
signingKeys.add(keyB);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
@@ -256,13 +257,13 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys1 = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys1.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys1);
/* collect second signing key and add */
ArraySet<PublicKey> signingKeys2 = new ArraySet<PublicKey>();
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
signingKeys2.add(keyB);
mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys2);
@@ -301,7 +302,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
@@ -334,12 +335,12 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
/* give ps2 a superset (add keyB) */
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
signingKeys.add(keyB);
mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
@@ -375,12 +376,12 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
/* now with additional key */
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
signingKeys.add(keyB);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
@@ -413,7 +414,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
@@ -440,7 +441,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
definedKS.put("aliasA2", keys);
@@ -470,14 +471,14 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
/* now upgrade to different defined key-set */
keys = new ArraySet<PublicKey>();
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
keys.add(keyB);
definedKS.remove("aliasA");
definedKS.put("aliasB", keys);
@@ -510,14 +511,14 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
/* now upgrade to different set w/same alias as before */
keys = new ArraySet<PublicKey>();
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
keys.add(keyB);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
@@ -548,8 +549,8 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys1 = new ArraySet<PublicKey>();
ArraySet<PublicKey> keys2 = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
keys1.add(keyA);
keys2.add(keyB);
definedKS.put("aliasA", keys1);
@@ -558,7 +559,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* now upgrade to different set (B, C) */
keys1 = new ArraySet<PublicKey>();
- PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
+ PublicKey keyC = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
keys1.add(keyC);
definedKS.remove("aliasA");
definedKS.put("aliasC", keys1);
@@ -612,14 +613,14 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys1 = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys1.add(keyA);
definedKS.put("aliasA", keys1);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
/* now upgrade to different set */
ArraySet<PublicKey> keys2 = new ArraySet<PublicKey>();
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
keys2.add(keyB);
definedKS.remove("aliasA");
definedKS.put("aliasB", keys2);
@@ -655,7 +656,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add, and denote as an upgrade keyset */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
@@ -677,7 +678,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add and try to specify bogus upgrade keyset */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
@@ -704,7 +705,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
definedKS.put("aliasA", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
@@ -713,7 +714,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
keys = new ArraySet<PublicKey>();
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
keys.add(keyB);
definedKS.remove("aliasA");
definedKS.put("aliasB", keys);
@@ -730,7 +731,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
@@ -755,7 +756,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect signing key and add for both packages */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
signingKeys.add(keyA);
mKsms.addSigningKeySetToPackageLPw(ps1, signingKeys);
mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
@@ -781,7 +782,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase {
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
keys.add(keyA);
/* removal requires signing keyset to be specified (since all apps are
diff --git a/services/tests/servicestests/src/com/android/server/pm/OWNERS b/services/tests/servicestests/src/com/android/server/pm/OWNERS
index e15b5f57069c..2f51994a2b1a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/pm/OWNERS
@@ -1,3 +1,7 @@
include /services/core/java/com/android/server/pm/OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+
+# apex support
+per-file ApexManagerTest.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 976a588273a7..f241fe1b86bd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -101,16 +101,15 @@ public class PackageManagerServiceTest {
PackageSenderImpl sender = new PackageSenderImpl();
PackageSetting setting = null;
- PackageManagerService.PackageRemovedInfo pri =
- new PackageManagerService.PackageRemovedInfo(sender);
+ PackageRemovedInfo pri = new PackageRemovedInfo(sender);
// Initial conditions: nothing there
- Assert.assertNull(pri.removedUsers);
- Assert.assertNull(pri.broadcastUsers);
+ Assert.assertNull(pri.mRemovedUsers);
+ Assert.assertNull(pri.mBroadcastUsers);
// populateUsers with nothing leaves nothing
pri.populateUsers(null, setting);
- Assert.assertNull(pri.broadcastUsers);
+ Assert.assertNull(pri.mBroadcastUsers);
// Create a real (non-null) PackageSetting and confirm that the removed
// users are copied properly
@@ -126,22 +125,22 @@ public class PackageManagerServiceTest {
pri.populateUsers(new int[] {
1, 2, 3, 4, 5
}, setting);
- Assert.assertNotNull(pri.broadcastUsers);
- Assert.assertEquals(5, pri.broadcastUsers.length);
- Assert.assertNotNull(pri.instantUserIds);
- Assert.assertEquals(0, pri.instantUserIds.length);
+ Assert.assertNotNull(pri.mBroadcastUsers);
+ Assert.assertEquals(5, pri.mBroadcastUsers.length);
+ Assert.assertNotNull(pri.mInstantUserIds);
+ Assert.assertEquals(0, pri.mInstantUserIds.length);
// Exclude a user
- pri.broadcastUsers = null;
+ pri.mBroadcastUsers = null;
final int EXCLUDED_USER_ID = 4;
setting.setInstantApp(true, EXCLUDED_USER_ID);
pri.populateUsers(new int[] {
1, 2, 3, EXCLUDED_USER_ID, 5
}, setting);
- Assert.assertNotNull(pri.broadcastUsers);
- Assert.assertEquals(4, pri.broadcastUsers.length);
- Assert.assertNotNull(pri.instantUserIds);
- Assert.assertEquals(1, pri.instantUserIds.length);
+ Assert.assertNotNull(pri.mBroadcastUsers);
+ Assert.assertEquals(4, pri.mBroadcastUsers.length);
+ Assert.assertNotNull(pri.mInstantUserIds);
+ Assert.assertEquals(1, pri.mInstantUserIds.length);
// TODO: test that sendApplicationHiddenForUser() actually fills in
// broadcastUsers
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 22fb76b1fbe5..45e2ab478c39 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -22,6 +22,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_MORE_DETAILS;
import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND;
+import static android.content.pm.parsing.ParsingPackageUtils.parsePublicKey;
import static android.content.res.Resources.ID_NULL;
import static org.hamcrest.CoreMatchers.is;
@@ -39,7 +40,6 @@ import android.annotation.NonNull;
import android.app.PropertyInvalidatedCache;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
@@ -1216,9 +1216,9 @@ public class PackageManagerSettingsTests {
assertThat(KeySetUtils.getPubKeyRefCount(ksms, 3), is(1));
/* verify public keys properly read */
- PublicKey keyA = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
- PublicKey keyB = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
- PublicKey keyC = PackageParser.parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
+ PublicKey keyA = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyA);
+ PublicKey keyB = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyB);
+ PublicKey keyC = parsePublicKey(KeySetStrings.ctsKeySetPublicKeyC);
assertThat(KeySetUtils.getPubKey(ksms, 1), is(keyA));
assertThat(KeySetUtils.getPubKey(ksms, 2), is(keyB));
assertThat(KeySetUtils.getPubKey(ksms, 3), is(keyC));
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 128cbaaffd74..38b98ca7a9c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,6 +15,10 @@
*/
package com.android.server.pm;
+import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,6 +29,7 @@ import static org.junit.Assert.fail;
import static java.lang.Boolean.TRUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.content.Context;
@@ -35,10 +40,10 @@ import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.Property;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedComponent;
@@ -83,6 +88,7 @@ import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -106,6 +112,7 @@ public class PackageParserTest {
private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+ private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
@@ -506,6 +513,59 @@ public class PackageParserTest {
}
}
+ @Test
+ public void testParseModernPackageHasNoCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP1_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ final List<String> compatPermissions =
+ Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+ .collect(toList());
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into uses permissions.")
+ .that(pkg.getUsesPermissions().stream().map(ParsedUsesPermission::getName)
+ .collect(toList()))
+ .containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into requested permissions.")
+ .that(pkg.getRequestedPermissions()).containsNoneIn(compatPermissions);
+ assertWithMessage(
+ "Compatibility permissions shouldn't be added into implicit permissions.")
+ .that(pkg.getImplicitPermissions()).containsNoneIn(compatPermissions);
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseLegacyPackageHasCompatPermissions() throws Exception {
+ final File testFile = extractFile(TEST_APP5_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2()
+ .parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
+ assertWithMessage(
+ "Compatibility permissions should be added into uses permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+ .allMatch(pkg.getUsesPermissions().stream()
+ .map(ParsedUsesPermission::getName)
+ .collect(toList())::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into requested permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+ .allMatch(pkg.getRequestedPermissions()::contains))
+ .isTrue();
+ assertWithMessage(
+ "Compatibility permissions should be added into implicit permissions.")
+ .that(Arrays.stream(COMPAT_PERMS).map(ParsedUsesPermission::getName)
+ .allMatch(pkg.getImplicitPermissions()::contains))
+ .isTrue();
+ } finally {
+ testFile.delete();
+ }
+ }
+
/**
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.
@@ -645,7 +705,8 @@ public class PackageParserTest {
assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
assertEquals(a.getVersionName(), b.getVersionName());
assertEquals(a.getSharedUserId(), b.getSharedUserId());
- assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
+ assertArrayEquals(a.getSigningDetails().getSignatures(),
+ b.getSigningDetails().getSignatures());
assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
@@ -653,7 +714,7 @@ public class PackageParserTest {
assertEquals(a.getOverlayCategory(), b.getOverlayCategory());
assertEquals(a.getOverlayPriority(), b.getOverlayPriority());
assertEquals(a.isOverlayIsStatic(), b.isOverlayIsStatic());
- assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
+ assertEquals(a.getSigningDetails().getPublicKeys(), b.getSigningDetails().getPublicKeys());
assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
@@ -873,9 +934,9 @@ public class PackageParserTest {
.setVersionName("foo17")
.setSharedUserId("foo18")
.setSigningDetails(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[]{new Signature(new byte[16])},
- 2,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
new ArraySet<>(),
null)
)
@@ -888,7 +949,7 @@ public class PackageParserTest {
.addConfigPreference(new ConfigurationInfo())
.addReqFeature(new FeatureInfo())
.addFeatureGroup(new FeatureGroupInfo())
- .setCompileSdkVersionCodename("foo23")
+ .setCompileSdkVersionCodeName("foo23")
.setCompileSdkVersion(100)
.setOverlayCategory("foo24")
.setOverlayIsStatic(true)
@@ -896,8 +957,8 @@ public class PackageParserTest {
.setVisibleToInstantApps(true)
.setSplitHasCode(0, true)
.hideAsParsed())
- .setBaseCodePath("foo5")
- .setCodePath("foo4")
+ .setBaseApkPath("foo5")
+ .setPath("foo4")
.setVersionCode(100)
.setRestrictUpdateHash(new byte[16])
.setVersionCodeMajor(100)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f75751bf54ae..f551ad15f72e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -16,8 +16,8 @@
package com.android.server.pm;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
+import android.content.pm.SigningDetails;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -47,7 +47,7 @@ public class PackageSettingBuilder {
private String[] mUsesStaticLibraries;
private long[] mUsesStaticLibrariesVersions;
private Map<String, ArraySet<String>> mMimeGroups;
- private PackageParser.SigningDetails mSigningDetails;
+ private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
public PackageSettingBuilder setPackage(AndroidPackage pkg) {
@@ -159,7 +159,7 @@ public class PackageSettingBuilder {
}
public PackageSettingBuilder setSigningDetails(
- PackageParser.SigningDetails signingDetails) {
+ SigningDetails signingDetails) {
mSigningDetails = signingDetails;
return this;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 27f3eec655e3..b9431bf86d65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -22,11 +22,9 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Context;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.util.TypedXmlPullParser;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -39,7 +37,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
-import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
@@ -107,10 +104,10 @@ public class PackageSignaturesTest {
}
private static final int[] CAPABILITIES =
- {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
- PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
- PackageParser.SigningDetails.CertCapabilities.PERMISSION,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+ {SigningDetails.CertCapabilities.INSTALLED_DATA,
+ SigningDetails.CertCapabilities.SHARED_USER_ID,
+ SigningDetails.CertCapabilities.PERMISSION,
+ SigningDetails.CertCapabilities.ROLLBACK};
@Before
public void setUp() throws Exception {
@@ -173,7 +170,7 @@ public class PackageSignaturesTest {
assertEquals(
"The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ " attribute",
- PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
}
@Test
@@ -181,14 +178,14 @@ public class PackageSignaturesTest {
// Verifies if the sigs count attribute is missing then the signature cannot be read but the
// method does not throw an exception.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+ SigningDetails.SignatureSchemeVersion.UNKNOWN);
}
@Test
public void testReadXmlWithMissingSchemeVersion() throws Exception {
// Verifies if the schemeVersion is an invalid value the signature can still be obtained.
verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE);
}
@@ -198,7 +195,7 @@ public class PackageSignaturesTest {
// obtained.
verifyReadXmlReturnsExpectedSignaturesAndLineage(
"xml/three-signers-in-lineage-missing-scheme-version.xml",
- PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+ SigningDetails.SignatureSchemeVersion.UNKNOWN,
FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
}
@@ -386,7 +383,7 @@ public class PackageSignaturesTest {
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value",
expectedSchemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
}
/**
@@ -402,7 +399,7 @@ public class PackageSignaturesTest {
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value", schemeVersion,
- mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+ mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
for (Signature signature : signatures) {
String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index f1930d7268d7..901b200417c9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.CUR_DEVELOPMENT;
private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
@@ -91,6 +91,16 @@ public class SELinuxMMACTest {
}
@Test
+ public void getSeInfoTargetingCurDevelopment() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
+ }
+
+ @Test
public void getSeInfoNoOptInButAlreadyR() {
AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
index 12fb400cbdb7..54bfe0199975 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -108,8 +108,8 @@ class ScanRequestBuilder {
return this;
}
- PackageManagerService.ScanRequest build() {
- return new PackageManagerService.ScanRequest(
+ ScanRequest build() {
+ return new ScanRequest(
mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
mUser, mCpuAbiOverride);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 8e1fc165fb25..6c6cfd45c6c5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -51,11 +51,11 @@ import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import com.android.server.compat.PlatformCompat;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.parsing.PackageInfoUtils;
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 com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -129,16 +129,16 @@ public class ScanTests {
@Test
public void newInstallSimpleAllNominal() throws Exception {
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
.addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
- assertThat(scanResult.existingSettingCopied, is(false));
+ assertThat(scanResult.mExistingSettingCopied, is(false));
assertPathsNotDerived(scanResult);
}
@@ -147,38 +147,38 @@ public class ScanTests {
final int[] userIds = {0, 10, 11};
when(mMockUserManager.getUserIds()).thenReturn(userIds);
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setRealPkgName(null)
.addScanFlag(PackageManagerService.SCAN_NEW_INSTALL)
.addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
for (int uid : userIds) {
- assertThat(scanResult.pkgSetting.readUserState(uid).installed, is(true));
+ assertThat(scanResult.mPkgSetting.readUserState(uid).installed, is(true));
}
}
@Test
public void installRealPackageName() throws Exception {
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setRealPkgName("com.package.real")
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.pkgSetting.realName, is("com.package.real"));
+ assertThat(scanResult.mPkgSetting.realName, is("com.package.real"));
- final PackageManagerService.ScanRequest scanRequestNoRealPkg =
+ final ScanRequest scanRequestNoRealPkg =
createBasicScanRequestBuilder(
createBasicPackage(DUMMY_PACKAGE_NAME)
.setRealPackage("com.package.real"))
.build();
- final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
- assertThat(scanResultNoReal.pkgSetting.realName, nullValue());
+ final ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
+ assertThat(scanResultNoReal.mPkgSetting.realName, nullValue());
}
@Test
@@ -189,25 +189,25 @@ public class ScanTests {
.setPrimaryCpuAbiString("primaryCpuAbi")
.setSecondaryCpuAbiString("secondaryCpuAbi")
.build();
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.addScanFlag(PackageManagerService.SCAN_AS_FULL_APP)
.setPkgSetting(pkgSetting)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.existingSettingCopied, is(true));
+ assertThat(scanResult.mExistingSettingCopied, is(true));
// ensure we don't overwrite the existing pkgSetting, in case something post-scan fails
- assertNotSame(pkgSetting, scanResult.pkgSetting);
+ assertNotSame(pkgSetting, scanResult.mPkgSetting);
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
- assertThat(scanResult.pkgSetting.primaryCpuAbiString, is("primaryCpuAbi"));
- assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi"));
- assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue());
+ assertThat(scanResult.mPkgSetting.primaryCpuAbiString, is("primaryCpuAbi"));
+ assertThat(scanResult.mPkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi"));
+ assertThat(scanResult.mPkgSetting.cpuAbiOverrideString, nullValue());
assertPathsNotDerived(scanResult);
}
@@ -221,13 +221,13 @@ public class ScanTests {
.setInstantAppUserState(0, true)
.build();
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setPkgSetting(existingPkgSetting)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
}
@@ -241,27 +241,27 @@ public class ScanTests {
.setPackageName("static.lib.pkg.123")
.setVersionCodeMajor(1)
.setVersionCode(234)
- .setBaseCodePath("/some/path.apk")
+ .setBaseApkPath("/some/path.apk")
.setSplitCodePaths(new String[] {"/some/other/path.apk"});
- final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder(pkg)
+ final ScanRequest scanRequest = new ScanRequestBuilder(pkg)
.setUser(UserHandle.of(0)).build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.staticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123"));
- assertThat(scanResult.staticSharedLibraryInfo.getName(), is("static.lib"));
- assertThat(scanResult.staticSharedLibraryInfo.getLongVersion(), is(123L));
- assertThat(scanResult.staticSharedLibraryInfo.getType(), is(TYPE_STATIC));
- assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getPackageName(),
+ assertThat(scanResult.mStaticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123"));
+ assertThat(scanResult.mStaticSharedLibraryInfo.getName(), is("static.lib"));
+ assertThat(scanResult.mStaticSharedLibraryInfo.getLongVersion(), is(123L));
+ assertThat(scanResult.mStaticSharedLibraryInfo.getType(), is(TYPE_STATIC));
+ assertThat(scanResult.mStaticSharedLibraryInfo.getDeclaringPackage().getPackageName(),
is("static.lib.pkg"));
- assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
+ assertThat(scanResult.mStaticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
is(pkg.getLongVersionCode()));
- assertThat(scanResult.staticSharedLibraryInfo.getAllCodePaths(),
+ assertThat(scanResult.mStaticSharedLibraryInfo.getAllCodePaths(),
hasItems("/some/path.apk", "/some/other/path.apk"));
- assertThat(scanResult.staticSharedLibraryInfo.getDependencies(), nullValue());
- assertThat(scanResult.staticSharedLibraryInfo.getDependentPackages(), empty());
+ assertThat(scanResult.mStaticSharedLibraryInfo.getDependencies(), nullValue());
+ assertThat(scanResult.mStaticSharedLibraryInfo.getDependentPackages(), empty());
}
@Test
@@ -273,16 +273,16 @@ public class ScanTests {
.hideAsParsed())
.setVersionCodeMajor(1)
.setVersionCode(234)
- .setBaseCodePath("/some/path.apk")
+ .setBaseApkPath("/some/path.apk")
.setSplitCodePaths(new String[] {"/some/other/path.apk"});
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- final SharedLibraryInfo dynamicLib0 = scanResult.dynamicSharedLibraryInfos.get(0);
+ final SharedLibraryInfo dynamicLib0 = scanResult.mDynamicSharedLibraryInfos.get(0);
assertThat(dynamicLib0.getPackageName(), is("dynamic.lib.pkg"));
assertThat(dynamicLib0.getName(), is("liba"));
assertThat(dynamicLib0.getLongVersion(), is((long) VERSION_UNDEFINED));
@@ -295,7 +295,7 @@ public class ScanTests {
assertThat(dynamicLib0.getDependencies(), nullValue());
assertThat(dynamicLib0.getDependentPackages(), empty());
- final SharedLibraryInfo dynamicLib1 = scanResult.dynamicSharedLibraryInfos.get(1);
+ final SharedLibraryInfo dynamicLib1 = scanResult.mDynamicSharedLibraryInfos.get(1);
assertThat(dynamicLib1.getPackageName(), is("dynamic.lib.pkg"));
assertThat(dynamicLib1.getName(), is("libb"));
assertThat(dynamicLib1.getLongVersion(), is((long) VERSION_UNDEFINED));
@@ -321,10 +321,10 @@ public class ScanTests {
.hideAsParsed());
- final PackageManagerService.ScanResult scanResult = executeScan(
+ final ScanResult scanResult = executeScan(
new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build());
- assertThat(scanResult.pkgSetting.volumeUuid, is(UUID_TWO.toString()));
+ assertThat(scanResult.mPkgSetting.volumeUuid, is(UUID_TWO.toString()));
}
@Test
@@ -337,7 +337,7 @@ public class ScanTests {
.hideAsParsed());
- final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder(
+ final ScanResult scanResult = executeScan(new ScanRequestBuilder(
basicPackage)
.setPkgSetting(pkgSetting)
.addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE)
@@ -356,12 +356,12 @@ public class ScanTests {
.hideAsParsed();
- final PackageManagerService.ScanResult result =
+ final ScanResult result =
executeScan(new ScanRequestBuilder(basicPackage)
.setOriginalPkgSetting(originalPkgSetting)
.build());
- assertThat(result.request.parsedPackage.getPackageName(), is("original.package"));
+ assertThat(result.mRequest.mParsedPackage.getPackageName(), is("original.package"));
}
@Test
@@ -373,14 +373,14 @@ public class ScanTests {
.setInstantAppUserState(0, true)
.build();
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setPkgSetting(existingPkgSetting)
.addScanFlag(SCAN_AS_FULL_APP)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
}
@@ -394,14 +394,14 @@ public class ScanTests {
.setInstantAppUserState(0, false)
.build();
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setPkgSetting(existingPkgSetting)
.addScanFlag(SCAN_AS_INSTANT_APP)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/);
}
@@ -413,17 +413,17 @@ public class ScanTests {
.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
.build();
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME))
.setPkgSetting(existingPkgSetting)
.setDisabledPkgSetting(existingPkgSetting)
.addScanFlag(SCAN_NEW_INSTALL)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.request.parsedPackage,
- scanResult.pkgSetting);
+ int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.mRequest.mParsedPackage,
+ scanResult.mPkgSetting);
assertThat(appInfoFlags, hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
}
@@ -432,14 +432,14 @@ public class ScanTests {
final ParsingPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
.addUsesPermission(new ParsedUsesPermission(Manifest.permission.FACTORY_TEST, 0));
- final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
+ final ScanResult scanResult = PackageManagerService.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
System.currentTimeMillis());
- int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.request.parsedPackage,
- scanResult.request.pkgSetting);
+ int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.mRequest.mParsedPackage,
+ scanResult.mRequest.mPkgSetting);
assertThat(appInfoFlags, hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
}
@@ -449,13 +449,13 @@ public class ScanTests {
.hideAsParsed())
.setSystem(true);
- final PackageManagerService.ScanRequest scanRequest =
+ final ScanRequest scanRequest =
createBasicScanRequestBuilder(pkg)
.build();
- final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
+ final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.pkgSetting.installSource.isOrphaned, is(true));
+ assertThat(scanResult.mPkgSetting.installSource.isOrphaned, is(true));
}
private static Matcher<Integer> hasFlag(final int flag) {
@@ -478,9 +478,9 @@ public class ScanTests {
};
}
- private PackageManagerService.ScanResult executeScan(
- PackageManagerService.ScanRequest scanRequest) throws PackageManagerException {
- PackageManagerService.ScanResult result = PackageManagerService.scanPackageOnlyLI(
+ private ScanResult executeScan(
+ ScanRequest scanRequest) throws PackageManagerException {
+ ScanResult result = PackageManagerService.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
@@ -488,7 +488,7 @@ public class ScanTests {
// Need to call hideAsFinal to cache derived fields. This is normally done in PMS, but not
// in this cut down flow used for the test.
- ((ParsedPackage) result.pkgSetting.pkg).hideAsFinal();
+ ((ParsedPackage) result.mPkgSetting.pkg).hideAsFinal();
return result;
}
@@ -529,10 +529,10 @@ public class ScanTests {
}
private static void assertBasicPackageScanResult(
- PackageManagerService.ScanResult scanResult, String packageName, boolean isInstant) {
- assertThat(scanResult.success, is(true));
+ ScanResult scanResult, String packageName, boolean isInstant) {
+ assertThat(scanResult.mSuccess, is(true));
- final PackageSetting pkgSetting = scanResult.pkgSetting;
+ final PackageSetting pkgSetting = scanResult.mPkgSetting;
assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
@@ -540,35 +540,35 @@ public class ScanTests {
assertBasicApplicationInfo(scanResult, applicationInfo);
}
- private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult,
+ private static void assertBasicPackageSetting(ScanResult scanResult,
String packageName, boolean isInstant, PackageSetting pkgSetting) {
assertThat(pkgSetting.pkg.getPackageName(), is(packageName));
assertThat(pkgSetting.getInstantApp(0), is(isInstant));
assertThat(pkgSetting.usesStaticLibraries,
arrayContaining("some.static.library", "some.other.static.library"));
assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
- assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
+ assertThat(pkgSetting.pkg, is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
}
- private static void assertBasicApplicationInfo(PackageManagerService.ScanResult scanResult,
+ private static void assertBasicApplicationInfo(ScanResult scanResult,
ApplicationInfo applicationInfo) {
assertThat(applicationInfo.processName,
- is(scanResult.request.parsedPackage.getPackageName()));
+ is(scanResult.mRequest.mParsedPackage.getPackageName()));
final int uid = applicationInfo.uid;
assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM));
final String calculatedCredentialId = Environment.getDataUserCePackageDirectory(
applicationInfo.volumeUuid, UserHandle.USER_SYSTEM,
- scanResult.request.parsedPackage.getPackageName()).getAbsolutePath();
+ scanResult.mRequest.mParsedPackage.getPackageName()).getAbsolutePath();
assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId));
assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir));
}
- private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) {
- PackageSetting pkgSetting = scanResult.pkgSetting;
+ private static void assertAbiAndPathssDerived(ScanResult scanResult) {
+ PackageSetting pkgSetting = scanResult.mPkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
@@ -581,8 +581,8 @@ public class ScanTests {
assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2"));
}
- private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) {
- PackageSetting pkgSetting = scanResult.pkgSetting;
+ private static void assertPathsNotDerived(ScanResult scanResult) {
+ PackageSetting pkgSetting = scanResult.mPkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index cd98d44075ca..f1acc6679877 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -81,7 +81,7 @@ public class UserManagerServiceUserTypeTest {
/* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
- .setEnabled(true)
+ .setEnabled(1)
.setMaxAllowed(21)
.setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
@@ -316,6 +316,7 @@ public class UserManagerServiceUserTypeTest {
builders.put(userTypeFull, new UserTypeDetails.Builder()
.setName(userTypeFull)
.setBaseType(FLAG_FULL)
+ .setEnabled(0)
.setDefaultRestrictions(restrictions));
final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
@@ -323,6 +324,7 @@ public class UserManagerServiceUserTypeTest {
UserTypeDetails details = builders.get(userTypeFull).createUserTypeDetails();
assertEquals(UNLIMITED_NUMBER_OF_USERS, details.getMaxAllowedPerParent());
+ assertFalse(details.isEnabled());
assertTrue(UserRestrictionsUtils.areEqual(
makeRestrictionsBundle("no_remove_user", "no_bluetooth"),
details.getDefaultRestrictions()));
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 182760b30005..b447857a05af 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -25,8 +25,8 @@ import static org.mockito.Mockito.mock;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.platform.test.annotations.Presubmit;
import android.test.MoreAsserts;
@@ -95,9 +95,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -114,9 +114,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -197,9 +197,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -219,9 +219,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -240,9 +240,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -262,9 +262,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -285,9 +285,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -309,9 +309,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
@@ -336,9 +336,9 @@ public class BackupUtilsTest {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test";
packageInfo.signingInfo = new SigningInfo(
- new PackageParser.SigningDetails(
+ new SigningDetails(
new Signature[] {SIGNATURE_1, SIGNATURE_2},
- PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
null,
null));
packageInfo.applicationInfo = new ApplicationInfo();
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 bc0a54047ff1..16f72f7b5535 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
@@ -23,7 +23,6 @@ import static org.junit.Assert.fail;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
@@ -137,25 +136,30 @@ public class DexMetadataHelperTest {
}
private static void validatePackageDexMetadata(AndroidPackage pkg, boolean requireManifest)
- throws PackageParserException {
+ throws PackageManagerException {
Collection<String> apkToDexMetadataList =
AndroidPackageUtils.getPackageDexMetadata(pkg).values();
String packageName = pkg.getPackageName();
long versionCode = pkg.toAppInfoWithoutState().longVersionCode;
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (String dexMetadata : apkToDexMetadataList) {
- DexMetadataHelper.validateDexMetadataFile(
- dexMetadata, packageName, versionCode, requireManifest);
+ final ParseResult result = DexMetadataHelper.validateDexMetadataFile(
+ input.reset(), dexMetadata, packageName, versionCode, requireManifest);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
}
}
private static void validatePackageDexMetatadataVaryingRequireManifest(ParsedPackage pkg)
- throws PackageParserException {
+ throws PackageManagerException {
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
}
@Test
- public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
+ public void testParsePackageWithDmFileValid() throws IOException, PackageManagerException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
@@ -172,7 +176,7 @@ public class DexMetadataHelperTest {
@Test
public void testParsePackageSplitsWithDmFileValid()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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_base.apk");
@@ -195,7 +199,7 @@ public class DexMetadataHelperTest {
@Test
public void testParsePackageSplitsNoBaseWithDmFileValid()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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");
@@ -221,7 +225,7 @@ public class DexMetadataHelperTest {
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
fail("Should fail validation: empty .dm file");
- } catch (PackageParserException e) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
@@ -229,14 +233,14 @@ public class DexMetadataHelperTest {
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
fail("Should fail validation: empty .dm file");
- } catch (PackageParserException e) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageSplitsWithDmFileInvalid()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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_base.apk");
@@ -247,7 +251,7 @@ public class DexMetadataHelperTest {
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
validatePackageDexMetadata(pkg, /*requireManifest=*/true);
fail("Should fail validation: empty .dm file");
- } catch (PackageParserException e) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
@@ -255,14 +259,14 @@ public class DexMetadataHelperTest {
ParsedPackage pkg = new TestPackageParser2().parsePackage(mTmpDir, /*flags=*/0, false);
validatePackageDexMetadata(pkg, /*requireManifest=*/false);
fail("Should fail validation: empty .dm file");
- } catch (PackageParserException e) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileInvalidManifest()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk", /*validManifest=*/false);
@@ -270,14 +274,14 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileEmptyManifest()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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);
@@ -286,14 +290,14 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileBadPackageName()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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);
@@ -302,14 +306,14 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileBadVersionCode()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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);
@@ -318,14 +322,14 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileMissingPackageName()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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);
@@ -334,14 +338,14 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@Test
public void testParsePackageWithDmFileMissingVersionCode()
- throws IOException, PackageParserException {
+ throws IOException, PackageManagerException {
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);
@@ -350,7 +354,7 @@ public class DexMetadataHelperTest {
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) {
+ } catch (PackageManagerException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
}
@@ -370,7 +374,7 @@ public class DexMetadataHelperTest {
@Test
public void testPackageSplitsWithDmFileNoMatch()
- throws IOException, PackageParserException {
+ throws IOException {
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_base.apk");
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 581ff5472e92..b7eddb3365e4 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
@@ -29,6 +29,7 @@ import android.content.pm.PackageUserState
import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
import android.content.pm.ServiceInfo
+import android.content.pm.parsing.ParsingPackageUtils
import android.os.Bundle
import android.os.Debug
import android.os.Environment
@@ -109,7 +110,7 @@ open class AndroidPackageParsingTestBase {
apks.mapNotNull {
try {
packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false) to
- packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR,
+ packageParser2.parsePackage(it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR,
false)
} catch (ignored: Exception) {
// It is intentional that a failure of either call here will result in failing
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index c2b385889802..15028399e2c4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -17,7 +17,6 @@
package com.android.server.pm.parsing;
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;
@@ -29,12 +28,14 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PermissionInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedComponent;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
@@ -48,6 +49,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -335,7 +337,7 @@ public class PackageParserLegacyCoreTest {
* Copies a specified {@code resourceId} to a file. Returns a non-null file if the copy
* succeeded, or {@code null} otherwise.
*/
- File copyRawResourceToFile(String baseName, int resourceId) throws Exception {
+ File copyRawResourceToFile(String baseName, int resourceId) {
// Copy the resource to a file.
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
InputStream is = context.getResources().openRawResource(resourceId);
@@ -537,8 +539,14 @@ public class PackageParserLegacyCoreTest {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
+ ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
ParsingPackage pkg = result.getResult();
- pkg.setSigningDetails(ParsingPackageUtils.getSigningDetails(pkg, false));
+ ParseResult<SigningDetails> ret = ParsingPackageUtils.getSigningDetails(
+ input, pkg, false /*skipVerify*/);
+ if (ret.isError()) {
+ throw new IllegalStateException(ret.getErrorMessage(), ret.getException());
+ }
+ pkg.setSigningDetails(ret.getResult());
PackageInfo pi = PackageInfoWithoutStateUtils.generate(pkg, apexInfo, flags);
assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
@@ -596,7 +604,7 @@ public class PackageParserLegacyCoreTest {
try {
parsePackage(filename, resId, x -> x);
expect.withMessage("Expected parsing error %d from %s", result, filename).fail();
- } catch (PackageParser.PackageParserException expected) {
+ } catch (PackageManagerException expected) {
expect.that(expected.error).isEqualTo(result);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 4cd057cb482f..ffa19575718b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,8 +17,9 @@
package com.android.server.pm.parsing
import android.content.pm.PackageManager
-import android.content.pm.PackageParser
+import android.content.pm.parsing.ParsingPackageUtils
import android.platform.test.annotations.Postsubmit
+import com.android.server.pm.PackageManagerException
import com.android.server.pm.PackageManagerService
import com.android.server.pm.PackageManagerServiceUtils
import org.junit.Rule
@@ -80,11 +81,12 @@ class SystemPartitionParseTest {
val exceptions = buildApks()
.map {
runCatching {
- parser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
+ parser.parsePackage(
+ it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false /*useCaches*/)
}
}
.mapNotNull { it.exceptionOrNull() }
- .filterNot { (it as? PackageParser.PackageParserException)?.error ==
+ .filterNot { (it as? PackageManagerException)?.error ==
PackageManager.INSTALL_PARSE_FAILED_SKIPPED }
if (exceptions.isEmpty()) return
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
index 5012ca9ab420..6e3f754e4882 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -210,7 +210,7 @@ public class NotifierTest {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector) {
+ FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
return mNotifierMock;
}
@@ -298,6 +298,7 @@ public class NotifierTest {
BatteryStats.SERVICE_NAME)),
mInjector.createSuspendBlocker(mService, "testBlocker"),
null,
+ null,
null);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5eabc1bea148..e84e365a814a 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -215,7 +215,7 @@ public class PowerManagerServiceTest {
@Override
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
- FaceDownDetector faceDownDetector) {
+ FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
return mNotifierMock;
}
@@ -741,6 +741,33 @@ public class PowerManagerServiceTest {
}
@Test
+ public void testSuspendBlockerHeldDuringBoot() throws Exception {
+ final String suspendBlockerName = "PowerManagerService.Booting";
+
+ final boolean[] isAcquired = new boolean[1];
+ doAnswer(inv -> {
+ isAcquired[0] = false;
+ return null;
+ }).when(mNativeWrapperMock).nativeReleaseSuspendBlocker(eq(suspendBlockerName));
+
+ doAnswer(inv -> {
+ isAcquired[0] = true;
+ return null;
+ }).when(mNativeWrapperMock).nativeAcquireSuspendBlocker(eq(suspendBlockerName));
+
+ // Need to create the service after we stub the mocks for this test because some of the
+ // mocks are used during the constructor.
+ createService();
+ assertTrue(isAcquired[0]);
+
+ mService.systemReady(null);
+ assertTrue(isAcquired[0]);
+
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ assertFalse(isAcquired[0]);
+ }
+
+ @Test
public void testInattentiveSleep_hideWarningIfStayOnIsEnabledAndPluggedIn() throws Exception {
setMinimumScreenOffTimeoutConfig(5);
setAttentiveWarningDuration(120);
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 443476ccf7b7..aa6ee09e0179 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -290,45 +290,6 @@ public class BatterySaverPolicyTest extends AndroidTestCase {
assertThat(batterySaverStateOff.batterySaverEnabled).isFalse();
}
- public void testDeviceSpecific() {
- mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_1;
- mMockGlobalSettings.put(Global.BATTERY_SAVER_CONSTANTS, "");
- mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS, "");
-
- mBatterySaverPolicy.onChange();
- assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
- assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
-
-
- mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_2;
-
- mBatterySaverPolicy.onChange();
- assertThat(mBatterySaverPolicy.getFileValues(true).toString()).isEqualTo("{}");
- assertThat(mBatterySaverPolicy.getFileValues(false).toString())
- .isEqualTo("{/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq=123, "
- + "/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=456}");
-
- mDeviceSpecificConfigResId = R.string.config_batterySaverDeviceSpecificConfig_3;
-
- mBatterySaverPolicy.onChange();
- assertThat(mBatterySaverPolicy.getFileValues(true).toString())
- .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=333, "
- + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=444}");
- assertThat(mBatterySaverPolicy.getFileValues(false).toString())
- .isEqualTo("{/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq=222}");
-
-
- mMockGlobalSettings.put(Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
- "cpufreq-i=3:1234567890/4:014/5:015");
-
- mBatterySaverPolicy.onChange();
- assertThat(mBatterySaverPolicy.getFileValues(true).toString())
- .isEqualTo("{/sys/devices/system/cpu/cpu3/cpufreq/scaling_max_freq=1234567890, "
- + "/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq=14, "
- + "/sys/devices/system/cpu/cpu5/cpufreq/scaling_max_freq=15}");
- assertThat(mBatterySaverPolicy.getFileValues(false).toString()).isEqualTo("{}");
- }
-
public void testSetPolicyLevel_Off() {
mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_OFF);
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
deleted file mode 100644
index 796394f179e1..000000000000
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.power.batterysaver;
-
-import static org.junit.Assert.assertEquals;
-
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/CpuFrequenciesTest.java
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class CpuFrequenciesTest {
- private void check(ArrayMap<String, String> expected, String config) {
- CpuFrequencies actual = new CpuFrequencies().parseString(config);
- assertEquals(expected, actual.toSysFileMap());
- }
-
- @Test
- public void test() {
- check(new ArrayMap<>(), "");
-
- final ArrayMap<String, String> expected = new ArrayMap<>();
-
- expected.clear();
- expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "0");
- check(expected, "0:0");
-
- expected.clear();
- expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "0");
- expected.put("/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq", "1");
- check(expected, "0:0/1:1");
-
- expected.clear();
- expected.put("/sys/devices/system/cpu/cpu2/cpufreq/scaling_max_freq", "0");
- expected.put("/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq", "1234567890");
- check(expected, "2:0/1:1234567890");
-
- expected.clear();
- expected.put("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq", "1900800");
- expected.put("/sys/devices/system/cpu/cpu4/cpufreq/scaling_max_freq", "1958400");
- check(expected, "0:1900800/4:1958400");
-
- check(expected, "0:1900800/4:1958400/"); // Shouldn't crash.
- check(expected, "0:1900800/4:1958400/1"); // Shouldn't crash.
- check(expected, "0:1900800/4:1958400/a:1"); // Shouldn't crash.
- check(expected, "0:1900800/4:1958400/1:"); // Shouldn't crash.
- check(expected, "0:1900800/4:1958400/1:b"); // Shouldn't crash.
- }
-
- @Test
- public void testToString_returnsSanitizedStringUsedToParse() {
- String inputString = "0:1900800/4:1958400/a:1";
- String expectedString = "0:1900800/4:1958400";
- CpuFrequencies cpuFrequencies = new CpuFrequencies();
- cpuFrequencies.parseString(inputString);
- assertEquals(expectedString, cpuFrequencies.toString());
- }
-
- @Test
- public void testEquals_objectsParsedFromSameStringShouldBeEqual() {
- String inputString = "0:1900800/4:1958400/a:1";
- CpuFrequencies cpuFrequencies1 = new CpuFrequencies().parseString(inputString);
- CpuFrequencies cpuFrequencies2 = new CpuFrequencies().parseString(inputString);
- assertEquals(cpuFrequencies1, cpuFrequencies2);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
deleted file mode 100644
index ecdc58e4549d..000000000000
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
+++ /dev/null
@@ -1,405 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.power.batterysaver;
-
-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.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.platform.test.annotations.FlakyTest;
-import android.util.ArrayMap;
-
-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.ArgumentMatchers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/FileUpdaterTest.java
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FileUpdaterTest {
-
- private class FileUpdaterTestable extends FileUpdater {
- FileUpdaterTestable(Context context, Looper looper, int maxRetries, int retryIntervalMs) {
- super(context, looper, maxRetries, retryIntervalMs);
- }
-
- @Override
- String injectReadFromFileTrimmed(String file) throws IOException {
- return mInjector.injectReadFromFileTrimmed(file);
- }
-
- @Override
- void injectWriteToFile(String file, String value) throws IOException {
- mInjector.injectWriteToFile(file, value);
- }
-
- @Override
- void injectWtf(String message, Throwable e) {
- mInjector.injectWtf(message, e);
- }
-
- @Override
- File injectDefaultValuesFilename() {
- return new File(InstrumentationRegistry.getContext().getCacheDir() +
- "/test-default.xml");
- }
-
- @Override
- boolean injectShouldSkipWrite() {
- return false;
- }
- }
-
- private interface Injector {
- String injectReadFromFileTrimmed(String file) throws IOException;
- void injectWriteToFile(String file, String value) throws IOException;
- void injectWtf(String message, Throwable e);
- }
-
- private Handler mMainHandler;
-
- @Mock
- private Injector mInjector;
-
- private static final int MAX_RETRIES = 3;
-
- private FileUpdaterTestable mInstance;
-
- public static <T> T anyOrNull(Class<T> clazz) {
- return ArgumentMatchers.argThat(value -> true);
- }
-
- public static String anyOrNullString() {
- return ArgumentMatchers.argThat(value -> true);
- }
-
- @Before
- public void setUp() {
- mMainHandler = new Handler(Looper.getMainLooper());
-
- MockitoAnnotations.initMocks(this);
-
- mInstance = newInstance();
- }
-
- private FileUpdaterTestable newInstance() {
- return new FileUpdaterTestable(
- InstrumentationRegistry.getContext(),
- Looper.getMainLooper(),
- MAX_RETRIES,
- 0 /* retry with no delays*/);
- }
-
- private void waitUntilMainHandlerDrain() throws Exception {
- final CountDownLatch l = new CountDownLatch(1);
- mMainHandler.post(() -> l.countDown());
- assertTrue(l.await(5, TimeUnit.SECONDS));
- }
-
- private void veriryWtf(int times) {
- verify(mInjector, times(times)).injectWtf(anyOrNullString(), anyOrNull(Throwable.class));
- }
-
- @Test
- public void testNoWrites() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(0)).injectWriteToFile(anyOrNullString(), anyOrNullString());
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(0)).injectWriteToFile(anyOrNullString(), anyOrNullString());
-
- // No WTF should have happened.
- veriryWtf(0);
- }
-
- @Test
- public void testSimpleWrite() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "11");
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "111");
-
- // No WTF should have happened.
- veriryWtf(0);
- }
-
- @Test
- public void testMultiWrites() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
- values.put("file2", "22");
- values.put("file3", "33");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "11");
- verify(mInjector, times(1)).injectWriteToFile("file2", "22");
- verify(mInjector, times(1)).injectWriteToFile("file3", "33");
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "111");
- verify(mInjector, times(1)).injectWriteToFile("file2", "222");
- verify(mInjector, times(1)).injectWriteToFile("file3", "333");
-
- // No WTF should have happened.
- veriryWtf(0);
- }
-
- @Test
- public void testCantReadDefault() throws Exception {
- doThrow(new IOException("can't read")).when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
- values.put("file2", "22");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(0)).injectWriteToFile("file1", "11");
- verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
- veriryWtf(1);
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(0)).injectWriteToFile("file1", "111");
- verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-
- veriryWtf(1);
- }
-
- @Test
- public void testWriteGiveUp() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("fail1");
-
- doThrow(new IOException("can't write")).when(mInjector).injectWriteToFile(
- eq("fail1"), eq("33"));
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
- values.put("file2", "22");
- values.put("fail1", "33");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "11");
- verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
- verify(mInjector, times(MAX_RETRIES + 1)).injectWriteToFile("fail1", "33");
-
- // 1 WTF.
- veriryWtf(1);
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "111");
- verify(mInjector, times(1)).injectWriteToFile("file2", "222");
-
- verify(mInjector, times(1)).injectWriteToFile("fail1", "333");
-
- // No further WTF.
- veriryWtf(1);
- }
-
- @Test
- public void testSuccessWithRetry() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("fail1");
-
- final AtomicInteger counter = new AtomicInteger();
- doAnswer((inv) -> {
- if (counter.getAndIncrement() <= 1) {
- throw new IOException();
- }
- return null;
- }).when(mInjector).injectWriteToFile(eq("fail1"), eq("33"));
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
- values.put("file2", "22");
- values.put("fail1", "33");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "11");
- verify(mInjector, times(1)).injectWriteToFile("file2", "22");
-
- // Should succeed after 2 retries.
- verify(mInjector, times(3)).injectWriteToFile("fail1", "33");
-
- // No WTF.
- veriryWtf(0);
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "111");
- verify(mInjector, times(1)).injectWriteToFile("file2", "222");
- verify(mInjector, times(1)).injectWriteToFile("fail1", "333");
-
- // Still no WTF.
- veriryWtf(0);
- }
-
- @FlakyTest
- @Test
- public void testAll() throws Exception {
- // Run multiple tests on the single target instance.
-
- reset(mInjector);
- testSimpleWrite();
-
- reset(mInjector);
- testWriteGiveUp();
-
- reset(mInjector);
- testMultiWrites();
-
- reset(mInjector);
- testSuccessWithRetry();
-
- reset(mInjector);
- testMultiWrites();
- }
-
- @Test
- public void testWriteReadDefault() throws Exception {
- doReturn("111").when(mInjector).injectReadFromFileTrimmed("file1");
- doReturn("222").when(mInjector).injectReadFromFileTrimmed("file2");
- doReturn("333").when(mInjector).injectReadFromFileTrimmed("file3");
-
- // Write
- final ArrayMap<String, String> values = new ArrayMap<>();
- values.put("file1", "11");
- values.put("file2", "22");
- values.put("file3", "33");
-
- mInstance.writeFiles(values);
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "11");
- verify(mInjector, times(1)).injectWriteToFile("file2", "22");
- verify(mInjector, times(1)).injectWriteToFile("file3", "33");
-
- // Clear and reload the default.
- assertEquals(3, mInstance.getDefaultValuesForTest().size());
- mInstance.getDefaultValuesForTest().clear();
- assertEquals(0, mInstance.getDefaultValuesForTest().size());
-
- mInstance.systemReady(/*runtimeRestarted=*/ true);
-
- assertEquals(3, mInstance.getDefaultValuesForTest().size());
-
- // Reset to default
- mInstance.restoreDefault();
- waitUntilMainHandlerDrain();
-
- verify(mInjector, times(1)).injectWriteToFile("file1", "111");
- verify(mInjector, times(1)).injectWriteToFile("file2", "222");
- verify(mInjector, times(1)).injectWriteToFile("file3", "333");
-
- // Make sure the default file still exists.
- assertTrue(mInstance.injectDefaultValuesFilename().exists());
-
- // Simulate a clean boot.
- mInstance.getDefaultValuesForTest().clear();
- assertEquals(0, mInstance.getDefaultValuesForTest().size());
-
- mInstance.systemReady(/*runtimeRestarted=*/ false);
-
- // Default is empty, and the file is gone.
- assertEquals(0, mInstance.getDefaultValuesForTest().size());
- assertFalse(mInstance.injectDefaultValuesFilename().exists());
-
- // No WTF should have happened.
- veriryWtf(0);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 4618157ef56a..2a4c3fdf8f43 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -32,6 +32,7 @@ import static org.mockito.MockitoAnnotations.initMocks;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.PackageRollbackInfo.RestoreInfo;
+import android.util.SparseIntArray;
import com.android.server.pm.ApexManager;
import com.android.server.pm.Installer;
@@ -119,8 +120,9 @@ public class AppDataRollbackHelperTest {
}
private static Rollback createRollbackForId(int rollbackId) {
- return new Rollback(rollbackId, new File("/does/not/exist"), -1,
- 0, "com.xyz");
+ return new Rollback(rollbackId, new File("/does/not/exist"), -1, /* isStaged */ false, 0,
+ "com.xyz", null, new SparseIntArray(0));
+
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index c42f936d3ab4..9d56a36196bb 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -82,7 +82,7 @@ public class RollbackStoreTest {
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer'}";
@@ -102,7 +102,7 @@ public class RollbackStoreTest {
+ "'ceSnapshotInodes':[]}],'isStaged':false,'causePackages':[{'packageName':'hello',"
+ "'longVersionCode':23},{'packageName':'something','longVersionCode':999}],"
+ "'committedSessionId':45654465},'timestamp':'2019-10-01T12:29:08.855Z',"
- + "'stagedSessionId':-1,'state':'enabling','apkSessionId':-1,"
+ + "'originalSessionId':567,'state':'enabling','apkSessionId':-1,"
+ "'restoreUserDataInProgress':true, 'userId':0,"
+ "'installerPackageName':'some.installer',"
+ "'extensionVersions':[{'sdkVersion':5,'extensionVersion':25},"
@@ -129,12 +129,13 @@ public class RollbackStoreTest {
SparseIntArray extensionVersions = new SparseIntArray();
extensionVersions.put(30, 71);
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
assertThat(rollback.getBackupDir().getAbsolutePath())
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isFalse();
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
assertThat(rollback.isEnabling()).isTrue();
@@ -153,7 +154,7 @@ public class RollbackStoreTest {
.isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(897);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(897);
assertThat(rollback.info.getRollbackId()).isEqualTo(ID);
assertThat(rollback.info.getPackages()).isEmpty();
@@ -168,7 +169,7 @@ public class RollbackStoreTest {
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
origRb.setRestoreUserDataInProgress(true);
origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -218,7 +219,7 @@ public class RollbackStoreTest {
@Test
public void loadFromJsonNoExtensionVersions() throws Exception {
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -268,7 +269,7 @@ public class RollbackStoreTest {
extensionVersions.put(5, 25);
extensionVersions.put(30, 71);
Rollback expectedRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, extensionVersions);
+ ID, 567, USER, INSTALLER, null, extensionVersions);
expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
expectedRb.setRestoreUserDataInProgress(true);
@@ -315,7 +316,7 @@ public class RollbackStoreTest {
@Test
public void saveAndDelete() {
Rollback rollback = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
RollbackStore.saveRollback(rollback);
@@ -331,7 +332,7 @@ public class RollbackStoreTest {
@Test
public void saveToHistoryAndLoad() {
Rollback origRb = mRollbackStore.createNonStagedRollback(
- ID, USER, INSTALLER, null, new SparseIntArray(0));
+ ID, 567, USER, INSTALLER, null, new SparseIntArray(0));
mRollbackStore.saveRollbackToHistory(origRb);
List<Rollback> loadedRollbacks = mRollbackStore.loadHistorialRollbacks();
@@ -364,7 +365,7 @@ public class RollbackStoreTest {
assertThat(b.getApexPackageNames())
.containsExactlyElementsIn(a.getApexPackageNames());
- assertThat(b.getStagedSessionId()).isEqualTo(a.getStagedSessionId());
+ assertThat(b.getOriginalSessionId()).isEqualTo(a.getOriginalSessionId());
assertThat(b.info.getCommittedSessionId()).isEqualTo(a.info.getCommittedSessionId());
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index cf1ed4815a74..5ba4851270fd 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -74,18 +74,28 @@ public class RollbackUnitTest {
when(mMockPmi.getPackageList()).thenReturn(mPackageList);
}
+ private Rollback createStagedRollback(int rollbackId, File backupDir, int originalSessionId) {
+ return new Rollback(rollbackId, backupDir, originalSessionId, /* isStaged */ true, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
+ private Rollback createNonStagedRollback(int rollbackId, File backupDir) {
+ return new Rollback(rollbackId, backupDir, -1, /* isStaged */ false, USER,
+ INSTALLER, null, new SparseIntArray(0));
+ }
+
@Test
public void newEmptyStagedRollbackDefaults() {
int rollbackId = 123;
int sessionId = 567;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, sessionId, USER, INSTALLER);
+ Rollback rollback = createStagedRollback(rollbackId, file, sessionId);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
assertThat(rollback.isStaged()).isTrue();
- assertThat(rollback.getStagedSessionId()).isEqualTo(567);
+ assertThat(rollback.getOriginalSessionId()).isEqualTo(567);
}
@Test
@@ -93,7 +103,7 @@ public class RollbackUnitTest {
int rollbackId = 123;
File file = new File("/test/testing");
- Rollback rollback = new Rollback(rollbackId, file, -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(rollbackId, file);
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.getBackupDir().getAbsolutePath()).isEqualTo("/test/testing");
@@ -102,8 +112,7 @@ public class RollbackUnitTest {
@Test
public void rollbackMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER,
- INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
assertThat(rollback.isEnabling()).isTrue();
assertThat(rollback.isAvailable()).isFalse();
@@ -121,7 +130,7 @@ public class RollbackUnitTest {
@Test
public void deletedRollbackCannotBeMadeAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
rollback.delete(mMockDataHelper, "test");
@@ -135,7 +144,7 @@ public class RollbackUnitTest {
@Test
public void getPackageNamesAllAndJustApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 11, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 19, 1, false);
@@ -149,7 +158,7 @@ public class RollbackUnitTest {
@Test
public void includesPackagesAfterEnable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
PackageRollbackInfo pkgInfo3 = newPkgInfoFor(PKG_3, 157, 156, false);
@@ -177,7 +186,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenEnabling() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -195,7 +204,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenAvailable() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -216,7 +225,7 @@ public class RollbackUnitTest {
@Test
public void snapshotWhenDeleted() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -237,7 +246,7 @@ public class RollbackUnitTest {
@Test
public void snapshotThenDeleteNoApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, false);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -259,7 +268,7 @@ public class RollbackUnitTest {
@Test
public void snapshotThenDeleteWithApex() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -282,7 +291,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataDoesNothingIfNotInProgress() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -297,7 +306,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataDoesNothingIfPackageNotFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -313,7 +322,7 @@ public class RollbackUnitTest {
@Test
public void restoreUserDataRestoresIfInProgressAndPackageFound() {
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+ Rollback rollback = createNonStagedRollback(123, new File("/test/testing"));
PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
@@ -329,20 +338,9 @@ public class RollbackUnitTest {
}
@Test
- public void notifySessionWithSuccess() {
- int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
- sessionIds, new SparseIntArray(0));
- // The 1st invocation returns false because not all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isFalse();
- // The 2nd invocation returns true because now all child sessions are notified.
- assertThat(rollback.notifySessionWithSuccess()).isTrue();
- }
-
- @Test
public void allPackagesEnabled() {
int[] sessionIds = new int[]{ 7777, 8888 };
- Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1, false, USER, INSTALLER,
sessionIds, new SparseIntArray(0));
// #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
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 fa2123c0d09a..03ccf8ccdd54 100644
--- a/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/rotationresolver/RotationResolverManagerPerUserServiceTest.java
@@ -76,7 +76,8 @@ public class RotationResolverManagerPerUserServiceTest {
// setup a spy for the RotationResolverManagerPerUserService.
final RotationResolverManagerService mainService = new RotationResolverManagerService(
mContext);
- mService = new RotationResolverManagerPerUserService(mainService, /* Lock */ new Object(),
+ final Object lock = new Object();
+ mService = new RotationResolverManagerPerUserService(mainService, lock,
mContext.getUserId());
mCancellationSignal = new CancellationSignal();
@@ -84,15 +85,13 @@ public class RotationResolverManagerPerUserServiceTest {
mRequest = new RotationResolutionRequest("", Surface.ROTATION_0, Surface.ROTATION_0,
true, 1000L);
this.mService.mCurrentRequest = new RemoteRotationResolverService.RotationRequest(
- mMockCallbackInternal, mRequest, mCancellationSignal);
+ mMockCallbackInternal, mRequest, mCancellationSignal, lock);
this.mService.getMaster().mIsServiceEnabled = true;
ComponentName componentName = new ComponentName(PACKAGE_NAME, CLASS_NAME);
this.mService.mRemoteService = new MockRemoteRotationResolverService(mContext,
- componentName, mContext.getUserId(),
- /* idleUnbindTimeoutMs */60000L,
- /* Lock */ new Object());
+ componentName, mContext.getUserId(), /* idleUnbindTimeoutMs */60000L);
}
@Test
@@ -126,13 +125,13 @@ public class RotationResolverManagerPerUserServiceTest {
}
static class MockRemoteRotationResolverService extends RemoteRotationResolverService {
- MockRemoteRotationResolverService(Context context, ComponentName serviceName,
- int userId, long idleUnbindTimeoutMs, Object lock) {
- super(context, serviceName, userId, idleUnbindTimeoutMs, lock);
+ MockRemoteRotationResolverService(Context context, ComponentName serviceName, int userId,
+ long idleUnbindTimeoutMs) {
+ super(context, serviceName, userId, idleUnbindTimeoutMs);
}
@Override
- public void resolveRotationLocked(RotationRequest request) {
+ public void resolveRotation(RotationRequest request) {
request.mCallbackInternal.onSuccess(request.mRemoteRequest.getProposedRotation());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
new file mode 100644
index 000000000000..19474812104a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.Status;
+import android.os.HwParcel;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+import android.os.RemoteException;
+import android.system.OsConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+
+import java.util.LinkedList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class SoundHw2CompatTest {
+ @Parameterized.Parameter(0) public String mVersion;
+ @Parameterized.Parameter(1) public boolean mSupportConcurrentCapture;
+
+ private final Runnable mRebootRunnable = mock(Runnable.class);
+ private ISoundTriggerHal mCanonical;
+ private CaptureStateNotifier mCaptureStateNotifier;
+ private android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+
+ // We run the test once for every version of the underlying driver.
+ @Parameterized.Parameters(name = "{0}, concurrent={1}")
+ public static Iterable<Object[]> data() {
+ List<Object[]> result = new LinkedList<>();
+
+ for (String version : new String[]{"V2_0", "V2_1", "V2_2", "V2_3", "V2_4",}) {
+ for (boolean concurrentCapture : new boolean[]{false, true}) {
+ result.add(new Object[]{version, concurrentCapture});
+ }
+ }
+
+ return result;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mHalDriver = (android.hardware.soundtrigger.V2_0.ISoundTriggerHw) mock(Class.forName(
+ String.format("android.hardware.soundtrigger.%s.ISoundTriggerHw", mVersion)));
+
+ clearInvocations(mRebootRunnable);
+
+ // This binder is associated with the mock, so it can be cast to either version of the
+ // HAL interface.
+ final IHwBinder binder = new IHwBinder() {
+ @Override
+ public void transact(int code, HwParcel request, HwParcel reply, int flags)
+ throws RemoteException {
+ // This is a little hacky, but a very easy way to gracefully reject a request for
+ // an unsupported interface (after queryLocalInterface() returns null, the client
+ // will attempt a remote transaction to obtain the interface. RemoteException will
+ // cause it to give up).
+ throw new RemoteException();
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
+ || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw
+ || descriptor.equals("android.hardware.soundtrigger@2.4::ISoundTriggerHw")
+ && mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return mHalDriver;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+ try {
+ return mHalDriver.linkToDeath(recipient, cookie);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ try {
+ return mHalDriver.unlinkToDeath(recipient);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ };
+ when(mHalDriver.asBinder()).thenReturn(binder);
+
+ android.hardware.soundtrigger.V2_3.Properties halProperties =
+ TestUtil.createDefaultProperties_2_3(mSupportConcurrentCapture);
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
+ 0)).onValues(0, halProperties.base);
+ return null;
+ }).when(mHalDriver).getProperties(any());
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ doAnswer(invocation -> {
+ ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback) invocation.getArgument(
+ 0)).onValues(0, halProperties);
+ return null;
+ }).when(driver).getProperties_2_3(any());
+ }
+
+ mCaptureStateNotifier = spy(new CaptureStateNotifier());
+
+ mCanonical = SoundTriggerHw2Compat.create(mHalDriver, mRebootRunnable,
+ mCaptureStateNotifier);
+
+ // During initialization any method can be called, but after we're starting to enforce that
+ // no additional methods are called.
+ clearInvocations(mHalDriver);
+ }
+
+ @After
+ public void tearDown() {
+ mCanonical.detach();
+ verifyNoMoreInteractions(mHalDriver);
+ verifyNoMoreInteractions(mRebootRunnable);
+ mCaptureStateNotifier.verifyNoMoreListeners();
+ }
+
+ @Test
+ public void testSetUpAndTearDown() {
+ }
+
+ @Test
+ public void testReboot() {
+ mCanonical.reboot();
+ verify(mRebootRunnable).run();
+ }
+
+ @Test
+ public void testGetProperties() throws Exception {
+ Properties properties = mCanonical.getProperties();
+
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(driver, atMost(1)).getProperties_2_3(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture);
+ } else {
+ // It is OK for the SUT to cache the properties, so the underlying method doesn't
+ // need to be called every single time.
+ verify(mHalDriver, atMost(1)).getProperties(any());
+ TestUtil.validateDefaultProperties(properties, mSupportConcurrentCapture, 0, "");
+ }
+ }
+
+ private int loadGenericModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(), anyInt(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle,
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
+
+ verify(driver_2_4).loadSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadGenericModel(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadGenericModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadGenericModel_2_1(canonicalCallback);
+ } else {
+ return loadGenericModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadGenericModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadGenericModel(canonicalCallback);
+ }
+
+ @Test
+ public void testMaxModels() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw);
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int maxModels = TestUtil.createDefaultProperties_2_0(false).maxSoundModels;
+ int[] modelHandles = new int[maxModels];
+
+ // Load as many models as we're allowed.
+ for (int i = 0; i < maxModels; ++i) {
+ modelHandles[i] = loadGenericModel(canonicalCallback);
+ verifyNoMoreInteractions(mHalDriver);
+ clearInvocations(mHalDriver);
+ }
+
+ // Now try to load an additional one and expect failure without invoking the underlying
+ // driver.
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Unload a single model and expect a onResourcesAvailable().
+ mCanonical.unloadSoundModel(modelHandles[0]);
+ verify(mHalDriver).unloadSoundModel(modelHandles[0]);
+
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ private void testLoadGenericModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadGenericModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadGenericModelBusy_2_4();
+ }
+ }
+
+ private int loadPhraseModel_2_0(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_0(modelCaptor.getValue());
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_1(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
+ resultCallback = invocation.getArgument(3);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_1).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
+ anyInt(), any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ private int loadPhraseModel_2_4(ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 29;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
+ modelCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback.class);
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, handle);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
+ canonicalCallback));
+
+ verify(driver_2_4).loadPhraseSoundModel_2_4(modelCaptor.capture(), callbackCaptor.capture(),
+ any());
+
+ TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+ validateCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ return handle;
+ }
+
+ public int loadPhraseModel(ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ return loadPhraseModel_2_4(canonicalCallback);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ return loadPhraseModel_2_1(canonicalCallback);
+ } else {
+ return loadPhraseModel_2_0(canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testLoadPhraseModel() throws Exception {
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ loadPhraseModel(canonicalCallback);
+ }
+
+ private void testLoadPhraseModelBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw.loadPhraseSoundModel_2_4Callback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(-OsConstants.EBUSY, 0);
+ return null;
+ }).when(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ try {
+ mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(), canonicalCallback);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).loadPhraseSoundModel_2_4(any(), any(), any());
+ }
+
+ @Test
+ public void testLoadPhraseModelBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testLoadPhraseModelBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testUnloadModel() throws Exception {
+ mCanonical.unloadSoundModel(14);
+ verify(mHalDriver).unloadSoundModel(14);
+ }
+
+ private void startRecognition_2_0(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
+
+ when(mHalDriver.startRecognition(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 203, 204, config);
+ verify(mHalDriver).startRecognition(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_0(configCaptor.getValue(), 203, 204);
+ validateCallback_2_0(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_1(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ final android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver_2_1 =
+ (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
+ configCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
+ ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
+
+ when(driver_2_1.startRecognition_2_1(eq(handle), any(), any(), anyInt())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 505, 506, config);
+ verify(driver_2_1).startRecognition_2_1(eq(handle), configCaptor.capture(),
+ callbackCaptor.capture(), anyInt());
+
+ TestUtil.validateRecognitionConfig_2_1(configCaptor.getValue(), 505, 506);
+ validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ private void startRecognition_2_3(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_3.startRecognition_2_3(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 808, 909, config);
+ verify(driver_2_3).startRecognition_2_3(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 808, 909);
+ }
+
+ private void startRecognition_2_4(int handle) throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+ ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig> configCaptor =
+ ArgumentCaptor.forClass(android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
+
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(0);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ mCanonical.startRecognition(handle, 21, 22, config);
+ verify(driver_2_4).startRecognition_2_4(eq(handle), configCaptor.capture());
+ TestUtil.validateRecognitionConfig_2_3(configCaptor.getValue(), 21, 22);
+ }
+
+ private void startRecognition(int handle, ISoundTriggerHal.ModelCallback canonicalCallback)
+ throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ startRecognition_2_4(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ startRecognition_2_3(handle);
+ } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
+ startRecognition_2_1(handle, canonicalCallback);
+ } else {
+ startRecognition_2_0(handle, canonicalCallback);
+ }
+ }
+
+ @Test
+ public void testStartRecognition() throws Exception {
+ // First load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+ }
+
+ private void testStartRecognitionBusy_2_4() throws Exception {
+ final android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ final int handle = 68;
+ when(driver_2_4.startRecognition_2_4(eq(handle), any())).thenReturn(-OsConstants.EBUSY);
+
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 34, 35, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+ verify(driver_2_4).startRecognition_2_4(eq(handle), any());
+ }
+
+ @Test
+ public void testStartRecognitionBusy() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testStartRecognitionBusy_2_4();
+ }
+ }
+
+ @Test
+ public void testNoRegisterCaptureStateListener() {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, never()).registerListener(any());
+ }
+
+ @Test
+ public void testConcurrentCaptureAbort() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load.
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Then start.
+ startRecognition(handle, canonicalCallback);
+
+ // Now activate external capture.
+ mCaptureStateNotifier.setState(true);
+
+ // Expect hardware to have been stopped.
+ verify(mHalDriver).stopRecognition(handle);
+
+ // Expect an abort event (async).
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testConcurrentCaptureReject() throws Exception {
+ assumeFalse(mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw
+ || mSupportConcurrentCapture);
+ verify(mCaptureStateNotifier, atLeast(1)).registerListener(any());
+
+ // Register global callback.
+ ISoundTriggerHal.GlobalCallback globalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(globalCallback);
+
+ // Load (this registers the callback).
+ ISoundTriggerHal.ModelCallback canonicalCallback = mock(
+ ISoundTriggerHal.ModelCallback.class);
+ final int handle = loadGenericModel(canonicalCallback);
+
+ // Report external capture active.
+ mCaptureStateNotifier.setState(true);
+
+ // Then start.
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ try {
+ mCanonical.startRecognition(handle, 203, 204, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ // Deactivate external capture.
+ mCaptureStateNotifier.setState(false);
+
+ // Expect a onResourcesAvailable().
+ mCanonical.flushCallbacks();
+ verify(globalCallback).onResourcesAvailable();
+ }
+
+ @Test
+ public void testStopRecognition() throws Exception {
+ mCanonical.stopRecognition(17);
+ verify(mHalDriver).stopRecognition(17);
+ }
+
+ @Test
+ public void testForceRecognition() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver_2_2 =
+ (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
+ mCanonical.forceRecognitionEvent(14);
+ verify(driver_2_2).getModelState(14);
+ } else {
+ try {
+ mCanonical.forceRecognitionEvent(14);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
+ }
+ }
+
+ @Test
+ public void testGetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback resultCallback =
+ invocation.getArgument(2);
+
+ // This is the return of this method.
+ resultCallback.onValues(0, 99);
+ return null;
+ }).when(driver_2_3).getParameter(eq(21), eq(47), any());
+
+ assertEquals(99, mCanonical.getModelParameter(21, 47));
+ verify(driver_2_3).getParameter(eq(21), eq(47), any());
+ }
+
+ @Test
+ public void testSetParameter() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ mCanonical.setModelParameter(212, 247, 80);
+ verify(driver_2_3).setParameter(212, 247, 80);
+ }
+
+ @Test
+ public void testQueryParameterSupported() throws Exception {
+ assumeTrue(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw);
+
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.ModelParameterRange range =
+ new android.hardware.soundtrigger.V2_3.ModelParameterRange();
+ range.start = 34;
+ range.end = 45;
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ optionalRange.range(range);
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNotNull(range);
+ assertEquals(34, range.minInclusive);
+ assertEquals(45, range.maxInclusive);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ }
+
+ @Test
+ public void testQueryParameterNotSupported() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver_2_3 =
+ (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
+
+ doAnswer(invocation -> {
+ android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
+ resultCallback = invocation.getArgument(2);
+
+ // This is the return of this method.
+ android.hardware.soundtrigger.V2_3.OptionalModelParameterRange optionalRange =
+ new android.hardware.soundtrigger.V2_3.OptionalModelParameterRange();
+ resultCallback.onValues(0, optionalRange);
+ return null;
+ }).when(driver_2_3).queryParameter(eq(11), eq(12), any());
+
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ verify(driver_2_3).queryParameter(eq(11), eq(12), any());
+ } else {
+ ModelParameterRange range = mCanonical.queryParameter(11, 12);
+ assertNull(range);
+ }
+ }
+
+ private void testGlobalCallback_2_0() {
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+ // We just care that it doesn't throw.
+ }
+
+ private void testGlobalCallback_2_4() throws Exception {
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHw driver_2_4 =
+ (android.hardware.soundtrigger.V2_4.ISoundTriggerHw) mHalDriver;
+
+ ISoundTriggerHal.GlobalCallback canonicalCallback = mock(
+ ISoundTriggerHal.GlobalCallback.class);
+ mCanonical.registerCallback(canonicalCallback);
+
+ ArgumentCaptor<android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback>
+ callbackCaptor = ArgumentCaptor.forClass(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback.class);
+ verify(driver_2_4).registerGlobalCallback(callbackCaptor.capture());
+ validateGlobalCallback_2_4(callbackCaptor.getValue(), canonicalCallback);
+ }
+
+ @Test
+ public void testGlobalCallback() throws Exception {
+ if (mHalDriver instanceof android.hardware.soundtrigger.V2_4.ISoundTriggerHw) {
+ testGlobalCallback_2_4();
+ } else {
+ testGlobalCallback_2_0();
+ }
+ }
+
+ @Test
+ public void testLinkToDeath() throws Exception {
+ IBinder.DeathRecipient canonicalRecipient = mock(IBinder.DeathRecipient.class);
+ when(mHalDriver.linkToDeath(any(), anyLong())).thenReturn(true);
+ mCanonical.linkToDeath(canonicalRecipient);
+
+ ArgumentCaptor<IHwBinder.DeathRecipient> recipientCaptor = ArgumentCaptor.forClass(
+ IHwBinder.DeathRecipient.class);
+ ArgumentCaptor<Long> cookieCaptor = ArgumentCaptor.forClass(Long.class);
+ verify(mHalDriver).linkToDeath(recipientCaptor.capture(), cookieCaptor.capture());
+
+ recipientCaptor.getValue().serviceDied(cookieCaptor.getValue());
+ mCanonical.flushCallbacks();
+ verify(canonicalRecipient).binderDied();
+
+ mCanonical.unlinkToDeath(canonicalRecipient);
+ verify(mHalDriver).unlinkToDeath(recipientCaptor.getValue());
+ }
+
+ @Test
+ public void testInterfaceDescriptor() throws Exception {
+ when(mHalDriver.interfaceDescriptor()).thenReturn("ABCD");
+ assertEquals("ABCD", mCanonical.interfaceDescriptor());
+ verify(mHalDriver).interfaceDescriptor();
+ }
+
+ private void validateGlobalCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwGlobalCallback hwCallback,
+ ISoundTriggerHal.GlobalCallback canonicalCallback) throws Exception {
+ hwCallback.onResourcesAvailable();
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).onResourcesAvailable();
+ }
+
+ private void validateCallback_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback(TestUtil.createRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback(
+ TestUtil.createPhraseRecognitionEvent_2_0(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ private void validateCallback_2_4(
+ android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback hwCallback,
+ ISoundTriggerHal.ModelCallback canonicalCallback) throws Exception {
+ {
+ final int handle = 85;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.ABORT;
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+
+ hwCallback.recognitionCallback_2_1(TestUtil.createRecognitionEvent_2_1(handle, status),
+ 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).recognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.ABORTED);
+ }
+
+ {
+ final int handle = 92;
+ final int status =
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS;
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+
+ hwCallback.phraseRecognitionCallback_2_1(
+ TestUtil.createPhraseRecognitionEvent_2_1(handle, status), 99);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).phraseRecognitionCallback(eq(handle), eventCaptor.capture());
+ TestUtil.validatePhraseRecognitionEvent(eventCaptor.getValue(),
+ RecognitionStatus.SUCCESS);
+ }
+
+ {
+ final int handle = 23;
+ hwCallback.modelUnloaded(handle);
+ mCanonical.flushCallbacks();
+ verify(canonicalCallback).modelUnloaded(handle);
+ }
+ verifyNoMoreInteractions(canonicalCallback);
+ clearInvocations(canonicalCallback);
+ }
+
+ public static class CaptureStateNotifier implements ICaptureStateNotifier {
+ private final List<Listener> mListeners = new LinkedList<>();
+
+ @Override
+ public boolean registerListener(Listener listener) {
+ mListeners.add(listener);
+ return false;
+ }
+
+ @Override
+ public void unregisterListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setState(boolean state) {
+ for (Listener listener : mListeners) {
+ listener.onCaptureStateChange(state);
+ }
+ }
+
+ public void verifyNoMoreListeners() {
+ assertEquals(0, mListeners.size());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 509eb2563376..1daf8317c541 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -16,89 +16,50 @@
package com.android.server.soundtrigger_middleware;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-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.doAnswer;
+import static org.mockito.Mockito.doThrow;
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.hardware.audio.common.V2_0.AudioConfig;
-import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.V2_3.OptionalModelParameterRange;
-import android.media.audio.common.AudioChannelMask;
-import android.media.audio.common.AudioFormat;
-import android.media.soundtrigger_middleware.AudioCapabilities;
-import android.media.soundtrigger_middleware.ConfidenceLevel;
+import android.media.soundtrigger.ModelParameter;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameter;
-import android.media.soundtrigger_middleware.ModelParameterRange;
-import android.media.soundtrigger_middleware.Phrase;
-import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
-import android.media.soundtrigger_middleware.PhraseRecognitionExtra;
-import android.media.soundtrigger_middleware.PhraseSoundModel;
-import android.media.soundtrigger_middleware.RecognitionConfig;
-import android.media.soundtrigger_middleware.RecognitionEvent;
-import android.media.soundtrigger_middleware.RecognitionMode;
-import android.media.soundtrigger_middleware.RecognitionStatus;
-import android.media.soundtrigger_middleware.SoundModel;
-import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
-import android.os.HidlMemoryUtil;
-import android.os.HwParcel;
-import android.os.IHwBinder;
-import android.os.IHwInterface;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.SharedMemory;
-import android.system.ErrnoException;
import android.util.Pair;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
-import java.io.FileDescriptor;
-import java.nio.ByteBuffer;
-
-@RunWith(Parameterized.class)
+@RunWith(JUnit4.class)
public class SoundTriggerMiddlewareImplTest {
- private static final String TAG = "SoundTriggerMiddlewareImplTest";
-
- // We run the test once for every version of the underlying driver.
- @Parameterized.Parameters
- public static Object[] data() {
- return new Object[]{
- mock(android.hardware.soundtrigger.V2_0.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_1.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_2.ISoundTriggerHw.class),
- mock(android.hardware.soundtrigger.V2_3.ISoundTriggerHw.class),
- };
- }
-
- @Mock
- @Parameterized.Parameter
- public android.hardware.soundtrigger.V2_0.ISoundTriggerHw mHalDriver;
+ @Mock public ISoundTriggerHal mHalDriver = mock(ISoundTriggerHal.class);
- @Mock
- private SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider = mock(
- SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
+ @Mock private final SoundTriggerMiddlewareImpl.AudioSessionProvider mAudioSessionProvider =
+ mock(SoundTriggerMiddlewareImpl.AudioSessionProvider.class);
private SoundTriggerMiddlewareImpl mService;
@@ -106,522 +67,41 @@ public class SoundTriggerMiddlewareImplTest {
return mock(ISoundTriggerCallback.Stub.class, Mockito.CALLS_REAL_METHODS);
}
- private static SoundModel createGenericSoundModel() {
- return createSoundModel(SoundModelType.GENERIC);
- }
-
- private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
- try {
- SharedMemory shmem = SharedMemory.create("", data.length);
- ByteBuffer buffer = shmem.mapReadWrite();
- buffer.put(data);
- return shmem.getFileDescriptor();
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SoundModel createSoundModel(int type) {
- SoundModel model = new SoundModel();
- model.type = type;
- model.uuid = "12345678-2345-3456-4567-abcdef987654";
- model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
- byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
- model.dataSize = data.length;
- return model;
- }
-
- private static PhraseSoundModel createPhraseSoundModel() {
- PhraseSoundModel model = new PhraseSoundModel();
- model.common = createSoundModel(SoundModelType.KEYPHRASE);
- model.phrases = new Phrase[1];
- model.phrases[0] = new Phrase();
- model.phrases[0].id = 123;
- model.phrases[0].users = new int[]{5, 6, 7};
- model.phrases[0].locale = "locale";
- model.phrases[0].text = "text";
- model.phrases[0].recognitionModes =
- RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
- return model;
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
- properties.implementor = "implementor";
- properties.description = "description";
- properties.version = 123;
- properties.uuid = new Uuid();
- properties.uuid.timeLow = 1;
- properties.uuid.timeMid = 2;
- properties.uuid.versionAndTimeHigh = 3;
- properties.uuid.variantAndClockSeqHigh = 4;
- properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
-
- properties.maxSoundModels = 456;
- properties.maxKeyPhrases = 567;
- properties.maxUsers = 678;
- properties.recognitionModes =
- android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- properties.captureTransition = true;
- properties.maxBufferMs = 321;
- properties.concurrentCapture = supportConcurrentCapture;
- properties.triggerInEvent = true;
- properties.powerConsumptionMw = 432;
- return properties;
- }
-
- private static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
- boolean supportConcurrentCapture) {
- android.hardware.soundtrigger.V2_3.Properties properties =
- new android.hardware.soundtrigger.V2_3.Properties();
- properties.base = createDefaultProperties(supportConcurrentCapture);
- properties.supportedModelArch = "supportedModelArch";
- properties.audioCapabilities =
- android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
- | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
- return properties;
- }
-
- private void validateDefaultProperties(SoundTriggerModuleProperties properties,
- boolean supportConcurrentCapture) {
- assertEquals("implementor", properties.implementor);
- assertEquals("description", properties.description);
- assertEquals(123, properties.version);
- assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
- assertEquals(456, properties.maxSoundModels);
- assertEquals(567, properties.maxKeyPhrases);
- assertEquals(678, properties.maxUsers);
- assertEquals(RecognitionMode.GENERIC_TRIGGER
- | RecognitionMode.USER_AUTHENTICATION
- | RecognitionMode.USER_IDENTIFICATION
- | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
- assertTrue(properties.captureTransition);
- assertEquals(321, properties.maxBufferMs);
- assertEquals(supportConcurrentCapture, properties.concurrentCapture);
- assertTrue(properties.triggerInEvent);
- assertEquals(432, properties.powerConsumptionMw);
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- assertEquals("supportedModelArch", properties.supportedModelArch);
- assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
- properties.audioCapabilities);
- } else {
- assertEquals("", properties.supportedModelArch);
- assertEquals(0, properties.audioCapabilities);
- }
- }
-
- private void verifyNotGetProperties() throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).getProperties(any());
- }
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.status = status;
- halEvent.type = SoundModelType.GENERIC;
- halEvent.model = hwHandle;
- halEvent.captureAvailable = true;
- // This field is ignored.
- halEvent.captureSession = 123;
- halEvent.captureDelayMs = 234;
- halEvent.capturePreambleMs = 345;
- halEvent.triggerInData = true;
- halEvent.audioConfig = new AudioConfig();
- halEvent.audioConfig.sampleRateHz = 456;
- halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
- halEvent.audioConfig.format = AudioFormat.MP3;
- // hwEvent.audioConfig.offloadInfo is irrelevant.
- halEvent.data.add((byte) 31);
- halEvent.data.add((byte) 32);
- halEvent.data.add((byte) 33);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
- int hwHandle,
- int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.RecognitionEvent();
- halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
- halEvent.header.data.clear();
- halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
- return halEvent;
- }
-
- private static void validateRecognitionEvent(RecognitionEvent event, int status) {
- assertEquals(status, event.status);
- assertEquals(SoundModelType.GENERIC, event.type);
- assertTrue(event.captureAvailable);
- assertEquals(101, event.captureSession);
- assertEquals(234, event.captureDelayMs);
- assertEquals(345, event.capturePreambleMs);
- assertTrue(event.triggerInData);
- assertEquals(456, event.audioConfig.sampleRateHz);
- assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
- assertEquals(AudioFormat.MP3, event.audioConfig.format);
- }
-
- private static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
- int hwHandle, int status) {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.PhraseRecognitionEvent();
- halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
-
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
- new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
- halExtra.id = 123;
- halExtra.confidenceLevel = 52;
- halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
- | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
- new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
- halLevel.userId = 31;
- halLevel.levelPercent = 43;
- halExtra.levels.add(halLevel);
- halEvent.phraseExtras.add(halExtra);
- return halEvent;
- }
-
- private static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event, int status) {
- validateRecognitionEvent(event.common, status);
-
- assertEquals(1, event.phraseExtras.length);
- assertEquals(123, event.phraseExtras[0].id);
- assertEquals(52, event.phraseExtras[0].confidenceLevel);
- assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
- event.phraseExtras[0].recognitionModes);
- assertEquals(1, event.phraseExtras[0].levels.length);
- assertEquals(31, event.phraseExtras[0].levels[0].userId);
- assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
- }
-
- private void initService(boolean supportConcurrentCapture) throws RemoteException {
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
- createDefaultProperties(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_0.ISoundTriggerHw.getPropertiesCallback) invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(mHalDriver).getProperties(any());
-
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.Properties properties =
- createDefaultProperties_2_3(
- supportConcurrentCapture);
- ((android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getProperties_2_3Callback)
- invocation.getArgument(
- 0)).onValues(0,
- properties);
- return null;
- }).when(driver).getProperties_2_3(any());
- }
-
- mService = new SoundTriggerMiddlewareImpl(() -> {
- return mHalDriver;
- }, mAudioSessionProvider);
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_0(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadSoundModelCallback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(mHalDriver).loadSoundModel(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.data.toArray());
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadGenericModel_2_1(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
- SoundModel model = createGenericSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
- resultCallback = invocation.getArgument(3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
-
- when(mAudioSessionProvider.acquireSession()).thenReturn(
- new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
-
- int handle = module.loadModel(model);
- verify(driver).loadSoundModel_2_1(any(), any(), anyInt(), any());
- verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel hidlModel =
- modelCaptor.getValue();
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC,
- hidlModel.header.type);
- assertEquals(model.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.uuid));
- assertEquals(model.vendorUuid, ConversionUtil.hidl2aidlUuid(hidlModel.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.data));
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
private Pair<Integer, SoundTriggerHwCallback> loadGenericModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadGenericModel_2_1(module, hwHandle);
- } else {
- return loadGenericModel_2_0(module, hwHandle);
- }
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_0(ISoundTriggerModule module,
- int hwHandle) throws RemoteException {
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.loadPhraseSoundModelCallback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.status =
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.model = hwHandle;
- callback.soundModelCallback(modelEvent, callbackCookie);
- return null;
- }).when(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
+ SoundModel model = TestUtil.createGenericSoundModel();
+ ArgumentCaptor<SoundModel> modelCaptor = ArgumentCaptor.forClass(SoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
+ when(mHalDriver.loadSoundModel(any(), any())).thenReturn(hwHandle);
when(mAudioSessionProvider.acquireSession()).thenReturn(
new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
- int handle = module.loadPhraseModel(model);
- verify(mHalDriver).loadPhraseSoundModel(any(), any(), anyInt(), any());
+ int handle = module.loadModel(model);
+ verify(mHalDriver).loadSoundModel(modelCaptor.capture(), callbackCaptor.capture());
verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.vendorUuid));
- assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, hidlModel.common.data.toArray());
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel_2_1(ISoundTriggerModule module,
+ private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(ISoundTriggerModule module,
int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- PhraseSoundModel model = createPhraseSoundModel();
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
- modelCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
- ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
-
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback callback =
- invocation.getArgument(
- 1);
- int callbackCookie = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
- resultCallback =
- invocation.getArgument(
- 3);
-
- // This is the return of this method.
- resultCallback.onValues(0, hwHandle);
-
- // This is the async mCallback that comes after.
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent modelEvent =
- new android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.ModelEvent();
- modelEvent.header.status =
- android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.SoundModelStatus.UPDATED;
- modelEvent.header.model = hwHandle;
- callback.soundModelCallback_2_1(modelEvent, callbackCookie);
- return null;
- }).when(driver).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
- cookieCaptor.capture(), any());
+ PhraseSoundModel model = TestUtil.createPhraseSoundModel();
+ ArgumentCaptor<PhraseSoundModel> modelCaptor = ArgumentCaptor.forClass(
+ PhraseSoundModel.class);
+ ArgumentCaptor<ISoundTriggerHal.ModelCallback> callbackCaptor = ArgumentCaptor.forClass(
+ ISoundTriggerHal.ModelCallback.class);
+ when(mHalDriver.loadPhraseSoundModel(any(), any())).thenReturn(hwHandle);
when(mAudioSessionProvider.acquireSession()).thenReturn(
new SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession(101, 102, 103));
int handle = module.loadPhraseModel(model);
- verify(driver).loadPhraseSoundModel_2_1(any(), any(), anyInt(), any());
+ verify(mHalDriver).loadPhraseSoundModel(modelCaptor.capture(), callbackCaptor.capture());
verify(mAudioSessionProvider).acquireSession();
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel hidlModel =
- modelCaptor.getValue();
-
- // Validate common part.
- assertEquals(android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE,
- hidlModel.common.header.type);
- assertEquals(model.common.uuid, ConversionUtil.hidl2aidlUuid(hidlModel.common.header.uuid));
- assertEquals(model.common.vendorUuid,
- ConversionUtil.hidl2aidlUuid(hidlModel.common.header.vendorUuid));
- assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
- HidlMemoryUtil.hidlMemoryToByteArray(hidlModel.common.data));
-
- // Validate phrase part.
- assertEquals(1, hidlModel.phrases.size());
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.Phrase hidlPhrase =
- hidlModel.phrases.get(0);
- assertEquals(123, hidlPhrase.id);
- assertArrayEquals(new Integer[]{5, 6, 7}, hidlPhrase.users.toArray());
- assertEquals("locale", hidlPhrase.locale);
- assertEquals("text", hidlPhrase.text);
- assertEquals(android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
- | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION,
- hidlPhrase.recognitionModes);
-
- return new Pair<>(handle,
- new SoundTriggerHwCallback(callbackCaptor.getValue(), cookieCaptor.getValue()));
- }
-
- private Pair<Integer, SoundTriggerHwCallback> loadPhraseModel(
- ISoundTriggerModule module, int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- return loadPhraseModel_2_1(module, hwHandle);
- } else {
- return loadPhraseModel_2_0(module, hwHandle);
- }
+ assertEquals(model, modelCaptor.getValue());
+ return new Pair<>(handle, new SoundTriggerHwCallback(callbackCaptor.getValue()));
}
private void unloadModel(ISoundTriggerModule module, int handle, int hwHandle)
@@ -631,204 +111,35 @@ public class SoundTriggerMiddlewareImplTest {
verify(mAudioSessionProvider).releaseSession(101);
}
- private void startRecognition_2_0(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- ArgumentCaptor<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig.class);
-
- when(mHalDriver.startRecognition(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(mHalDriver).startRecognition(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.captureRequested);
- assertEquals(102, halConfig.captureHandle);
- assertEquals(103, halConfig.captureDevice);
- assertEquals(1, halConfig.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, halConfig.data.toArray());
- }
-
- private void startRecognition_2_1(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig.class);
-
- when(driver.startRecognition_2_1(eq(hwHandle), configCaptor.capture(), any(), anyInt()))
- .thenReturn(0);
-
- RecognitionConfig config = createRecognitionConfig();
-
- module.startRecognition(handle, config);
- verify(driver).startRecognition_2_1(eq(hwHandle), any(), any(), anyInt());
-
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig =
- configCaptor.getValue();
- assertTrue(halConfig.header.captureRequested);
- assertEquals(102, halConfig.header.captureHandle);
- assertEquals(103, halConfig.header.captureDevice);
- assertEquals(1, halConfig.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig.data));
- }
-
- private void startRecognition_2_3(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- ArgumentCaptor<android.hardware.soundtrigger.V2_3.RecognitionConfig>
- configCaptor = ArgumentCaptor.forClass(
- android.hardware.soundtrigger.V2_3.RecognitionConfig.class);
-
- when(driver.startRecognition_2_3(eq(hwHandle), configCaptor.capture())).thenReturn(0);
+ private void startRecognition(ISoundTriggerModule module, int handle, int hwHandle)
+ throws RemoteException {
+ ArgumentCaptor<RecognitionConfig> configCaptor = ArgumentCaptor.forClass(
+ RecognitionConfig.class);
- RecognitionConfig config = createRecognitionConfig();
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
module.startRecognition(handle, config);
- verify(driver).startRecognition_2_3(eq(hwHandle), any());
-
- android.hardware.soundtrigger.V2_3.RecognitionConfig halConfigExtended =
- configCaptor.getValue();
- android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig halConfig_2_1 =
- halConfigExtended.base;
-
- assertTrue(halConfig_2_1.header.captureRequested);
- assertEquals(102, halConfig_2_1.header.captureHandle);
- assertEquals(103, halConfig_2_1.header.captureDevice);
- assertEquals(1, halConfig_2_1.header.phrases.size());
- android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
- halConfig_2_1.header.phrases.get(0);
- assertEquals(123, halPhraseExtra.id);
- assertEquals(4, halPhraseExtra.confidenceLevel);
- assertEquals(5, halPhraseExtra.recognitionModes);
- assertEquals(1, halPhraseExtra.levels.size());
- android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
- assertEquals(234, halLevel.userId);
- assertEquals(34, halLevel.levelPercent);
- assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
- HidlMemoryUtil.hidlMemoryToByteArray(halConfig_2_1.data));
- assertEquals(AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION, halConfigExtended.audioCapabilities);
- }
-
- private void startRecognition(ISoundTriggerModule module, int handle,
- int hwHandle) throws RemoteException {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- startRecognition_2_3(module, handle, hwHandle);
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- startRecognition_2_1(module, handle, hwHandle);
- } else {
- startRecognition_2_0(module, handle, hwHandle);
- }
- }
-
- private RecognitionConfig createRecognitionConfig() {
- RecognitionConfig config = new RecognitionConfig();
- config.captureRequested = true;
- config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
- config.phraseRecognitionExtras[0].id = 123;
- config.phraseRecognitionExtras[0].confidenceLevel = 4;
- config.phraseRecognitionExtras[0].recognitionModes = 5;
- config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
- config.phraseRecognitionExtras[0].levels[0].userId = 234;
- config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
- config.data = new byte[]{5, 4, 3, 2, 1};
- config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
- | AudioCapabilities.NOISE_SUPPRESSION;
- return config;
+ verify(mHalDriver).startRecognition(eq(hwHandle), eq(103), eq(102), configCaptor.capture());
+ assertEquals(config, configCaptor.getValue());
}
private void stopRecognition(ISoundTriggerModule module, int handle, int hwHandle)
throws RemoteException {
- when(mHalDriver.stopRecognition(hwHandle)).thenReturn(0);
module.stopRecognition(handle);
verify(mHalDriver).stopRecognition(hwHandle);
}
- private void verifyNotStartRecognition() throws RemoteException {
- verify(mHalDriver, never()).startRecognition(anyInt(), any(), any(), anyInt());
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_1(anyInt(), any(), any(), anyInt());
- } else if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- verify((android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver,
- never()).startRecognition_2_3(anyInt(), any());
- }
- }
-
-
@Before
public void setUp() throws Exception {
clearInvocations(mHalDriver);
clearInvocations(mAudioSessionProvider);
+ when(mHalDriver.getProperties()).thenReturn(TestUtil.createDefaultProperties(false));
+ mService = new SoundTriggerMiddlewareImpl(() -> mHalDriver, mAudioSessionProvider);
+ }
- // This binder is associated with the mock, so it can be cast to either version of the
- // HAL interface.
- final IHwBinder binder = new IHwBinder() {
- @Override
- public void transact(int code, HwParcel request, HwParcel reply, int flags)
- throws RemoteException {
- // This is a little hacky, but a very easy way to gracefully reject a request for
- // an unsupported interface (after queryLocalInterface() returns null, the client
- // will attempt a remote transaction to obtain the interface. RemoteException will
- // cause it to give up).
- throw new RemoteException();
- }
-
- @Override
- public IHwInterface queryLocalInterface(String descriptor) {
- if (descriptor.equals("android.hardware.soundtrigger@2.0::ISoundTriggerHw")
- || descriptor.equals("android.hardware.soundtrigger@2.1::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.2::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw
- || descriptor.equals("android.hardware.soundtrigger@2.3::ISoundTriggerHw")
- && mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return mHalDriver;
- }
- return null;
- }
-
- @Override
- public boolean linkToDeath(DeathRecipient recipient, long cookie) {
- return true;
- }
-
- @Override
- public boolean unlinkToDeath(DeathRecipient recipient) {
- return true;
- }
- };
-
- when(mHalDriver.asBinder()).thenReturn(binder);
+ @After
+ public void tearDown() {
+ verify(mHalDriver, never()).reboot();
}
@Test
@@ -836,70 +147,58 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testListModules() throws Exception {
- initService(true);
+ public void testListModules() {
// Note: input and output properties are NOT the same type, even though they are in any way
// equivalent. One is a type that's exposed by the HAL and one is a type that's exposed by
// the service. The service actually performs a (trivial) conversion between the two.
SoundTriggerModuleDescriptor[] allDescriptors = mService.listModules();
assertEquals(1, allDescriptors.length);
- SoundTriggerModuleProperties properties = allDescriptors[0].properties;
-
- validateDefaultProperties(properties, true);
- verifyNotGetProperties();
+ Properties properties = allDescriptors[0].properties;
+ assertEquals(TestUtil.createDefaultProperties(false), properties);
}
@Test
public void testAttachDetach() throws Exception {
// Normal attachment / detachment.
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
assertNotNull(module);
module.detach();
}
@Test
- public void testAttachDetachNotAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module not supporting concurrent
- // capture.
- initService(false);
+ public void testLoadUnloadModel() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(false);
- assertNotNull(module);
- module.detach();
- }
- @Test
- public void testAttachDetachAvailable() throws Exception {
- // Attachment / detachment during external capture, with a module supporting concurrent
- // capture.
- initService(true);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- assertNotNull(module);
+ final int hwHandle = 7;
+ int handle = loadGenericModel(module, hwHandle).first;
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
- public void testLoadUnloadModel() throws Exception {
- initService(true);
+ public void testLoadPreemptModel() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 7;
- int handle = loadGenericModel(module, hwHandle).first;
- unloadModel(module, handle, hwHandle);
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
+
+ // Signal preemption.
+ hwCallback.sendUnloadEvent(hwHandle);
+
+ verify(callback).onModelUnloaded(handle);
+
module.detach();
}
@Test
public void testLoadUnloadPhraseModel() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -911,7 +210,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testStartStopRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -931,8 +229,31 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
+ public void testStartRecognitionBusy() throws Exception {
+ ISoundTriggerCallback callback = createCallbackMock();
+ ISoundTriggerModule module = mService.attach(0, callback);
+
+ // Load the model.
+ final int hwHandle = 7;
+ int handle = loadGenericModel(module, hwHandle).first;
+
+ // Start the model.
+ doThrow(new RecoverableException(Status.RESOURCE_CONTENTION)).when(
+ mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+
+ try {
+ RecognitionConfig config = TestUtil.createRecognitionConfig();
+ module.startRecognition(handle, config);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.RESOURCE_CONTENTION, e.errorCode);
+ }
+
+ verify(mHalDriver).startRecognition(eq(7), eq(103), eq(102), any());
+ }
+
+ @Test
public void testStartStopPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -953,7 +274,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -967,15 +287,15 @@ public class SoundTriggerMiddlewareImplTest {
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -984,7 +304,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testPhraseRecognition() throws Exception {
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -998,15 +317,15 @@ public class SoundTriggerMiddlewareImplTest {
startRecognition(module, handle, hwHandle);
// Signal a capture from the driver.
- hwCallback.sendPhraseRecognitionEvent(hwHandle,
- android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionStatus.SUCCESS);
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.SUCCESS);
ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.SUCCESS);
+ assertEquals(event, eventCaptor.getValue());
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1015,14 +334,6 @@ public class SoundTriggerMiddlewareImplTest {
@Test
public void testForceRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
@@ -1037,18 +348,18 @@ public class SoundTriggerMiddlewareImplTest {
// Force a trigger.
module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
// Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendRecognitionEvent(hwHandle, 3);
+ RecognitionEvent event = hwCallback.sendRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
// Validate the event.
- validateRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ assertEquals(event, eventCaptor.getValue());
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1059,41 +370,27 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testForcePhraseRecognition() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_2.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_2.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_2.ISoundTriggerHw) mHalDriver;
-
- initService(true);
+ public void testForceRecognitionNotSupported() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
// Load the model.
final int hwHandle = 17;
- Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadGenericModel(module, hwHandle);
int handle = modelHandles.first;
- SoundTriggerHwCallback hwCallback = modelHandles.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
// Force a trigger.
- module.forceRecognitionEvent(handle);
- verify(driver).getModelState(hwHandle);
-
- // Signal a capture from the driver.
- // '3' means 'forced', there's no constant for that in the HAL.
- hwCallback.sendPhraseRecognitionEvent(hwHandle, 3);
-
- ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
-
- // Validate the event.
- validatePhraseRecognitionEvent(eventCaptor.getValue(), RecognitionStatus.FORCED);
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
// Stop the recognition.
stopRecognition(module, handle, hwHandle);
@@ -1104,47 +401,36 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testAbortRecognition() throws Exception {
- // Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
+ public void testForcePhraseRecognition() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
- final int hwHandle = 11;
- int handle = loadGenericModel(module, hwHandle).first;
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ int handle = modelHandles.first;
+ SoundTriggerHwCallback hwCallback = modelHandles.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Abort.
- mService.setCaptureState(true);
-
- ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- RecognitionEvent.class);
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
+ // Force a trigger.
+ module.forceRecognitionEvent(handle);
+ verify(mHalDriver).forceRecognitionEvent(hwHandle);
- // Validate the event.
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
+ // Signal a capture from the driver.
+ PhraseRecognitionEvent event = hwCallback.sendPhraseRecognitionEvent(hwHandle,
+ RecognitionStatus.FORCED);
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
- verifyNotStartRecognition();
+ // Validate the event.
+ assertEquals(event, eventCaptor.getValue());
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1152,47 +438,30 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testAbortPhraseRecognition() throws Exception {
- // Make sure the HAL doesn't support concurrent capture.
- initService(false);
- mService.setCaptureState(false);
-
+ public void testForcePhraseRecognitionNotSupported() throws Exception {
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
// Load the model.
- final int hwHandle = 11;
- int handle = loadPhraseModel(module, hwHandle).first;
+ final int hwHandle = 17;
+ Pair<Integer, SoundTriggerHwCallback> modelHandles = loadPhraseModel(module, hwHandle);
+ int handle = modelHandles.first;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Abort.
- mService.setCaptureState(true);
-
- ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
- PhraseRecognitionEvent.class);
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
-
- // Validate the event.
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
-
- // Make sure we are notified of the lost availability.
- verify(callback).onRecognitionAvailabilityChange(false);
-
- // Attempt to start a new recognition - should get an abort event immediately, without
- // involving the HAL.
- clearInvocations(callback);
- clearInvocations(mHalDriver);
- module.startRecognition(handle, createRecognitionConfig());
- verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture());
- assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
- verifyNotStartRecognition();
+ // Force a trigger.
+ doThrow(new RecoverableException(Status.OPERATION_NOT_SUPPORTED)).when(
+ mHalDriver).forceRecognitionEvent(hwHandle);
+ try {
+ module.forceRecognitionEvent(handle);
+ fail("Expected an exception");
+ } catch (RecoverableException e) {
+ assertEquals(Status.OPERATION_NOT_SUPPORTED, e.errorCode);
+ }
- // Now enable it and make sure we are notified.
- mService.setCaptureState(false);
- verify(callback).onRecognitionAvailabilityChange(true);
+ // Stop the recognition.
+ stopRecognition(module, handle, hwHandle);
// Unload the model.
unloadModel(module, handle, hwHandle);
@@ -1200,252 +469,156 @@ public class SoundTriggerMiddlewareImplTest {
}
@Test
- public void testNotAbortRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
+ public void testAbortRecognition() throws Exception {
+ // Make sure the HAL doesn't support concurrent capture.
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
// Load the model.
- final int hwHandle = 13;
- int handle = loadGenericModel(module, hwHandle).first;
+ final int hwHandle = 11;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadGenericModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+ // Abort.
+ hwCallback.sendRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
+ ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ RecognitionEvent.class);
+ verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
+ // Validate the event.
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status);
// Unload the model.
- module.unloadModel(handle);
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
- public void testNotAbortPhraseRecognitionConcurrent() throws Exception {
- // Make sure the HAL supports concurrent capture.
- initService(true);
-
+ public void testAbortPhraseRecognition() throws Exception {
+ // Make sure the HAL doesn't support concurrent capture.
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
- verify(callback).onRecognitionAvailabilityChange(true);
- clearInvocations(callback);
// Load the model.
- final int hwHandle = 13;
- int handle = loadPhraseModel(module, hwHandle).first;
+ final int hwHandle = 11;
+ Pair<Integer, SoundTriggerHwCallback> loadResult = loadPhraseModel(module, hwHandle);
+ int handle = loadResult.first;
+ SoundTriggerHwCallback hwCallback = loadResult.second;
// Initiate a recognition.
startRecognition(module, handle, hwHandle);
- // Signal concurrent capture. Shouldn't abort.
- mService.setCaptureState(true);
- verify(callback, never()).onPhraseRecognition(anyInt(), any());
- verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean());
+ // Abort.
+ hwCallback.sendPhraseRecognitionEvent(hwHandle, RecognitionStatus.ABORTED);
- // Stop the recognition.
- stopRecognition(module, handle, hwHandle);
+ ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass(
+ PhraseRecognitionEvent.class);
+ verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101));
- // Initiating a new one should work fine.
- clearInvocations(mHalDriver);
- startRecognition(module, handle, hwHandle);
- verify(callback, never()).onRecognition(anyInt(), any());
- stopRecognition(module, handle, hwHandle);
+ // Validate the event.
+ assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status);
// Unload the model.
- module.unloadModel(handle);
+ unloadModel(module, handle, hwHandle);
module.detach();
}
@Test
public void testParameterSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 12;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer((Answer<Void>) invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- android.hardware.soundtrigger.V2_3.ModelParameterRange range =
- new android.hardware.soundtrigger.V2_3.ModelParameterRange();
- range.start = 23;
- range.end = 45;
- OptionalModelParameterRange optionalRange = new OptionalModelParameterRange();
- optionalRange.range(range);
- resultCallback.onValues(0, optionalRange);
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
-
- ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
- ModelParameter.THRESHOLD_FACTOR);
+ ModelParameterRange halRange = new ModelParameterRange();
+ halRange.minInclusive = 23;
+ halRange.maxInclusive = 45;
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
-
- assertEquals(23, range.minInclusive);
- assertEquals(45, range.maxInclusive);
- }
-
- @Test
- public void testParameterNotSupportedOld() throws Exception {
- if (mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw) {
- return;
- }
-
- initService(false);
- ISoundTriggerCallback callback = createCallbackMock();
- ISoundTriggerModule module = mService.attach(0, callback);
- final int hwHandle = 13;
- int modelHandle = loadGenericModel(module, hwHandle).first;
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(halRange);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- assertNull(range);
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
+
+ assertEquals(halRange, range);
}
@Test
public void testParameterNotSupported() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 13;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.queryParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, new OptionalModelParameterRange());
- return null;
- }).when(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.queryParameter(eq(hwHandle),
+ eq(ModelParameter.THRESHOLD_FACTOR))).thenReturn(null);
ModelParameterRange range = module.queryModelParameterSupport(modelHandle,
ModelParameter.THRESHOLD_FACTOR);
- verify(driver).queryParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).queryParameter(eq(hwHandle), eq(ModelParameter.THRESHOLD_FACTOR));
assertNull(range);
}
@Test
public void testGetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 14;
int modelHandle = loadGenericModel(module, hwHandle).first;
- doAnswer(invocation -> {
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw.getParameterCallback
- resultCallback = invocation.getArgument(2);
- // This is the return of this method.
- resultCallback.onValues(0, 234);
- return null;
- }).when(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ when(mHalDriver.getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR)).thenReturn(
+ 234);
int value = module.getModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR);
- verify(driver).getParameter(eq(hwHandle),
- eq(android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR), any());
+ verify(mHalDriver).getModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR);
assertEquals(234, value);
}
@Test
public void testSetParameter() throws Exception {
- if (!(mHalDriver instanceof android.hardware.soundtrigger.V2_3.ISoundTriggerHw)) {
- return;
- }
-
- android.hardware.soundtrigger.V2_3.ISoundTriggerHw driver =
- (android.hardware.soundtrigger.V2_3.ISoundTriggerHw) mHalDriver;
-
- initService(false);
ISoundTriggerCallback callback = createCallbackMock();
ISoundTriggerModule module = mService.attach(0, callback);
final int hwHandle = 17;
int modelHandle = loadGenericModel(module, hwHandle).first;
- when(driver.setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR,
- 456)).thenReturn(0);
-
module.setModelParameter(modelHandle, ModelParameter.THRESHOLD_FACTOR, 456);
- verify(driver).setParameter(hwHandle,
- android.hardware.soundtrigger.V2_3.ModelParameter.THRESHOLD_FACTOR, 456);
+ verify(mHalDriver).setModelParameter(hwHandle, ModelParameter.THRESHOLD_FACTOR, 456);
}
private static class SoundTriggerHwCallback {
- private final android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback mCallback;
- private final int mCookie;
+ private final ISoundTriggerHal.ModelCallback mCallback;
- SoundTriggerHwCallback(android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback callback,
- int cookie) {
+ SoundTriggerHwCallback(ISoundTriggerHal.ModelCallback callback) {
mCallback = callback;
- mCookie = cookie;
}
- private void sendRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).recognitionCallback_2_1(
- createRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.recognitionCallback(createRecognitionEvent_2_0(hwHandle, status),
- mCookie);
- }
+ private RecognitionEvent sendRecognitionEvent(int hwHandle, @RecognitionStatus int status) {
+ RecognitionEvent event = TestUtil.createRecognitionEvent(status);
+ mCallback.recognitionCallback(hwHandle, event);
+ return event;
+ }
+
+ private PhraseRecognitionEvent sendPhraseRecognitionEvent(int hwHandle,
+ @RecognitionStatus int status) {
+ PhraseRecognitionEvent event = TestUtil.createPhraseRecognitionEvent(status);
+ mCallback.phraseRecognitionCallback(hwHandle, event);
+ return event;
}
- private void sendPhraseRecognitionEvent(int hwHandle, int status) throws RemoteException {
- if (mCallback instanceof android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) {
- ((android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback) mCallback).phraseRecognitionCallback_2_1(
- createPhraseRecognitionEvent_2_1(hwHandle, status), mCookie);
- } else {
- mCallback.phraseRecognitionCallback(
- createPhraseRecognitionEvent_2_0(hwHandle, status), mCookie);
- }
+ private void sendUnloadEvent(int hwHandle) {
+ mCallback.modelUnloaded(hwHandle);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
new file mode 100644
index 000000000000..4eca298f68a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_4.ISoundTriggerHwCallback;
+import android.media.audio.common.AudioChannelMask;
+import android.media.audio.common.AudioConfig;
+import android.media.audio.common.AudioFormat;
+import android.media.soundtrigger.AudioCapabilities;
+import android.media.soundtrigger.ConfidenceLevel;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseRecognitionEvent;
+import android.media.soundtrigger.PhraseRecognitionExtra;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionEvent;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.RecognitionStatus;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Test utilities, aimed at generating populated objects of the various types and validating
+ * corresponding objects generated by the system under test.
+ */
+class TestUtil {
+ static SoundModel createGenericSoundModel() {
+ return createSoundModel(SoundModelType.GENERIC);
+ }
+
+ private static SoundModel createSoundModel(@SoundModelType int type) {
+ SoundModel model = new SoundModel();
+ model.type = type;
+ model.uuid = "12345678-2345-3456-4567-abcdef987654";
+ model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+ byte[] data = new byte[]{91, 92, 93, 94, 95};
+ model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
+ model.dataSize = data.length;
+ return model;
+ }
+
+ private static void validateSoundModel_2_1(ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.header.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.header.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.header.vendorUuid));
+ assertArrayEquals(new byte[]{91, 92, 93, 94, 95},
+ HidlMemoryUtil.hidlMemoryToByteArray(model.data));
+ }
+
+ private static void validateSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
+ assertEquals(type, model.type);
+ assertEquals("12345678-2345-3456-4567-abcdef987654",
+ ConversionUtil.hidl2aidlUuid(model.uuid));
+ assertEquals("87654321-5432-6543-7654-456789fedcba",
+ ConversionUtil.hidl2aidlUuid(model.vendorUuid));
+ assertArrayEquals(new Byte[]{91, 92, 93, 94, 95}, model.data.toArray());
+ }
+
+ static void validateGenericSoundModel_2_1(ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_1(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static void validateGenericSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model) {
+ validateSoundModel_2_0(model, android.hardware.soundtrigger.V2_0.SoundModelType.GENERIC);
+ }
+
+ static PhraseSoundModel createPhraseSoundModel() {
+ PhraseSoundModel model = new PhraseSoundModel();
+ model.common = createSoundModel(SoundModelType.KEYPHRASE);
+ model.phrases = new Phrase[1];
+ model.phrases[0] = new Phrase();
+ model.phrases[0].id = 123;
+ model.phrases[0].users = new int[]{5, 6, 7};
+ model.phrases[0].locale = "locale";
+ model.phrases[0].text = "text";
+ model.phrases[0].recognitionModes =
+ RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+ return model;
+ }
+
+ static void validatePhraseSoundModel_2_1(ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_1(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ static void validatePhraseSoundModel_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
+ validateSoundModel_2_0(model.common,
+ android.hardware.soundtrigger.V2_0.SoundModelType.KEYPHRASE);
+ validatePhrases_2_0(model.phrases);
+ }
+
+ private static void validatePhrases_2_0(
+ List<android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Phrase> phrases) {
+ assertEquals(1, phrases.size());
+ assertEquals(123, phrases.get(0).id);
+ assertArrayEquals(new Integer[]{5, 6, 7}, phrases.get(0).users.toArray());
+ assertEquals("locale", phrases.get(0).locale);
+ assertEquals("text", phrases.get(0).text);
+ assertEquals(RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION,
+ phrases.get(0).recognitionModes);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties createDefaultProperties_2_0(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties properties =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHw.Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid.timeLow = 1;
+ properties.uuid.timeMid = 2;
+ properties.uuid.versionAndTimeHigh = 3;
+ properties.uuid.variantAndClockSeqHigh = 4;
+ properties.uuid.node = new byte[]{5, 6, 7, 8, 9, 10};
+
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ return properties;
+ }
+
+ static android.hardware.soundtrigger.V2_3.Properties createDefaultProperties_2_3(
+ boolean supportConcurrentCapture) {
+ android.hardware.soundtrigger.V2_3.Properties properties =
+ new android.hardware.soundtrigger.V2_3.Properties();
+ properties.base = createDefaultProperties_2_0(supportConcurrentCapture);
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities =
+ android.hardware.soundtrigger.V2_3.AudioCapabilities.ECHO_CANCELLATION
+ | android.hardware.soundtrigger.V2_3.AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static Properties createDefaultProperties(boolean supportConcurrentCapture) {
+ Properties properties = new Properties();
+ properties.implementor = "implementor";
+ properties.description = "description";
+ properties.version = 123;
+ properties.uuid = "00000001-0002-0003-0004-05060708090a";
+ properties.maxSoundModels = 456;
+ properties.maxKeyPhrases = 567;
+ properties.maxUsers = 678;
+ properties.recognitionModes =
+ RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.GENERIC_TRIGGER;
+ properties.captureTransition = true;
+ properties.maxBufferMs = 321;
+ properties.concurrentCapture = supportConcurrentCapture;
+ properties.triggerInEvent = true;
+ properties.powerConsumptionMw = 432;
+ properties.supportedModelArch = "supportedModelArch";
+ properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return properties;
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture) {
+ validateDefaultProperties(properties, supportConcurrentCapture,
+ AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+ "supportedModelArch");
+ }
+
+ static void validateDefaultProperties(Properties properties,
+ boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
+ @NonNull String supportedModelArch) {
+ assertEquals("implementor", properties.implementor);
+ assertEquals("description", properties.description);
+ assertEquals(123, properties.version);
+ assertEquals("00000001-0002-0003-0004-05060708090a", properties.uuid);
+ assertEquals(456, properties.maxSoundModels);
+ assertEquals(567, properties.maxKeyPhrases);
+ assertEquals(678, properties.maxUsers);
+ assertEquals(RecognitionMode.GENERIC_TRIGGER
+ | RecognitionMode.USER_AUTHENTICATION
+ | RecognitionMode.USER_IDENTIFICATION
+ | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+ assertTrue(properties.captureTransition);
+ assertEquals(321, properties.maxBufferMs);
+ assertEquals(supportConcurrentCapture, properties.concurrentCapture);
+ assertTrue(properties.triggerInEvent);
+ assertEquals(432, properties.powerConsumptionMw);
+ assertEquals(supportedModelArch, properties.supportedModelArch);
+ assertEquals(audioCapabilities, properties.audioCapabilities);
+ }
+
+ static RecognitionConfig createRecognitionConfig() {
+ RecognitionConfig config = new RecognitionConfig();
+ config.captureRequested = true;
+ config.phraseRecognitionExtras = new PhraseRecognitionExtra[]{new PhraseRecognitionExtra()};
+ config.phraseRecognitionExtras[0].id = 123;
+ config.phraseRecognitionExtras[0].confidenceLevel = 4;
+ config.phraseRecognitionExtras[0].recognitionModes = 5;
+ config.phraseRecognitionExtras[0].levels = new ConfidenceLevel[]{new ConfidenceLevel()};
+ config.phraseRecognitionExtras[0].levels[0].userId = 234;
+ config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
+ config.data = new byte[]{5, 4, 3, 2, 1};
+ config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION;
+ return config;
+ }
+
+ static void validateRecognitionConfig_2_0(
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.captureRequested);
+ assertEquals(captureDevice, config.captureDevice);
+ assertEquals(captureHandle, config.captureHandle);
+ assertEquals(1, config.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new Byte[]{5, 4, 3, 2, 1}, config.data.toArray());
+ }
+
+ static void validateRecognitionConfig_2_1(
+ android.hardware.soundtrigger.V2_1.ISoundTriggerHw.RecognitionConfig config,
+ int captureDevice, int captureHandle) {
+ assertTrue(config.header.captureRequested);
+ assertEquals(captureDevice, config.header.captureDevice);
+ assertEquals(captureHandle, config.header.captureHandle);
+ assertEquals(1, config.header.phrases.size());
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halPhraseExtra =
+ config.header.phrases.get(0);
+ assertEquals(123, halPhraseExtra.id);
+ assertEquals(4, halPhraseExtra.confidenceLevel);
+ assertEquals(5, halPhraseExtra.recognitionModes);
+ assertEquals(1, halPhraseExtra.levels.size());
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel = halPhraseExtra.levels.get(0);
+ assertEquals(234, halLevel.userId);
+ assertEquals(34, halLevel.levelPercent);
+ assertArrayEquals(new byte[]{5, 4, 3, 2, 1},
+ HidlMemoryUtil.hidlMemoryToByteArray(config.data));
+ }
+
+ static void validateRecognitionConfig_2_3(
+ android.hardware.soundtrigger.V2_3.RecognitionConfig config, int captureDevice,
+ int captureHandle) {
+ validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
+
+ assertEquals(AudioCapabilities.ECHO_CANCELLATION
+ | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
+ int hwHandle,
+ int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.status = status;
+ halEvent.type = SoundModelType.GENERIC;
+ halEvent.model = hwHandle;
+ halEvent.captureAvailable = true;
+ // This field is ignored.
+ halEvent.captureSession = 9999;
+ halEvent.captureDelayMs = 234;
+ halEvent.capturePreambleMs = 345;
+ halEvent.triggerInData = true;
+ halEvent.audioConfig.sampleRateHz = 456;
+ halEvent.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ halEvent.audioConfig.format = AudioFormat.MP3;
+ // hwEvent.audioConfig.offloadInfo is irrelevant.
+ halEvent.data.add((byte) 31);
+ halEvent.data.add((byte) 32);
+ halEvent.data.add((byte) 33);
+ return halEvent;
+ }
+
+ static RecognitionEvent createRecognitionEvent(@RecognitionStatus int status) {
+ RecognitionEvent event = new RecognitionEvent();
+ event.status = status;
+ event.type = SoundModelType.GENERIC;
+ event.captureAvailable = true;
+ event.captureDelayMs = 234;
+ event.capturePreambleMs = 345;
+ event.triggerInData = true;
+ event.audioConfig = new AudioConfig();
+ event.audioConfig.sampleRateHz = 456;
+ event.audioConfig.channelMask = AudioChannelMask.IN_LEFT;
+ event.audioConfig.format = AudioFormat.MP3;
+ //event.audioConfig.offloadInfo is irrelevant.
+ event.data = new byte[]{31, 32, 33};
+ return event;
+ }
+
+ static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
+ int hwHandle,
+ int status) {
+ ISoundTriggerHwCallback.RecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.RecognitionEvent();
+ halEvent.header = createRecognitionEvent_2_0(hwHandle, status);
+ halEvent.header.data.clear();
+ halEvent.data = HidlMemoryUtil.byteArrayToHidlMemory(new byte[]{31, 32, 33});
+ return halEvent;
+ }
+
+ static void validateRecognitionEvent(RecognitionEvent event, @RecognitionStatus int status) {
+ assertEquals(status, event.status);
+ assertEquals(SoundModelType.GENERIC, event.type);
+ assertTrue(event.captureAvailable);
+ assertEquals(234, event.captureDelayMs);
+ assertEquals(345, event.capturePreambleMs);
+ assertTrue(event.triggerInData);
+ assertEquals(456, event.audioConfig.sampleRateHz);
+ assertEquals(AudioChannelMask.IN_LEFT, event.audioConfig.channelMask);
+ assertEquals(AudioFormat.MP3, event.audioConfig.format);
+ assertArrayEquals(new byte[]{31, 32, 33}, event.data);
+ }
+
+ static PhraseRecognitionEvent createPhraseRecognitionEvent(@RecognitionStatus int status) {
+ PhraseRecognitionEvent event = new PhraseRecognitionEvent();
+ event.common = createRecognitionEvent(status);
+
+ PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
+ extra.id = 123;
+ extra.confidenceLevel = 52;
+ extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
+ | RecognitionMode.GENERIC_TRIGGER;
+ ConfidenceLevel level = new ConfidenceLevel();
+ level.userId = 31;
+ level.levelPercent = 43;
+ extra.levels = new ConfidenceLevel[]{level};
+ event.phraseExtras = new PhraseRecognitionExtra[]{extra};
+ return event;
+ }
+
+ static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
+ createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+ android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_1(
+ int hwHandle, int status) {
+ ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
+ new ISoundTriggerHwCallback.PhraseRecognitionEvent();
+ halEvent.common = createRecognitionEvent_2_1(hwHandle, status);
+
+ android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra halExtra =
+ new android.hardware.soundtrigger.V2_0.PhraseRecognitionExtra();
+ halExtra.id = 123;
+ halExtra.confidenceLevel = 52;
+ halExtra.recognitionModes = android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+ | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
+ android.hardware.soundtrigger.V2_0.ConfidenceLevel halLevel =
+ new android.hardware.soundtrigger.V2_0.ConfidenceLevel();
+ halLevel.userId = 31;
+ halLevel.levelPercent = 43;
+ halExtra.levels.add(halLevel);
+ halEvent.phraseExtras.add(halExtra);
+ return halEvent;
+ }
+
+ static void validatePhraseRecognitionEvent(PhraseRecognitionEvent event,
+ @RecognitionStatus int status) {
+ validateRecognitionEvent(event.common, status);
+
+ assertEquals(1, event.phraseExtras.length);
+ assertEquals(123, event.phraseExtras[0].id);
+ assertEquals(52, event.phraseExtras[0].confidenceLevel);
+ assertEquals(RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER,
+ event.phraseExtras[0].recognitionModes);
+ assertEquals(1, event.phraseExtras[0].levels.length);
+ assertEquals(31, event.phraseExtras[0].levels[0].userId);
+ assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
+ }
+
+ private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
+ try {
+ SharedMemory shmem = SharedMemory.create("", data.length);
+ ByteBuffer buffer = shmem.mapReadWrite();
+ buffer.put(data);
+ return shmem.getFileDescriptor();
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/AgentTest.java b/services/tests/servicestests/src/com/android/server/tare/AgentTest.java
new file mode 100644
index 000000000000..546b84ad44aa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/AgentTest.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.server.tare;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.util.ArraySet;
+import android.util.SparseLongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.tare.Agent.ActionAffordabilityNote;
+import com.android.server.tare.Agent.OngoingEvent;
+import com.android.server.tare.Agent.TrendCalculator;
+import com.android.server.tare.EconomyManagerInternal.ActionBill;
+import com.android.server.tare.EconomyManagerInternal.AffordabilityChangeListener;
+import com.android.server.tare.EconomyManagerInternal.AnticipatedAction;
+
+import libcore.util.EmptyArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/** Tests various aspects of the Agent. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AgentTest {
+
+ private MockEconomicPolicy mEconomicPolicy;
+
+ private static class MockEconomicPolicy extends EconomicPolicy {
+ private final SparseLongArray mEventCosts = new SparseLongArray();
+
+ MockEconomicPolicy(InternalResourceService irs) {
+ super(irs);
+ }
+
+ @Override
+ long getMinSatiatedBalance(int userId, String pkgName) {
+ return 0;
+ }
+
+ @Override
+ long getMaxSatiatedBalance() {
+ return 0;
+ }
+
+ @Override
+ long getMaxSatiatedCirculation() {
+ return 0;
+ }
+
+ @Override
+ int[] getCostModifiers() {
+ return EmptyArray.INT;
+ }
+
+ @Override
+ Action getAction(int actionId) {
+ if (mEventCosts.indexOfKey(actionId) < 0) {
+ return null;
+ }
+ return new Action(actionId, 0, mEventCosts.get(actionId));
+ }
+
+ @Override
+ Reward getReward(int rewardId) {
+ if (mEventCosts.indexOfKey(rewardId) < 0) {
+ return null;
+ }
+ return new Reward(rewardId, mEventCosts.get(rewardId), mEventCosts.get(rewardId),
+ 10 * mEventCosts.get(rewardId));
+ }
+ }
+
+ @Before
+ public void setUp() {
+ mEconomicPolicy = new MockEconomicPolicy(mock(InternalResourceService.class));
+ }
+
+ @Test
+ public void testTrendCalculator_NoOngoingEvents() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 20);
+
+ trendCalculator.reset(0, null);
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ trendCalculator.reset(1234, affordabilityNotes);
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendCalculator_NoAffordabilityNotes() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+
+ OngoingEvent[] events = new OngoingEvent[]{
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ null, 1, 1),
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ null, 2, 3),
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "3",
+ null, 3, -3),
+ };
+
+ trendCalculator.reset(0, null);
+ for (OngoingEvent event : events) {
+ trendCalculator.accept(event);
+ }
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ trendCalculator.reset(1234, affordabilityNotes);
+ for (OngoingEvent event : events) {
+ trendCalculator.accept(event);
+ }
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendCalculator_NoTrendToThreshold() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 10);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING, 0, 1000))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is already above threshold and events are all positive delta.
+ // There should be no time to report.
+ trendCalculator.reset(1234, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ null, 1, 1));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ null, 2, 3));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is already below threshold and events are all negative delta.
+ // There should be no time to report.
+ trendCalculator.reset(1, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ null, 1, -1));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ null, 2, -3));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendCalculator_SimpleTrendToThreshold() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is below threshold and events are all positive delta.
+ // Should report the correct time to the upper threshold.
+ trendCalculator.reset(0, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+ null, 1, 1));
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
+ null, 2, 3));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is above the threshold and events are all negative delta.
+ // Should report the correct time to the lower threshold.
+ trendCalculator.reset(40, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ null, 1, -1));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING, "2",
+ null, 2, -3));
+
+ assertEquals(5_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendCalculator_SelectCorrectThreshold() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 15);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 10);
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 5);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MIN_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is below threshold and events are all positive delta.
+ // Should report the correct time to the correct upper threshold.
+ trendCalculator.reset(0, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+ null, 1, 1));
+
+ assertEquals("Expected not to cross lower threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(5_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+
+ // Balance is above the threshold and events are all negative delta.
+ // Should report the correct time to the correct lower threshold.
+ trendCalculator.reset(30, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "1",
+ null, 1, -1));
+
+ assertEquals(10_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals("Expected not to cross upper threshold",
+ TrendCalculator.WILL_NOT_CROSS_THRESHOLD,
+ trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+
+ @Test
+ public void testTrendCalculator_TrendsToBothThresholds() {
+ TrendCalculator trendCalculator = new TrendCalculator();
+ mEconomicPolicy.mEventCosts.put(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 20);
+ mEconomicPolicy.mEventCosts.put(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 50);
+
+ ArraySet<ActionAffordabilityNote> affordabilityNotes = new ArraySet<>();
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ affordabilityNotes.add(new ActionAffordabilityNote(new ActionBill(List.of(
+ new AnticipatedAction(AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK, 1, 0))),
+ mock(AffordabilityChangeListener.class), mEconomicPolicy));
+ for (ActionAffordabilityNote note : affordabilityNotes) {
+ note.recalculateModifiedPrice(mEconomicPolicy, 0, "com.test.app");
+ }
+
+ // Balance is between both thresholds and events are mixed positive/negative delta.
+ // Should report the correct time to each threshold.
+ trendCalculator.reset(35, affordabilityNotes);
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_TOP_ACTIVITY, "1",
+ null, 1, 3));
+ trendCalculator.accept(
+ new OngoingEvent(EconomicPolicy.REWARD_OTHER_USER_INTERACTION, "2",
+ null, 2, 2));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING, "3",
+ null, 3, -2));
+ trendCalculator.accept(
+ new OngoingEvent(JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING, "4",
+ null, 4, -3));
+
+ assertEquals(3_000, trendCalculator.getTimeToCrossLowerThresholdMs());
+ assertEquals(3_000, trendCalculator.getTimeToCrossUpperThresholdMs());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
new file mode 100644
index 000000000000..4a253234b59e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/LedgerTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.tare;
+
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.getCurrentTimeMillis;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+
+/** Test that the ledger records transactions correctly. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LedgerTest {
+
+ @Before
+ public void setUp() {
+ TareUtils.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ }
+
+ @Test
+ public void testInitialState() {
+ final Ledger ledger = new Ledger();
+ assertEquals(0, ledger.getCurrentBalance());
+ assertEquals(0, ledger.get24HourSum(0, 0));
+ }
+
+ @Test
+ public void testMultipleTransactions() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 5));
+ assertEquals(5, ledger.getCurrentBalance());
+ assertEquals(5, ledger.get24HourSum(1, 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(2000, 2000, 1, null, 25));
+ assertEquals(30, ledger.getCurrentBalance());
+ assertEquals(30, ledger.get24HourSum(1, 60_000));
+ ledger.recordTransaction(new Ledger.Transaction(5000, 5500, 1, null, -10));
+ assertEquals(20, ledger.getCurrentBalance());
+ assertEquals(20, ledger.get24HourSum(1, 60_000));
+ }
+
+ @Test
+ public void test24HourSum() {
+ final Ledger ledger = new Ledger();
+ ledger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 500));
+ assertEquals(500, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(2 * HOUR_IN_MILLIS, 3 * HOUR_IN_MILLIS, 1, null, 2500));
+ assertEquals(3000, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ ledger.recordTransaction(
+ new Ledger.Transaction(4 * HOUR_IN_MILLIS, 4 * HOUR_IN_MILLIS, 1, null, 1));
+ assertEquals(3001, ledger.get24HourSum(1, 24 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum(1, 25 * HOUR_IN_MILLIS));
+ assertEquals(2501, ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS));
+ // Pro-rated as the second transaction phases out
+ assertEquals(1251,
+ ledger.get24HourSum(1, 26 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS));
+ assertEquals(1, ledger.get24HourSum(1, 27 * HOUR_IN_MILLIS));
+ assertEquals(0, ledger.get24HourSum(1, 28 * HOUR_IN_MILLIS));
+ }
+
+ @Test
+ public void testRemoveOldTransactions() {
+ final Ledger ledger = new Ledger();
+ ledger.removeOldTransactions(24 * HOUR_IN_MILLIS);
+ assertNull(ledger.getEarliestTransaction());
+
+ final long now = getCurrentTimeMillis();
+ Ledger.Transaction transaction1 = new Ledger.Transaction(
+ now - 48 * HOUR_IN_MILLIS, now - 40 * HOUR_IN_MILLIS, 1, null, 4800);
+ Ledger.Transaction transaction2 = new Ledger.Transaction(
+ now - 24 * HOUR_IN_MILLIS, now - 23 * HOUR_IN_MILLIS, 1, null, 600);
+ Ledger.Transaction transaction3 = new Ledger.Transaction(
+ now - 22 * HOUR_IN_MILLIS, now - 21 * HOUR_IN_MILLIS, 1, null, 600);
+ // Instant event
+ Ledger.Transaction transaction4 = new Ledger.Transaction(
+ now - 20 * HOUR_IN_MILLIS, now - 20 * HOUR_IN_MILLIS, 1, null, 500);
+ // Recent event
+ Ledger.Transaction transaction5 = new Ledger.Transaction(
+ now - 5 * MINUTE_IN_MILLIS, now - MINUTE_IN_MILLIS, 1, null, 400);
+ ledger.recordTransaction(transaction1);
+ ledger.recordTransaction(transaction2);
+ ledger.recordTransaction(transaction3);
+ ledger.recordTransaction(transaction4);
+ ledger.recordTransaction(transaction5);
+
+ assertEquals(transaction1, ledger.getEarliestTransaction());
+ ledger.removeOldTransactions(24 * HOUR_IN_MILLIS);
+ assertEquals(transaction2, ledger.getEarliestTransaction());
+ ledger.removeOldTransactions(23 * HOUR_IN_MILLIS);
+ assertEquals(transaction3, ledger.getEarliestTransaction());
+ // Shouldn't delete transaction3 yet since there's still a piece of it within the min age
+ // window.
+ ledger.removeOldTransactions(21 * HOUR_IN_MILLIS + 30 * MINUTE_IN_MILLIS);
+ assertEquals(transaction3, ledger.getEarliestTransaction());
+ // Instant event should be removed as soon as we hit the exact threshold.
+ ledger.removeOldTransactions(20 * HOUR_IN_MILLIS);
+ assertEquals(transaction5, ledger.getEarliestTransaction());
+ ledger.removeOldTransactions(0);
+ assertNull(ledger.getEarliestTransaction());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tare/OWNERS b/services/tests/servicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 000000000000..217a5edff08b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 8af2c4d04fd9..28838ae14f2f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -97,7 +97,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.getCapabilitiesAndConfig();
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -129,7 +129,7 @@ public class TimeZoneDetectorServiceTest {
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
try {
mTimeZoneDetectorService.addListener(mockListener);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
@@ -235,7 +235,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SET_TIME_ZONE),
@@ -268,7 +268,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingOrSelfPermission(
eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
@@ -301,7 +301,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
@@ -317,7 +317,7 @@ public class TimeZoneDetectorServiceTest {
try {
mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail();
+ fail("Expected SecurityException");
} finally {
verify(mMockContext).enforceCallingPermission(
eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index a0e9d977954f..da746ca46def 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -15,6 +15,7 @@
*/
package com.android.server.timezonedetector.location;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -728,6 +729,9 @@ public class ControllerImplTest {
// Simulate the user change (but geo detection still enabled).
testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
+ // Confirm that the previous suggestion was overridden.
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
// We expect the provider to end up in PROVIDER_STATE_STARTED_INITIALIZING, but it should
// have been stopped when the user changed.
int[] expectedStateTransitions =
@@ -1002,24 +1006,25 @@ public class ControllerImplTest {
@Test
public void stateRecording() {
+ // The test provider enables state recording by default.
ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
TestEnvironment testEnvironment = new TestEnvironment(
mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
- // Initialize and check initial state.
+ // Initialize and check initial states.
controllerImpl.initialize(testEnvironment, mTestCallback);
{
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
assertNull(state.getLastSuggestion());
- assertTrue(state.getPrimaryProviderStates().isEmpty());
- assertTrue(state.getSecondaryProviderStates().isEmpty());
+ assertProviderStates(state.getPrimaryProviderStates(),
+ PROVIDER_STATE_STOPPED, PROVIDER_STATE_STARTED_INITIALIZING);
+ assertProviderStates(state.getSecondaryProviderStates(), PROVIDER_STATE_STOPPED);
}
+ controllerImpl.clearRecordedProviderStates();
- // State recording and simulate some provider behavior that will show up in the state
- // recording.
- controllerImpl.setProviderStateRecordingEnabled(true);
+ // Simulate some provider behavior that will show up in the state recording.
// Simulate an uncertain event from the primary. This will start the secondary.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
@@ -1028,19 +1033,14 @@ public class ControllerImplTest {
{
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
assertNull(state.getLastSuggestion());
- List<LocationTimeZoneProvider.ProviderState> primaryProviderStates =
- state.getPrimaryProviderStates();
- assertEquals(1, primaryProviderStates.size());
- assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN,
- primaryProviderStates.get(0).stateEnum);
- List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates =
- state.getSecondaryProviderStates();
- assertEquals(1, secondaryProviderStates.size());
- assertEquals(PROVIDER_STATE_STARTED_INITIALIZING,
- secondaryProviderStates.get(0).stateEnum);
+ assertProviderStates(
+ state.getPrimaryProviderStates(), PROVIDER_STATE_STARTED_UNCERTAIN);
+ assertProviderStates(
+ state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_INITIALIZING);
}
+ controllerImpl.clearRecordedProviderStates();
- // Simulate an uncertain event from the primary. This will start the secondary.
+ // Simulate a certain event from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
@@ -1048,26 +1048,72 @@ public class ControllerImplTest {
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
state.getLastSuggestion().getZoneIds());
- List<LocationTimeZoneProvider.ProviderState> primaryProviderStates =
- state.getPrimaryProviderStates();
- assertEquals(1, primaryProviderStates.size());
- assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, primaryProviderStates.get(0).stateEnum);
- List<LocationTimeZoneProvider.ProviderState> secondaryProviderStates =
- state.getSecondaryProviderStates();
- assertEquals(2, secondaryProviderStates.size());
- assertEquals(PROVIDER_STATE_STARTED_CERTAIN, secondaryProviderStates.get(1).stateEnum);
+ assertProviderStates(state.getPrimaryProviderStates());
+ assertProviderStates(
+ state.getSecondaryProviderStates(), PROVIDER_STATE_STARTED_CERTAIN);
}
- controllerImpl.setProviderStateRecordingEnabled(false);
+ controllerImpl.clearRecordedProviderStates();
{
LocationTimeZoneManagerServiceState state = controllerImpl.getStateForTests();
assertEquals(USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds(),
state.getLastSuggestion().getZoneIds());
- assertTrue(state.getPrimaryProviderStates().isEmpty());
- assertTrue(state.getSecondaryProviderStates().isEmpty());
+ assertProviderStates(state.getPrimaryProviderStates());
+ assertProviderStates(state.getSecondaryProviderStates());
}
}
+ private static void assertProviderStates(
+ List<LocationTimeZoneProvider.ProviderState> providerStates,
+ int... expectedStates) {
+ assertEquals(expectedStates.length, providerStates.size());
+ for (int i = 0; i < expectedStates.length; i++) {
+ assertEquals(expectedStates[i], providerStates.get(i).stateEnum);
+ }
+ }
+
+ @Test
+ public void destroy() {
+ ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+ mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+ TestEnvironment testEnvironment = new TestEnvironment(
+ mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+ // Initialize and check initial state.
+ controllerImpl.initialize(testEnvironment, mTestCallback);
+
+ mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestCallback.assertNoSuggestionMade();
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+ // Simulate the primary provider suggesting a time zone.
+ mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+ // Receiving a "success" provider event should cause a suggestion to be made synchronously,
+ // and also clear the scheduled uncertainty suggestion.
+ mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+ PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+ mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
+ mTestCallback.assertSuggestionMadeAndCommit(
+ USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+ // Trigger destroy().
+ controllerImpl.destroy();
+
+ // Confirm that the previous suggestion was overridden.
+ mTestCallback.assertUncertainSuggestionMadeAndCommit();
+
+ mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+ PROVIDER_STATE_STOPPED, PROVIDER_STATE_DESTROYED);
+ mTestSecondaryLocationTimeZoneProvider.assertStateChangesAndCommit(
+ PROVIDER_STATE_DESTROYED);
+ assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+ }
+
private static void assertUncertaintyTimeoutSet(
LocationTimeZoneProviderController.Environment environment,
LocationTimeZoneProviderController controller) {
@@ -1175,7 +1221,6 @@ public class ControllerImplTest {
private final TestState<ProviderState> mTestProviderState = new TestState<>();
private boolean mFailDuringInitialization;
private boolean mInitialized;
- private boolean mDestroyed;
/**
* Creates the instance.
@@ -1183,7 +1228,7 @@ public class ControllerImplTest {
TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
ThreadingDomain threadingDomain, String providerName) {
super(providerMetricsLogger, threadingDomain, providerName,
- new FakeTimeZoneProviderEventPreProcessor());
+ new FakeTimeZoneProviderEventPreProcessor(), true /* recordStateChanges */);
}
public void setFailDuringInitialization(boolean failInitialization) {
@@ -1200,7 +1245,7 @@ public class ControllerImplTest {
@Override
void onDestroy() {
- mDestroyed = true;
+ // No behavior needed.
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 0edb559b04b3..03d56c782b59 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -15,9 +15,6 @@
*/
package com.android.server.timezonedetector.location;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
-import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -26,8 +23,6 @@ import static com.android.server.timezonedetector.location.LocationTimeZoneProvi
import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
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.assertSame;
import static org.junit.Assert.assertTrue;
@@ -36,8 +31,6 @@ import static java.util.Arrays.asList;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Bundle;
-import android.os.RemoteCallback;
import android.platform.test.annotations.Presubmit;
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
@@ -54,7 +47,6 @@ import java.time.Duration;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
/**
* Tests for {@link LocationTimeZoneProvider}.
@@ -169,27 +161,6 @@ public class LocationTimeZoneProviderTest {
}
@Test
- public void defaultHandleTestCommandImpl() {
- String providerName = "primary";
- StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
- TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
- providerMetricsLogger,
- mTestThreadingDomain,
- providerName,
- mTimeZoneProviderEventPreProcessor);
-
- TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
- AtomicReference<Bundle> resultReference = new AtomicReference<>();
- RemoteCallback callback = new RemoteCallback(resultReference::set);
- provider.handleTestCommand(testCommand, callback);
-
- Bundle result = resultReference.get();
- assertNotNull(result);
- assertFalse(result.getBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY));
- assertNotNull(result.getString(TEST_COMMAND_RESULT_ERROR_KEY));
- }
-
- @Test
public void stateRecording() {
String providerName = "primary";
StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
@@ -198,7 +169,6 @@ public class LocationTimeZoneProviderTest {
mTestThreadingDomain,
providerName,
mTimeZoneProviderEventPreProcessor);
- provider.setStateChangeRecordingEnabled(true);
// initialize()
provider.initialize(mProviderListener);
@@ -244,7 +214,6 @@ public class LocationTimeZoneProviderTest {
mTestThreadingDomain,
providerName,
mTimeZoneProviderEventPreProcessor);
- provider.setStateChangeRecordingEnabled(true);
provider.initialize(mProviderListener);
mTimeZoneProviderEventPreProcessor.enterUncertainMode();
@@ -315,8 +284,9 @@ public class LocationTimeZoneProviderTest {
@NonNull ThreadingDomain threadingDomain,
@NonNull String providerName,
@NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) {
- super(providerMetricsLogger,
- threadingDomain, providerName, timeZoneProviderEventPreProcessor);
+ super(providerMetricsLogger, threadingDomain, providerName,
+ timeZoneProviderEventPreProcessor,
+ true /* recordStateChanges */);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index aadab6ea4fd9..b8cb1492ad52 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -25,7 +25,6 @@ import android.content.ContextWrapper;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
-import android.media.tv.tuner.TunerFrontendInfo;
import android.media.tv.tuner.frontend.FrontendSettings;
import android.media.tv.tunerresourcemanager.CasSessionRequest;
import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
@@ -33,6 +32,7 @@ import android.media.tv.tunerresourcemanager.ResourceClientProfile;
import android.media.tv.tunerresourcemanager.TunerCiCamRequest;
import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
import android.media.tv.tunerresourcemanager.TunerLnbRequest;
import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -365,13 +365,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -415,13 +415,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init frontend resources.
TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -511,13 +511,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -567,13 +567,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init cicam/cas resources.
mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
@@ -697,13 +697,13 @@ public class TunerResourceManagerServiceTest {
mTunerResourceManagerService.registerClientProfileInternal(
profiles[0], listener, clientId0);
assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId0[0])
- .setPriority(clientPriorities[0]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId0[0], clientPriorities[0], 0/*niceValue*/);
mTunerResourceManagerService.registerClientProfileInternal(
profiles[1], new TestResourcesReclaimListener(), clientId1);
assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
- mTunerResourceManagerService.getClientProfile(clientId1[0])
- .setPriority(clientPriorities[1]);
+ mTunerResourceManagerService.updateClientPriorityInternal(
+ clientId1[0], clientPriorities[1], 0/*niceValue*/);
// Init lnb resources.
int[] lnbHandles = {1};
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 69f006568c8e..25b51dad460a 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -55,6 +55,7 @@ import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.Process;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -356,7 +357,7 @@ public class UriGrantsManagerServiceTest {
final UriPermissionOwner owner = new UriPermissionOwner(mService, "primary");
final ProviderInfo cameraInfo = mContext.mPmInternal.resolveContentProvider(
- PKG_CAMERA, 0, USER_PRIMARY);
+ PKG_CAMERA, 0, USER_PRIMARY, Process.SYSTEM_UID);
// By default no social can see any camera
assertFalse(mService.checkAuthorityGrants(UID_PRIMARY_SOCIAL,
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
index a6307b38f6a5..37165075e1ba 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
@@ -35,6 +35,7 @@ import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.FileUtils;
import android.os.PatternMatcher;
+import android.os.Process;
import android.os.UserHandle;
import android.test.mock.MockContentResolver;
import android.test.mock.MockPackageManager;
@@ -133,27 +134,32 @@ public class UriGrantsMockContext extends ContextWrapper {
when(mPmInternal.getPackageUid(eq(PKG_COMPLEX), anyInt(), eq(userId)))
.thenReturn(UserHandle.getUid(userId, UID_COMPLEX));
- when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId)))
+ when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId),
+ eq(Process.SYSTEM_UID)))
.thenReturn(buildCameraProvider(userId));
when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId),
eq(UserHandle.getUid(userId, UID_CAMERA))))
.thenReturn(buildCameraProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId)))
+ when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId),
+ eq(Process.SYSTEM_UID)))
.thenReturn(buildPrivateProvider(userId));
when(mPmInternal.resolveContentProvider(eq(PKG_PRIVATE), anyInt(), eq(userId),
eq(UserHandle.getUid(userId, UID_PRIVATE))))
.thenReturn(buildPrivateProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId)))
+ when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId),
+ eq(Process.SYSTEM_UID)))
.thenReturn(buildPublicProvider(userId));
when(mPmInternal.resolveContentProvider(eq(PKG_PUBLIC), anyInt(), eq(userId),
eq(UserHandle.getUid(userId, UID_PUBLIC))))
.thenReturn(buildPublicProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyInt(), eq(userId)))
+ when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyInt(), eq(userId),
+ eq(Process.SYSTEM_UID)))
.thenReturn(buildForceProvider(userId));
when(mPmInternal.resolveContentProvider(eq(PKG_FORCE), anyInt(), eq(userId),
eq(UserHandle.getUid(userId, UID_FORCE))))
.thenReturn(buildForceProvider(userId));
- when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId)))
+ when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId),
+ eq(Process.SYSTEM_UID)))
.thenReturn(buildComplexProvider(userId));
when(mPmInternal.resolveContentProvider(eq(PKG_COMPLEX), anyInt(), eq(userId),
eq(UserHandle.getUid(userId, UID_COMPLEX))))
diff --git a/services/tests/servicestests/src/com/android/server/uwb/OWNERS b/services/tests/servicestests/src/com/android/server/uwb/OWNERS
deleted file mode 100644
index c31a2f1ed3bc..000000000000
--- a/services/tests/servicestests/src/com/android/server/uwb/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/frameworks/base:/core/java/android/uwb/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java b/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java
deleted file mode 100644
index 11554c7a7dc5..000000000000
--- a/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java
+++ /dev/null
@@ -1,365 +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.server.uwb;
-
-import static android.Manifest.permission.UWB_PRIVILEGED;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doThrow;
-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.AttributionSource;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.uwb.IUwbAdapter;
-import android.uwb.IUwbAdapterStateCallbacks;
-import android.uwb.IUwbRangingCallbacks;
-import android.uwb.RangingReport;
-import android.uwb.RangingSession;
-import android.uwb.SessionHandle;
-
-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.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link UwbServiceImpl}.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class UwbServiceImplTest {
- private static final int UID = 343453;
- private static final String PACKAGE_NAME = "com.uwb.test";
- private static final AttributionSource ATTRIBUTION_SOURCE =
- new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
-
- @Mock private IUwbAdapter mVendorService;
- @Mock private IBinder mVendorServiceBinder;
- @Mock private Context mContext;
- @Mock private UwbInjector mUwbInjector;
- @Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
- @Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor;
- @Captor private ArgumentCaptor<IBinder.DeathRecipient> mVendorServiceDeathCaptor;
-
- private UwbServiceImpl mUwbServiceImpl;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- when(mUwbInjector.getVendorService()).thenReturn(mVendorService);
- when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(true);
- when(mVendorService.asBinder()).thenReturn(mVendorServiceBinder);
- mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
- }
-
- @Test
- public void testApiCallThrowsIllegalStateExceptionIfVendorServiceNotFound() throws Exception {
- when(mUwbInjector.getVendorService()).thenReturn(null);
-
- final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
- try {
- mUwbServiceImpl.registerAdapterStateCallbacks(cb);
- fail();
- } catch (IllegalStateException e) { /* pass */ }
- }
-
- @Test
- public void testRegisterAdapterStateCallbacks() throws Exception {
- final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
- mUwbServiceImpl.registerAdapterStateCallbacks(cb);
-
- verify(mVendorService).registerAdapterStateCallbacks(cb);
- }
-
- @Test
- public void testUnregisterAdapterStateCallbacks() throws Exception {
- final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
- mUwbServiceImpl.unregisterAdapterStateCallbacks(cb);
-
- verify(mVendorService).unregisterAdapterStateCallbacks(cb);
- }
-
- @Test
- public void testGetTimestampResolutionNanos() throws Exception {
- final long timestamp = 34L;
- when(mVendorService.getTimestampResolutionNanos()).thenReturn(timestamp);
- assertThat(mUwbServiceImpl.getTimestampResolutionNanos()).isEqualTo(timestamp);
-
- verify(mVendorService).getTimestampResolutionNanos();
- }
-
- @Test
- public void testGetSpecificationInfo() throws Exception {
- final PersistableBundle specification = new PersistableBundle();
- when(mVendorService.getSpecificationInfo()).thenReturn(specification);
- assertThat(mUwbServiceImpl.getSpecificationInfo()).isEqualTo(specification);
-
- verify(mVendorService).getSpecificationInfo();
- }
-
- @Test
- public void testOpenRanging() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
-
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
-
- verify(mVendorService).openRanging(
- eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
- eq(parameters));
- assertThat(mRangingCbCaptor.getValue()).isNotNull();
- }
-
- @Test
- public void testStartRanging() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final PersistableBundle parameters = new PersistableBundle();
-
- mUwbServiceImpl.startRanging(sessionHandle, parameters);
-
- verify(mVendorService).startRanging(sessionHandle, parameters);
- }
-
- @Test
- public void testReconfigureRanging() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final PersistableBundle parameters = new PersistableBundle();
-
- mUwbServiceImpl.reconfigureRanging(sessionHandle, parameters);
-
- verify(mVendorService).reconfigureRanging(sessionHandle, parameters);
- }
-
- @Test
- public void testStopRanging() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
-
- mUwbServiceImpl.stopRanging(sessionHandle);
-
- verify(mVendorService).stopRanging(sessionHandle);
- }
-
- @Test
- public void testCloseRanging() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
-
- mUwbServiceImpl.closeRanging(sessionHandle);
-
- verify(mVendorService).closeRanging(sessionHandle);
- }
-
- @Test
- public void testRangingCallbacks() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
-
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
-
- verify(mVendorService).openRanging(
- eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
- eq(parameters));
- assertThat(mRangingCbCaptor.getValue()).isNotNull();
-
- // Invoke vendor service callbacks and ensure that the corresponding app callback is
- // invoked.
- mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
- verify(cb).onRangingOpened(sessionHandle);
-
- mRangingCbCaptor.getValue().onRangingOpenFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingOpenFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
-
- mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters);
- verify(cb).onRangingStarted(sessionHandle, parameters);
-
- mRangingCbCaptor.getValue().onRangingStartFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingStartFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
-
- mRangingCbCaptor.getValue().onRangingReconfigured(sessionHandle, parameters);
- verify(cb).onRangingReconfigured(sessionHandle, parameters);
-
- mRangingCbCaptor.getValue().onRangingReconfigureFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingReconfigureFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
-
- mRangingCbCaptor.getValue().onRangingStopped(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingStopped(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
-
- mRangingCbCaptor.getValue().onRangingStopFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingStopFailed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
-
- final RangingReport rangingReport = new RangingReport.Builder().build();
- mRangingCbCaptor.getValue().onRangingResult(sessionHandle, rangingReport);
- verify(cb).onRangingResult(sessionHandle, rangingReport);
-
- mRangingCbCaptor.getValue().onRangingClosed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- verify(cb).onRangingClosed(
- sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters);
- }
-
- @Test
- public void testHandleClientDeath() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
-
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
-
- verify(mVendorService).openRanging(
- eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
- eq(parameters));
- assertThat(mRangingCbCaptor.getValue()).isNotNull();
-
- verify(cbBinder).linkToDeath(mClientDeathCaptor.capture(), anyInt());
- assertThat(mClientDeathCaptor.getValue()).isNotNull();
-
- clearInvocations(cb);
-
- // Invoke cb, ensure it reaches the client.
- mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
- verify(cb).onRangingOpened(sessionHandle);
-
- // Trigger client death and ensure the session is stopped.
- mClientDeathCaptor.getValue().binderDied();
- verify(mVendorService).stopRanging(sessionHandle);
- verify(mVendorService).closeRanging(sessionHandle);
-
- // Invoke cb, it should be ignored.
- mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters);
- verify(cb, never()).onRangingStarted(any(), any());
- }
-
- @Test
- public void testHandleVendorServiceDeath() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
-
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
-
- verify(mVendorServiceBinder).linkToDeath(mVendorServiceDeathCaptor.capture(), anyInt());
- assertThat(mVendorServiceDeathCaptor.getValue()).isNotNull();
-
- verify(mVendorService).openRanging(
- eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
- eq(parameters));
- assertThat(mRangingCbCaptor.getValue()).isNotNull();
-
- clearInvocations(cb);
-
- // Invoke cb, ensure it reaches the client.
- mRangingCbCaptor.getValue().onRangingOpened(sessionHandle);
- verify(cb).onRangingOpened(sessionHandle);
-
- // Trigger vendor service death and ensure that the client is informed of session end.
- mVendorServiceDeathCaptor.getValue().binderDied();
- verify(cb).onRangingClosed(
- eq(sessionHandle), eq(RangingSession.Callback.REASON_UNKNOWN),
- argThat((p) -> p.isEmpty()));
- }
-
- @Test
- public void testThrowSecurityExceptionWhenCalledWithoutUwbPrivilegedPermission()
- throws Exception {
- doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
- eq(UWB_PRIVILEGED), any());
- final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
- try {
- mUwbServiceImpl.registerAdapterStateCallbacks(cb);
- fail();
- } catch (SecurityException e) { /* pass */ }
- }
-
- @Test
- public void testThrowSecurityExceptionWhenOpenRangingCalledWithoutUwbRangingPermission()
- throws Exception {
- doThrow(new SecurityException()).when(mUwbInjector).enforceUwbRangingPermissionForPreflight(
- any());
-
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
- try {
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
- fail();
- } catch (SecurityException e) { /* pass */ }
- }
-
- @Test
- public void testOnRangingResultCallbackNotSentWithoutUwbRangingPermission() throws Exception {
- final SessionHandle sessionHandle = new SessionHandle(5);
- final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
- final PersistableBundle parameters = new PersistableBundle();
- final IBinder cbBinder = mock(IBinder.class);
- when(cb.asBinder()).thenReturn(cbBinder);
-
- mUwbServiceImpl.openRanging(ATTRIBUTION_SOURCE, sessionHandle, cb, parameters);
-
- verify(mVendorService).openRanging(
- eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
- eq(parameters));
- assertThat(mRangingCbCaptor.getValue()).isNotNull();
-
- when(mUwbInjector.checkUwbRangingPermissionForDataDelivery(any(), any())).thenReturn(false);
-
- // Ensure the ranging cb is not delivered to the client.
- final RangingReport rangingReport = new RangingReport.Builder().build();
- mRangingCbCaptor.getValue().onRangingResult(sessionHandle, rangingReport);
- verify(cb, never()).onRangingResult(sessionHandle, rangingReport);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 5a00e0d6530d..8f9eb22f0d01 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -95,6 +95,8 @@ import org.mockito.junit.MockitoRule;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
@@ -696,10 +698,12 @@ public class VibratorManagerServiceTest {
VibratorManagerService.OnSyncedVibrationCompleteListener.class);
verify(mNativeWrapperMock).init(listenerCaptor.capture());
- // Mock trigger callback on registered listener.
+ CountDownLatch triggerCountDown = new CountDownLatch(1);
+ // Mock trigger callback on registered listener right after the synced vibration starts.
when(mNativeWrapperMock.prepareSynced(eq(new int[]{1, 2}))).thenReturn(true);
when(mNativeWrapperMock.triggerSynced(anyLong())).then(answer -> {
listenerCaptor.getValue().onComplete(answer.getArgument(0));
+ triggerCountDown.countDown();
return true;
});
@@ -708,20 +712,19 @@ public class VibratorManagerServiceTest {
.compose();
CombinedVibration effect = CombinedVibration.createParallel(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));
+ // VibrationThread will start this vibration async, so wait until vibration is triggered.
+ triggerCountDown.await(TEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
verify(mNativeWrapperMock).prepareSynced(eq(new int[]{1, 2}));
verify(mNativeWrapperMock).triggerSynced(anyLong());
-
PrimitiveSegment expected = new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
assertEquals(Arrays.asList(expected), mVibratorProviders.get(1).getEffectSegments());
assertEquals(Arrays.asList(expected), mVibratorProviders.get(2).getEffectSegments());
+
+ // VibrationThread needs some time to react to native callbacks and stop the vibrator.
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
@Test
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index b11c85c008ad..c611e38b068a 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -74,3 +74,17 @@ android_test_helper_app {
resource_dirs: ["res"],
manifest: "AndroidManifestApp4.xml",
}
+
+android_test_helper_app {
+ name: "PackageParserTestApp5",
+ sdk_version: "current",
+ srcs: ["**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ resource_dirs: ["res"],
+ manifest: "AndroidManifestApp5.xml",
+}
diff --git a/core/tests/uwbtests/AndroidManifest.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
index dc991ff636d2..cc6caad8e2e3 100644
--- a/core/tests/uwbtests/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp5.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2020 The Android Open Source Project
+<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,17 +15,14 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.uwb">
+ package="com.android.servicestests.apps.packageparserapp" >
+
+ <uses-sdk android:minSdkVersion="3"
+ android:targetSdkVersion="3" />
<application>
- <uses-library android:name="android.test.runner" />
+ <activity android:name=".TestActivity"
+ android:exported="true" />
</application>
- <!-- This is a self-instrumenting test package. -->
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.uwb"
- android:label="UWB Manager Tests">
- </instrumentation>
-
-</manifest>
-
+</manifest> \ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
index 78afb7b72c04..fdaf7ccc5f0d 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/AndroidManifest.xml
@@ -27,6 +27,13 @@
<service android:name=".SimpleIsolatedService"
android:isolatedProcess="true"
android:exported="true" />
+ <receiver android:name=".SimpleReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.servicestests.apps.simpleservicetestapp.TEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
index ccfc0b7f0ef1..56e1ab7d8749 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleFgService.java
@@ -38,6 +38,7 @@ public class SimpleFgService extends Service {
private static final int MSG_DONE = 1;
private static final int MSG_START_FOREGROUND = 2;
private static final int MSG_STOP_FOREGROUND = 3;
+ private static final int MSG_STOP_SERVICE = 4;
private static final String ACTION_FGS_STATS_TEST =
"com.android.servicestests.apps.simpleservicetestapp.ACTION_FGS_STATS_TEST";
@@ -57,6 +58,11 @@ public class SimpleFgService extends Service {
stopForeground(true);
sendRemoteMessage(MSG_DONE, 0, 0, null);
} break;
+ case MSG_STOP_SERVICE: {
+ Log.i(TAG, "stopSelf");
+ stopSelf();
+ sendRemoteMessage(MSG_DONE, 0, 0, null);
+ } break;
}
}
};
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleReceiver.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleReceiver.java
new file mode 100644
index 000000000000..1eced8415cea
--- /dev/null
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.servicestests.apps.simpleservicetestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class SimpleReceiver extends BroadcastReceiver {
+ private static final String TAG = SimpleReceiver.class.getSimpleName();
+ private static final String EXTRA_CALLBACK = "callback";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "onReceive " + intent);
+ final Bundle extra = intent.getExtras();
+ if (extra != null) {
+ final IBinder binder = extra.getBinder(EXTRA_CALLBACK);
+ if (binder != null) {
+ IRemoteCallback callback = IRemoteCallback.Stub.asInterface(binder);
+ try {
+ callback.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
index 4e981b22cd32..ae46f52ff70c 100644
--- a/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
+++ b/services/tests/servicestests/test-apps/SimpleServiceTestApp/src/com/android/servicestests/apps/simpleservicetestapp/SimpleService.java
@@ -60,6 +60,9 @@ public class SimpleService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand");
+ if (intent == null) {
+ return START_STICKY;
+ }
int command = intent.getIntExtra(EXTRA_COMMAND, COMMAND_INVALID);
if (command != COMMAND_INVALID) {
final String targetPkg = intent.getStringExtra(EXTRA_TARGET_PACKAGE);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 4b3771b95c05..f21991defbec 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -475,7 +475,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsForBogusPackageName() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
.thenReturn(TestInjector.CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
@@ -485,7 +485,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfNameNotFound() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
@@ -495,7 +495,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfNoProjectionTypes() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
assertThrows(IllegalArgumentException.class,
() -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
@@ -507,7 +508,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfMultipleProjectionTypes() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
// Don't use PROJECTION_TYPE_ALL because that's actually == -1 and will fail the > 0 check.
int multipleProjectionTypes = PROJECTION_TYPE_AUTOMOTIVE | 0x0002 | 0x0004;
@@ -522,7 +524,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
- doThrow(new SecurityException()).when(mPackageManager).getPackageUid(PACKAGE_NAME, 0);
+ doThrow(new SecurityException())
+ .when(mPackageManager).getPackageUidAsUser(eq(PACKAGE_NAME), anyInt());
assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -531,12 +534,14 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_automotive_failsIfAlreadySetByOtherPackage() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
String otherPackage = "Raconteurs";
- when(mPackageManager.getPackageUid(otherPackage, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(otherPackage), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, otherPackage));
assertThat(mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE),
contains(PACKAGE_NAME));
@@ -544,7 +549,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection_failsIfCannotLinkToDeath() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
doThrow(new RemoteException()).when(mBinder).linkToDeath(any(), anyInt());
assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -553,7 +559,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void requestProjection() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
// Should work for all powers of two.
for (int i = 0; i < Integer.SIZE; ++i) {
int projectionType = 1 << i;
@@ -568,11 +575,12 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_failsForBogusPackageName() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
.thenReturn(TestInjector.CALLING_UID + 1);
assertThrows(SecurityException.class, () -> mService.releaseProjection(
@@ -582,10 +590,11 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_failsIfNameNotFound() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
assertThrows(SecurityException.class, () -> mService.releaseProjection(
@@ -595,7 +604,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
@@ -613,7 +623,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void releaseProjection() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
requestAllPossibleProjectionTypes();
assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
@@ -632,7 +643,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void binderDeath_releasesProjection() throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
requestAllPossibleProjectionTypes();
assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor = ArgumentCaptor.forClass(
@@ -647,7 +659,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void getActiveProjectionTypes() throws Exception {
assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
@@ -657,7 +670,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void getProjectingPackages() throws Exception {
assertTrue(mService.getProjectingPackages(PROJECTION_TYPE_ALL).isEmpty());
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE).size());
assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_ALL).size());
@@ -681,7 +695,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
@Test
public void addOnProjectionStateChangedListener_callsListenerIfProjectionActive()
throws Exception {
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
@@ -710,7 +725,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
mService.removeOnProjectionStateChangedListener(listener);
// Now set automotive projection, should not call back.
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener, never()).onProjectionStateChanged(anyInt(), any());
}
@@ -726,7 +742,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
verifyNoMoreInteractions(listener);
// Now set automotive projection, should call back.
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
eq(List.of(PACKAGE_NAME)));
@@ -752,8 +769,9 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
int fakeProjectionType = 0x0002;
int otherFakeProjectionType = 0x0004;
String otherPackageName = "Internet Arms";
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
- when(mPackageManager.getPackageUid(otherPackageName, 0))
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(otherPackageName), anyInt()))
.thenReturn(TestInjector.CALLING_UID);
IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
@@ -806,7 +824,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
// Now kill the binder for the listener. This should remove it from the list of listeners.
listenerDeathRecipient.getValue().binderDied();
- when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+ when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+ .thenReturn(TestInjector.CALLING_UID);
mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
verify(listener, never()).onProjectionStateChanged(anyInt(), any());
}
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 f57c416e4a97..7bbf3e6c3b2e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4793,6 +4793,52 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testSetNotificationsShownFromListener_protectsCrossUserInformation()
+ throws RemoteException {
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag" + System.currentTimeMillis(), UserHandle.PER_USER_RANGE, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE),
+ null, 0);
+ final NotificationRecord r =
+ new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ r.setTextChanged(true);
+ mService.addNotification(r);
+
+ // no security exception!
+ mBinderService.setNotificationsShownFromListener(null, new String[] {r.getKey()});
+
+ verify(mAppUsageStats, never()).reportInterruptiveNotification(
+ anyString(), anyString(), anyInt());
+ }
+
+ @Test
+ public void testCancelNotificationsFromListener_protectsCrossUserInformation()
+ throws RemoteException {
+ Notification.Builder nb = new Notification.Builder(
+ mContext, mTestNotificationChannel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+ "tag" + System.currentTimeMillis(), UserHandle.PER_USER_RANGE, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE),
+ null, 0);
+ final NotificationRecord r =
+ new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ r.setTextChanged(true);
+ mService.addNotification(r);
+
+ // no security exception!
+ mBinderService.cancelNotificationsFromListener(null, new String[] {r.getKey()});
+
+ waitForIdle();
+ assertEquals(1, mService.getNotificationRecordCount());
+ }
+
+ @Test
public void testMaybeRecordInterruptionLocked_doesNotRecordTwice()
throws RemoteException {
final NotificationRecord r = generateNotificationRecord(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index fd6804641d2c..0169a7d96f9f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -16,10 +16,13 @@
package com.android.server.notification;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -116,4 +119,15 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase {
SmallHash.hash(group.hashCode()),
p.getGroupIdHash());
}
+
+ @Test
+ public void testIsForegroundService() {
+ NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+ 0, null);
+ assertFalse(NotificationRecordLogger.isForegroundService(p.r));
+
+ // Set foreground service
+ p.r.getSbn().getNotification().flags |= FLAG_FOREGROUND_SERVICE;
+ assertTrue(NotificationRecordLogger.isForegroundService(p.r));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 11162043bb27..9ad007d6a840 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -36,6 +36,7 @@ import org.junit.runner.RunWith;
import java.util.Calendar;
import java.util.GregorianCalendar;
+import java.util.TimeZone;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -171,6 +172,119 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
@Test
+ public void testGetNextChangeTime_startTomorrowInDaylight() {
+ // Test that the correct thing happens when the next start time would be tomorrow, during
+ // a schedule start time that doesn't exist that day. Consistent with "start times" as
+ // implemented in isInSchedule, this should get adjusted to the closest actual time.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day before the skipped hour for daylight savings.
+ Calendar today = getDaylightSavingsForwardDay();
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsForwardDay();
+ tomorrow.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 3AM as 2:15AM doesn't exist.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, 3);
+ expected.set(Calendar.MINUTE, 0);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
+ public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylight() {
+ // Test that the correct thing happens when the next start time would be tomorrow, but
+ // today is the day when daylight time switches over (so the "schedule start time" today
+ // may not exist).
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day with the skipped hour for daylight savings.
+ Calendar today = getDaylightSavingsForwardDay();
+ today.add(Calendar.DATE, 1);
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsForwardDay();
+ tomorrow.add(Calendar.DATE, 2);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 2:15AM.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+ expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
+ public void testGetNextChangeTime_startTomorrowWhenTodayIsDaylightBackward() {
+ // Test that the correct thing happens when the next start time would be tomorrow, but
+ // today is the day when clocks are adjusted backwards (so the "schedule start time" today
+ // exists twice).
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // "today" = the day with the extra hour for daylight savings.
+ Calendar today = getDaylightSavingsBackwardDay();
+ today.add(Calendar.DATE, 1);
+ today.set(Calendar.HOUR_OF_DAY, 23);
+ today.set(Calendar.MINUTE, 15);
+ Calendar tomorrow = getDaylightSavingsBackwardDay();
+ tomorrow.add(Calendar.DATE, 2);
+ mScheduleInfo.days = new int[] {today.get(Calendar.DAY_OF_WEEK),
+ tomorrow.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 1;
+ mScheduleInfo.endHour = 4;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endMinute = 15;
+ mScheduleInfo.exitAtAlarm = false;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // The expected next change time should be tomorrow, 1:15AM.
+ Calendar expected = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ expected.setTimeInMillis(tomorrow.getTimeInMillis());
+ expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
+ expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
+ expected.set(Calendar.SECOND, 0);
+ expected.set(Calendar.MILLISECOND, 0);
+
+ long actualMs = mScheduleCalendar.getNextChangeTime(today.getTimeInMillis());
+ GregorianCalendar actual = new GregorianCalendar(TimeZone.getTimeZone("America/New_York"));
+ actual.setTimeInMillis(actualMs);
+ assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
+ actualMs);
+ }
+
+ @Test
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
@@ -416,22 +530,264 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
@Test
+ public void testIsInSchedule_daylightSavingsForward_startDuringChange() {
+ // Test that if the start time of a ScheduleCalendar is during the nonexistent
+ // hour of daylight savings forward time, the evaluation of whether a time is in the
+ // schedule still works.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 2:30AM - 4:00AM.
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.startMinute = 30;
+ mScheduleInfo.endHour = 4;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: there are 2 "on" periods. These cover: before the first schedule
+ // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+ // (one on each calendar day), during the second (3:30AM), and after the second (4:30AM)
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 1);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsForwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 2);
+ in1.set(Calendar.MINUTE, 45);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut1 = getDaylightSavingsForwardDay();
+ midOut1.set(Calendar.HOUR_OF_DAY, 7);
+ midOut1.set(Calendar.MINUTE, 30);
+ midOut1.set(Calendar.SECOND, 0);
+ midOut1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut2 = getDaylightSavingsForwardDay();
+ midOut2.add(Calendar.DATE, 1);
+ midOut2.set(Calendar.HOUR_OF_DAY, 1);
+ midOut2.set(Calendar.MINUTE, 30);
+ midOut2.set(Calendar.SECOND, 0);
+ midOut2.set(Calendar.MILLISECOND, 0);
+
+ // Question: should 3:15AM be in the 2:30-4 schedule on a day when 2:30-3 doesn't exist?
+ Calendar in2 = getDaylightSavingsForwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 3);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 4);
+ out2.set(Calendar.MINUTE, 30);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavingsForward_endDuringChange() {
+ // Test that if the end time of a ScheduleCalendar is during the nonexistent
+ // hour of daylight savings forward time, the evaluation of whether a time is in the
+ // schedule still works.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 11:00PM - 2:30AM. On the day when 2AM doesn't exist, this should
+ // effectively finish at 3:30AM(?)
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 23;
+ mScheduleInfo.endHour = 2;
+ mScheduleInfo.endMinute = 30;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: before the time period on the previous day; during the time period when
+ // the calendar day is still the previous day; during the time period when the calendar
+ // day is the change day; afterwards.
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 22);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsForwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 23);
+ in1.set(Calendar.MINUTE, 30);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in2 = getDaylightSavingsForwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 1);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ // Question: Should 3:15AM be out of the schedule on a day when 2-3 doesn't exist?
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 3);
+ out2.set(Calendar.MINUTE, 45);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavingsBackward_startDuringChange() {
+ // Test that if the start time of a ScheduleCalendar is during the duplicated
+ // hour of daylight savings backward time, the evaluation of whether a time is in the
+ // schedule still works. It's not clear what correct behavior is during the duplicated
+ // 1:00->1:59->1:00->1:59 time period, but times outside that should still work.
+
+ // Set timezone to make sure we're evaluating the correct days.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 1:15AM - 4:00AM.
+ final Calendar dstYesterday = getDaylightSavingsBackwardDay();
+ final Calendar dstToday = getDaylightSavingsBackwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 1;
+ mScheduleInfo.startMinute = 15;
+ mScheduleInfo.endHour = 4;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // Test cases: there are 2 "on" periods. These cover: before the first schedule
+ // (1AM previous day), during the first schedule (2:30AM), two between the two schedules
+ // (one on each calendar day), during the second (2:30AM), and after the second (4:30AM)
+ Calendar out1 = getDaylightSavingsBackwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 1);
+ out1.set(Calendar.MINUTE, 00);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar in1 = getDaylightSavingsBackwardDay();
+ in1.set(Calendar.HOUR_OF_DAY, 2);
+ in1.set(Calendar.MINUTE, 30);
+ in1.set(Calendar.SECOND, 0);
+ in1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut1 = getDaylightSavingsBackwardDay();
+ midOut1.set(Calendar.HOUR_OF_DAY, 7);
+ midOut1.set(Calendar.MINUTE, 30);
+ midOut1.set(Calendar.SECOND, 0);
+ midOut1.set(Calendar.MILLISECOND, 0);
+
+ Calendar midOut2 = getDaylightSavingsBackwardDay();
+ midOut2.add(Calendar.DATE, 1);
+ midOut2.set(Calendar.HOUR_OF_DAY, 0);
+ midOut2.set(Calendar.MINUTE, 30);
+ midOut2.set(Calendar.SECOND, 0);
+ midOut2.set(Calendar.MILLISECOND, 0);
+
+ Calendar in2 = getDaylightSavingsBackwardDay();
+ in2.add(Calendar.DATE, 1);
+ in2.set(Calendar.HOUR_OF_DAY, 2);
+ in2.set(Calendar.MINUTE, 30);
+ in2.set(Calendar.SECOND, 0);
+ in2.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsBackwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 4);
+ out2.set(Calendar.MINUTE, 30);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(midOut2.getTimeInMillis()));
+ assertTrue(mScheduleCalendar.isInSchedule(in2.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
+ public void testIsInSchedule_daylightSavings_flippedSchedule() {
+ // This test is for the unlikely edge case where the skipped hour due to daylight savings
+ // causes the evaluated start time to be "later" than the schedule's end time on that day,
+ // for instance if the schedule is 2:30AM-3:15AM; 2:30AM may evaluate to 3:30AM on the day
+ // of daylight change.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+
+ // Set up schedule for 2:30AM - 3:15AM.
+ final Calendar dstYesterday = getDaylightSavingsForwardDay();
+ final Calendar dstToday = getDaylightSavingsForwardDay();
+ dstToday.add(Calendar.DATE, 1);
+ mScheduleInfo.days = new int[] {dstYesterday.get(Calendar.DAY_OF_WEEK),
+ dstToday.get(Calendar.DAY_OF_WEEK)};
+ mScheduleInfo.startHour = 2;
+ mScheduleInfo.startMinute = 30;
+ mScheduleInfo.endHour = 3;
+ mScheduleInfo.endMinute = 15;
+ mScheduleCalendar.setSchedule(mScheduleInfo);
+
+ // It may not be well-defined what times around the 2-3AM range one might expect to be
+ // included or not included on the weird day when 2AM doesn't exist, but other unrelated
+ // times of day (here, 3PM) should definitely be out.
+ Calendar out1 = getDaylightSavingsForwardDay();
+ out1.set(Calendar.HOUR_OF_DAY, 15);
+ out1.set(Calendar.MINUTE, 0);
+ out1.set(Calendar.SECOND, 0);
+ out1.set(Calendar.MILLISECOND, 0);
+
+ Calendar out2 = getDaylightSavingsForwardDay();
+ out2.add(Calendar.DATE, 1);
+ out2.set(Calendar.HOUR_OF_DAY, 15);
+ out2.set(Calendar.MINUTE, 0);
+ out2.set(Calendar.SECOND, 0);
+ out2.set(Calendar.MILLISECOND, 0);
+
+ assertFalse(mScheduleCalendar.isInSchedule(out1.getTimeInMillis()));
+ assertFalse(mScheduleCalendar.isInSchedule(out2.getTimeInMillis()));
+ }
+
+ @Test
public void testIsAlarmInSchedule_alarmAndNowInSchedule_sameScheduleTrigger_daylightSavings() {
- Calendar alarm = getDaylightSavingsDay();
+ // Need to set the time zone explicitly to a US one so that the daylight savings time day is
+ // correct.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+ Calendar alarm = getDaylightSavingsForwardDay();
alarm.set(Calendar.HOUR_OF_DAY, 23);
alarm.set(Calendar.MINUTE, 15);
alarm.set(Calendar.SECOND, 0);
alarm.set(Calendar.MILLISECOND, 0);
- Calendar now = getDaylightSavingsDay();
+ Calendar now = getDaylightSavingsForwardDay();
now.set(Calendar.HOUR_OF_DAY, 2);
now.set(Calendar.MINUTE, 10);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
now.add(Calendar.DATE, 1); // add a day, on daylight savings this becomes 3:10am
- final Calendar tempToday = getDaylightSavingsDay();
- final Calendar tempTomorrow = getDaylightSavingsDay();
+ final Calendar tempToday = getDaylightSavingsForwardDay();
+ final Calendar tempTomorrow = getDaylightSavingsForwardDay();
tempTomorrow.add(Calendar.DATE, 1);
mScheduleInfo.days = new int[] {tempToday.get(Calendar.DAY_OF_WEEK),
tempTomorrow.get(Calendar.DAY_OF_WEEK)};
@@ -506,6 +862,80 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
now.getTimeInMillis()));
}
+ @Test
+ public void testClosestActualTime_regularTimesAndSkippedTime() {
+ // Make sure we're operating in the relevant time zone for the assumed Daylight Savings day
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("America/New_York"));
+ Calendar day = getDaylightSavingsForwardDay();
+ day.set(Calendar.HOUR_OF_DAY, 15);
+ day.set(Calendar.MINUTE, 25);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+
+ // Check a skipped time
+ day.add(Calendar.DATE, 1);
+ day.set(Calendar.HOUR_OF_DAY, 3);
+ day.set(Calendar.MINUTE, 0);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 2, 15));
+
+ // Check a non-skipped time after the clocks have moved forward
+ day.set(Calendar.HOUR_OF_DAY, 15);
+ day.set(Calendar.MINUTE, 25);
+ day.set(Calendar.SECOND, 0);
+ day.set(Calendar.MILLISECOND, 0);
+ assertEquals(day.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(day.getTimeInMillis(), 15, 25));
+ }
+
+ @Test
+ public void testClosestActualTime_otherTimeZones() {
+ // Make sure this doesn't only work for US/Eastern time.
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/London"));
+ Calendar ukDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/London"));
+ ukDstDay.set(2021, Calendar.MARCH, 28);
+
+ // Check a skipped time, which is 01:xx on that day in the UK
+ ukDstDay.set(Calendar.HOUR_OF_DAY, 2);
+ ukDstDay.set(Calendar.MINUTE, 0);
+ ukDstDay.set(Calendar.SECOND, 0);
+ ukDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(ukDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 1, 25));
+
+ // Check a non-skipped time
+ ukDstDay.set(Calendar.HOUR_OF_DAY, 11);
+ ukDstDay.set(Calendar.MINUTE, 23);
+ ukDstDay.set(Calendar.SECOND, 0);
+ ukDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(ukDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(ukDstDay.getTimeInMillis(), 11, 23));
+
+ mScheduleCalendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
+ Calendar frDstDay = new GregorianCalendar(TimeZone.getTimeZone("Europe/Paris"));
+ frDstDay.set(2021, Calendar.MARCH, 28);
+
+ // Check a skipped time, which is 02:xx on that day in France
+ frDstDay.set(Calendar.HOUR_OF_DAY, 3);
+ frDstDay.set(Calendar.MINUTE, 0);
+ frDstDay.set(Calendar.SECOND, 0);
+ frDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(frDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 2, 25));
+
+ // Check a regular time
+ frDstDay.set(Calendar.HOUR_OF_DAY, 14);
+ frDstDay.set(Calendar.MINUTE, 59);
+ frDstDay.set(Calendar.SECOND, 0);
+ frDstDay.set(Calendar.MILLISECOND, 0);
+ assertEquals(frDstDay.getTimeInMillis(),
+ mScheduleCalendar.getClosestActualTime(frDstDay.getTimeInMillis(), 14, 59));
+ }
+
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
@@ -517,9 +947,23 @@ public class ScheduleCalendarTest extends UiServiceTestCase {
}
- private Calendar getDaylightSavingsDay() {
- // the day before daylight savings in the US - March 9, 2019
- Calendar daylightSavingsDay = new GregorianCalendar(2019, 2, 9);
+ private Calendar getDaylightSavingsForwardDay() {
+ // the day before daylight savings rolls forward in the US - March 9, 2019
+ // 2AM March 10, 2019 does not exist -- goes straight from 1:59 to 3:00
+ // Specifically set to US/Eastern time zone rather than relying on a default time zone
+ // to make sure the date is the correct one, since DST changes vary by region.
+ Calendar daylightSavingsDay = new GregorianCalendar(
+ TimeZone.getTimeZone("America/New_York"));
+ daylightSavingsDay.set(2019, Calendar.MARCH, 9);
+ return daylightSavingsDay;
+ }
+
+ private Calendar getDaylightSavingsBackwardDay() {
+ // the day before daylight savings rolls backward in the US - November 2, 2019
+ // In this instance, 1AM November 3 2019 is repeated twice; 1:00->1:59->1:00->1:59->2:00
+ Calendar daylightSavingsDay = new GregorianCalendar(
+ TimeZone.getTimeZone("America/New_York"));
+ daylightSavingsDay.set(2019, Calendar.NOVEMBER, 2);
return daylightSavingsDay;
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 4410404b0cd7..31be33e98363 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -28,6 +28,7 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CA
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -1263,6 +1264,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_AMBIENT
| SUPPRESSED_EFFECT_PEEK,
mZenModeHelperSpy.mConfig.suppressedVisualEffects);
@@ -1296,7 +1298,9 @@ public class ZenModeHelperTest extends UiServiceTestCase {
parser.nextTag();
mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL);
- assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_LIGHTS,
+ assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
+ | SUPPRESSED_EFFECT_LIGHTS
+ | SUPPRESSED_EFFECT_AMBIENT,
mZenModeHelperSpy.mConfig.suppressedVisualEffects);
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 222c692a9778..6b36fe808a2e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -69,7 +69,7 @@ public class SingleKeyGestureTests {
@Before
public void setUp() {
- mDetector = new SingleKeyGestureDetector(mContext);
+ mDetector = new SingleKeyGestureDetector();
initSingleKeyGestureRules();
mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50;
mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50;
@@ -78,7 +78,7 @@ public class SingleKeyGestureTests {
}
private void initSingleKeyGestureRules() {
- mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER,
+ mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(mContext, KEYCODE_POWER,
KEY_LONGPRESS | KEY_VERYLONGPRESS) {
@Override
int getMaxMultiPressCount() {
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 32a4774ca4f2..89a126b92bfa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -27,6 +27,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
@@ -44,6 +45,7 @@ import android.os.IBinder;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
+import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -69,6 +71,7 @@ import java.util.function.ToIntFunction;
@Presubmit
@RunWith(WindowTestRunner.class)
public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
+ private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
private ActivityMetricsLogger mActivityMetricsLogger;
private ActivityMetricsLogger.LaunchingState mLaunchingState;
private ActivityMetricsLaunchObserver mLaunchObserver;
@@ -136,7 +139,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// messages that are waiting for the lock.
waitHandlerIdle(mAtm.mH);
// AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
- return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
+ return verify(mock, timeout(TIMEOUT_MS));
}
private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
@@ -257,15 +260,40 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testOnActivityLaunchWhileSleeping() {
- notifyActivityLaunching(mTopActivity.intent);
- notifyActivityLaunched(START_SUCCESS, mTopActivity);
- doReturn(true).when(mTopActivity.mDisplayContent).isSleeping();
- mTopActivity.setState(Task.ActivityState.RESUMED, "test");
- mTopActivity.setVisibility(false);
+ notifyActivityLaunching(mTrampolineActivity.intent);
+ notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+ doReturn(true).when(mTrampolineActivity.mDisplayContent).isSleeping();
+ mTrampolineActivity.setState(ActivityRecord.State.RESUMED, "test");
+ mTrampolineActivity.setVisibility(false);
waitHandlerIdle(mAtm.mH);
// Not cancel immediately because in one of real cases, the keyguard may be going away or
// occluded later, then the activity can be drawn.
- verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTopActivity));
+ verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
+
+ clearInvocations(mLaunchObserver);
+ mLaunchTopByTrampoline = true;
+ mTopActivity.mVisibleRequested = false;
+ notifyActivityLaunching(mTopActivity.intent);
+ // It should schedule a message with UNKNOWN_VISIBILITY_CHECK_DELAY_MS to check whether
+ // the launch event is still valid.
+ notifyActivityLaunched(START_SUCCESS, mTopActivity);
+
+ // The posted message will acquire wm lock, so the test needs to release the lock to verify.
+ final Throwable error = awaitInWmLock(() -> {
+ try {
+ // Though the aborting target should be eqProto(mTopActivity), use any() to avoid
+ // any changes in proto that may cause failure by different arguments.
+ verify(mLaunchObserver, timeout(TIMEOUT_MS)).onActivityLaunchCancelled(any());
+ } catch (Throwable e) {
+ // Catch any errors including assertion because this runs in another thread.
+ return e;
+ }
+ return null;
+ });
+ // The launch event must be cancelled because the activity keeps invisible.
+ if (error != null) {
+ throw new AssertionError(error);
+ }
}
@Test
@@ -376,6 +404,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
// Another round without setting visibility of the trampoline activity.
onActivityLaunchedTrampoline();
+ mTrampolineActivity.setState(ActivityRecord.State.PAUSING, "test");
notifyWindowsDrawn(mTopActivity);
// If the transition can start, the invisible activities should be discarded and the launch
// event be reported successfully.
@@ -449,8 +478,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
@Test
public void testConsecutiveLaunchNewTask() {
final IBinder launchCookie = mock(IBinder.class);
+ final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
mTrampolineActivity.noDisplay = true;
mTrampolineActivity.mLaunchCookie = launchCookie;
+ mTrampolineActivity.mLaunchRootTask = launchRootTask;
onActivityLaunched(mTrampolineActivity);
final ActivityRecord activityOnNewTask = new ActivityBuilder(mAtm)
.setCreateTask(true)
@@ -464,6 +495,10 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
mTrampolineActivity.mLaunchCookie).isNull();
assertWithMessage("The last launch task has the transferred cookie").that(
activityOnNewTask.mLaunchCookie).isEqualTo(launchCookie);
+ assertWithMessage("Trampoline's launch root task must be transferred").that(
+ mTrampolineActivity.mLaunchRootTask).isNull();
+ assertWithMessage("The last launch task has the transferred launch root task").that(
+ activityOnNewTask.mLaunchRootTask).isEqualTo(launchRootTask);
}
@Test
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 293e862a6b74..9ca09d20cd49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -66,19 +67,19 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.INITIALIZING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STARTED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
@@ -136,7 +137,7 @@ import android.window.TaskSnapshot;
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.Task.ActivityState;
+import com.android.server.wm.ActivityRecord.State;
import org.junit.Assert;
import org.junit.Before;
@@ -145,6 +146,8 @@ import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
@@ -171,25 +174,25 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testStackCleanupOnClearingTask() {
+ public void testTaskFragmentCleanupOnClearingTask() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
- final Task rootTask = activity.getRootTask();
+ final TaskFragment taskFragment = activity.getTaskFragment();
activity.onParentChanged(null /*newParent*/, task);
- verify(rootTask, times(1)).cleanUpActivityReferences(any());
+ verify(taskFragment).cleanUpActivityReferences(any());
}
@Test
- public void testStackCleanupOnActivityRemoval() {
+ public void testTaskFragmentCleanupOnActivityRemoval() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
- final Task rootTask = activity.getRootTask();
+ final TaskFragment taskFragment = activity.getTaskFragment();
task.removeChild(activity);
- verify(rootTask, times(1)).cleanUpActivityReferences(any());
+ verify(taskFragment).cleanUpActivityReferences(any());
}
@Test
- public void testStackCleanupOnTaskRemoval() {
+ public void testRootTaskCleanupOnTaskRemoval() {
final ActivityRecord activity = createActivityWith2LevelTask();
final Task task = activity.getTask();
final Task rootTask = activity.getRootTask();
@@ -344,7 +347,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_NotDragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -369,7 +372,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_DragResizing() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -396,7 +399,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testRelaunchClearTopWaitingTranslucent() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -417,7 +420,7 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testSetsRelaunchReason_NonResizeConfigChanges() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
task.onRequestedOverrideConfigurationChanged(task.getConfiguration());
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
@@ -465,7 +468,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setCreateTask(true)
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
- activity.setState(Task.ActivityState.RESUMED, "Testing");
+ activity.setState(RESUMED, "Testing");
activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
activity.getConfiguration()));
@@ -628,7 +631,7 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testShouldMakeActive_deferredResume() {
final ActivityRecord activity = createActivityWithTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
mSupervisor.beginDeferResume();
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
@@ -644,7 +647,7 @@ public class ActivityRecordTests extends WindowTestsBase {
ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
finishingActivity.finishing = true;
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
assertEquals(false, activity.shouldMakeActive(null /* activeActivity */));
}
@@ -653,15 +656,16 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testShouldResume_stackVisibility() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT)
+ .when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
- doReturn(TASK_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_INVISIBLE).when(task).getVisibility(null);
assertEquals(false, activity.shouldResumeActivity(null /* activeActivity */));
}
@@ -669,13 +673,13 @@ public class ActivityRecordTests extends WindowTestsBase {
public void testShouldResumeOrPauseWithResults() {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
topActivity.finishing = true;
- doReturn(TASK_VISIBILITY_VISIBLE).when(task).getVisibility(null);
+ doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
assertEquals(true, activity.shouldResumeActivity(null /* activeActivity */));
assertEquals(false, activity.shouldPauseActivity(null /*activeActivity */));
}
@@ -688,7 +692,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
.build();
final Task task = activity.getTask();
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
@@ -731,7 +735,7 @@ public class ActivityRecordTests extends WindowTestsBase {
final ActivityRecord activity = createActivityWithTask();
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
topActivity.setOccludesParent(false);
- activity.setState(Task.ActivityState.STOPPED, "Testing");
+ activity.setState(STOPPED, "Testing");
activity.setVisibility(true);
activity.makeActiveIfNeeded(null /* activeActivity */);
assertEquals(STARTED, activity.getState());
@@ -1015,8 +1019,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
final ActivityRecord activity = createActivityWithTask();
- final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
- for (ActivityState state : states) {
+ final State[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+ for (State state : states) {
activity.finishing = false;
activity.setState(state, "test");
reset(activity);
@@ -1151,7 +1155,7 @@ public class ActivityRecordTests extends WindowTestsBase {
/**
* Verify that finish request won't change the state of next top activity if the current
* finishing activity doesn't need to be destroyed immediately. The case is usually like
- * from {@link ActivityStack#completePauseLocked(boolean, ActivityRecord)} to
+ * from {@link Task#completePause(boolean, ActivityRecord)} to
* {@link ActivityRecord#completeFinishing(String)}, so the complete-pause should take the
* responsibility to resume the next activity with updating the state.
*/
@@ -1397,7 +1401,7 @@ public class ActivityRecordTests extends WindowTestsBase {
}
private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
- ActivityState secondActivityState) {
+ State secondActivityState) {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -1449,7 +1453,8 @@ public class ActivityRecordTests extends WindowTestsBase {
@Test
public void testDestroyIfPossible() {
final ActivityRecord activity = createActivityWithTask();
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
activity.destroyIfPossible("test");
assertEquals(DESTROYING, activity.getState());
@@ -1471,7 +1476,8 @@ public class ActivityRecordTests extends WindowTestsBase {
homeStack.removeChild(t, "test");
}, true /* traverseTopToBottom */);
activity.finishing = true;
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
// Try to destroy the last activity above the home stack.
activity.destroyIfPossible("test");
@@ -1598,16 +1604,23 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testRemoveImmediately() throws RemoteException {
- final ActivityRecord activity = createActivityWithTask();
- final WindowProcessController wpc = activity.app;
- activity.getTask().removeImmediately("test");
-
- verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken),
- isA(DestroyActivityItem.class));
- assertNull(activity.app);
- assertEquals(DESTROYED, activity.getState());
- assertFalse(wpc.hasActivities());
+ public void testRemoveImmediately() {
+ final Consumer<Consumer<ActivityRecord>> test = setup -> {
+ final ActivityRecord activity = createActivityWithTask();
+ final WindowProcessController wpc = activity.app;
+ setup.accept(activity);
+ activity.getTask().removeImmediately("test");
+ try {
+ verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken),
+ isA(DestroyActivityItem.class));
+ } catch (RemoteException ignored) {
+ }
+ assertNull(activity.app);
+ assertEquals(DESTROYED, activity.getState());
+ assertFalse(wpc.hasActivities());
+ };
+ test.accept(activity -> activity.setState(RESUMED, "test"));
+ test.accept(activity -> activity.finishing = true);
}
@Test
@@ -1851,7 +1864,7 @@ public class ActivityRecordTests extends WindowTestsBase {
doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */,
- any() /* requestedVisibility */, any() /* outInputChannel */,
+ any() /* requestedVisibilities */, any() /* outInputChannel */,
any() /* outInsetsState */, any() /* outActiveControls */);
mAtm.mWindowManager.mStartingSurfaceController
.createTaskSnapshotSurface(activity, snapshot);
@@ -1907,8 +1920,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertTrue(wpc.registeredForActivityConfigChanges());
// Create a new task with custom config to reparent the activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -1940,8 +1952,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.diff(wpc.getRequestedOverrideConfiguration()));
// Create a new task with custom config to reparent the second activity to.
- final Task newTask =
- new TaskBuilder(mSupervisor).setParentTask(initialTask.getRootTask()).build();
+ final Task newTask = new TaskBuilder(mSupervisor).build();
final Configuration newConfig = newTask.getConfiguration();
newConfig.densityDpi += 100;
newTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -2151,7 +2162,7 @@ public class ActivityRecordTests extends WindowTestsBase {
assertFalse(activity.supportsPictureInPicture());
}
- private void verifyProcessInfoUpdate(ActivityRecord activity, ActivityState state,
+ private void verifyProcessInfoUpdate(ActivityRecord activity, State state,
boolean shouldUpdate, boolean activityChange) {
reset(activity.app);
activity.setState(state, "test");
@@ -2505,7 +2516,7 @@ public class ActivityRecordTests extends WindowTestsBase {
false, false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
true, true, false, true, false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
@@ -2522,7 +2533,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Surprise, ...! Transfer window in the middle of the creation flow.
activity2.addStartingWindow(mPackageName,
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
- activity1.appToken.asBinder(), true, true, false,
+ activity1, true, true, false,
true, false, false);
});
activity1.addStartingWindow(mPackageName,
@@ -2543,7 +2554,7 @@ public class ActivityRecordTests extends WindowTestsBase {
false, false);
waitUntilHandlersIdle();
activity2.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
true, true, false, true, false, false);
waitUntilHandlersIdle();
assertNoStartingWindow(activity1);
@@ -2589,17 +2600,17 @@ public class ActivityRecordTests extends WindowTestsBase {
false /* activityCreate */, false /* suggestEmpty */);
waitUntilHandlersIdle();
assertHasStartingWindow(activity);
- activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
doCallRealMethod().when(task).startActivityLocked(
any(), any(), anyBoolean(), anyBoolean(), any(), any());
// In normal case, resumeFocusedTasksTopActivities() should be called after
// startActivityLocked(). So skip resumeFocusedTasksTopActivities() in ActivityBuilder.
- doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
+ doReturn(false).when(mRootWindowContainer)
+ .resumeFocusedTasksTopActivities();
// Make mVisibleSetFromTransferredStartingWindow true.
final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
task.startActivityLocked(middle, null /* focusedTopActivity */,
- false /* newTask */, false /* keepCurTransition */, null /* options */,
+ false /* newTask */, false /* isTaskSwitch */, null /* options */,
null /* sourceRecord */);
middle.makeFinishingLocked();
@@ -2612,7 +2623,7 @@ public class ActivityRecordTests extends WindowTestsBase {
top.setVisible(false);
// The finishing middle should be able to transfer starting window to top.
task.startActivityLocked(top, null /* focusedTopActivity */,
- false /* newTask */, false /* keepCurTransition */, null /* options */,
+ false /* newTask */, false /* isTaskSwitch */, null /* options */,
null /* sourceRecord */);
assertNull(middle.mStartingWindow);
@@ -2649,7 +2660,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// Make sure the fixed rotation transform linked to activity2 when adding starting window
// on activity2.
topActivity.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
false, false, false, true, false, false);
waitUntilHandlersIdle();
assertTrue(topActivity.hasFixedRotationTransform());
@@ -2681,6 +2692,52 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
+ public void testStartingWindowInTaskFragment() {
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final WindowState startingWindow = createWindowState(
+ new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1);
+ activity1.addWindow(startingWindow);
+ activity1.attachStartingWindow(startingWindow);
+ activity1.mStartingData = mock(StartingData.class);
+ final Task task = activity1.getTask();
+ final Rect taskBounds = task.getBounds();
+ final int width = taskBounds.width();
+ final int height = taskBounds.height();
+ final BiConsumer<TaskFragment, Rect> fragmentSetup = (fragment, bounds) -> {
+ final Configuration config = fragment.getRequestedOverrideConfiguration();
+ config.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ config.windowConfiguration.setBounds(bounds);
+ fragment.onRequestedOverrideConfigurationChanged(config);
+ };
+
+ final TaskFragment taskFragment1 = new TaskFragment(
+ mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
+ fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
+ task.addChild(taskFragment1, POSITION_TOP);
+
+ final TaskFragment taskFragment2 = new TaskFragment(
+ mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
+ fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
+ task.addChild(taskFragment2, POSITION_TOP);
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+ activity2.mVisibleRequested = true;
+ taskFragment2.addChild(activity2);
+ activity1.reparent(taskFragment1, POSITION_TOP);
+
+ assertEquals(task, activity1.mStartingData.mAssociatedTask);
+ assertEquals(taskFragment1.getBounds(), activity1.getBounds());
+ // The activity was resized by task fragment, but starting window must still cover the task.
+ assertEquals(taskBounds, activity1.mStartingWindow.getBounds());
+
+ // The starting window is only removed when all embedded activities are drawn.
+ final WindowState activityWindow = mock(WindowState.class);
+ activity1.onFirstWindowDrawn(activityWindow);
+ assertNotNull(activity1.mStartingWindow);
+ activity2.onFirstWindowDrawn(activityWindow);
+ assertNull(activity1.mStartingWindow);
+ }
+
+ @Test
public void testTransitionAnimationBounds() {
removeGlobalMinSizeRestriction();
final Task task = new TaskBuilder(mSupervisor)
@@ -2738,6 +2795,40 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
+ public void testCloseToSquareFixedOrientationPortrait() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed portrait activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build();
+
+ // check that both the configuration and app bounds are portrait
+ assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ <= activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
+ @Test
+ public void testCloseToSquareFixedOrientationLandscape() {
+ // create a square display
+ final DisplayContent squareDisplay = new TestDisplayContent.Builder(mAtm, 2000, 2000)
+ .setSystemDecorations(true).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
+
+ // create a fixed landscape activity
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task)
+ .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE).build();
+
+ // check that both the configuration and app bounds are landscape
+ assertEquals(ORIENTATION_LANDSCAPE, activity.getConfiguration().orientation);
+ assertTrue(activity.getConfiguration().windowConfiguration.getAppBounds().width()
+ > activity.getConfiguration().windowConfiguration.getAppBounds().height());
+ }
+
+ @Test
public void testSetVisibility_visibleToVisible() {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true).build();
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 d0588a30ca67..1b4d0a4ab5a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -32,6 +32,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
@@ -755,12 +756,12 @@ public class ActivityStarterTests extends WindowTestsBase {
}
/**
- * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will
- * move the existing task to front if the current focused stack doesn't have running task.
+ * This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will
+ * move the existing task to front if the current focused root task doesn't have running task.
*/
@Test
- public void testBringTaskToFrontWhenFocusedStackIsFinising() {
- // Put 2 tasks in the same stack (simulate the behavior of home stack).
+ public void testBringTaskToFrontWhenFocusedTaskIsFinishing() {
+ // Put 2 tasks in the same root task (simulate the behavior of home root task).
final Task rootTask = new TaskBuilder(mSupervisor).build();
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setParentTask(rootTask)
@@ -777,13 +778,16 @@ public class ActivityStarterTests extends WindowTestsBase {
assertEquals(finishingTopActivity, mRootWindowContainer.topRunningActivity());
finishingTopActivity.finishing = true;
- // Launch the bottom task of the target stack.
+ // Launch the bottom task of the target root task.
prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */)
- .setReason("testBringTaskToFrontWhenTopStackIsFinising")
- .setIntent(activity.intent)
+ .setReason("testBringTaskToFrontWhenFocusedTaskIsFinishing")
+ .setIntent(activity.intent.addFlags(
+ FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
.execute();
+ verify(activity.getRootTask()).startActivityLocked(any(), any(), anyBoolean(),
+ eq(true) /* isTaskSwitch */, any(), any());
// The hierarchies of the activity should move to front.
- assertEquals(activity, mRootWindowContainer.topRunningActivity());
+ assertEquals(activity.getTask(), mRootWindowContainer.topRunningActivity().getTask());
}
/**
@@ -1136,6 +1140,7 @@ public class ActivityStarterTests extends WindowTestsBase {
/* doResume */true,
/* options */null,
/* inTask */null,
+ /* inTaskFragment */ null,
/* restrictedBgActivity */false,
/* intentGrants */null);
@@ -1146,6 +1151,31 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
+ public void testStartActivityInner_inTaskFragment() {
+ final ActivityStarter starter = prepareStarter(0, false);
+ final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+ final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+ true /* createdByOrganizer */);
+ sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+ starter.startActivityInner(
+ /* r */targetRecord,
+ /* sourceRecord */ sourceRecord,
+ /* voiceSession */null,
+ /* voiceInteractor */ null,
+ /* startFlags */ 0,
+ /* doResume */true,
+ /* options */null,
+ /* inTask */null,
+ /* inTaskFragment */ taskFragment,
+ /* restrictedBgActivity */false,
+ /* intentGrants */null);
+
+ assertTrue(taskFragment.hasChild());
+ }
+
+ @Test
public void testLaunchCookie_newAndExistingTask() {
final ActivityStarter starter = prepareStarter(0, false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 741f33f8aca7..8ca14bc0bb86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -244,7 +248,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
.setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask())
.build();
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
mSupervisor.endDeferResume();
assertEquals(activity.app, mAtm.mInternal.getTopApp());
@@ -254,13 +258,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
activity.mVisibleRequested = false;
activity.setVisible(false);
activity.getTask().setPausingActivity(activity);
- homeActivity.setState(Task.ActivityState.PAUSED, "test");
+ homeActivity.setState(PAUSED, "test");
// Even the visibility states are invisible, the next activity should be resumed because
// the crashed activity was pausing.
mAtm.mInternal.handleAppDied(activity.app, false /* restarting */,
null /* finishInstrumentationCallback */);
- assertEquals(Task.ActivityState.RESUMED, homeActivity.getState());
+ assertEquals(RESUMED, homeActivity.getState());
assertEquals(homeActivity.app, mAtm.mInternal.getTopApp());
}
@@ -271,7 +275,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
final Task rootHomeTask = mWm.mRoot.getDefaultTaskDisplayArea().getOrCreateRootHomeTask();
final ActivityRecord homeActivity = new ActivityBuilder(mAtm).setTask(rootHomeTask).build();
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
- topActivity.setState(Task.ActivityState.RESUMED, "test");
+ topActivity.setState(RESUMED, "test");
final Consumer<ActivityRecord> assertTopNonSleeping = activity -> {
assertFalse(mAtm.mInternal.isSleeping());
@@ -287,7 +291,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
verify(mSupervisor.mGoingToSleepWakeLock).acquire();
doReturn(true).when(mSupervisor.mGoingToSleepWakeLock).isHeld();
- assertEquals(Task.ActivityState.PAUSING, topActivity.getState());
+ assertEquals(PAUSING, topActivity.getState());
assertTrue(mAtm.mInternal.isSleeping());
assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
mAtm.mInternal.getTopProcessState());
@@ -298,7 +302,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
final Task topRootTask = topActivity.getRootTask();
doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
doReturn(true).when(topRootTask).goToSleepIfPossible(anyBoolean());
- topActivity.setState(Task.ActivityState.STOPPING, "test");
+ topActivity.setState(STOPPING, "test");
topActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */,
null /* description */);
verify(mSupervisor.mGoingToSleepWakeLock).release();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 0d177c14427b..26a68821a672 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -49,6 +49,7 @@ import androidx.test.filters.MediumTest;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
import java.util.concurrent.TimeUnit;
@@ -108,16 +109,18 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
final ActivityMetricsLogger.LaunchingState launchingState =
new ActivityMetricsLogger.LaunchingState();
spyOn(launchingState);
- doReturn(true).when(launchingState).contains(eq(secondActivity));
+ doReturn(true).when(launchingState).hasActiveTransitionInfo();
+ doReturn(true).when(launchingState).contains(
+ ArgumentMatchers.argThat(r -> r == firstActivity || r == secondActivity));
// The test case already runs inside global lock, so above thread can only execute after
// this waiting method that releases the lock.
mSupervisor.waitActivityVisibleOrLaunched(taskToFrontWait, firstActivity, launchingState);
// Assert that the thread is finished.
assertTrue(condition.block(TIMEOUT_MS));
- assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
- assertEquals(taskToFrontWait.who, secondActivity.mActivityComponent);
- assertEquals(taskToFrontWait.launchState, WaitResult.LAUNCH_STATE_HOT);
+ assertEquals(START_TASK_TO_FRONT, taskToFrontWait.result);
+ assertEquals(secondActivity.mActivityComponent, taskToFrontWait.who);
+ assertEquals(WaitResult.LAUNCH_STATE_HOT, taskToFrontWait.launchState);
// START_TASK_TO_FRONT means that another component will be visible, so the component
// should not be assigned as the first activity.
assertNull(launchedComponent[0]);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 31d46125fd70..af21e02ce27c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -35,10 +35,10 @@ import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
+import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
-import static com.android.server.wm.DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID;
import static com.google.common.truth.Truth.assertThat;
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 4e4e0edc694a..1f123ccef164 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -63,6 +63,7 @@ import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
import com.google.android.collect.Lists;
@@ -570,6 +571,31 @@ public class DisplayAreaTest extends WindowTestsBase {
}
@Test
+ public void testGetDisplayAreaInfo() {
+ final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+ mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
+ mDisplayContent.addChild(displayArea, 0);
+ final DisplayAreaInfo info = displayArea.getDisplayAreaInfo();
+
+ assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken());
+ assertThat(info.configuration).isEqualTo(displayArea.getConfiguration());
+ assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId());
+ assertThat(info.featureId).isEqualTo(displayArea.mFeatureId);
+ assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId);
+
+ final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
+ final int tdaIndex = tda.getParent().mChildren.indexOf(tda);
+ final RootDisplayArea root =
+ new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1);
+ mDisplayContent.addChild(root, tdaIndex + 1);
+ displayArea.reparent(root, 0);
+
+ final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo();
+
+ assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId);
+ }
+
+ @Test
public void testRegisterSameFeatureOrganizer_expectThrowsException() {
final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
final IBinder binder = mock(IBinder.class);
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 12fc2f4ea1f4..473d3038e3dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -67,6 +67,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
@@ -107,9 +108,12 @@ import android.app.WindowConfiguration;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.metrics.LogMaker;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -122,6 +126,7 @@ import android.view.IDisplayWindowRotationController;
import android.view.ISystemGestureExclusionListener;
import android.view.IWindowManager;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -134,6 +139,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -141,6 +147,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.Arrays;
@@ -592,7 +600,9 @@ public class DisplayContentTests extends WindowTestsBase {
dc.setImeLayeringTarget(ws);
// Adjust bounds so that matchesRootDisplayAreaBounds() returns false.
- ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1));
+ final Rect bounds = new Rect(dc.getBounds());
+ bounds.scale(0.5f);
+ ws.mActivityRecord.setBounds(bounds);
assertFalse("matchesRootDisplayAreaBounds() should return false",
ws.matchesDisplayAreaBounds());
@@ -870,19 +880,6 @@ public class DisplayContentTests extends WindowTestsBase {
.setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
}
- @UseTestDisplay
- @Test
- public void testClearLastFocusWhenReparentingFocusedWindow() {
- final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
- defaultDisplay, "window");
- defaultDisplay.mLastFocus = window;
- mDisplayContent.mCurrentFocus = window;
- mDisplayContent.reParentWindowToken(window.mToken);
-
- assertNull(defaultDisplay.mLastFocus);
- }
-
@Test
public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() {
final DisplayContent portraitDisplay = createNewDisplay();
@@ -1217,10 +1214,10 @@ public class DisplayContentTests extends WindowTestsBase {
win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
win.getAttrs().insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
- requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- win.updateRequestedVisibility(requestedState);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_NAVIGATION_BAR, false);
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ win.setRequestedVisibilities(requestedVisibilities);
win.mActivityRecord.mTargetSdk = P;
performLayout(dc);
@@ -1311,7 +1308,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
@UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
- W_NOTIFICATION_SHADE })
+ W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
public void testApplyTopFixedRotationTransform() {
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
@@ -1415,6 +1412,14 @@ public class DisplayContentTests extends WindowTestsBase {
assertEquals("The process should receive rotated configuration for compatibility",
expectedProcConfig, app2.app.getConfiguration());
+ // If the rotated activity requests to show IME, the IME window should use the
+ // transformation from activity to lay out in the same orientation.
+ mDisplayContent.setImeLayeringTarget(mAppWindow);
+ LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
+ app.token, app.token, mDisplayContent.mDisplayId);
+ assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
+ assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+
// The fixed rotation transform can only be finished when all animation finished.
doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
@@ -1530,7 +1535,7 @@ public class DisplayContentTests extends WindowTestsBase {
unblockDisplayRotation(mDisplayContent);
final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
app.setVisible(false);
- app.setState(Task.ActivityState.RESUMED, "test");
+ app.setState(ActivityRecord.State.RESUMED, "test");
mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
mDisplayContent.mOpeningApps.add(app);
final int newOrientation = getRotatedOrientation(mDisplayContent);
@@ -2230,6 +2235,113 @@ public class DisplayContentTests extends WindowTestsBase {
assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
}
+ @Test
+ public void testVirtualDisplayContent() {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .spyStatic(SurfaceControl.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+ // mirror.
+ final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+
+ // GIVEN SurfaceControl can successfully mirror the provided surface.
+ Point surfaceSize = new Point(
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+ surfaceControlMirrors(surfaceSize);
+
+ // WHEN creating the DisplayContent for a new virtual display.
+ final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
+ mDisplayInfo).build();
+
+ // THEN mirroring is initiated for the default display's DisplayArea.
+ assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
+
+ mockSession.finishMocking();
+ }
+
+ @Test
+ public void testVirtualDisplayContent_capturedAreaResized() {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .spyStatic(SurfaceControl.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+ // mirror.
+ final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+
+ // GIVEN SurfaceControl can successfully mirror the provided surface.
+ Point surfaceSize = new Point(
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+ mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+ SurfaceControl mirroredSurface = surfaceControlMirrors(surfaceSize);
+
+ // WHEN creating the DisplayContent for a new virtual display.
+ final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
+ mDisplayInfo).build();
+
+ // THEN mirroring is initiated for the default display's DisplayArea.
+ assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
+
+ float xScale = 0.7f;
+ float yScale = 2f;
+ Rect displayAreaBounds = new Rect(0, 0, Math.round(surfaceSize.x * xScale),
+ Math.round(surfaceSize.y * yScale));
+ virtualDisplay.updateMirroredSurface(mTransaction, displayAreaBounds);
+
+ // THEN content in the captured DisplayArea is scaled to fit the surface size.
+ verify(mTransaction, atLeastOnce()).setMatrix(mirroredSurface, 1.0f / yScale, 0, 0,
+ 1.0f / yScale);
+ // THEN captured content is positioned in the centre of the output surface.
+ float scaledWidth = displayAreaBounds.width() / xScale;
+ float xInset = (surfaceSize.x - scaledWidth) / 2;
+ verify(mTransaction, atLeastOnce()).setPosition(mirroredSurface, xInset, 0);
+
+ mockSession.finishMocking();
+ }
+
+ private class TestToken extends Binder {
+ }
+
+ /**
+ * Creates a WindowToken associated with the default task DisplayArea, in order for that
+ * DisplayArea to be mirrored.
+ */
+ private IBinder setUpDefaultTaskDisplayAreaWindowToken() {
+ // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+ // mirror.
+ final IBinder tokenToMirror = new TestToken();
+ doReturn(tokenToMirror).when(mWm.mDisplayManagerInternal).getWindowTokenClientToMirror(
+ anyInt());
+
+ // GIVEN the default task display area is represented by the WindowToken.
+ spyOn(mWm.mWindowContextListenerController);
+ doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
+ mWm.mWindowContextListenerController).getContainer(any());
+ return tokenToMirror;
+ }
+
+ /**
+ * SurfaceControl successfully creates a mirrored surface of the given size.
+ */
+ private SurfaceControl surfaceControlMirrors(Point surfaceSize) {
+ // Do not set the parent, since the mirrored surface is the root of a new surface hierarchy.
+ SurfaceControl mirroredSurface = new SurfaceControl.Builder()
+ .setName("mirroredSurface")
+ .setBufferSize(surfaceSize.x, surfaceSize.y)
+ .setCallsite("mirrorSurface")
+ .build();
+ doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any()));
+ doReturn(surfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
+ anyInt());
+ return mirroredSurface;
+ }
+
private void removeRootTaskTests(Runnable runnable) {
final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 03304bb9456a..4957ab96ace1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -24,7 +24,7 @@ import static android.view.InsetsState.ITYPE_TOP_GESTURES;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
@@ -50,13 +50,13 @@ import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.expectThrows;
import android.graphics.Insets;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -65,11 +65,11 @@ import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.PrivacyIndicatorBounds;
import android.view.RoundedCorners;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
-import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -109,18 +109,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
// We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
// changing those frames.
- doNothing().when(mWindow).computeFrame();
-
- final WindowManager.LayoutParams attrs = mWindow.mAttrs;
- attrs.width = MATCH_PARENT;
- attrs.height = MATCH_PARENT;
- attrs.format = PixelFormat.TRANSLUCENT;
+ doNothing().when(mWindow).computeFrame(any());
spyOn(mStatusBarWindow);
spyOn(mNavBarWindow);
// Disabling this call for most tests since it can override the systemUiFlags when called.
- doReturn(false).when(mDisplayPolicy).updateSystemUiVisibilityLw();
+ doNothing().when(mDisplayPolicy).updateSystemBarAttributes();
updateDisplayFrames();
}
@@ -219,7 +214,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
}
@Test
- public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+ public void addingWindow_InWindowTypeWithPredefinedInsets() {
mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one.
WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
@@ -230,7 +225,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
InsetsSourceProvider provider =
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
- assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ // In the new flexible insets setup, the insets frame should always respect the window
+ // layout result.
+ assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ } else {
+ assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+ }
}
@Test
@@ -478,9 +479,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- mWindow.updateRequestedVisibility(requestedState);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ mWindow.setRequestedVisibilities(requestedVisibilities);
addWindowWithRawInsetsState(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -498,9 +499,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mDisplayContent.getInsetsStateController().getRawInsetsState()
.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- mWindow.updateRequestedVisibility(requestedState);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ mWindow.setRequestedVisibilities(requestedVisibilities);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
addWindowWithRawInsetsState(mWindow);
@@ -733,10 +734,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
@Test
public void testFixedRotationInsetsSourceFrame() {
+ mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT;
+ mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH;
doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
- mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
- .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
+ mWindow.mAboveInsetsState.set(
+ mDisplayContent.getInsetsStateController().getRawInsetsState());
final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
.getSource(ITYPE_STATUS_BAR).getFrame();
mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index b793be74c033..70aa2a2f32bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -37,7 +37,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
-import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
import static org.junit.Assert.assertEquals;
@@ -107,8 +106,7 @@ public class DisplayPolicyTests extends WindowTestsBase {
@Test
public void testChooseNavigationColorWindowLw() {
- final WindowState opaque = createOpaqueFullscreen(false);
-
+ final WindowState candidate = createOpaqueFullscreen(false);
final WindowState dimmingImTarget = createDimmingDialogWindow(true);
final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
@@ -116,45 +114,51 @@ public class DisplayPolicyTests extends WindowTestsBase {
final WindowState invisibleIme = createInputMethodWindow(false, true, false);
final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
- // If everything is null, return null
+ // If everything is null, return null.
assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
- null, null, null, NAV_BAR_BOTTOM));
+ null, null, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, null, NAV_BAR_BOTTOM));
+ // If no IME windows, return candidate window.
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, null, NAV_BAR_BOTTOM));
assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, null, NAV_BAR_BOTTOM));
+ dimmingImTarget, null, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, null, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- null, null, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ // If IME is not visible, return candidate window.
+ assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, invisibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingImTarget, invisibleIme, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, invisibleIme, NAV_BAR_BOTTOM));
+
+ // If IME is visible, return candidate when the candidate window is not dimming.
assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_BOTTOM));
+ null, visibleIme, NAV_BAR_BOTTOM));
assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
+ candidate, visibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, invisibleIme, NAV_BAR_BOTTOM));
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, visibleIme, NAV_BAR_RIGHT));
+ // If IME is visible and the candidate window is dimming, checks whether the dimming window
+ // can be IME tartget or not.
+ assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingImTarget, visibleIme, NAV_BAR_BOTTOM));
+ assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
+ dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM));
// Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
// window.
- assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
+ null, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
+ candidate, imeNonDrawNavBar, NAV_BAR_BOTTOM));
assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
- opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
+ dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM));
}
@UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
@@ -182,59 +186,32 @@ public class DisplayPolicyTests extends WindowTestsBase {
// If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed.
assertEquals(0,
- displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, null, null,
- null, null));
-
- // Opaque top fullscreen window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, opaqueDarkNavBar, null,
- opaqueDarkNavBar));
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar,
- opaqueLightNavBar, null, opaqueLightNavBar));
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS,
- opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar));
+ displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null));
// Dimming window clears APPEARANCE_LIGHT_NAVIGATION_BARS.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, dimming));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- 0, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, null, dimming));
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, dimming, imeDrawLightNavBar,
- dimming));
+ APPEARANCE_LIGHT_NAVIGATION_BARS, dimming));
- // IME window clears APPEARANCE_LIGHT_NAVIGATION_BARS
+ // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
+ assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, null, null, imeDrawDarkNavBar,
- imeDrawDarkNavBar));
-
- // Even if the top fullscreen has APPEARANCE_LIGHT_NAVIGATION_BARS, IME window wins.
- assertEquals(0, displayPolicy.updateLightNavigationBarLw(
- APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar, opaqueLightNavBar,
- imeDrawDarkNavBar, imeDrawDarkNavBar));
-
- // IME window should be able to use APPEARANCE_LIGHT_NAVIGATION_BARS.
- assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS,
- displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar,
- opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar));
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar));
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+ 0, opaqueLightNavBar));
+ assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
+ APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar));
}
- @UseTestDisplay(addWindows = W_ACTIVITY)
+ @UseTestDisplay(addWindows = {W_ACTIVITY, W_STATUS_BAR})
@Test
public void testComputeTopFullscreenOpaqueWindow() {
final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
attrs.x = attrs.y = 0;
attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
+ policy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
+
policy.applyPostLayoutPolicyLw(
mAppWindow, attrs, null /* attached */, null /* imeTarget */);
@@ -313,7 +290,9 @@ public class DisplayPolicyTests extends WindowTestsBase {
displayInfo.logicalHeight = 2000;
displayInfo.rotation = ROTATION_0;
- displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+ WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs;
+ displayPolicy.addWindowLw(mNavBarWindow, attrs);
+ mNavBarWindow.setRequestedSize(attrs.width, attrs.height);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
mImeWindow.mAboveInsetsState.set(state);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 683ed889d283..3982a83d7778 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -58,8 +58,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
static final int DISPLAY_HEIGHT = 1000;
static final int DISPLAY_DENSITY = 320;
- static final int STATUS_BAR_HEIGHT = 10;
- static final int NAV_BAR_HEIGHT = 15;
static final int DISPLAY_CUTOUT_HEIGHT = 8;
static final int IME_HEIGHT = 415;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f2418c68358d..a8ede13e5de6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -82,7 +82,7 @@ public class InputMethodMenuControllerTest extends WindowTestsBase {
mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
null /* options */);
- return true;
+ return dc.getImeContainer().getConfiguration();
}).when(wms).attachWindowContextToDisplayArea(any(), eq(TYPE_INPUT_METHOD_DIALOG),
anyInt(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index bf3ed692dc8e..07d467bc07d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -48,6 +49,7 @@ import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
@@ -80,7 +82,7 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_dockedStackVisible() {
+ public void testControlsForDispatch_dockedTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
@@ -93,25 +95,26 @@ public class InsetsPolicyTest extends WindowTestsBase {
}
@Test
- public void testControlsForDispatch_freeformStackVisible() {
+ public void testControlsForDispatch_multiWindowTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
- final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ final WindowState win = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
- // The app must not control any bars.
+ // The app must not control any system bars.
assertNull(controls);
}
@Test
- public void testControlsForDispatch_dockedDividerControllerResizing() {
+ public void testControlsForDispatch_freeformTaskVisible() {
addWindow(TYPE_STATUS_BAR, "statusBar");
addWindow(TYPE_NAVIGATION_BAR, "navBar");
- mDisplayContent.getDockedDividerController().setResizing(true);
- final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+ final WindowState win = createWindow(null, WINDOWING_MODE_FREEFORM,
+ ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+ final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
// The app must not control any system bars.
assertNull(controls);
@@ -179,9 +182,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
// Add a fullscreen (MATCH_PARENT x MATCH_PARENT) app window which hides status bar.
final WindowState fullscreenApp = addWindow(TYPE_APPLICATION, "fullscreenApp");
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- fullscreenApp.updateRequestedVisibility(requestedState);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ fullscreenApp.setRequestedVisibilities(requestedVisibilities);
// Add a non-fullscreen dialog window.
final WindowState dialog = addWindow(TYPE_APPLICATION, "dialog");
@@ -214,9 +217,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
// Assume mFocusedWindow is updated but mTopFullscreenOpaqueWindowState hasn't.
final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp");
- final InsetsState newRequestedState = new InsetsState();
- newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true);
- newFocusedFullscreenApp.updateRequestedVisibility(newRequestedState);
+ final InsetsVisibilities newRequestedVisibilities = new InsetsVisibilities();
+ newRequestedVisibilities.setVisibility(ITYPE_STATUS_BAR, true);
+ newFocusedFullscreenApp.setRequestedVisibilities(newRequestedVisibilities);
// Make sure status bar is hidden by previous insets state.
mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp);
@@ -277,10 +280,10 @@ public class InsetsPolicyTest extends WindowTestsBase {
doNothing().when(policy).startAnimation(anyBoolean(), any());
// Make both system bars invisible.
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
- requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false);
- mAppWindow.updateRequestedVisibility(requestedState);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ requestedVisibilities.setVisibility(ITYPE_NAVIGATION_BAR, false);
+ mAppWindow.setRequestedVisibilities(requestedVisibilities);
policy.updateBarControlTarget(mAppWindow);
waitUntilWindowAnimatorIdle();
assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState()
@@ -371,7 +374,10 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible());
assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
- mAppWindow.updateRequestedVisibility(state);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, true);
+ requestedVisibilities.setVisibility(ITYPE_NAVIGATION_BAR, true);
+ mAppWindow.setRequestedVisibilities(requestedVisibilities);
policy.onInsetsModified(mAppWindow);
waitUntilWindowAnimatorIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index c483ae9fa4c5..2987f943f1c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -31,7 +31,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
-import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
@@ -203,9 +203,9 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindow(statusBar, null, null);
mProvider.updateControlForTarget(target, false /* force */);
- InsetsState state = new InsetsState();
- state.getSource(ITYPE_STATUS_BAR).setVisible(false);
- target.updateRequestedVisibility(state);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ target.setRequestedVisibilities(requestedVisibilities);
mProvider.updateClientVisibility(target);
assertFalse(mSource.isVisible());
}
@@ -216,9 +216,9 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
statusBar.getFrame().set(0, 0, 500, 100);
mProvider.setWindow(statusBar, null, null);
- InsetsState state = new InsetsState();
- state.getSource(ITYPE_STATUS_BAR).setVisible(false);
- target.updateRequestedVisibility(state);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ target.setRequestedVisibilities(requestedVisibilities);
mProvider.updateClientVisibility(target);
assertTrue(mSource.isVisible());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 80961d7afb70..f8c84df53749 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -48,6 +48,7 @@ import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import androidx.test.filters.SmallTest;
@@ -174,10 +175,10 @@ public class InsetsStateControllerTest extends WindowTestsBase {
mImeWindow.setHasSurface(true);
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
getController().onImeControlTargetChanged(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
- final InsetsState requestedState = new InsetsState();
- requestedState.getSource(ITYPE_IME).setVisible(true);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_IME, true);
mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow()
- .updateRequestedVisibility(requestedState);
+ .setRequestedVisibilities(requestedVisibilities);
getController().onInsetsModified(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
// Send our spy window (app) into the system so that we can detect the invocation.
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 647a898a5361..609d15937b2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -200,28 +200,37 @@ public class LetterboxTest {
assertTrue(mLetterbox.needsApplySurfaceChanges());
mLetterbox.applySurfaceChanges(mTransaction);
- verify(mTransaction).setAlpha(mSurfaces.top, mDarkScrimAlpha);
+ verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
}
@Test
- public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
+ public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNull(mSurfaces.behind);
+ assertNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
+ public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
- assertNotNull(mSurfaces.behind);
+ assertNotNull(mSurfaces.fullWindowSurface);
}
@Test
- public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
+ public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
+ mHasWallpaperBackground = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertNotNull(mSurfaces.fullWindowSurface);
+ }
+
+ @Test
+ public void testNotIntersectsOrFullyContains_cornersRounded() {
mAreCornersRounded = true;
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
mLetterbox.applySurfaceChanges(mTransaction);
@@ -249,8 +258,8 @@ public class LetterboxTest {
public SurfaceControl right;
private SurfaceControl.Builder mBottomBuilder;
public SurfaceControl bottom;
- private SurfaceControl.Builder mBehindBuilder;
- public SurfaceControl behind;
+ private SurfaceControl.Builder mFullWindowSurfaceBuilder;
+ public SurfaceControl fullWindowSurface;
@Override
public SurfaceControl.Builder get() {
@@ -265,8 +274,8 @@ public class LetterboxTest {
mRightBuilder = (SurfaceControl.Builder) i.getMock();
} else if (((String) i.getArgument(0)).contains("bottom")) {
mBottomBuilder = (SurfaceControl.Builder) i.getMock();
- } else if (((String) i.getArgument(0)).contains("behind")) {
- mBehindBuilder = (SurfaceControl.Builder) i.getMock();
+ } else if (((String) i.getArgument(0)).contains("fullWindow")) {
+ mFullWindowSurfaceBuilder = (SurfaceControl.Builder) i.getMock();
}
return i.getMock();
});
@@ -281,8 +290,8 @@ public class LetterboxTest {
right = control;
} else if (i.getMock() == mBottomBuilder) {
bottom = control;
- } else if (i.getMock() == mBehindBuilder) {
- behind = control;
+ } else if (i.getMock() == mFullWindowSurfaceBuilder) {
+ fullWindowSurface = control;
}
return control;
}).when(builder).build();
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 5af68021b201..1b078b7454b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -788,6 +788,19 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
+ public void testVisibleEmbeddedTask_expectNotVisible() {
+ Task task = createTaskBuilder(".Task")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .build();
+ doReturn(true).when(task).isEmbedded();
+ mRecentTasks.add(task);
+
+ assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+ assertFalse("embedded task should not be visible recents",
+ mRecentTasks.isVisibleRecentTask(task));
+ }
+
+ @Test
public void testFreezeTaskListOrder_reorderExistingTask() {
// Add some tasks
mRecentTasks.add(mTasks.get(0));
@@ -859,6 +872,40 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Test
+ public void testFreezeTaskListOrder_replaceTask() {
+ // Create two tasks with the same affinity
+ Task affinityTask1 = createTaskBuilder(".AffinityTask1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .build();
+ Task affinityTask2 = createTaskBuilder(".AffinityTask2")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .build();
+ affinityTask2.affinity = affinityTask1.affinity = "affinity";
+
+ // Add some tasks
+ mRecentTasks.add(mTasks.get(0));
+ mRecentTasks.add(affinityTask1);
+ mRecentTasks.add(mTasks.get(1));
+ mCallbacksRecorder.clear();
+
+ // Freeze the list
+ mRecentTasks.setFreezeTaskListReordering();
+ assertTrue(mRecentTasks.isFreezeTaskListReorderingSet());
+
+ // Add the affinity task
+ mRecentTasks.add(affinityTask2);
+
+ assertRecentTasksOrder(mTasks.get(1),
+ affinityTask2,
+ mTasks.get(0));
+
+ assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+ assertThat(mCallbacksRecorder.mAdded).contains(affinityTask2);
+ assertThat(mCallbacksRecorder.mRemoved).hasSize(1);
+ assertThat(mCallbacksRecorder.mRemoved).contains(affinityTask1);
+ }
+
+ @Test
public void testFreezeTaskListOrder_timeout() {
// Add some tasks
mRecentTasks.add(mTasks.get(0));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d017c19cac86..1b19a28a9790 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -31,8 +31,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 0c6545c2438f..56d01cd34e01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -30,28 +30,28 @@ import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.INITIALIZING;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.Task.ActivityState.DESTROYED;
-import static com.android.server.wm.Task.ActivityState.DESTROYING;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
-import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
+import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
@@ -89,9 +89,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
/**
* Tests for the root {@link Task} behavior.
*
@@ -194,7 +191,6 @@ public class RootTaskTests extends WindowTestsBase {
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
// Root task removal is deferred if one of its child is animating.
- doReturn(true).when(rootTask).hasWindowsAlive();
doReturn(rootTask).when(task).getAnimatingContainer(
eq(TRANSITION | CHILDREN), anyInt());
@@ -280,11 +276,11 @@ public class RootTaskTests extends WindowTestsBase {
public void testResumedActivity() {
final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build();
final Task task = r.getTask();
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
r.setState(RESUMED, "testResumedActivity");
- assertEquals(r, task.getResumedActivity());
+ assertEquals(r, task.getTopResumedActivity());
r.setState(PAUSING, "testResumedActivity");
- assertNull(task.getResumedActivity());
+ assertNull(task.getTopResumedActivity());
}
@Test
@@ -295,15 +291,15 @@ public class RootTaskTests extends WindowTestsBase {
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /* toTop */, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
false /* animate */, true /* deferResume*/,
"testResumedActivityFromTaskReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -314,15 +310,15 @@ public class RootTaskTests extends WindowTestsBase {
final Task task = r.getTask();
// Ensure moving task between two root tasks updates resumed activity
r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
- assertEquals(r, rootTask.getResumedActivity());
+ assertEquals(r, rootTask.getTopResumedActivity());
final Task destRootTask = new TaskBuilder(mSupervisor).setOnTop(true).build();
task.reparent(destRootTask, true /*toTop*/, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
false /* animate */, false /* deferResume*/,
"testResumedActivityFromActivityReparenting");
- assertNull(rootTask.getResumedActivity());
- assertEquals(r, destRootTask.getResumedActivity());
+ assertNull(rootTask.getTopResumedActivity());
+ assertEquals(r, destRootTask.getTopResumedActivity());
}
@Test
@@ -618,9 +614,9 @@ public class RootTaskTests extends WindowTestsBase {
doReturn(false).when(splitScreenSecondary2).isTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// First split-screen secondary should be visible behind another translucent split-screen
@@ -628,9 +624,9 @@ public class RootTaskTests extends WindowTestsBase {
doReturn(true).when(splitScreenSecondary2).isTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
final Task assistantRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
@@ -642,13 +638,13 @@ public class RootTaskTests extends WindowTestsBase {
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen root tasks should be visible behind a translucent fullscreen root task.
@@ -657,13 +653,13 @@ public class RootTaskTests extends WindowTestsBase {
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant root task shouldn't be visible behind translucent split-screen root task,
@@ -678,25 +674,25 @@ public class RootTaskTests extends WindowTestsBase {
assertTrue(assistantRootTask.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
} else {
assertFalse(assistantRootTask.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
assistantRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
}
}
@@ -707,45 +703,51 @@ public class RootTaskTests extends WindowTestsBase {
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, true /* onTop */);
final Task splitPrimary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ // Creating as two-level tasks so home task can be reparented to split-secondary root task.
final Task splitSecondary = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED, true /* onTop */,
+ true /* twoLevelTask */);
doReturn(false).when(homeRootTask).isTranslucent(any());
doReturn(false).when(splitPrimary).isTranslucent(any());
doReturn(false).when(splitSecondary).isTranslucent(any());
-
// Re-parent home to split secondary.
homeRootTask.reparent(splitSecondary, POSITION_TOP);
// Current tasks should be visible.
- assertEquals(TASK_VISIBILITY_VISIBLE, splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitPrimary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
// Home task should still be visible even though it is a child of another visible task.
- assertEquals(TASK_VISIBILITY_VISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
// Add fullscreen translucent task that partially occludes split tasks
final Task translucentRootTask = createStandardRootTaskForVisibilityTest(
WINDOWING_MODE_FULLSCREEN, true /* translucent */);
// Fullscreen translucent task should be visible
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Split tasks should be visible behind translucent
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitPrimary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
splitSecondary.getVisibility(null /* starting */));
// Home task should be visible behind translucent since its parent is visible behind
// translucent.
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
homeRootTask.getVisibility(null /* starting */));
// Hide split-secondary
splitSecondary.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */);
// Home split secondary and home task should be invisible.
- assertEquals(TASK_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE, homeRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ splitSecondary.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ homeRootTask.getVisibility(null /* starting */));
}
@Test
@@ -757,9 +759,9 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -775,10 +777,12 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
translucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -793,10 +797,11 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE, bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
+ bottomRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
opaqueRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -809,9 +814,9 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
}
@@ -824,9 +829,10 @@ public class RootTaskTests extends WindowTestsBase {
createStandardRootTaskForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- assertEquals(TASK_VISIBILITY_INVISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE,
bottomTranslucentRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE, opaqueRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ opaqueRootTask.getVisibility(null /* starting */));
}
@Test
@@ -840,16 +846,17 @@ public class RootTaskTests extends WindowTestsBase {
final Task pinnedRootTask = createTaskForShouldBeVisibleTest(mDefaultTaskDisplayArea,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- assertEquals(TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
bottomRootTask.getVisibility(null /* starting */));
- assertEquals(TASK_VISIBILITY_VISIBLE,
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
translucentRootTask.getVisibility(null /* starting */));
// Add an activity to the pinned root task so it isn't considered empty for visibility
// check.
final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm)
.setTask(pinnedRootTask)
.build();
- assertEquals(TASK_VISIBILITY_VISIBLE, pinnedRootTask.getVisibility(null /* starting */));
+ assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
+ pinnedRootTask.getVisibility(null /* starting */));
}
@Test
@@ -1142,12 +1149,12 @@ public class RootTaskTests extends WindowTestsBase {
} else if (twoLevelTask) {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
- .setWindowingMode(windowingMode)
.setActivityType(activityType)
.setOnTop(onTop)
.setCreateActivity(true)
.setCreateParentTask(true)
.build().getRootTask();
+ task.setWindowingMode(windowingMode);
} else {
task = new TaskBuilder(mSupervisor)
.setTaskDisplayArea(taskDisplayArea)
@@ -1301,9 +1308,9 @@ public class RootTaskTests extends WindowTestsBase {
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.info.flags |= FLAG_RESUME_WHILE_PAUSING;
- task.startPausingLocked(false /* uiSleeping */, topActivity,
+ task.startPausing(false /* uiSleeping */, topActivity,
"test");
- verify(task).completePauseLocked(anyBoolean(), eq(topActivity));
+ verify(task).completePause(anyBoolean(), eq(topActivity));
}
@Test
@@ -1494,41 +1501,6 @@ public class RootTaskTests extends WindowTestsBase {
}
@Test
- public void testIterateOccludedActivity() {
- final ArrayList<ActivityRecord> occludedActivities = new ArrayList<>();
- final Consumer<ActivityRecord> handleOccludedActivity = occludedActivities::add;
- final Task task = new TaskBuilder(mSupervisor).build();
- final ActivityRecord bottomActivity = new ActivityBuilder(mAtm).setTask(task).build();
- final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
- // Top activity occludes bottom activity.
- doReturn(true).when(task).shouldBeVisible(any());
- assertTrue(topActivity.shouldBeVisible());
- assertFalse(bottomActivity.shouldBeVisible());
-
- task.forAllOccludedActivities(handleOccludedActivity);
- assertThat(occludedActivities).containsExactly(bottomActivity);
-
- // Top activity doesn't occlude parent, so the bottom activity is not occluded.
- doReturn(false).when(topActivity).occludesParent();
- assertTrue(bottomActivity.shouldBeVisible());
-
- occludedActivities.clear();
- task.forAllOccludedActivities(handleOccludedActivity);
- assertThat(occludedActivities).isEmpty();
-
- // A finishing activity should not occlude other activities behind.
- final ActivityRecord finishingActivity = new ActivityBuilder(mAtm).setTask(task).build();
- finishingActivity.finishing = true;
- doCallRealMethod().when(finishingActivity).occludesParent();
- assertTrue(topActivity.shouldBeVisible());
- assertTrue(bottomActivity.shouldBeVisible());
-
- occludedActivities.clear();
- task.forAllOccludedActivities(handleOccludedActivity);
- assertThat(occludedActivities).isEmpty();
- }
-
- @Test
public void testClearUnknownAppVisibilityBehindFullscreenActivity() {
final UnknownAppVisibilityController unknownAppVisibilityController =
mDefaultTaskDisplayArea.mDisplayContent.mUnknownAppVisibilityController;
@@ -1544,7 +1516,7 @@ public class RootTaskTests extends WindowTestsBase {
activities[i] = r;
doReturn(null).when(mAtm).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
- r.setState(Task.ActivityState.INITIALIZING, "test");
+ r.setState(INITIALIZING, "test");
// Ensure precondition that the activity is opaque.
assertTrue(r.occludesParent());
mSupervisor.startSpecificActivity(r, false /* andResume */,
@@ -1552,14 +1524,13 @@ public class RootTaskTests extends WindowTestsBase {
}
mSupervisor.endDeferResume();
- setBooted(mAtm);
// 2 activities are started while keyguard is locked, so they are waiting to be resolved.
assertFalse(unknownAppVisibilityController.allResolved());
- // Assume the top activity is going to resume and
- // {@link RootWindowContainer#cancelInitializingActivities} should clear the unknown
- // visibility records that are occluded.
- task.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+ // Any common path that updates activity visibility should clear the unknown visibility
+ // records that are no longer visible according to hierarchy.
+ task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+ false /* preserveWindows */);
// Assume the top activity relayouted, just remove it directly.
unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
// All unresolved records should be removed.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9cf29d4dcc50..8d4acbbac993 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -31,13 +31,14 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE;
-import static com.android.server.wm.Task.ActivityState.FINISHING;
-import static com.android.server.wm.Task.ActivityState.PAUSED;
-import static com.android.server.wm.Task.ActivityState.PAUSING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
-import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.google.common.truth.Truth.assertThat;
@@ -365,7 +366,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea();
doReturn(isFocusedTask ? task : null).when(defaultTaskDisplayArea).getFocusedRootTask();
mRootWindowContainer.applySleepTokens(true);
- verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+ verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
null /* target */, null /* targetOptions */);
}
@@ -386,7 +387,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
// landscape and the portrait lockscreen is shown.
activity.setLastReportedConfiguration(
new MergedConfiguration(mAtm.getGlobalConfiguration(), rotatedConfig));
- activity.setState(Task.ActivityState.STOPPED, "sleep");
+ activity.setState(STOPPED, "sleep");
display.setIsSleeping(true);
doReturn(false).when(display).shouldSleep();
@@ -557,8 +558,8 @@ public class RootWindowContainerTests extends WindowTestsBase {
doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
// Use the task as target to resume.
- mRootWindowContainer.resumeFocusedTasksTopActivities(
- rootTask, activity, null /* targetOptions */);
+ mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity,
+ null /* targetOptions */);
// Verify the target task should resume its activity.
verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
@@ -626,7 +627,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
// Assume the task is at the topmost position
assertTrue(rootTask.isTopRootTaskInDisplayArea());
@@ -646,7 +647,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(rootTask).setOnTop(true).build();
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
taskDisplayArea.positionChildAt(POSITION_BOTTOM, rootTask, false /*includingParents*/);
// Assume the task is at the topmost position
@@ -774,7 +775,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
}
/**
- * Tests that when starting {@link #ResolverActivity} for home, it should use the standard
+ * Tests that when starting {@link ResolverActivity} for home, it should use the standard
* activity type (in a new root task) so the order of back stack won't be broken.
*/
@Test
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 f35e85c3b14c..3bebf6b39de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,8 +40,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
-import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -130,7 +132,7 @@ public class SizeCompatTests extends WindowTestsBase {
doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
mActivity.mVisibleRequested = true;
mActivity.setSavedState(null /* savedState */);
- mActivity.setState(Task.ActivityState.RESUMED, "testRestart");
+ mActivity.setState(RESUMED, "testRestart");
prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
@@ -138,7 +140,7 @@ public class SizeCompatTests extends WindowTestsBase {
// The visible activity should recompute configuration according to the last parent bounds.
mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
- assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
+ assertEquals(RESTARTING_PROCESS, mActivity.getState());
assertNotEquals(originalOverrideBounds, mActivity.getBounds());
}
@@ -318,6 +320,11 @@ public class SizeCompatTests extends WindowTestsBase {
assertScaled();
// Activity is sandboxed due to size compat mode.
assertActivityMaxBoundsSandboxed();
+
+ final WindowState appWindow = addWindowToActivity(mActivity);
+ assertTrue(mActivity.hasSizeCompatBounds());
+ assertEquals("App window must use size compat bounds for layout in screen space",
+ mActivity.getBounds(), appWindow.getBounds());
}
@Test
@@ -585,7 +592,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testHandleActivitySizeCompatModeChanged() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -604,7 +611,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mVisibleRequested = true;
mActivity.restartProcessIfVisible();
// The full lifecycle isn't hooked up so manually set state to resumed
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(mActivity);
// Expect null token when switching to non-size-compat mode activity.
@@ -618,7 +625,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testHandleActivitySizeCompatModeChangedOnDifferentTask() {
setUpDisplaySizeWithApp(1000, 2000);
doReturn(true).when(mTask).isOrganized();
- mActivity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatModeChanged");
+ mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged");
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
@@ -637,7 +644,7 @@ public class SizeCompatTests extends WindowTestsBase {
.setCreateActivity(true).build();
final ActivityRecord secondActivity = secondTask.getTopNonFinishingActivity();
doReturn(true).when(secondTask).isOrganized();
- secondActivity.setState(Task.ActivityState.RESUMED,
+ secondActivity.setState(RESUMED,
"testHandleActivitySizeCompatModeChanged");
prepareUnresizable(secondActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -1553,6 +1560,30 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
+ // Set up a display in landscape with an unresizable app.
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted();
+
+ // Activity max bounds not be sandboxed since sandboxing is disabled.
+ assertMaxBoundsInheritDisplayAreaBounds();
+ }
+
+ @Test
+ public void testSandboxDisplayApis_unresizableAppSandboxed() {
+ // Set up a display in landscape with an unresizable app.
+ setUpDisplaySizeWithApp(2500, 1000);
+ mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
+ prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
+ assertFitted();
+
+ // Activity max bounds should be sandboxed since sandboxing is enabled.
+ assertActivityMaxBoundsSandboxed();
+ }
+
+ @Test
public void testResizableApp_notSandboxed() {
// Set up a display in landscape with a fully resizable app.
setUpDisplaySizeWithApp(2500, 1000);
@@ -1955,6 +1986,61 @@ public class SizeCompatTests extends WindowTestsBase {
assertTrue(mActivity.areBoundsLetterboxed());
}
+ /**
+ * Tests that all three paths in which aspect ratio logic can be applied yield the same
+ * result, which is that aspect ratio is respected on app bounds. The three paths are
+ * fixed orientation, no fixed orientation but fixed aspect ratio, and size compat mode.
+ */
+ @Test
+ public void testAllAspectRatioLogicConsistent() {
+ // Create display that has all stable insets and does not rotate. Make sure that status bar
+ // height is greater than notch height so that stable bounds do not equal app bounds.
+ final int notchHeight = 75;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+ .setSystemDecorations(true).setNotch(notchHeight)
+ .setStatusBarHeight(notchHeight + 20).setCanRotate(false).build();
+
+ // Create task on test display.
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+ // Target min aspect ratio must be larger than parent aspect ratio to be applied.
+ final float targetMinAspectRatio = 3.0f;
+
+ // Create fixed portait activity with min aspect ratio greater than parent aspect ratio.
+ final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(targetMinAspectRatio).build();
+ final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ // Create activity with no fixed orientation and min aspect ratio greater than parent aspect
+ // ratio.
+ final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm).setTask(task)
+ .setMinAspectRatio(targetMinAspectRatio).build();
+ final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ // Create unresizeable fixed portait activity with min aspect ratio greater than parent
+ // aspect ratio.
+ final ActivityRecord sizeCompatActivity = new ActivityBuilder(mAtm)
+ .setTask(task).setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(targetMinAspectRatio).build();
+ // Resize display running unresizeable activity to make it enter size compat mode.
+ resizeDisplay(display, 1800, 1000);
+ final Rect sizeCompatAppBounds = new Rect(sizeCompatActivity.getConfiguration()
+ .windowConfiguration.getAppBounds());
+
+ // Check that aspect ratio of app bounds is equal to the min aspect ratio.
+ final float delta = 0.01f;
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(fixedOrientationAppBounds), delta);
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(minAspectRatioAppBounds), delta);
+ assertEquals(targetMinAspectRatio, ActivityRecord
+ .computeAspectRatio(sizeCompatAppBounds), delta);
+ }
+
private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxHorizontalPositionMultiplier) {
// Set up a display in landscape and ignoring orientation request.
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index 3a2190d13354..cac948c97b25 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -178,6 +178,11 @@ public class StubTransaction extends SurfaceControl.Transaction {
}
@Override
+ public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken,
int orientation, Rect layerStackRect, Rect displayRect) {
return this;
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 67b273a5a82d..c45c18d16c38 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -37,8 +37,8 @@ import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.google.common.truth.Truth.assertThat;
@@ -84,8 +84,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -111,8 +110,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -133,8 +131,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.mAdjacentTask = rootTask;
- rootTask.mAdjacentTask = adjacentRootTask;
+ adjacentRootTask.setAdjacentTaskFragment(rootTask);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
new file mode 100644
index 000000000000..c35f31702961
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+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 android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentCreationParams;
+import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizerToken;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest WmTests:TaskFragmentOrganizerControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
+
+ private TaskFragmentOrganizerController mController;
+ private TaskFragmentOrganizer mOrganizer;
+ private TaskFragmentOrganizerToken mOrganizerToken;
+ private ITaskFragmentOrganizer mIOrganizer;
+ private TaskFragment mTaskFragment;
+ private TaskFragmentInfo mTaskFragmentInfo;
+ private IBinder mFragmentToken;
+ private WindowContainerTransaction mTransaction;
+ private WindowContainerToken mFragmentWindowToken;
+
+ @Before
+ public void setup() {
+ mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
+ mOrganizer = new TaskFragmentOrganizer(Runnable::run);
+ mOrganizerToken = mOrganizer.getOrganizerToken();
+ mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
+ mTaskFragmentInfo = mock(TaskFragmentInfo.class);
+ mFragmentToken = new Binder();
+ mTaskFragment =
+ new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
+ mTransaction = new WindowContainerTransaction();
+ mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+
+ spyOn(mController);
+ spyOn(mOrganizer);
+ spyOn(mTaskFragment);
+ doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
+ doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
+ doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
+ doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
+ doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+ }
+
+ @Test
+ public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
+
+ assertThrows(IllegalArgumentException.class, () -> mController
+ .onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment));
+ }
+
+ @Test
+ public void testOnTaskFragmentAppeared() {
+ mController.registerOrganizer(mIOrganizer);
+
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentAppeared(any());
+ }
+
+ @Test
+ public void testOnTaskFragmentInfoChanged() {
+ mController.registerOrganizer(mIOrganizer);
+ mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ // No callback if the info is not changed.
+ doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+ doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
+
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
+
+ // Trigger callback if the info is changed.
+ doReturn(false).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
+
+ mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
+ mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentInfoChanged(mTaskFragmentInfo);
+ }
+
+ @Test
+ public void testOnTaskFragmentVanished() {
+ mController.registerOrganizer(mIOrganizer);
+
+ mTaskFragment.mTaskFragmentAppearedSent = true;
+ mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentVanished(any());
+ }
+
+ @Test
+ public void testOnTaskFragmentParentInfoChanged() {
+ mController.registerOrganizer(mIOrganizer);
+ final Task parent = mock(Task.class);
+ final Configuration parentConfig = new Configuration();
+ parentConfig.smallestScreenWidthDp = 10;
+ doReturn(parent).when(mTaskFragment).getParent();
+ doReturn(parentConfig).when(parent).getConfiguration();
+ doReturn(parent).when(parent).asTask();
+
+ mTaskFragment.mTaskFragmentAppearedSent = true;
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+
+ // No extra callback if the info is not changed.
+ clearInvocations(mOrganizer);
+
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
+
+ // Trigger callback if the info is changed.
+ parentConfig.smallestScreenWidthDp = 100;
+
+ mController.onTaskFragmentParentInfoChanged(
+ mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
+ }
+
+ @Test
+ public void testOnTaskFragmentError() throws RemoteException {
+ final IBinder errorCallbackToken = new Binder();
+ final Throwable exception = new IllegalArgumentException("Test exception");
+
+ mController.registerOrganizer(mIOrganizer);
+ mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(),
+ errorCallbackToken, exception);
+ mController.dispatchPendingEvents();
+
+ verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
+ }
+
+ @Test
+ public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
+ mOrganizer.applyTransaction(mTransaction);
+
+ assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
+
+ mTransaction = new WindowContainerTransaction();
+ mOrganizer.applySyncTransaction(
+ mTransaction, mock(WindowContainerTransactionCallback.class));
+
+ assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
+ }
+
+ @Test
+ public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment()
+ throws RemoteException {
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Throw exception if the transaction is trying to change a window that is not organized by
+ // the organizer.
+ mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
+
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+
+ // Allow transaction to change a TaskFragment created by the organizer.
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ }
+
+ @Test
+ public void testApplyTransaction_enforceHierarchyChange_reorder() throws RemoteException {
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Throw exception if the transaction is trying to change a window that is not organized by
+ // the organizer.
+ mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
+
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+
+ // Allow transaction to change a TaskFragment created by the organizer.
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ }
+
+ @Test
+ public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()
+ throws RemoteException {
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Throw exception if the transaction is trying to change a window that is not organized by
+ // the organizer.
+ mTransaction.deleteTaskFragment(mFragmentWindowToken);
+
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+
+ // Allow transaction to change a TaskFragment created by the organizer.
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ }
+
+ @Test
+ public void testApplyTransaction_enforceHierarchyChange_setAdjacentRoots()
+ throws RemoteException {
+ final TaskFragment taskFragment2 =
+ new TaskFragment(mAtm, new Binder(), true /* createdByOrganizer */);
+ final WindowContainerToken token2 = taskFragment2.mRemoteToken.toWindowContainerToken();
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Throw exception if the transaction is trying to change a window that is not organized by
+ // the organizer.
+ mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
+
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+
+ // Allow transaction to change a TaskFragment created by the organizer.
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+ taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ }
+
+ @Test
+ public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
+ final TaskFragmentCreationParams mockParams = mock(TaskFragmentCreationParams.class);
+ doReturn(mOrganizerToken).when(mockParams).getOrganizer();
+ mTransaction.createTaskFragment(mockParams);
+ mTransaction.startActivityInTaskFragment(
+ mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
+ mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
+ mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class));
+
+ // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
+ // testing the security check here.
+ assertThrows(IllegalArgumentException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+ }
+
+ @Test
+ public void testApplyTransaction_enforceHierarchyChange_reparentChildren()
+ throws RemoteException {
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Throw exception if the transaction is trying to change a window that is not organized by
+ // the organizer.
+ mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
+
+ assertThrows(SecurityException.class, () -> {
+ try {
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ } catch (RemoteException e) {
+ fail();
+ }
+ });
+
+ // Allow transaction to change a TaskFragment created by the organizer.
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* pid */);
+
+ mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0ebff1d253ef..807494429999 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -126,8 +126,15 @@ public class TaskTests extends WindowTestsBase {
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- task.removeIfPossible();
- // Assert that the container was removed.
+ task.remove(false /* withTransition */, "testRemoveContainer");
+ // There is still an activity to be destroyed, so the task is not removed immediately.
+ assertNotNull(task.getParent());
+ assertTrue(rootTask.hasChild());
+ assertTrue(task.hasChild());
+ assertTrue(activity.finishing);
+
+ activity.destroyed("testRemoveContainer");
+ // Assert that the container was removed after the activity is destroyed.
assertNull(task.getParent());
assertEquals(0, task.getChildCount());
assertNull(activity.getParent());
@@ -1257,7 +1264,8 @@ public class TaskTests extends WindowTestsBase {
LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
spyOn(persister);
- final Task task = getTestTask();
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setCreateParentTask(true).build().getRootTask();
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ce2d74859931..061a4c5fe261 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -19,7 +19,9 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -77,6 +79,7 @@ class TestDisplayContent extends DisplayContent {
private int mPosition = POSITION_BOTTOM;
protected final ActivityTaskManagerService mService;
private boolean mSystemDecorations = false;
+ private int mStatusBarHeight = 0;
Builder(ActivityTaskManagerService service, int width, int height) {
mService = service;
@@ -125,6 +128,10 @@ class TestDisplayContent extends DisplayContent {
Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
return this;
}
+ Builder setStatusBarHeight(int height) {
+ mStatusBarHeight = height;
+ return this;
+ }
Builder setCanRotate(boolean canRotate) {
mCanRotate = canRotate;
return this;
@@ -153,11 +160,20 @@ class TestDisplayContent extends DisplayContent {
if (mSystemDecorations) {
doReturn(true).when(newDisplay).supportsSystemDecorations();
doReturn(true).when(displayPolicy).hasNavigationBar();
+ doReturn(20).when(displayPolicy).getNavigationBarHeight(anyInt(), anyInt());
} else {
doReturn(false).when(displayPolicy).hasNavigationBar();
doReturn(false).when(displayPolicy).hasStatusBar();
doReturn(false).when(newDisplay).supportsSystemDecorations();
}
+ if (mStatusBarHeight > 0) {
+ doReturn(true).when(displayPolicy).hasStatusBar();
+ doAnswer(invocation -> {
+ Rect inOutInsets = (Rect) invocation.getArgument(0);
+ inOutInsets.top = mStatusBarHeight;
+ return null;
+ }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt());
+ }
Configuration c = new Configuration();
newDisplay.computeScreenConfiguration(c);
c.windowConfiguration.setWindowingMode(mWindowingMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2dfb3a1a84bc..6d60bcf1fce6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -41,6 +42,7 @@ import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.window.ITaskOrganizer;
+import android.window.ITransitionPlayer;
import android.window.TransitionInfo;
import androidx.test.filters.SmallTest;
@@ -339,6 +341,44 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
+ public void testTargets_noIntermediatesToWallpaper() {
+ final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ // Make DA organized so we can check that they don't get included.
+ WindowContainer parent = wallpaperWindowToken.getParent();
+ while (parent != null && parent != mDisplayContent) {
+ if (parent.asDisplayArea() != null) {
+ parent.asDisplayArea().setOrganizer(
+ mock(android.window.IDisplayAreaOrganizer.class), true /* skipAppear */);
+ }
+ parent = parent.getParent();
+ }
+ final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+ "wallpaperWindow");
+ wallpaperWindowToken.setVisibleRequested(false);
+ transition.collect(wallpaperWindowToken);
+ wallpaperWindowToken.setVisibleRequested(true);
+ wallpaperWindow.mHasSurface = true;
+ doReturn(true).when(mDisplayContent).isAttached();
+ transition.collect(mDisplayContent);
+ mDisplayContent.getWindowConfiguration().setRotation(
+ (mDisplayContent.getWindowConfiguration().getRotation() + 1) % 4);
+
+ ArraySet<WindowContainer> targets = Transition.calculateTargets(
+ transition.mParticipants, transition.mChanges);
+ TransitionInfo info = Transition.calculateTransitionInfo(
+ 0, 0, targets, transition.mChanges);
+ // The wallpaper is not organized, so it won't have a token; however, it will be marked
+ // as IS_WALLPAPER
+ assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags());
+ // Make sure no intermediate display areas were pulled in between wallpaper and display.
+ assertEquals(mDisplayContent.mRemoteToken.toWindowContainerToken(),
+ info.getChanges().get(0).getParent());
+ }
+
+ @Test
public void testIndependent() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
@@ -406,6 +446,71 @@ public class TransitionTests extends WindowTestsBase {
info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
}
+ @Test
+ public void testIntermediateVisibility() {
+ final TransitionController controller = new TransitionController(mAtm);
+ final ITransitionPlayer player = new ITransitionPlayer.Default();
+ controller.registerTransitionPlayer(player);
+ ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
+ final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
+
+ // Start out with task2 visible and set up a transition that closes task2 and opens task1
+ final Task task1 = createTask(mDisplayContent);
+ task1.mTaskOrganizer = mockOrg;
+ final ActivityRecord activity1 = createActivityRecord(task1);
+ activity1.mVisibleRequested = false;
+ activity1.setVisible(false);
+ final Task task2 = createTask(mDisplayContent);
+ task2.mTaskOrganizer = mockOrg;
+ final ActivityRecord activity2 = createActivityRecord(task1);
+ activity2.mVisibleRequested = true;
+ activity2.setVisible(true);
+
+ openTransition.collectExistenceChange(task1);
+ openTransition.collectExistenceChange(activity1);
+ openTransition.collectExistenceChange(task2);
+ openTransition.collectExistenceChange(activity2);
+
+ activity1.mVisibleRequested = true;
+ activity1.setVisible(true);
+ activity2.mVisibleRequested = false;
+
+ // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
+ // We didn't call abort on the transition itself, so it will still run onTransactionReady
+ // normally.
+ mWm.mSyncEngine.abort(openTransition.getSyncId());
+
+ // Before finishing openTransition, we are now going to simulate closing task1 to return
+ // back to (open) task2.
+ final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
+
+ closeTransition.collectExistenceChange(task1);
+ closeTransition.collectExistenceChange(activity1);
+ closeTransition.collectExistenceChange(task2);
+ closeTransition.collectExistenceChange(activity2);
+
+ activity1.mVisibleRequested = false;
+ activity2.mVisibleRequested = true;
+
+ openTransition.finishTransition();
+
+ // We finished the openTransition. Even though activity1 is visibleRequested=false, since
+ // the closeTransition animation hasn't played yet, make sure that we didn't commit
+ // visible=false on activity1 since it needs to remain visible for the animation.
+ assertTrue(activity1.isVisible());
+ assertTrue(activity2.isVisible());
+
+ // Using abort to force-finish the sync (since we obviously can't wait for drawing).
+ // We didn't call abort on the actual transition, so it will still run onTransactionReady
+ // normally.
+ mWm.mSyncEngine.abort(closeTransition.getSyncId());
+
+ closeTransition.finishTransition();
+
+ assertFalse(activity1.isVisible());
+ assertTrue(activity2.isVisible());
+ }
+
/** Fill the change map with all the parents of top. Change maps are usually fully populated */
private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
WindowContainer top) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index a1f89ec75784..316309c8440e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -35,6 +35,7 @@ import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.InsetsSource;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
@@ -57,12 +58,14 @@ import org.mockito.Mockito;
public class WindowFrameTests extends WindowTestsBase {
private DisplayContent mTestDisplayContent;
+ private DisplayFrames mTestDisplayFrames;
@Before
public void setUp() throws Exception {
DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
testDisplayInfo.displayCutout = null;
mTestDisplayContent = createNewDisplay(testDisplayInfo);
+ mTestDisplayFrames = mTestDisplayContent.mDisplayFrames;
}
// Do not use this function directly in the tests below. Instead, use more explicit function
@@ -99,7 +102,7 @@ public class WindowFrameTests extends WindowTestsBase {
// Here the window has FILL_PARENT, FILL_PARENT
// so we expect it to fill the entire available frame.
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
assertRelFrame(w, 0, 0, 1000, 1000);
@@ -108,14 +111,14 @@ public class WindowFrameTests extends WindowTestsBase {
// and we use mRequestedWidth/mRequestedHeight
w.mAttrs.width = 300;
w.mAttrs.height = 300;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// Explicit width and height without requested width/height
// gets us nothing.
assertFrame(w, 0, 0, 0, 0);
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// With requestedWidth/Height we can freely choose our size within the
// parent bounds.
assertFrame(w, 0, 0, 300, 300);
@@ -128,14 +131,14 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = -1;
w.mAttrs.width = 100;
w.mAttrs.height = 100;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 100, 100);
w.mAttrs.flags = 0;
// But sizes too large will be clipped to the containing frame
w.mRequestedWidth = 1200;
w.mRequestedHeight = 1200;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
// Before they are clipped though windows will be shifted
@@ -143,7 +146,7 @@ public class WindowFrameTests extends WindowTestsBase {
w.mAttrs.y = 300;
w.mRequestedWidth = 1000;
w.mRequestedHeight = 1000;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 0, 0, 1000, 1000);
// If there is room to move around in the parent frame the window will be shifted according
@@ -153,18 +156,18 @@ public class WindowFrameTests extends WindowTestsBase {
w.mRequestedWidth = 300;
w.mRequestedHeight = 300;
w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 700, 0, 1000, 300);
assertRelFrame(w, 700, 0, 1000, 300);
w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 700, 700, 1000, 1000);
assertRelFrame(w, 700, 700, 1000, 1000);
// Window specified x and y are interpreted as offsets in the opposite
// direction of gravity
w.mAttrs.x = 100;
w.mAttrs.y = 100;
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, 600, 600, 900, 900);
assertRelFrame(w, 600, 600, 900, 900);
}
@@ -191,7 +194,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertEquals(resolvedTaskBounds, w.getFrame());
@@ -204,7 +207,7 @@ public class WindowFrameTests extends WindowTestsBase {
final int cfBottom = logicalHeight / 2;
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertEquals(resolvedTaskBounds, w.getFrame());
assertEquals(0, w.getRelativeFrame().left);
assertEquals(0, w.getRelativeFrame().top);
@@ -233,7 +236,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -249,7 +252,7 @@ public class WindowFrameTests extends WindowTestsBase {
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
windowFrames.setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, cf);
}
@@ -274,7 +277,9 @@ public class WindowFrameTests extends WindowTestsBase {
imeFrame.top = 400;
imeSource.setFrame(imeFrame);
imeSource.setVisible(true);
- w.updateRequestedVisibility(state);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_IME, true);
+ w.setRequestedVisibilities(requestedVisibilities);
w.mAboveInsetsState.addSource(imeSource);
// With no insets or system decor all the frames incoming from PhoneWindowManager
@@ -285,7 +290,7 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect winRect = new Rect(200, 200, 300, 500);
task.setBounds(winRect);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top);
// Now check that it won't get moved beyond the top
@@ -293,7 +298,7 @@ public class WindowFrameTests extends WindowTestsBase {
task.setBounds(winRect);
w.setBounds(winRect);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
// Now we have status bar. Check that it won't go into the status bar area.
@@ -301,14 +306,14 @@ public class WindowFrameTests extends WindowTestsBase {
statusBarFrame.bottom = 60;
state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right,
statusBarFrame.bottom + winRect.height());
// Check that it's moved back without ime insets
state.removeSource(ITYPE_IME);
w.getWindowFrames().setFrames(pf, pf);
- w.computeFrame();
+ w.computeFrame(mTestDisplayFrames);
assertEquals(winRect, w.getFrame());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index d9aa871447be..a91298f73d08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -56,6 +56,7 @@ import android.platform.test.annotations.Presubmit;
import android.view.IWindowSessionCallback;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowManager;
@@ -107,9 +108,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -128,9 +129,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -151,9 +152,9 @@ public class WindowManagerServiceTests extends WindowTestsBase {
Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
spyOn(mWm.mAtmService);
- mWm.handleTaskFocusChange(tappedTask);
+ mWm.handleTaskFocusChange(tappedTask, null /* window */);
- verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId);
+ verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
}
@Test
@@ -278,7 +279,7 @@ public class WindowManagerServiceTests extends WindowTestsBase {
.getWindowType(eq(windowContextToken));
mWm.addWindow(session, new TestIWindow(), params, View.VISIBLE, DEFAULT_DISPLAY,
- UserHandle.USER_SYSTEM, new InsetsState(), null, new InsetsState(),
+ UserHandle.USER_SYSTEM, new InsetsVisibilities(), null, new InsetsState(),
new InsetsSourceControl[0]);
verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
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 d6a8401f5b18..9160109e7e7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,8 +42,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
-import static com.android.server.wm.Task.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
@@ -62,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IRequestFinishCallback;
import android.app.PictureInPictureParams;
@@ -70,7 +70,6 @@ import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -82,6 +81,7 @@ import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
@@ -346,7 +346,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testDisplayAreaTransaction() {
removeGlobalMinSizeRestriction();
- final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
testTransaction(displayArea);
}
@@ -364,7 +364,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
.setWindowingMode(WINDOWING_MODE_FREEFORM).build();
testSetWindowingMode(rootTask);
- final DisplayArea displayArea = new DisplayArea<>(mWm, ABOVE_TASKS, "DisplayArea");
+ final DisplayArea displayArea = mDisplayContent.getDefaultTaskDisplayArea();
displayArea.setWindowingMode(WINDOWING_MODE_FREEFORM);
testSetWindowingMode(displayArea);
}
@@ -1256,21 +1256,42 @@ public class WindowOrganizerTests extends WindowTestsBase {
@Test
public void testStartTasksInTransaction() {
WindowContainerTransaction wct = new WindowContainerTransaction();
- Bundle testOptions = new Bundle();
- testOptions.putInt("test", 20);
+ ActivityOptions testOptions = ActivityOptions.makeBasic();
+ testOptions.setTransientLaunch();
wct.startTask(1, null /* options */);
- wct.startTask(2, testOptions);
- spyOn(mWm.mAtmService);
- doReturn(START_CANCELED).when(mWm.mAtmService).startActivityFromRecents(anyInt(), any());
+ wct.startTask(2, testOptions.toBundle());
+ spyOn(mWm.mAtmService.mTaskSupervisor);
+ doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
+ anyInt(), anyInt(), anyInt(), any());
clearInvocations(mWm.mAtmService);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(1), bundleCaptor.capture());
- assertTrue(bundleCaptor.getValue().isEmpty());
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(1), any());
- verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(2), bundleCaptor.capture());
- assertEquals(20, bundleCaptor.getValue().getInt("test"));
+ final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
+ ArgumentCaptor.forClass(SafeActivityOptions.class);
+ verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
+ anyInt(), anyInt(), eq(2), optionsCaptor.capture());
+ assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
+ }
+
+ @Test
+ public void testResumeTopsWhenLeavingPinned() {
+ final ActivityRecord record = makePipableActivity();
+ final Task rootTask = record.getRootTask();
+
+ clearInvocations(mWm.mAtmService.mRootWindowContainer);
+ final WindowContainerTransaction t = new WindowContainerTransaction();
+ WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
+ t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+
+ clearInvocations(mWm.mAtmService.mRootWindowContainer);
+ t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+ verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index ed18d26f8448..d3f2d1407a46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -23,6 +23,12 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityRecord.State.PAUSED;
+import static com.android.server.wm.ActivityRecord.State.PAUSING;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.ActivityRecord.State.STARTED;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
+import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -311,17 +317,17 @@ public class WindowProcessControllerTests extends WindowTestsBase {
callbackResult[0] = 0;
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.PAUSED, "test");
+ activity.setState(PAUSED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(paused, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPING, "test");
+ activity.setState(STOPPING, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(stopping, callbackResult[0]);
callbackResult[0] = 0;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
mWpc.computeOomAdjFromActivities(callback);
assertEquals(other, callbackResult[0]);
}
@@ -332,25 +338,25 @@ public class WindowProcessControllerTests extends WindowTestsBase {
spyOn(tracker);
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
- activity.setState(Task.ActivityState.STARTED, "test");
+ activity.setState(STARTED, "test");
verify(tracker).onAnyActivityVisible(mWpc);
assertTrue(mWpc.hasVisibleActivities());
- activity.setState(Task.ActivityState.RESUMED, "test");
+ activity.setState(RESUMED, "test");
verify(tracker).onActivityResumedWhileVisible(mWpc);
assertTrue(tracker.hasResumedActivity(mWpc.mUid));
activity.makeFinishingLocked();
- activity.setState(Task.ActivityState.PAUSING, "test");
+ activity.setState(PAUSING, "test");
assertFalse(tracker.hasResumedActivity(mWpc.mUid));
assertTrue(mWpc.hasForegroundActivities());
activity.setVisibility(false);
activity.mVisibleRequested = false;
- activity.setState(Task.ActivityState.STOPPED, "test");
+ activity.setState(STOPPED, "test");
verify(tracker).onAllActivitiesInvisible(mWpc);
assertFalse(mWpc.hasVisibleActivities());
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 92b670ed9699..17288c21adc8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -85,6 +85,7 @@ import android.view.Gravity;
import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -437,9 +438,9 @@ public class WindowStateTests extends WindowTestsBase {
.setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */);
mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
- final InsetsState state = new InsetsState();
- state.getSource(ITYPE_STATUS_BAR).setVisible(false);
- app.updateRequestedVisibility(state);
+ final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
+ requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
+ app.setRequestedVisibilities(requestedVisibilities);
mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
.updateClientVisibility(app);
waitUntilHandlersIdle();
@@ -946,4 +947,19 @@ public class WindowStateTests extends WindowTestsBase {
assertNotNull(state.peekSource(ITYPE_IME));
assertTrue(state.getSource(ITYPE_IME).isVisible());
}
+
+ @Test
+ public void testRequestedVisibility() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mActivityRecord.setVisible(false);
+ app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+ assertFalse(app.isVisibleRequested());
+
+ // It doesn't have a surface yet, but should still be visible requested.
+ app.setHasSurface(false);
+ app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+
+ assertFalse(app.isVisible());
+ assertTrue(app.isVisibleRequested());
+ }
}
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 588089996d6c..050fd80411fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -29,11 +30,13 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.os.Process.SYSTEM_UID;
import static android.view.View.VISIBLE;
+import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -83,10 +86,12 @@ import android.service.voice.IVoiceInteractionSession;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
@@ -132,6 +137,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
DisplayInfo mDisplayInfo = new DisplayInfo();
DisplayContent mDefaultDisplay;
+ static final int STATUS_BAR_HEIGHT = 10;
+ static final int NAV_BAR_HEIGHT = 15;
+
/**
* It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with
* {@link UseTestDisplay}, it will be an additional display.
@@ -268,6 +276,14 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) {
mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow");
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT;
+ mStatusBarWindow.mAttrs.gravity = Gravity.TOP;
+ mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT,
+ STATUS_BAR_HEIGHT);
+ }
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) {
mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE,
@@ -275,6 +291,15 @@ class WindowTestsBase extends SystemServiceTestsBase {
}
if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) {
mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow");
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT;
+ mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM;
+ mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
+ for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
+ mNavBarWindow.mAttrs.paramsForRotation[rot] =
+ getNavBarLayoutParamsForRotation(rot);
+ }
+ }
}
if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) {
mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER,
@@ -302,6 +327,37 @@ class WindowTestsBase extends SystemServiceTestsBase {
waitUntilHandlersIdle();
}
+ private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) {
+ int width = WindowManager.LayoutParams.MATCH_PARENT;
+ int height = WindowManager.LayoutParams.MATCH_PARENT;
+ int gravity = Gravity.BOTTOM;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ switch (rotation) {
+ case ROTATION_UNDEFINED:
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ height = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_90:
+ gravity = Gravity.RIGHT;
+ width = NAV_BAR_HEIGHT;
+ break;
+ case Surface.ROTATION_270:
+ gravity = Gravity.LEFT;
+ width = NAV_BAR_HEIGHT;
+ break;
+ }
+ }
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR);
+ lp.width = width;
+ lp.height = height;
+ if (INSETS_LAYOUT_GENERALIZATION) {
+ lp.gravity = gravity;
+ }
+ return lp;
+ }
+
void beforeCreateTestDisplay() {
// Called before display is created.
}
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 9daa0930e73a..6c20e746f35e 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -2,3 +2,5 @@ mwachens@google.com
varunshah@google.com
huiyu@google.com
yamasani@google.com
+
+per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS \ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index b056de075c36..763159a2b615 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -208,7 +208,7 @@ public class StorageStatsService extends IStorageStatsManager.Stub {
public boolean isReservedSupported(String volumeUuid, String callingPackage) {
if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
return SystemProperties.getBoolean(StorageManager.PROP_HAS_RESERVED, false)
- || Build.IS_CONTAINER;
+ || Build.IS_ARC;
} else {
return false;
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceLogger.java b/services/usb/java/com/android/server/usb/UsbDeviceLogger.java
new file mode 100644
index 000000000000..fab00bcda941
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbDeviceLogger.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.usb;
+
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.LinkedList;
+
+/**
+* Constructor UsbDeviceLogger class
+*/
+public class UsbDeviceLogger {
+ private final Object mLock = new Object();
+
+ // ring buffer of events to log.
+ @GuardedBy("mLock")
+ private final LinkedList<Event> mEvents;
+
+ private final String mTitle;
+
+ // the maximum number of events to keep in log
+ private final int mMemSize;
+
+ /**
+ * Constructor for Event class.
+ */
+ public abstract static class Event {
+ // formatter for timestamps
+ private static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+ private final Calendar mCalendar;
+
+ Event() {
+ mCalendar = Calendar.getInstance();
+ }
+
+ /**
+ * Convert event to String
+ * @return StringBuilder
+ */
+ public String toString() {
+ return (new StringBuilder(String.format("%tm-%td %tH:%tM:%tS.%tL",
+ mCalendar, mCalendar, mCalendar, mCalendar, mCalendar, mCalendar)))
+ .append(" ").append(eventToString()).toString();
+ }
+
+ /**
+ * Causes the string message for the event to appear in the logcat.
+ * Here is an example of how to create a new event (a StringEvent), adding it to the logger
+ * (an instance of UsbDeviceLoggerr) while also making it show in the logcat:
+ * <pre>
+ * myLogger.log(
+ * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
+ * </pre>
+ * @param tag the tag for the android.util.Log.v
+ * @return the same instance of the event
+ */
+ public Event printLog(String tag) {
+ Log.i(tag, eventToString());
+ return this;
+ }
+
+ /**
+ * Convert event to String.
+ * This method is only called when the logger history is about to the dumped,
+ * so this method is where expensive String conversions should be made, not when the Event
+ * subclass is created.
+ * Timestamp information will be automatically added, do not include it.
+ * @return a string representation of the event that occurred.
+ */
+ public abstract String eventToString();
+ }
+
+ /**
+ * Constructor StringEvent class
+ */
+ public static class StringEvent extends Event {
+ private final String mMsg;
+
+ public StringEvent(String msg) {
+ mMsg = msg;
+ }
+
+ @Override
+ public String eventToString() {
+ return mMsg;
+ }
+ }
+
+ /**
+ * Constructor for logger.
+ * @param size the maximum number of events to keep in log
+ * @param title the string displayed before the recorded log
+ */
+ public UsbDeviceLogger(int size, String title) {
+ mEvents = new LinkedList<Event>();
+ mMemSize = size;
+ mTitle = title;
+ }
+
+ /**
+ * Constructor for logger.
+ * @param evt the maximum number of events to keep in log
+ */
+ public synchronized void log(Event evt) {
+ synchronized (mLock) {
+ if (mEvents.size() >= mMemSize) {
+ mEvents.removeFirst();
+ }
+ mEvents.add(evt);
+ }
+ }
+
+ /**
+ * Constructor for logger.
+ * @param dump the maximum number of events to keep in log
+ * @param id the category of events
+ */
+ public synchronized void dump(DualDumpOutputStream dump, long id) {
+ dump.write("USB Event Log", id, mTitle);
+ for (Event evt : mEvents) {
+ dump.write("USB Event", id, evt.toString());
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 85f16eb00987..0d7b23db3063 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -191,6 +191,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
*/
private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000;
+ private static final int DUMPSYS_LOG_BUFFER = 200;
+
private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
@@ -210,6 +212,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private static Set<Integer> sDenyInterfaces;
private HashMap<Long, FileDescriptor> mControlFds;
+ private static UsbDeviceLogger sEventLogger;
+
static {
sDenyInterfaces = new HashSet<>();
sDenyInterfaces.add(UsbConstants.USB_CLASS_AUDIO);
@@ -232,15 +236,16 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString());
+ sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString()));
String state = event.get("USB_STATE");
String accessory = event.get("ACCESSORY");
+
if (state != null) {
mHandler.updateState(state);
} else if ("GETPROTOCOL".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory get protocol");
- long elapsedRealtime = SystemClock.elapsedRealtime();
- mHandler.setAccessoryUEventTime(elapsedRealtime);
+ mHandler.setAccessoryUEventTime(SystemClock.elapsedRealtime());
resetAccessoryHandshakeTimeoutHandler();
} else if ("SENDSTRING".equals(accessory)) {
if (DEBUG) Slog.d(TAG, "got accessory send string");
@@ -383,6 +388,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mUEventObserver = new UsbUEventObserver();
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+ sEventLogger = new UsbDeviceLogger(DUMPSYS_LOG_BUFFER, "UsbDeviceManager activity");
}
UsbProfileGroupSettingsManager getCurrentSettings() {
@@ -465,6 +472,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
}
+ //TODO It is not clear that this method serves any purpose (at least on Pixel devices)
+ // consider removing
private static void initRndisAddress() {
// configure RNDIS ethernet address based on our serial number using the same algorithm
// we had been previously using in kernel board files
@@ -484,7 +493,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
try {
FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
} catch (IOException e) {
- Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+ Slog.i(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
}
}
@@ -760,9 +769,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
private void broadcastUsbAccessoryHandshake() {
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long accessoryHandShakeEnd = elapsedRealtime;
-
// send a sticky broadcast containing USB accessory handshake information
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE)
.putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME,
@@ -772,7 +778,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
.putExtra(UsbManager.EXTRA_ACCESSORY_START,
mStartAccessory)
.putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END,
- accessoryHandShakeEnd);
+ SystemClock.elapsedRealtime());
if (DEBUG) {
Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras());
@@ -780,7 +786,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
sendStickyBroadcast(intent);
resetUsbAccessoryHandshakeDebuggingInfo();
- accessoryHandShakeEnd = 0L;
}
protected void updateUsbStateBroadcastIfNeeded(long functions) {
@@ -817,6 +822,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
protected void sendStickyBroadcast(Intent intent) {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ sEventLogger.log(new UsbDeviceLogger.StringEvent("USB intent: " + intent));
}
private void updateUsbFunctions() {
@@ -1936,14 +1942,14 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
- Slog.e(TAG, "prcessing MSG_GET_CURRENT_USB_FUNCTIONS");
+ Slog.i(TAG, "processing MSG_GET_CURRENT_USB_FUNCTIONS");
mCurrentUsbFunctionsReceived = true;
if (mCurrentUsbFunctionsRequested) {
- Slog.e(TAG, "updating mCurrentFunctions");
+ Slog.i(TAG, "updating mCurrentFunctions");
// Mask out adb, since it is stored in mAdbEnabled
mCurrentFunctions = ((Long) msg.obj) & ~UsbManager.FUNCTION_ADB;
- Slog.e(TAG,
+ Slog.i(TAG,
"mCurrentFunctions:" + mCurrentFunctions + "applied:" + msg.arg1);
mCurrentFunctionsApplied = msg.arg1 == 1;
}
@@ -2289,6 +2295,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
if (mHandler != null) {
mHandler.dump(dump, "handler", UsbDeviceManagerProto.HANDLER);
+ sEventLogger.dump(dump, UsbHandlerProto.UEVENT);
}
dump.end(token);
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index f63866054064..bb0c4e903ff8 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -60,7 +60,6 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -75,7 +74,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ProtocolException;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -86,6 +84,8 @@ class UsbProfileGroupSettingsManager {
private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int DUMPSYS_LOG_BUFFER = 200;
+
/** Legacy settings file, before multi-user */
private static final File sSingleUserSettingsFile = new File(
"/data/system/usb_device_manager.xml");
@@ -130,6 +130,8 @@ class UsbProfileGroupSettingsManager {
@GuardedBy("mLock")
private boolean mIsWriteSettingsScheduled;
+ private static UsbDeviceLogger sEventLogger;
+
/**
* A package of a user.
*/
@@ -260,6 +262,9 @@ class UsbProfileGroupSettingsManager {
device, false /* showMtpNotification */));
mUsbHandlerManager = usbResolveActivityManager;
+
+ sEventLogger = new UsbDeviceLogger(DUMPSYS_LOG_BUFFER,
+ "UsbProfileGroupSettingsManager activity");
}
/**
@@ -965,6 +970,7 @@ class UsbProfileGroupSettingsManager {
matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
}
+ sEventLogger.log(new UsbDeviceLogger.StringEvent("accessoryAttached: " + intent));
resolveActivity(intent, matches, defaultActivity, null, accessory);
}
@@ -1518,6 +1524,7 @@ class UsbProfileGroupSettingsManager {
}
}
+ sEventLogger.dump(dump, UsbProfileGroupSettingsManagerProto.INTENT);
dump.end(token);
}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 7b6ccd31adcc..49ea9df5b26f 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -689,8 +689,10 @@ class UsbUserPermissionManager {
try {
ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
if (aInfo.uid != uid) {
- throw new IllegalArgumentException("package " + packageName
+ Slog.w(TAG, "package " + packageName
+ " does not match caller's uid " + uid);
+ throw new IllegalArgumentException("package " + packageName
+ + " not found");
}
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("package " + packageName + " not found");
diff --git a/services/uwb/Android.bp b/services/uwb/Android.bp
deleted file mode 100644
index da30d43a4536..000000000000
--- a/services/uwb/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the '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.uwb-sources",
- srcs: ["java/**/*.java"],
- path: "java",
- visibility: ["//frameworks/base/services"],
-}
-
-java_library_static {
- name: "services.uwb",
- defaults: ["platform_service_defaults"],
- srcs: [
- ":services.uwb-sources",
- ],
- libs: [
- "services.core",
- ],
-}
diff --git a/services/uwb/OWNERS b/services/uwb/OWNERS
deleted file mode 100644
index c31a2f1ed3bc..000000000000
--- a/services/uwb/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/frameworks/base:/core/java/android/uwb/OWNERS
diff --git a/services/uwb/java/com/android/server/uwb/UwbInjector.java b/services/uwb/java/com/android/server/uwb/UwbInjector.java
deleted file mode 100644
index 64f1da1c8e16..000000000000
--- a/services/uwb/java/com/android/server/uwb/UwbInjector.java
+++ /dev/null
@@ -1,83 +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.server.uwb;
-
-import static android.Manifest.permission.UWB_RANGING;
-import static android.content.PermissionChecker.PERMISSION_GRANTED;
-
-import android.annotation.NonNull;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.content.PermissionChecker;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.uwb.IUwbAdapter;
-
-
-/**
- * To be used for dependency injection (especially helps mocking static dependencies).
- */
-public class UwbInjector {
- private static final String TAG = "UwbInjector";
-
- private static final String VENDOR_SERVICE_NAME = "uwb_vendor";
-
- private final Context mContext;
-
- public UwbInjector(@NonNull Context context) {
- mContext = context;
- }
-
- /**
- * @return Returns the vendor service handle.
- */
- public IUwbAdapter getVendorService() {
- IBinder b = ServiceManager.getService(VENDOR_SERVICE_NAME);
- if (b == null) return null;
- return IUwbAdapter.Stub.asInterface(b);
- }
-
- /**
- * Throws security exception if the UWB_RANGING permission is not granted for the calling app.
- *
- * <p>Should be used in situations where the app op should not be noted.
- */
- public void enforceUwbRangingPermissionForPreflight(
- @NonNull AttributionSource attributionSource) {
- if (!attributionSource.checkCallingUid()) {
- throw new SecurityException("Invalid attribution source " + attributionSource);
- }
- int permissionCheckResult = PermissionChecker.checkPermissionForPreflight(
- mContext, UWB_RANGING, attributionSource);
- if (permissionCheckResult != PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold UWB_RANGING permission");
- }
- }
-
- /**
- * Returns true if the UWB_RANGING permission is granted for the calling app.
- *
- * <p>Should be used in situations where data will be delivered and hence the app op should
- * be noted.
- */
- public boolean checkUwbRangingPermissionForDataDelivery(
- @NonNull AttributionSource attributionSource, @NonNull String message) {
- int permissionCheckResult = PermissionChecker.checkPermissionForDataDelivery(
- mContext, UWB_RANGING, -1, attributionSource, message);
- return permissionCheckResult == PERMISSION_GRANTED;
- }
-}
diff --git a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
deleted file mode 100644
index 4dd26a66cf0e..000000000000
--- a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java
+++ /dev/null
@@ -1,325 +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.server.uwb;
-
-import android.annotation.NonNull;
-import android.content.AttributionSource;
-import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.PersistableBundle;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.uwb.IUwbAdapter;
-import android.uwb.IUwbAdapterStateCallbacks;
-import android.uwb.IUwbRangingCallbacks;
-import android.uwb.RangingReport;
-import android.uwb.RangingSession;
-import android.uwb.SessionHandle;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Map;
-
-/**
- * Implementation of {@link android.uwb.IUwbAdapter} binder service.
- */
-public class UwbServiceImpl extends IUwbAdapter.Stub implements IBinder.DeathRecipient{
- private static final String TAG = "UwbServiceImpl";
-
- private final Context mContext;
- private final UwbInjector mUwbInjector;
- /**
- * Map for storing the callbacks wrapper for each session.
- */
- @GuardedBy("mCallbacksMap")
- private final Map<SessionHandle, UwbRangingCallbacksWrapper> mCallbacksMap = new ArrayMap<>();
-
- /**
- * Used for caching the vendor implementation of {@link IUwbAdapter} interface.
- */
- private IUwbAdapter mVendorUwbAdapter;
-
- /**
- * Wrapper for callback registered with vendor service. This wrapper is needed for performing
- * permission check before sending the callback to the external app.
- *
- * Access to these callbacks are synchronized.
- */
- private class UwbRangingCallbacksWrapper extends IUwbRangingCallbacks.Stub
- implements IBinder.DeathRecipient {
- private final AttributionSource mAttributionSource;
- private final SessionHandle mSessionHandle;
- private final IUwbRangingCallbacks mExternalCb;
- private boolean mIsValid;
-
- UwbRangingCallbacksWrapper(@NonNull AttributionSource attributionSource,
- @NonNull SessionHandle sessionHandle,
- @NonNull IUwbRangingCallbacks externalCb) {
- mAttributionSource = attributionSource;
- mSessionHandle = sessionHandle;
- mExternalCb = externalCb;
- mIsValid = true;
-
- // Link to death for external callback.
- linkToDeath();
- }
-
- private void linkToDeath() {
- IBinder binder = mExternalCb.asBinder();
- try {
- binder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to link to client death event.", e);
- }
- }
-
- private void removeClientAndUnlinkToDeath() {
- // Remove from the map.
- synchronized (mCallbacksMap) {
- mCallbacksMap.remove(mSessionHandle);
- }
- IBinder binder = mExternalCb.asBinder();
- binder.unlinkToDeath(this, 0);
- mIsValid = false;
- }
-
-
- @Override
- public synchronized void onRangingOpened(SessionHandle sessionHandle)
- throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingOpened(sessionHandle);
- }
-
- @Override
- public synchronized void onRangingOpenFailed(SessionHandle sessionHandle,
- int reason, PersistableBundle parameters) throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingOpenFailed(sessionHandle, reason, parameters);
- }
-
- @Override
- public synchronized void onRangingStarted(SessionHandle sessionHandle,
- PersistableBundle parameters)
- throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingStarted(sessionHandle, parameters);
- }
-
- @Override
- public synchronized void onRangingStartFailed(SessionHandle sessionHandle,
- int reason, PersistableBundle parameters) throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingStartFailed(sessionHandle, reason, parameters);
- }
-
- @Override
- public synchronized void onRangingReconfigured(SessionHandle sessionHandle,
- PersistableBundle parameters)
- throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingReconfigured(sessionHandle, parameters);
- }
-
- @Override
- public synchronized void onRangingReconfigureFailed(SessionHandle sessionHandle,
- int reason, PersistableBundle parameters) throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingReconfigureFailed(sessionHandle, reason, parameters);
- }
-
- @Override
- public synchronized void onRangingStopped(SessionHandle sessionHandle, int reason,
- PersistableBundle parameters)
- throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingStopped(sessionHandle, reason, parameters);
- }
-
- @Override
- public synchronized void onRangingStopFailed(SessionHandle sessionHandle, int reason,
- PersistableBundle parameters) throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingStopFailed(sessionHandle, reason, parameters);
- }
-
- @Override
- public synchronized void onRangingClosed(SessionHandle sessionHandle, int reason,
- PersistableBundle parameters) throws RemoteException {
- if (!mIsValid) return;
- mExternalCb.onRangingClosed(sessionHandle, reason, parameters);
- removeClientAndUnlinkToDeath();
- }
-
- @Override
- public synchronized void onRangingResult(SessionHandle sessionHandle,
- RangingReport rangingReport)
- throws RemoteException {
- if (!mIsValid) return;
- boolean permissionGranted = Binder.withCleanCallingIdentity(
- () -> mUwbInjector.checkUwbRangingPermissionForDataDelivery(
- mAttributionSource, "uwb ranging result"));
- if (!permissionGranted) {
- Log.e(TAG, "Not delivering ranging result because of permission denial"
- + mSessionHandle);
- return;
- }
- mExternalCb.onRangingResult(sessionHandle, rangingReport);
- }
-
- @Override
- public synchronized void binderDied() {
- if (!mIsValid) return;
- Log.i(TAG, "Client died: ending session: " + mSessionHandle);
- try {
- removeClientAndUnlinkToDeath();
- stopRanging(mSessionHandle);
- closeRanging(mSessionHandle);
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception while handling client death", e);
- }
- }
- }
-
- private void linkToVendorServiceDeath() {
- IBinder binder = mVendorUwbAdapter.asBinder();
- try {
- binder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to link to vendor service death event.", e);
- }
- }
-
- @Override
- public void binderDied() {
- Log.i(TAG, "Vendor service died: sending session close callbacks");
- synchronized (mCallbacksMap) {
- for (Map.Entry<SessionHandle, UwbRangingCallbacksWrapper> e : mCallbacksMap.entrySet()) {
- try {
- e.getValue().mExternalCb.onRangingClosed(
- e.getKey(), RangingSession.Callback.REASON_UNKNOWN,
- new PersistableBundle());
- } catch (RemoteException ex) {
- Log.e(TAG, "Failed to send session close callback " + e.getKey(), ex);
- }
- }
- // Clear all sessions.
- mCallbacksMap.clear();
- }
- mVendorUwbAdapter = null;
- }
-
- private synchronized IUwbAdapter getVendorUwbAdapter() throws IllegalStateException {
- if (mVendorUwbAdapter != null) return mVendorUwbAdapter;
- mVendorUwbAdapter = mUwbInjector.getVendorService();
- if (mVendorUwbAdapter == null) {
- throw new IllegalStateException("No vendor service found!");
- }
- Log.i(TAG, "Retrieved vendor service");
- linkToVendorServiceDeath();
- return mVendorUwbAdapter;
- }
-
- UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) {
- mContext = context;
- mUwbInjector = uwbInjector;
- }
-
- private void enforceUwbPrivilegedPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.UWB_PRIVILEGED,
- "UwbService");
- }
-
- @Override
- public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
- throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().registerAdapterStateCallbacks(adapterStateCallbacks);
- }
-
- @Override
- public void unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks)
- throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().unregisterAdapterStateCallbacks(adapterStateCallbacks);
- }
-
- @Override
- public long getTimestampResolutionNanos() throws RemoteException {
- enforceUwbPrivilegedPermission();
- return getVendorUwbAdapter().getTimestampResolutionNanos();
- }
-
- @Override
- public PersistableBundle getSpecificationInfo() throws RemoteException {
- enforceUwbPrivilegedPermission();
- return getVendorUwbAdapter().getSpecificationInfo();
- }
-
- @Override
- public void openRanging(AttributionSource attributionSource,
- SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks,
- PersistableBundle parameters) throws RemoteException {
- enforceUwbPrivilegedPermission();
- mUwbInjector.enforceUwbRangingPermissionForPreflight(attributionSource);
-
- UwbRangingCallbacksWrapper wrapperCb =
- new UwbRangingCallbacksWrapper(attributionSource, sessionHandle, rangingCallbacks);
- synchronized (mCallbacksMap) {
- mCallbacksMap.put(sessionHandle, wrapperCb);
- }
- getVendorUwbAdapter().openRanging(attributionSource, sessionHandle, wrapperCb, parameters);
- }
-
- @Override
- public void startRanging(SessionHandle sessionHandle, PersistableBundle parameters)
- throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().startRanging(sessionHandle, parameters);
- }
-
- @Override
- public void reconfigureRanging(SessionHandle sessionHandle, PersistableBundle parameters)
- throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().reconfigureRanging(sessionHandle, parameters);
- }
-
- @Override
- public void stopRanging(SessionHandle sessionHandle) throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().stopRanging(sessionHandle);
- }
-
- @Override
- public void closeRanging(SessionHandle sessionHandle) throws RemoteException {
- enforceUwbPrivilegedPermission();
- getVendorUwbAdapter().closeRanging(sessionHandle);
- }
-
- @Override
- public synchronized int getAdapterState() throws RemoteException {
- return getVendorUwbAdapter().getAdapterState();
- }
-
- @Override
- public synchronized void setEnabled(boolean enabled) throws RemoteException {
- getVendorUwbAdapter().setEnabled(enabled);
- }
-}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index a3b5fc7ba872..be37a9139bb4 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -36,7 +36,6 @@ import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.SoundModel;
-import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.Binder;
import android.os.DeadObjectException;
@@ -109,9 +108,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private boolean mCallActive = false;
private @SoundTriggerPowerSaveMode int mSoundTriggerPowerSaveMode =
PowerManager.SOUND_TRIGGER_MODE_ALL_ENABLED;
- // Indicates if the native sound trigger service is disabled or not.
- // This is an indirect indication of the microphone being open in some other application.
- private boolean mServiceDisabled = false;
// Whether ANY recognition (keyphrase or generic) has been requested.
private boolean mRecognitionRequested = false;
@@ -862,23 +858,19 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
@Override
- public void onSoundModelUpdate(SoundModelEvent event) {
- if (event == null) {
- Slog.w(TAG, "Invalid sound model event!");
- return;
- }
- if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
+ public void onModelUnloaded(int modelHandle) {
+ if (DBG) Slog.d(TAG, "onModelUnloaded: " + modelHandle);
synchronized (mLock) {
MetricsLogger.count(mContext, "sth_sound_model_updated", 1);
- onSoundModelUpdatedLocked(event);
+ onModelUnloadedLocked(modelHandle);
}
}
@Override
- public void onServiceStateChange(int state) {
- if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
+ public void onResourcesAvailable() {
+ if (DBG) Slog.d(TAG, "onResourcesAvailable");
synchronized (mLock) {
- onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
+ onResourcesAvailableLocked();
}
}
@@ -910,15 +902,14 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
updateAllRecognitionsLocked();
}
- private void onSoundModelUpdatedLocked(SoundModelEvent event) {
- // TODO: Handle sound model update here.
+ private void onModelUnloadedLocked(int modelHandle) {
+ ModelData modelData = getModelDataForLocked(modelHandle);
+ if (modelData != null) {
+ modelData.setNotLoaded();
+ }
}
- private void onServiceStateChangedLocked(boolean disabled) {
- if (disabled == mServiceDisabled) {
- return;
- }
- mServiceDisabled = disabled;
+ private void onResourcesAvailableLocked() {
updateAllRecognitionsLocked();
}
@@ -1039,7 +1030,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (mModule != null) {
mModule.detach();
mModule = null;
- mServiceDisabled = false;
}
}
}
@@ -1114,8 +1104,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
pw.print(" call active=");
pw.println(mCallActive);
pw.println(" SoundTrigger Power State=" + mSoundTriggerPowerSaveMode);
- pw.print(" service disabled=");
- pw.println(mServiceDisabled);
}
}
@@ -1329,8 +1317,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mSoundTriggerPowerSaveMode = mPowerManager.getSoundTriggerPowerSaveMode();
}
- return !mCallActive && !mServiceDisabled
- && isRecognitionAllowedByPowerState(
+ return !mCallActive && isRecognitionAllowedByPowerState(
modelData);
}
@@ -1571,6 +1558,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mModelState = MODEL_LOADED;
}
+ synchronized void setNotLoaded() {
+ mModelState = MODEL_NOTLOADED;
+ }
+
synchronized boolean isModelStarted() {
return mModelState == MODEL_STARTED;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index a9aeb985d115..734172fc1549 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -23,8 +23,11 @@ import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPH
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
+import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
import android.content.Context;
@@ -932,12 +935,11 @@ final class HotwordDetectionConnection {
// TODO: Share this code with SoundTriggerMiddlewarePermission.
private void enforcePermissionsForDataDelivery() {
Binder.withCleanCallingIdentity(() -> {
- // Hack to make sure we show the mic privacy-indicator since the Trusted Hotword
- // requirement isn't being enforced for now. Normally, we would note the HOTWORD op here
- // instead.
- enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
- RECORD_AUDIO, OP_MESSAGE);
-
+ enforcePermissionForPreflight(mContext, mVoiceInteractorIdentity, RECORD_AUDIO);
+ int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD);
+ mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp,
+ mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+ mVoiceInteractorIdentity.attributionTag, OP_MESSAGE);
enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity,
CAPTURE_AUDIO_HOTWORD, OP_MESSAGE);
});
diff --git a/telecomm/TEST_MAPPING b/telecomm/TEST_MAPPING
index 1963ff3cabc7..391dce1772f1 100644
--- a/telecomm/TEST_MAPPING
+++ b/telecomm/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"presubmit-large": [
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index 402b70b63204..93989b6744dc 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -89,6 +89,13 @@ public abstract class CallRedirectionService extends Service {
boolean allowInteractiveResponse);
/**
+ * Telecom calls this method when times out waiting for the {@link CallRedirectionService} to
+ * call {@link #placeCallUnmodified()}, {@link #redirectCall(Uri, PhoneAccountHandle, boolean)},
+ * or {@link #cancelCall()}
+ */
+ public void onRedirectionTimeout() {}
+
+ /**
* The implemented {@link CallRedirectionService} calls this method to response a request
* received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
* no changes are required to the outgoing call, and that the call should be placed as-is.
@@ -167,6 +174,12 @@ public abstract class CallRedirectionService extends Service {
private static final int MSG_PLACE_CALL = 1;
/**
+ * A handler message to process the attempt to notify the operation of redirection service timed
+ * out from Telecom
+ */
+ private static final int MSG_TIMEOUT = 2;
+
+ /**
* A handler to process the attempt to place call with redirection service from Telecom
*/
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -183,6 +196,9 @@ public abstract class CallRedirectionService extends Service {
args.recycle();
}
break;
+ case MSG_TIMEOUT:
+ onRedirectionTimeout();
+ break;
}
}
};
@@ -209,6 +225,15 @@ public abstract class CallRedirectionService extends Service {
args.arg4 = allowInteractiveResponse;
mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
}
+
+ /**
+ * Telecom calls this method to inform the CallRedirectionService of the timeout waiting for
+ * it to complete its operation.
+ */
+ @Override
+ public void notifyTimeout() {
+ mHandler.obtainMessage(MSG_TIMEOUT).sendToTarget();
+ }
}
@Override
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 759afd72d539..30fd52814685 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -775,6 +775,21 @@ public abstract class Connection extends Conferenceable {
"android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE";
/**
+ * The Telecom call ID of the conference an existing connection should be added to. This is
+ * required when {@link com.android.services.telephony.TelephonyConnectionService} adds a
+ * {@link Conference} to Telecom using the
+ * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection, Conference)}
+ * API. That API specifies a parent conference associated with the new existing connection
+ * being added, and there is no equivalent as part of the {@link RemoteConnectionService} API.
+ * This extra key is used to stack the ID of the conference to which the existing connection
+ * will be added so that Telecom can link it up correctly when the {@link RemoteConference}
+ * is added to Telecom by the connection manager.
+ * @hide
+ */
+ public static final String EXTRA_ADD_TO_CONFERENCE_ID =
+ "android.telecom.extra.ADD_TO_CONFERENCE_ID";
+
+ /**
* Extra key set from a {@link ConnectionService} when using the remote connection APIs
* (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle,
* ConnectionRequest, boolean)}) to create a remote connection. Provides the receiving
@@ -1765,11 +1780,13 @@ public abstract class Connection extends Conferenceable {
public abstract void onSetDeviceOrientation(int rotation);
/**
- * Sets camera zoom ratio.
+ * Sets the camera zoom ratio.
* <p>
* Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
*
- * @param value The camera zoom ratio.
+ * @param value The camera zoom ratio; for the current camera, should be a value in the
+ * range defined by
+ * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
*/
public abstract void onSetZoom(float value);
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index f20ee7e56d05..c365648db8f8 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -764,11 +764,13 @@ public abstract class InCallService extends Service {
public abstract void setDeviceOrientation(int rotation);
/**
- * Sets camera zoom ratio.
+ * Sets the camera zoom ratio.
* <p>
* Handled by {@link Connection.VideoProvider#onSetZoom(float)}.
*
- * @param value The camera zoom ratio.
+ * @param value The camera zoom ratio; for the current camera, should be a value in the
+ * range defined by
+ * {@link android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE}.
*/
public abstract void setZoom(float value);
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index a427ed612b31..02bd0010de99 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -139,6 +139,8 @@ public final class Phone {
*/
private final int mTargetSdkVersion;
+ private final Object mLock = new Object();
+
Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
mInCallAdapter = adapter;
mCallingPackage = callingPackage;
@@ -156,8 +158,12 @@ public final class Phone {
if (call == null) {
call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
- mCallByTelecomCallId.put(parcelableCall.getId(), call);
- mCalls.add(call);
+
+ synchronized (mLock) {
+ mCallByTelecomCallId.put(parcelableCall.getId(), call);
+ mCalls.add(call);
+ }
+
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
fireCallAdded(call);
@@ -169,8 +175,10 @@ public final class Phone {
}
final void internalRemoveCall(Call call) {
- mCallByTelecomCallId.remove(call.internalGetCallId());
- mCalls.remove(call);
+ synchronized (mLock) {
+ mCallByTelecomCallId.remove(call.internalGetCallId());
+ mCalls.remove(call);
+ }
InCallService.VideoCall videoCall = call.getVideoCall();
if (videoCall != null) {
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index e1bcb5fbdf00..e3485deb9080 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -34,7 +34,10 @@ import java.util.Objects;
* <ul>
* <li>The component name of the associated connection service.</li>
* <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
- * component name.</li>
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * {@link #getId()} provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the {@link PhoneAccountHandle}
+ * identifier.</li>
* </ul>
*
* Note: This Class requires a non-null {@link ComponentName} and {@link UserHandle} to operate
@@ -49,12 +52,35 @@ public final class PhoneAccountHandle implements Parcelable {
private final String mId;
private final UserHandle mUserHandle;
+ /**
+ * Creates a new {@link PhoneAccountHandle}.
+ *
+ * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+ * services this {@link PhoneAccountHandle}.
+ * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * ID provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the
+ * {@link PhoneAccountHandle} identifier.
+ */
public PhoneAccountHandle(
@NonNull ComponentName componentName,
@NonNull String id) {
this(componentName, id, Process.myUserHandle());
}
+ /**
+ * Creates a new {@link PhoneAccountHandle}.
+ *
+ * @param componentName The {@link ComponentName} of the {@link ConnectionService} which
+ * services this {@link PhoneAccountHandle}.
+ * @param id A string identifier that is unique across {@code PhoneAccountHandle}s with the same
+ * component name. Apps registering {@link PhoneAccountHandle}s should ensure that the
+ * ID provided does not expose personally identifying information. A
+ * {@link ConnectionService} should use an opaque token as the
+ * {@link PhoneAccountHandle} identifier.
+ * @param userHandle The {@link UserHandle} associated with this {@link PhoneAccountHandle}.
+ */
public PhoneAccountHandle(
@NonNull ComponentName componentName,
@NonNull String id,
@@ -80,17 +106,17 @@ public final class PhoneAccountHandle implements Parcelable {
* others supported by the connection service that created it.
* <p>
* A connection service must select identifiers that are stable for the lifetime of
- * their users' relationship with their service, across many Android devices. For example, a
- * good set of identifiers might be the email addresses with which with users registered for
- * their accounts with a particular service. Depending on how a service chooses to operate,
- * a bad set of identifiers might be an increasing series of integers
- * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
- * collide with values generated on other phones or after a data wipe of a given phone.
- *
+ * their users' relationship with their service, across many Android devices. The identifier
+ * should be a stable opaque token which uniquely identifies the user within the service.
+ * Depending on how a service chooses to operate, a bad set of identifiers might be an
+ * increasing series of integers ({@code 0}, {@code 1}, {@code 2}, ...) that are generated
+ * locally on each phone and could collide with values generated on other phones or after a data
+ * wipe of a given phone.
+ * <p>
* Important: A non-unique identifier could cause non-deterministic call-log backup/restore
* behavior.
*
- * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
+ * @return A service-specific unique opaque identifier for this {@code PhoneAccountHandle}.
*/
public String getId() {
return mId;
@@ -157,7 +183,8 @@ public final class PhoneAccountHandle implements Parcelable {
}
}
- public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR = new Creator<PhoneAccountHandle>() {
+ public static final @android.annotation.NonNull Creator<PhoneAccountHandle> CREATOR =
+ new Creator<PhoneAccountHandle>() {
@Override
public PhoneAccountHandle createFromParcel(Parcel in) {
return new PhoneAccountHandle(in);
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bf6a6ef793ff..efe35d21c003 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -239,13 +239,9 @@ final class RemoteConnectionService {
conference.addConnection(c);
}
}
- if (conference.getConnections().size() == 0) {
- // A conference was created, but none of its connections are ones that have been
- // created by, and therefore being tracked by, this remote connection service. It
- // is of no interest to us.
- Log.d(this, "addConferenceCall - skipping");
- return;
- }
+ // We used to skip adding empty conferences; however in the world of IMS conference
+ // calls we need to add them to the remote connection service because they will always
+ // start with no participants.
conference.setState(parcel.getState());
conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
@@ -379,6 +375,8 @@ final class RemoteConnectionService {
@Override
public void addExistingConnection(String callId, ParcelableConnection connection,
Session.Info sessionInfo) {
+ Log.i(RemoteConnectionService.this, "addExistingConnection: callId=%s, conn=%s", callId,
+ connection);
String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
getOpPackageName();
int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
@@ -390,6 +388,20 @@ final class RemoteConnectionService {
Bundle newExtras = new Bundle();
newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
connection.getPhoneAccount());
+ if (connection.getParentCallId() != null) {
+ RemoteConference parentConf = mConferenceById.get(connection.getParentCallId());
+ // If there is a parent being set, we need to stash the conference ID here.
+ // Telephony can add an existing connection while specifying a parent conference.
+ // There is no equivalent version of that operation as part of the remote connection
+ // API, so we will stash the pre-defined parent's ID in the extras. When the
+ // connectionmanager copies over the extras from the remote connection to the
+ // actual one, it'll get passed to Telecom so that it can make the association.
+ if (parentConf != null) {
+ newExtras.putString(Connection.EXTRA_ADD_TO_CONFERENCE_ID, parentConf.getId());
+ Log.i(this, "addExistingConnection: stash parent of %s as %s",
+ connection.getParentCallId(), parentConf.getId());
+ }
+ }
remoteConnection.putExtras(newExtras);
mConnectionById.put(callId, remoteConnection);
remoteConnection.registerCallback(new RemoteConnection.Callback() {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e000265f0a2c..abbce1cd869d 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2430,7 +2430,8 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.isIncomingCallPermitted(phoneAccountHandle);
+ return service.isIncomingCallPermitted(phoneAccountHandle,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error isIncomingCallPermitted", e);
}
@@ -2463,7 +2464,8 @@ public class TelecomManager {
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.isOutgoingCallPermitted(phoneAccountHandle);
+ return service.isOutgoingCallPermitted(phoneAccountHandle,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error isOutgoingCallPermitted", e);
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
index c1bc44007b0b..ce1938b81727 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -31,4 +31,6 @@ import com.android.internal.telecom.ICallRedirectionAdapter;
oneway interface ICallRedirectionService {
void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse);
+
+ void notifyTimeout();
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 6f286d9f3006..a75f79caeee0 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -306,12 +306,14 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#isIncomingCallPermitted
*/
- boolean isIncomingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
+ boolean isIncomingCallPermitted(in PhoneAccountHandle phoneAccountHandle,
+ String callingPackage);
/**
* @see TelecomServiceImpl#isOutgoingCallPermitted
*/
- boolean isOutgoingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
+ boolean isOutgoingCallPermitted(in PhoneAccountHandle phoneAccountHandle,
+ String callingPackage);
/**
* @see TelecomServiceImpl#waitOnHandler
diff --git a/telephony/TEST_MAPPING b/telephony/TEST_MAPPING
index d58566673eec..02d4eb36221c 100644
--- a/telephony/TEST_MAPPING
+++ b/telephony/TEST_MAPPING
@@ -23,6 +23,46 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsTelephony2TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonySdk28TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephony3TestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsSimRestrictedApisTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsTelephonyProviderTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/telephony/common/Android.bp b/telephony/common/Android.bp
index 201ab530fe86..1cacc0365fe1 100644
--- a/telephony/common/Android.bp
+++ b/telephony/common/Android.bp
@@ -19,6 +19,14 @@ filegroup {
],
}
+filegroup {
+ name: "framework-mms-shared-srcs",
+ visibility: ["//packages/apps/Bluetooth"],
+ srcs: [
+ "com/google/android/mms/**/*.java",
+ ],
+}
+
genrule {
name: "statslog-telephony-common-java-gen",
tools: ["stats-log-api-gen"],
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index 125296540688..3c799e0bfeea 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -26,7 +26,6 @@ import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.telephony.TelephonyManager;
@@ -74,11 +73,6 @@ public final class TelephonyUtils {
return cur == null ? Collections.emptyList() : cur;
}
- /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
- public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
- throw new RuntimeException(remoteException);
- }
-
/**
* Returns a {@link ComponentInfo} from the {@link ResolveInfo},
* or throws an {@link IllegalStateException} if not available.
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index a1c5bbefbbe1..ae597e02f33c 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -19,6 +19,7 @@ package android.telephony;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.RadioAccessSpecifier;
import java.util.ArrayList;
import java.util.Arrays;
@@ -76,13 +77,28 @@ public final class AvailableNetworkInfo implements Parcelable {
* Opportunistic network service will use these bands to scan.
*
* When no specific bands are specified (empty array or null) CBRS band
- * {@link AccessNetworkConstants.EutranBand.BAND_48} will be used for network scan.
+ * {@link AccessNetworkConstants.EutranBand.BAND_48
+ * } will be used for network scan.
*
* See {@link AccessNetworkConstants} for details.
+ *
+ * @deprecated use {@link #mRadioAccessSpecifiers} instead
*/
+ @Deprecated
private ArrayList<Integer> mBands;
/**
+ * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+ * Opportunistic network service will use this to determine which bands to scan for.
+ *
+ * If this entry is left empty, {@link RadioAcccessSpecifier}s with {@link AccessNetworkType}s
+ * of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
+ * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
+ * by Opportunistic network service.
+ */
+ private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers;
+
+ /**
* Return subscription Id of the available network.
* This value must be one of the entry retrieved from
* {@link SubscriptionManager#getOpportunisticSubscriptions}
@@ -129,6 +145,22 @@ public final class AvailableNetworkInfo implements Parcelable {
return (List<Integer>) mBands.clone();
}
+ /**
+ * Returns a list of {@link RadioAccessSpecifier} associated with the available network.
+ * Opportunistic network service will use this to determine which bands to scan for.
+ *
+ * the returned value is one of {@link AccessNetworkConstants.AccessNetworkType}. When no
+ * specific access network type is specified, {@link RadioAccessSpecifier}s with {@link
+ * AccessNetworkType}s of {@link AccessNetworkConstants.AccessNetworkType.EUTRAN} and {@link
+ * AccessNetworkConstants.AccessNetworkType.NGRAN} with bands 48 and 71 on each will be assumed
+ * by Opportunistic network service.
+ * @return the access network type associated with the available network.
+ * @hide
+ */
+ public List<RadioAccessSpecifier> getRadioAccessSpecifiers() {
+ return (List<RadioAccessSpecifier>) mRadioAccessSpecifiers.clone();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -140,6 +172,7 @@ public final class AvailableNetworkInfo implements Parcelable {
dest.writeInt(mPriority);
dest.writeStringList(mMccMncs);
dest.writeList(mBands);
+ dest.writeList(mRadioAccessSpecifiers);
}
private AvailableNetworkInfo(Parcel in) {
@@ -149,14 +182,25 @@ public final class AvailableNetworkInfo implements Parcelable {
in.readStringList(mMccMncs);
mBands = new ArrayList<>();
in.readList(mBands, Integer.class.getClassLoader());
+ mRadioAccessSpecifiers = new ArrayList<>();
+ in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader());
}
public AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
@NonNull List<Integer> bands) {
+ this(subId, priority, mccMncs, bands,
+ new ArrayList<RadioAccessSpecifier>());
+ }
+
+ /** @hide */
+ private AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs,
+ @NonNull List<Integer> bands, @NonNull List<RadioAccessSpecifier>
+ radioAccessSpecifiers) {
mSubId = subId;
mPriority = priority;
mMccMncs = new ArrayList<String>(mccMncs);
mBands = new ArrayList<Integer>(bands);
+ mRadioAccessSpecifiers = new ArrayList<RadioAccessSpecifier>(radioAccessSpecifiers);
}
@Override
@@ -177,12 +221,13 @@ public final class AvailableNetworkInfo implements Parcelable {
&& mPriority == ani.mPriority
&& (((mMccMncs != null)
&& mMccMncs.equals(ani.mMccMncs)))
- && mBands.equals(ani.mBands));
+ && mBands.equals(ani.mBands))
+ && mRadioAccessSpecifiers.equals(ani.getRadioAccessSpecifiers());
}
@Override
public int hashCode() {
- return Objects.hash(mSubId, mPriority, mMccMncs, mBands);
+ return Objects.hash(mSubId, mPriority, mMccMncs, mBands, mRadioAccessSpecifiers);
}
public static final @android.annotation.NonNull Parcelable.Creator<AvailableNetworkInfo> CREATOR =
@@ -204,6 +249,72 @@ public final class AvailableNetworkInfo implements Parcelable {
+ " mSubId: " + mSubId
+ " mPriority: " + mPriority
+ " mMccMncs: " + Arrays.toString(mMccMncs.toArray())
- + " mBands: " + Arrays.toString(mBands.toArray()));
+ + " mBands: " + Arrays.toString(mBands.toArray())
+ + " mRadioAccessSpecifiers: " + Arrays.toString(mRadioAccessSpecifiers.toArray()));
+ }
+
+ /**
+ * Provides a convenient way to set the fields of a {@link AvailableNetworkInfo} when
+ * creating a new instance.
+ *
+ * <p>The example below shows how you might create a new {@code AvailableNetworkInfo}:
+ *
+ * <pre><code>
+ *
+ * AvailableNetworkInfo aNI = new AvailableNetworkInfo.Builder()
+ * .setSubId(1)
+ * .setPriority(AvailableNetworkInfo.PRIORITY_MED)
+ * .build();
+ * </code></pre>
+ *
+ * @hide
+ */
+ public static final class Builder {
+ private int mSubId = Integer.MIN_VALUE;
+ private int mPriority = AvailableNetworkInfo.PRIORITY_LOW;
+ private ArrayList<String> mMccMncs = new ArrayList<>();
+ private ArrayList<Integer> mBands = new ArrayList<>();
+ private ArrayList<RadioAccessSpecifier> mRadioAccessSpecifiers = new ArrayList<>();
+
+ public @NonNull Builder setSubId(int subId) {
+ mSubId = subId;
+ return this;
+ }
+
+ public @NonNull Builder setPriority(int priority) {
+ if (priority > AvailableNetworkInfo.PRIORITY_LOW
+ || priority < AvailableNetworkInfo.PRIORITY_HIGH) {
+ throw new IllegalArgumentException("A valid priority must be set");
+ }
+ mPriority = priority;
+ return this;
+ }
+
+ public @NonNull Builder setMccMncs(@NonNull ArrayList<String> mccMncs) {
+ Objects.requireNonNull(mccMncs, "A non-null ArrayList of mccmncs must be set. An empty "
+ + "list is still accepted. Please read documentation in "
+ + "AvailableNetworkService to see consequences of an empty Arraylist.");
+ mMccMncs = mccMncs;
+ return this;
+ }
+
+ public @NonNull Builder setRadioAccessSpecifiers(
+ @NonNull ArrayList<RadioAccessSpecifier> radioAccessSpecifiers) {
+ Objects.requireNonNull(radioAccessSpecifiers, "A non-null ArrayList of "
+ + "RadioAccessSpecifiers must be set. An empty list is still accepted. Please "
+ + "read documentation in AvailableNetworkService to see consequences of an "
+ + "empty Arraylist.");
+ mRadioAccessSpecifiers = radioAccessSpecifiers;
+ return this;
+ }
+
+ public @NonNull AvailableNetworkInfo build() {
+ if (mSubId == Integer.MIN_VALUE) {
+ throw new IllegalArgumentException("A valid subId must be set");
+ }
+
+ return new AvailableNetworkInfo(mSubId, mPriority, mMccMncs, mBands,
+ mRadioAccessSpecifiers);
+ }
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 72ad23b8edb1..0e4c44eb7d42 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -490,6 +490,12 @@ public class CarrierConfigManager {
/** Used in Cellular Network Settings for preferred network type. */
public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+ /**
+ * Used in Cellular Network Settings for preferred network type to show 4G only mode.
+ * @hide
+ */
+ public static final String KEY_4G_ONLY_BOOL = "4g_only_bool";
+
/** Show cdma network mode choices 1x, 3G, global etc. */
public static final String KEY_SHOW_CDMA_CHOICES_BOOL = "show_cdma_choices_bool";
@@ -3751,6 +3757,109 @@ public class CarrierConfigManager {
"opportunistic_network_max_backoff_time_long";
/**
+ * Controls SS-RSRP threshold in dBm at which 5G opportunistic network will be considered good
+ * enough for internet data.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT =
+ "opportunistic_network_entry_threshold_ss_rsrp_int";
+
+ /**
+ * Controls SS-RSRQ threshold in dB at which 5G opportunistic network will be considered good
+ * enough for internet data.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE =
+ "opportunistic_network_entry_threshold_ss_rsrq_double";
+
+ /**
+ * Controls SS-RSRP threshold in dBm below which 5G opportunistic network available will not
+ * be considered good enough for internet data.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT =
+ "opportunistic_network_exit_threshold_ss_rsrp_int";
+
+ /**
+ * Controls SS-RSRQ threshold in dB below which 5G opportunistic network available will not
+ * be considered good enough for internet data.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
+ "opportunistic_network_exit_threshold_ss_rsrq_double";
+
+ /**
+ * Controls back off time in milliseconds for switching back to
+ * 5G opportunistic subscription. This time will be added to
+ * {@link CarrierConfigManager#KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG} to
+ * determine hysteresis time if there is ping pong situation
+ * (determined by system app or 1st party app) between primary and 5G opportunistic
+ * subscription. Ping ping situation is defined in
+ * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG.
+ * If ping pong situation continuous #KEY_OPPORTUNISTIC_5G_NETWORK_BACKOFF_TIME_LONG
+ * will be added to previously determined hysteresis time.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG =
+ "opportunistic_network_5g_backoff_time_long";
+
+ /**
+ * Controls the max back off time in milliseconds for switching back to
+ * 5G opportunistic subscription.
+ * This time will be the max hysteresis that can be determined irrespective of there is
+ * continuous ping pong situation or not as described in
+ * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG and
+ * #KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG =
+ "opportunistic_network_5g_max_backoff_time_long";
+
+ /**
+ * Controls the ping pong determination of 5G opportunistic network.
+ * If opportunistic network is determined as out of service or below
+ * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT or
+ * #KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_INT within
+ * #KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG of switching to opportunistic network,
+ * it will be determined as ping pong situation by system app or 1st party app.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG =
+ "opportunistic_network_5g_ping_pong_time_long";
+
+ /**
+ * Controls hysteresis time in milliseconds for which will be waited before switching
+ * data to a 5G opportunistic network.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG =
+ "opportunistic_network_5g_data_switch_hysteresis_time_long";
+
+ /**
+ * Controls hysteresis time in milliseconds for which will be waited before switching from
+ * 5G opportunistic network to primary network.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG =
+ "opportunistic_network_5g_data_switch_exit_hysteresis_time_long";
+ /**
+ * Controls whether 4G opportunistic networks should be scanned for possible data switch.
+ *
+ * @hide
+ */
+ public static final String KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL =
+ "enabled_4g_opportunistic_network_scan_bool";
+
+ /**
* Indicates zero or more emergency number prefix(es), because some carrier requires
* if users dial an emergency number address with a specific prefix, the combination of the
* prefix and the address is also a valid emergency number to dial. For example, an emergency
@@ -5154,6 +5263,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
sDefaults.putBoolean(KEY_PREFER_2G_BOOL, true);
+ sDefaults.putBoolean(KEY_4G_ONLY_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false);
sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false);
@@ -5581,7 +5691,25 @@ public class CarrierConfigManager {
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_BACKOFF_TIME_LONG, 10000);
/* Default value is 60 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
- sDefaults.putAll(ImsServiceEntitlement.getDefaults());
+ /* Default value is -111 dBm. */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRP_INT, -111);
+ /* Default value is -18.5 dB. */
+ sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+ /* Default value is -120 dBm. */
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRP_INT, -120);
+ /* Default value is -18.5 dB. */
+ sDefaults.putDouble(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_SS_RSRQ_DOUBLE, -18.5);
+ /* Default value is 10 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_BACKOFF_TIME_LONG, 10000);
+ /* Default value is 60 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_MAX_BACKOFF_TIME_LONG, 60000);
+ /* Default value is 60 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_PING_PONG_TIME_LONG, 60000);
+ /* Default value is 2 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_HYSTERESIS_TIME_LONG, 2000);
+ /* Default value is 2 seconds. */
+ sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_5G_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 2000);
+ sDefaults.putBoolean(KEY_ENABLE_4G_OPPORTUNISTIC_NETWORK_SCAN_BOOL, true);
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d745dc215f34..a51b5c146d6b 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1407,7 +1407,7 @@ public class ServiceState implements Parcelable {
m.putString("data-operator-numeric", mOperatorNumeric);
m.putBoolean("manual", mIsManualNetworkSelection);
m.putInt("radioTechnology", getRilVoiceRadioTechnology());
- m.putInt("dataRadioTechnology", getRadioTechnology());
+ m.putInt("dataRadioTechnology", getRilDataRadioTechnology());
m.putBoolean("cssIndicator", mCssIndicator);
m.putInt("networkId", mNetworkId);
m.putInt("systemId", mSystemId);
@@ -1546,17 +1546,6 @@ public class ServiceState implements Parcelable {
}
/**
- * @hide
- * @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or
- * {@link #getRilVoiceRadioTechnology}
- */
- @UnsupportedAppUsage
- public int getRadioTechnology() {
- Rlog.e(LOG_TAG, "ServiceState.getRadioTechnology() DEPRECATED will be removed *******");
- return getRilDataRadioTechnology();
- }
-
- /**
* Transform RIL radio technology {@link RilRadioTechnology} value to Network
* type {@link NetworkType}.
*
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 5171cf9dcea7..a0b8970ee56c 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -774,37 +774,6 @@ public final class SmsManager {
}
/**
- * Send a text based SMS without writing it into the SMS Provider.
- *
- * <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
- * privileges.
- * </p>
- *
- * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
- * applications or the Telephony framework and will never trigger an SMS disambiguation
- * dialog. If this method is called on a device that has multiple active subscriptions, this
- * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
- * default subscription is defined, the subscription ID associated with this message will be
- * INVALID, which will result in the SMS being sent on the subscription associated with logical
- * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the
- * correct subscription.
- * </p>
- *
- * @see #sendTextMessage(String, String, String, PendingIntent,
- * PendingIntent, int, boolean, int)
- * @hide
- */
- @UnsupportedAppUsage
- public void sendTextMessageWithoutPersisting(
- String destinationAddress, String scAddress, String text,
- PendingIntent sentIntent, PendingIntent deliveryIntent, int priority,
- boolean expectMore, int validityPeriod) {
- sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent,
- false /* persistMessage */, priority, expectMore, validityPeriod);
- }
-
- /**
*
* Inject an SMS PDU into the android application framework.
*
@@ -2666,7 +2635,8 @@ public final class SmsManager {
* @throws IllegalArgumentException if contentUri is empty
*/
public void sendMultimediaMessage(@NonNull Context context, @NonNull Uri contentUri,
- @Nullable String locationUrl, @Nullable Bundle configOverrides,
+ @Nullable String locationUrl,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent sentIntent, long messageId) {
if (contentUri == null) {
throw new IllegalArgumentException("Uri contentUri null");
@@ -2742,7 +2712,8 @@ public final class SmsManager {
* @throws IllegalArgumentException if locationUrl or contentUri is empty
*/
public void downloadMultimediaMessage(@NonNull Context context, @NonNull String locationUrl,
- @NonNull Uri contentUri, @Nullable Bundle configOverrides,
+ @NonNull Uri contentUri,
+ @SuppressWarnings("NullableCollection") @Nullable Bundle configOverrides,
@Nullable PendingIntent downloadedIntent, long messageId) {
if (TextUtils.isEmpty(locationUrl)) {
throw new IllegalArgumentException("Empty MMS location URL");
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 2d50e08ab922..63a7acfb5142 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -115,9 +116,9 @@ public class SubscriptionInfo implements Parcelable {
private int mDataRoaming;
/**
- * SIM Icon bitmap
+ * SIM icon bitmap cache
*/
- private Bitmap mIconBitmap;
+ @Nullable private Bitmap mIconBitmap;
/**
* Mobile Country Code
@@ -405,6 +406,10 @@ public class SubscriptionInfo implements Parcelable {
* @return A bitmap icon for this {@code SubscriptionInfo}.
*/
public Bitmap createIconBitmap(Context context) {
+ if (mIconBitmap == null) {
+ mIconBitmap = BitmapFactory.decodeResource(context.getResources(),
+ com.android.internal.R.drawable.ic_sim_card_multi_24px_clr);
+ }
int width = mIconBitmap.getWidth();
int height = mIconBitmap.getHeight();
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
@@ -774,7 +779,6 @@ public class SubscriptionInfo implements Parcelable {
String mcc = source.readString();
String mnc = source.readString();
String countryIso = source.readString();
- Bitmap iconBitmap = source.readParcelable(Bitmap.class.getClassLoader());
boolean isEmbedded = source.readBoolean();
UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
String cardString = source.readString();
@@ -793,10 +797,10 @@ public class SubscriptionInfo implements Parcelable {
boolean areUiccApplicationsEnabled = source.readBoolean();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
- carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
- countryIso, isEmbedded, nativeAccessRules, cardString, cardId, isOpportunistic,
- groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner,
- carrierConfigAccessRules, areUiccApplicationsEnabled);
+ carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
+ mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
+ isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
+ groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -821,7 +825,7 @@ public class SubscriptionInfo implements Parcelable {
dest.writeString(mMcc);
dest.writeString(mMnc);
dest.writeString(mCountryIso);
- dest.writeParcelable(mIconBitmap, flags);
+ // Do not write mIconBitmap since it should be lazily loaded on first usage
dest.writeBoolean(mIsEmbedded);
dest.writeTypedArray(mNativeAccessRules, flags);
dest.writeString(mCardString);
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 88c66acdca6f..f4e2ade643c7 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -79,7 +79,7 @@ public final class TelephonyDisplayInfo implements Parcelable {
* <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
+ * 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+"
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 255a61266ebf..412c02cc2719 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -8512,13 +8512,13 @@ public class TelephonyManager {
/**
* Get the PLMN chosen for Manual Network Selection if active.
- * Return null string if in automatic selection.
+ * Return empty string if in automatic selection.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
* (see {@link #hasCarrierPrivileges})
*
- * @return manually selected network info on success or null string on failure
+ * @return manually selected network info on success or empty string on failure
*/
@SuppressAutoDoc // No support carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -8531,7 +8531,7 @@ public class TelephonyManager {
} catch (RemoteException ex) {
Rlog.e(TAG, "getManualNetworkSelectionPlmn RemoteException", ex);
}
- return null;
+ return "";
}
/**
@@ -11115,14 +11115,10 @@ public class TelephonyManager {
@UnsupportedAppUsage
public int getSubIdForPhoneAccount(@Nullable PhoneAccount phoneAccount) {
int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- retval = service.getSubIdForPhoneAccount(phoneAccount);
- }
- } catch (RemoteException e) {
+ if (phoneAccount != null
+ && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ retval = getSubscriptionId(phoneAccount.getAccountHandle());
}
-
return retval;
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 018dabfdb552..4fc777668b66 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1354,11 +1354,6 @@ interface ITelephony {
String callingFeatureId);
/**
- * Returns the subscription ID associated with the specified PhoneAccount.
- */
- int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
-
- /**
* Returns the subscription ID associated with the specified PhoneAccountHandle.
*/
int getSubIdForPhoneAccountHandle(in PhoneAccountHandle phoneAccountHandle,
diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml
index 7633d5283f5e..ab5728e75b9f 100644
--- a/tests/BatteryStatsPerfTest/AndroidManifest.xml
+++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml
@@ -20,6 +20,8 @@
<application>
<uses-library android:name="android.test.runner" />
+ <service android:name="com.android.internal.os.BatteryUsageStatsPerfTest$BatteryUsageStatsService"
+ android:process=":BatteryUsageStatsService" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 54d70478f762..2e4b6da3ba13 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -18,13 +18,25 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import android.app.Service;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.BatteryConsumer;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Binder;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -54,7 +66,8 @@ public class BatteryUsageStatsPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+ BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build());
state.pauseTiming();
@@ -71,4 +84,118 @@ public class BatteryUsageStatsPerfTest {
state.resumeTiming();
}
}
+
+ private final ConditionVariable mServiceConnected = new ConditionVariable();
+ private IBinder mService;
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = service;
+ mServiceConnected.open();
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ /**
+ * Measures the performance of transferring BatteryUsageStats over a Binder.
+ */
+ @Test
+ public void testBatteryUsageStatsTransferOverBinder() throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ context.bindService(
+ new Intent(context, BatteryUsageStatsService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ mServiceConnected.block(30000);
+ assertThat(mService).isNotNull();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ mService.transact(42, data, reply, 0);
+ final BatteryUsageStats batteryUsageStats =
+ BatteryUsageStats.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+
+ state.pauseTiming();
+
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getUidBatteryConsumers()).hasSize(1000);
+ final UidBatteryConsumer uidBatteryConsumer =
+ batteryUsageStats.getUidBatteryConsumers().get(0);
+ assertThat(uidBatteryConsumer.getConsumedPower(1)).isEqualTo(123);
+
+ state.resumeTiming();
+ }
+
+ context.unbindService(mConnection);
+ }
+
+ /* This service runs in a separate process */
+ public static class BatteryUsageStatsService extends Service {
+ private final BatteryUsageStats mBatteryUsageStats;
+
+ public BatteryUsageStatsService() {
+ mBatteryUsageStats = buildBatteryUsageStats();
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder() {
+ @Override
+ protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+ int flags) {
+ mBatteryUsageStats.writeToParcel(reply, 0);
+ return true;
+ }
+ };
+ }
+ }
+
+ private static BatteryUsageStats buildBatteryUsageStats() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
+ .setBatteryCapacity(4000)
+ .setDischargePercentage(20)
+ .setDischargedPowerRange(1000, 2000)
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(123)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+
+ for (int i = 0; i < 1000; i++) {
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(i)
+ .setPackageWithHighestDrain("example.packagename" + i)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+ }
+
+ consumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+ }
+ return builder.build();
+ }
}
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
index 7ee0d9a5e77e..57303e748738 100644
--- a/tests/BootImageProfileTest/OWNERS
+++ b/tests/BootImageProfileTest/OWNERS
@@ -1,4 +1,4 @@
calin@google.com
-mathieuc@google.com
ngeoffray@google.com
+vmarko@google.com
yawanng@google.com
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 4ecca2dc4c39..cf5658644a61 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -31,6 +31,8 @@ public class BootImageProfileTest implements IDeviceTest {
private static final String SYSTEM_SERVER_PROFILE =
"/data/misc/profiles/cur/0/android/primary.prof";
private static final boolean USE_PHENOTYPE = false;
+ private static final String DALVIK_VM_EXTRA_OPTS =
+ "-Xusejit:false -Xint -Xjitsaveprofilinginfo";
@Override
public void setDevice(ITestDevice testDevice) {
@@ -54,10 +56,10 @@ public class BootImageProfileTest implements IDeviceTest {
private String setProperty(String property, String value) throws Exception {
if (USE_PHENOTYPE) {
return mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot " + property + " " + value);
+ String.format("device_config put runtime_native_boot %s '%s'", property, value));
} else {
return mTestDevice.executeShellCommand(
- "setprop dalvik.vm." + property + " " + value);
+ String.format("setprop dalvik.vm.%s '%s'", property, value));
}
}
@@ -69,6 +71,8 @@ public class BootImageProfileTest implements IDeviceTest {
assertTrue("profile boot class path not enabled: " + res, "true".equals(res));
res = getProperty("profilesystemserver");
assertTrue("profile system server not enabled: " + res, "true".equals(res));
+ res = getProperty("extra-opts");
+ assertTrue("extra options not set: " + res, DALVIK_VM_EXTRA_OPTS.equals(res));
}
private boolean forceSaveProfile(String pkg) throws Exception {
@@ -91,16 +95,20 @@ public class BootImageProfileTest implements IDeviceTest {
boolean profileBootClassPath = "true".equals(pbcp);
String pss = getProperty("profilesystemserver");
boolean profileSystemServer = "true".equals(pss);
- if (profileBootClassPath && profileSystemServer) {
+ String extraOpts = getProperty("extra-opts");
+ boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts);
+ if (profileBootClassPath && profileSystemServer && extraOptsOk) {
break;
}
if (i == numIterations) {
assertTrue("profile system server not enabled: " + pss, profileSystemServer);
assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ assertTrue("extra options not set: " + extraOpts, extraOptsOk);
}
setProperty("profilebootclasspath", "true");
setProperty("profilesystemserver", "true");
+ setProperty("extra-opts", DALVIK_VM_EXTRA_OPTS);
Thread.sleep(1000);
}
@@ -114,12 +122,15 @@ public class BootImageProfileTest implements IDeviceTest {
boolean profileBootClassPath = "true".equals(pbcp);
String pss = getProperty("profilesystemserver");
boolean profileSystemServer = "true".equals(pss);
+ String extraOpts = getProperty("extra-opts");
+ boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts);
if (profileBootClassPath && profileSystemServer) {
break;
}
if (i == numIterations) {
assertTrue("profile system server not enabled: " + pss, profileSystemServer);
assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ assertTrue("extra options not set: " + extraOpts, extraOptsOk);
}
Thread.sleep(1000);
}
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 217a72b90fd4..7731e098d9f5 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -25,11 +25,17 @@ package {
android_test {
name: "FlickerTests",
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
manifest: "AndroidManifest.xml",
test_config: "AndroidTest.xml",
platform_apis: true,
certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
test_suites: ["device-tests"],
libs: ["android.test.runner"],
static_libs: [
@@ -46,6 +52,9 @@ android_test {
java_library {
name: "wm-flicker-common-assertions",
platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
srcs: [
"src/**/*Assertions.java",
"src/**/*Assertions.kt",
@@ -56,20 +65,23 @@ java_library {
static_libs: [
"flickerlib",
"truth-prebuilt",
- "app-helpers-core"
+ "app-helpers-core",
],
}
java_library {
name: "wm-flicker-common-app-helpers",
platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
srcs: [
- "**/helpers/*"
+ "**/helpers/*",
],
static_libs: [
"flickerlib",
"flickertestapplib",
"truth-prebuilt",
- "app-helpers-core"
+ "app-helpers-core",
],
-} \ 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 a540dffb3c9c..08c9e5dc7b64 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -14,77 +14,30 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker
-import android.platform.helpers.IAppHelper
+import android.content.ComponentName
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
+val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+ "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
-fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.statusBarWindowIsVisible() {
assertWm {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+ this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
}
}
-fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.navBarWindowIsVisible() {
assertWm {
- this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
- }
-}
-
-fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesVisible() {
- assertWm {
- this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
- .then()
- .showsBelowAppWindow(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesInvisible() {
- assertWm {
- this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
- .then()
- .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) {
- assertWm {
- this.showsAppWindowOnTop(packageName)
- }
-}
-
-fun FlickerTestParameter.appWindowBecomesVisible(appName: String) {
- assertWm {
- this.hidesAppWindow(appName)
- .then()
- .showsAppWindow(appName)
- }
-}
-
-fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) {
- assertWm {
- this.showsAppWindow(appName)
- .then()
- .hidesAppWindow(appName)
+ this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
@JvmOverloads
-fun FlickerTestParameter.noUncoveredRegions(
+fun FlickerTestParameter.entireScreenCovered(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true
@@ -111,37 +64,21 @@ fun FlickerTestParameter.noUncoveredRegions(
}
}
-@JvmOverloads
-fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(NAV_BAR_LAYER_NAME)
- .then()
- .isInvisible(NAV_BAR_LAYER_NAME)
- .then()
- .isVisible(NAV_BAR_LAYER_NAME)
- }
- } else {
- assertLayers {
- this.isVisible(NAV_BAR_LAYER_NAME)
- }
+fun FlickerTestParameter.navBarLayerIsVisible() {
+ assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ assertLayersEnd {
+ this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
}
}
-@JvmOverloads
-fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(STATUS_BAR_LAYER_NAME)
- .then()
- .isInvisible(STATUS_BAR_LAYER_NAME)
- .then()
- .isVisible(STATUS_BAR_LAYER_NAME)
- }
- } else {
- assertLayers {
- this.isVisible(STATUS_BAR_LAYER_NAME)
- }
+fun FlickerTestParameter.statusBarLayerIsVisible() {
+ assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ assertLayersEnd {
+ this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
}
}
@@ -154,10 +91,10 @@ fun FlickerTestParameter.navBarLayerRotatesAndScales(
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
+ this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
+ this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
}
}
@@ -170,54 +107,46 @@ fun FlickerTestParameter.statusBarLayerRotatesScales(
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(startingPos)
+ this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(endingPos)
- }
-}
-
-fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
- assertLayers {
- this.isVisible(*HOME_WINDOW_TITLE)
- .then()
- .isVisible(appName)
+ this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
}
}
-fun FlickerTestParameter.launcherLayerReplacesApp(testApp: IAppHelper) {
+/**
+ * Asserts that:
+ * [originalLayer] is visible at the start of the trace
+ * [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
+ * becomes visible
+ * [newLayer] remains visible until the end of the trace
+ *
+ * @param originalLayer Layer that should be visible at the start
+ * @param newLayer Layer that should be visible at the end
+ * @param ignoreSnapshot If the snapshot layer should be ignored during the transition
+ * (useful mostly for app launch)
+ */
+fun FlickerTestParameter.replacesLayer(
+ originalLayer: ComponentName,
+ newLayer: ComponentName,
+ ignoreSnapshot: Boolean = false
+) {
assertLayers {
- this.isVisible(testApp.getPackage())
- .then()
- .isInvisible(testApp.getPackage())
- .isVisible(*HOME_WINDOW_TITLE)
+ val assertion = this.isVisible(originalLayer)
+ if (ignoreSnapshot) {
+ assertion.then()
+ .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ }
+ assertion.then().isVisible(newLayer)
}
-}
-fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
- assertLayers {
- this.isInvisible(packageName)
- .then()
- .isVisible(packageName)
- }
-}
-
-fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
- assertLayers {
- this.isVisible(packageName)
- .then()
- .isInvisible(packageName)
+ assertLayersStart {
+ this.isVisible(originalLayer)
+ .isInvisible(newLayer)
}
-}
-fun FlickerTestParameter.focusChanges(vararg windows: String) {
- assertEventLog {
- this.focusChanges(windows)
+ assertLayersEnd {
+ this.isInvisible(originalLayer)
+ .isVisible(newLayer)
}
}
-
-fun FlickerTestParameter.focusDoesNotChange() {
- assertEventLog {
- this.focusDoesNotChange()
- }
-} \ No newline at end of file
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 71184c2e0aa2..90c851d6e266 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
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2020 The Android Open Source Project
*
@@ -16,6 +17,8 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,6 +50,13 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Postsubmit
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
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 6786279ae107..e8391ed9cfa1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
+import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -46,6 +49,13 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
}
}
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Postsubmit
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index f7f977d7bd0a..f9e6babee938 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,30 +19,35 @@ package com.android.server.wm.flicker.close
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
import android.view.Surface
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherLayerReplacesApp
-import com.android.server.wm.flicker.launcherWindowBecomesVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.replacesLayer
import org.junit.Test
+/**
+ * Base test class for transitions that close an app back to the launcher screen
+ */
abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ /**
+ * Specification of the test transition to execute
+ */
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
setup {
eachRun {
@@ -66,29 +71,29 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -118,25 +123,33 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
}
@Presubmit
@Test
open fun launcherReplacesAppWindowAsTopWindow() {
- testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ .then()
+ .isAppWindowOnTop(LAUNCHER_COMPONENT)
+ }
}
@Presubmit
@Test
open fun launcherWindowBecomesVisible() {
- testSpec.launcherWindowBecomesVisible()
+ testSpec.assertWm {
+ this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowOnTop(LAUNCHER_COMPONENT)
+ }
}
@Presubmit
@Test
open fun launcherLayerReplacesApp() {
- testSpec.launcherLayerReplacesApp(testApp)
+ testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 83fddae5b1a7..d224af97462e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,8 @@ open class ImeAppHelper @JvmOverloads constructor(
if (wmHelper == null) {
device.waitForIdle()
} else {
- wmHelper.waitImeWindowShown()
+ wmHelper.waitImeShown()
+ wmHelper.waitForAppTransitionIdle()
}
}
@@ -78,7 +79,7 @@ open class ImeAppHelper @JvmOverloads constructor(
if (wmHelper == null) {
device.waitForIdle()
} else {
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
new file mode 100644
index 000000000000..3074e28b43fa
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class NonResizeableAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
+ component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME,
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
new file mode 100644
index 000000000000..19fefb93b487
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class TwoActivitiesAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
+ component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME,
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+ fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ val button = device.wait(
+ Until.findObject(By.res(getPackage(), "launch_second_activity")),
+ FIND_TIMEOUT)
+
+ require(button != null) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. in split screen)"
+ }
+ button.click()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(component)
+ }
+} \ No newline at end of file
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 b5757fd21ee0..384d8e8e998d 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
@@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,15 +28,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -46,6 +46,14 @@ import org.junit.runners.Parameterized
/**
* Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
*/
@RequiresDevice
@@ -79,37 +87,55 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
}
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+ fun imeAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleStart() {
+ testSpec.assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ testSpec.assertLayersEnd {
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -117,15 +143,19 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Presubmit
@Test
- fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+ fun imeAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
}
- @FlakyTest
+ @Presubmit
@Test
fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
@@ -145,8 +175,11 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 5,
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
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 549e44c511b9..ade215b3022d 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
@@ -30,14 +30,14 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -47,6 +47,14 @@ import org.junit.runners.Parameterized
/**
* Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
@RequiresDevice
@@ -75,51 +83,73 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
}
}
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
}
- @FlakyTest
+ @FlakyTest(bugId = 190189685)
@Test
- fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
+ fun imeAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ .then()
+ .appWindowNotOnTop(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
- fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
+ Surface.ROTATION_0)
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
- Surface.ROTATION_0)
+ fun imeLayerVisibleStart() {
+ testSpec.assertLayersStart {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @FlakyTest
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ testSpec.assertLayersEnd {
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
+
+ @Presubmit
@Test
fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
@Presubmit
@Test
- fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+ fun imeAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
- @FlakyTest
+ @Presubmit
@Test
fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -133,18 +163,19 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
- @FlakyTest
+ @Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
}
}
@@ -154,8 +185,11 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(repetitions = 1,
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
)
}
}
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 82ca074b5ef2..cdfcff3d4beb 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
@@ -28,13 +28,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import org.junit.Assume
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
@@ -61,7 +61,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
return FlickerBuilder(instrumentation).apply {
setup {
test {
- testApp.launchViaIntent()
+ testApp.launchViaIntent(wmHelper)
}
eachRun {
testApp.openIME(device, wmHelper)
@@ -80,37 +80,42 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT))
}
}
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+ fun imeAppWindowIsAlwaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
@@ -146,7 +151,11 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+ fun imeAppLayerIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
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 703e4a125440..05fc2672168e 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
@@ -30,13 +30,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -68,7 +68,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
}
teardown {
eachRun {
@@ -84,19 +84,20 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
- WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT))
}
}
@@ -106,19 +107,25 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@FlakyTest
@Test
- fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+ fun imeAppWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp.component)
+ .then()
+ .isAppWindowInvisible(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
Surface.ROTATION_0)
@Presubmit
@@ -127,7 +134,13 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+ fun imeAppLayerBecomesInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ }
+ }
@Presubmit
@Test
@@ -144,8 +157,9 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+ WindowManagerStateHelper.IME_COMPONENT,
+ WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
}
}
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 7e34469b8188..7659d9471e2f 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
@@ -14,128 +14,56 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker.ime
-import android.platform.helpers.IAppHelper
import com.android.server.wm.flicker.FlickerTestParameter
-
-const val IME_WINDOW_TITLE = "InputMethod"
-
-fun FlickerTestParameter.imeLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
- if (rotatesScreen) {
- assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
- .then()
- .isInvisible(IME_WINDOW_TITLE)
- .then()
- .isVisible(IME_WINDOW_TITLE)
- }
- } else {
- assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
- }
- }
-}
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(IME_WINDOW_TITLE)
+ this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .isVisible(IME_WINDOW_TITLE)
+ .isVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(IME_WINDOW_TITLE)
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .isInvisible(IME_WINDOW_TITLE)
- }
-}
-
-fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
- assertLayers {
- this.isVisible(testApp.getPackage())
- }
-}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
+ .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
} else {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.hidesNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .showsNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.showsNonAppWindow(IME_WINDOW_TITLE)
+ this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
.then()
- .hidesNonAppWindow(IME_WINDOW_TITLE)
+ .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
}
}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(
- testApp: IAppHelper,
- rotatesScreen: Boolean = false
-) {
- if (rotatesScreen) {
- assertWm {
- this.showsAppWindow(testApp.getPackage())
- .then()
- .hidesAppWindow(testApp.getPackage())
- .then()
- .showsAppWindow(testApp.getPackage())
- }
- } else {
- assertWm {
- this.showsAppWindow(testApp.getPackage())
- }
- }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) {
- assertWm {
- this.hidesAppWindow(windowName)
- .then()
- .showsAppWindow(windowName)
- }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) {
- assertWm {
- this.showsAppWindowOnTop(testApp.getPackage())
- .then()
- .appWindowNotOnTop(testApp.getPackage())
- }
-}
-
-fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
- assertLayers {
- this.isVisible(testApp.getPackage())
- .then()
- .isInvisible(testApp.getPackage())
- }
-} \ No newline at end of file
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 cae1b16c1c8c..f35a180e1ad6 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
@@ -28,16 +28,15 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.appWindowAlwaysVisibleOnTop
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,11 +80,11 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
@@ -93,19 +92,23 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+ fun appWindowAlwaysVisibleOnTop() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
@Presubmit
@Test
@@ -115,7 +118,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Test
fun layerAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(testApp.`package`)
+ this.isVisible(testApp.component)
}
}
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 b7673d5b0107..3bcf793e9071 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
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -26,23 +27,22 @@ import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,7 +61,6 @@ import org.junit.runners.Parameterized
class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
- private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -73,14 +72,14 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
eachRun {
device.pressRecentApps()
- wmHelper.waitImeWindowGone()
+ wmHelper.waitImeGone()
wmHelper.waitForAppTransitionIdle()
this.setRotation(testSpec.config.startRotation)
}
}
transitions {
device.reopenAppFromOverview(wmHelper)
- wmHelper.waitImeWindowShown()
+ wmHelper.waitImeShown()
}
teardown {
test {
@@ -92,23 +91,34 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
@Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ val component = ComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ component)
+ )
}
}
@Presubmit
@Test
- fun launcherWindowBecomesInvisible() = testSpec.launcherWindowBecomesInvisible()
+ fun launcherWindowBecomesInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
+ }
@Presubmit
@Test
@@ -116,30 +126,57 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp, true)
+ fun imeAppWindowVisibility() {
+ // the app starts visible in live tile, then becomes invisible during animation and
+ // is again launched. Since we log 1x per frame, sometimes the activity visibility and
+ // the app visibility are updated together, sometimes not, thus ignore activity check
+ // at the start
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .then()
+ .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ .then()
+ .isAppWindowVisible(testApp.component)
+ }
+ }
@Presubmit
@Test
// During testing the launcher is always in portrait mode
- fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation)
@Presubmit
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
@Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
@Presubmit
@Test
- fun imeLayerIsAlwaysVisible() = testSpec.imeLayerIsAlwaysVisible(true)
+ fun imeLayerIsBecomesVisible() {
+ testSpec.assertLayers {
+ this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .then()
+ .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .then()
+ .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
@Presubmit
@Test
- fun appLayerReplacesLauncher() =
- testSpec.appLayerReplacesLauncher(testAppComponentName.className)
+ fun appLayerReplacesLauncher() {
+ testSpec.assertLayers {
+ this.isVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(testApp.component)
+ }
+ }
@Presubmit
@Test
@@ -156,8 +193,14 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // depends on how much of the animation transactions are sent to SF at once
+ // sometimes this layer appears for 2-3 frames, sometimes for only 1
+ val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 0cae37c8d5ab..f9dd88e8cb29 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,27 +17,26 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
+import android.view.Surface
import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -54,10 +53,11 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group2
+@Presubmit
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
- private val imeTestApp = ImeAppHelper(instrumentation)
+ private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -66,7 +66,13 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
eachRun {
this.setRotation(testSpec.config.startRotation)
testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+
imeTestApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+
imeTestApp.openIME(device, wmHelper)
}
}
@@ -74,57 +80,86 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
eachRun {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- }
- test {
- imeTestApp.exit(wmHelper)
+ testApp.exit()
+ imeTestApp.exit()
}
}
transitions {
// [Step1]: Swipe right from imeTestApp to testApp task
+ createTag(TAG_IME_VISIBLE)
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- val displayCenterX = displayBounds.bounds.width() / 2
- device.swipe(displayCenterX, displayBounds.bounds.height(),
- displayBounds.bounds.width(), displayBounds.bounds.height(), 20)
+ device.swipe(0, displayBounds.bounds.height(),
+ displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+
wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+ createTag(TAG_IME_INVISIBLE)
}
transitions {
// [Step2]: Swipe left to back to imeTestApp task
val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- val displayCenterX = displayBounds.bounds.width() / 2
device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
- displayCenterX, displayBounds.bounds.height(), 20)
+ 0, displayBounds.bounds.height(), 50)
wmHelper.waitForFullScreenApp(imeTestApp.component)
}
}
}
- @FlakyTest
@Test
- fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(imeTestApp)
+ fun imeAppWindowVisibility() {
+ val component = ComponentName(imeTestApp.`package`, "")
+ testSpec.assertWm {
+ this.isAppWindowOnTop(component)
+ .then()
+ .isAppWindowVisible(component, ignoreActivity = true)
+ }
+ }
- @FlakyTest
@Test
- fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+ fun navBarLayerIsVisibleAroundSwitching() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ }
- @FlakyTest
@Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+ fun statusBarLayerIsVisibleAroundSwitching() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ }
- @Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+ fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+ testSpec.assertLayersStart {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ testSpec.assertLayersTag(TAG_IME_VISIBLE) {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @FlakyTest
@Test
- fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+ fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
+ testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
+ isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ }
+ }
- @Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
- @FlakyTest
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
companion object {
@Parameterized.Parameters(name = "{0}")
@@ -134,10 +169,13 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
.getConfigNonRotationTests(
repetitions = 3,
supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
+
+ private const val TAG_IME_VISIBLE = "imeVisible"
+ private const val TAG_IME_INVISIBLE = "imeInVisible"
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
new file mode 100644
index 000000000000..a21eae8cb4ed
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the back and forward transition between 2 activities.
+ * To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ }
+ teardown {
+ test {
+ testApp.exit()
+ }
+ }
+ transitions {
+ testApp.openSecondActivity(device, wmHelper)
+ device.pressBack()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun finishSubActivity() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
+ .then()
+ .isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME)
+ .then()
+ .isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun entireScreenCovered() {
+ testSpec.entireScreenCovered(testSpec.config.startRotation)
+ }
+
+ @Presubmit
+ @Test
+ fun launcherWindowNotVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(LAUNCHER_COMPONENT, ignoreActivity = true)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun launcherLayerNotVisible() {
+ testSpec.assertLayers { this.isInvisible(LAUNCHER_COMPONENT) }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
+ }
+ }
+}
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 9ff0bdfe66ba..e6dc8523acbf 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
@@ -64,35 +64,17 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
@FlakyTest
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
-
- @FlakyTest
- @Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
- @FlakyTest
- @Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
+ @FlakyTest(bugId = 192721431)
@Test
override fun appLayerReplacesLauncher() {
super.appLayerReplacesLauncher()
}
- @FlakyTest
+ @FlakyTest(bugId = 192721431)
@Test
override fun appWindowReplacesLauncherAsTopWindow() {
super.appWindowReplacesLauncherAsTopWindow()
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 b073a7ca1495..7833e2f25eec 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
@@ -65,34 +65,10 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
- @FlakyTest
- @Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
- }
-
- @FlakyTest
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
-
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
new file mode 100644
index 000000000000..0adcca21528d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Launch an app while the phone is locked
+ * To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+ override val testApp = NonResizeableAppHelper(instrumentation)
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = {
+ super.transition(this, it)
+ setup {
+ eachRun {
+ device.sleep()
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun navBarLayerIsVisible() {
+ testSpec.assertLayersEnd {
+ isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun nonResizableAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.notContains(testApp.component)
+ .then()
+ .isInvisible(testApp.component)
+ .then()
+ .isVisible(testApp.component)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun nonResizableAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.notContains(testApp.component)
+ .then()
+ .isAppWindowInvisible(testApp.component,
+ ignoreActivity = true, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun nonResizableAppWindowBecomesVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ this.isVisible(testApp.component)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+
+ @Postsubmit
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ @FlakyTest
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @FlakyTest
+ @Test
+ override fun focusChanges() = super.focusChanges()
+
+ @FlakyTest
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ @FlakyTest
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY),
+ supportedRotations = listOf(Surface.ROTATION_0)
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index b304d5f999df..1435120e2cfb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -22,29 +22,29 @@ import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.replacesLayer
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
import org.junit.Test
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+ protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
withTestName { testSpec.name }
@@ -71,14 +71,14 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
@Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
@Presubmit
@@ -89,14 +89,14 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
+ open fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
@Presubmit
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+ open fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
}
@Presubmit
@@ -124,31 +124,43 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
// During testing the launcher is always in portrait mode
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
}
@Presubmit
@Test
open fun focusChanges() {
- testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+ testSpec.assertEventLog {
+ this.focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
@Presubmit
@Test
open fun appLayerReplacesLauncher() {
- testSpec.appLayerReplacesLauncher(testApp.`package`)
+ testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
}
@Presubmit
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
- testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+ testSpec.assertWm {
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp.component)
+ }
}
@Presubmit
@Test
open fun launcherWindowBecomesInvisible() {
- testSpec.launcherWindowBecomesInvisible()
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
}
} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index e2705c764917..b509c61d2aba 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
@@ -67,15 +67,7 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
@FlakyTest
@Test
- override fun navBarLayerIsAlwaysVisible() {
- super.navBarLayerIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
- }
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
new file mode 100644
index 000000000000..3c94be01a59b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching to last opened app from launcher
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest`
+ *
+ * Actions:
+ * Launch an app
+ * Navigate home to show launcher
+ * Swipe right from the bottom of the screen to quick switch back to the app
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ testApp.launchViaIntent(wmHelper)
+ device.pressHome()
+ wmHelper.waitForHomeActivityVisible()
+ wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
+ }
+ }
+ transitions {
+ // Swipe right from bottom to quick switch back
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 50
+ )
+
+ wmHelper.waitForFullScreenApp(testApp.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that [testApp] windows fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from the launcher back to the [testApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppWindowsCoveringFullScreen() {
+ testSpec.assertWmEnd {
+ this.frameRegion(testApp.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp] layers fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from the launcher back to the [testApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppLayersCoveringFullScreen() {
+ testSpec.assertLayersEnd {
+ this.visibleRegion(testApp.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp] is the top window at the end of the transition once we have fully quick
+ * switched from the launcher back to the [testApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppBeingOnTop() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the home activity being tagged as visible.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithHomeActivityFlaggedVisible() {
+ testSpec.assertWmStart {
+ this.isHomeActivityVisible(true)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the launcher windows filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherWindowsCoverFullScreen() {
+ testSpec.assertWmStart {
+ this.frameRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the launcher layers filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherLayersCoverFullScreen() {
+ testSpec.assertLayersStart {
+ this.visibleRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the launcher being the top window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherBeingOnTop() {
+ testSpec.assertWmStart {
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that the transition ends with the home activity being flagged as not visible. By this
+ * point we should have quick switched away from the launcher back to the [testApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithHomeActivityFlaggedInvisible() {
+ testSpec.assertWmEnd {
+ this.isHomeActivityVisible(false)
+ }
+ }
+
+ /**
+ * Checks that the [testApp] window starts off invisible and once it becomes visible, stays
+ * visible until the end of the transition.
+ *
+ * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay
+ * invisible throughout and pass this check), that is what [endsWithAppBeingVisible] is for.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowBecomesAndStaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ .then()
+ .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ }
+ }
+
+ /**
+ * Checks that the [testApp] layer starts off invisible and once it becomes visible, stays
+ * visible until the end of the transition.
+ *
+ * NOTE: It does not ensure that the app becomes visible at some point ([testApp] can stay
+ * invisible throughout and pass this check).
+ */
+ @Postsubmit
+ @Test
+ fun appLayerBecomesAndStaysVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(testApp.component)
+ .then()
+ .isVisible(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the launcher window starts off visible and once it becomes invisible, stays
+ * invisible until the end of the transition.
+ *
+ * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can
+ * stay invisible throughout and pass this check).
+ */
+ @Postsubmit
+ @Test
+ fun launcherWindowBecomesAndStaysInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that the launcher layer starts off visible and once it becomes invisible, stays
+ * invisible until the end of the transition.
+ *
+ * NOTE: It does not ensure that the launcher becomes invisible at some point, (the launcher can
+ * stay invisible throughout and pass this check).
+ */
+ @Postsubmit
+ @Test
+ fun launcherLayerBecomesAndStaysInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isInvisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that the launcher window is visible at least until the app window is visible. Ensures
+ * that at any point, either the launcher or [testApp] windows are at least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isAppWindowVisible(SNAPSHOT_COMPONENT)
+ .then()
+ .isAppWindowVisible(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the launcher layer is visible at least until the app layer is visible. Ensures
+ * that at any point, either the launcher or [testApp] layers are at least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(LAUNCHER_COMPONENT)
+ .then()
+ .isVisible(SNAPSHOT_COMPONENT)
+ .then()
+ .isVisible(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the navbar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the navbar is always in the right position and covers the expected region.
+ *
+ * NOTE: This doesn't check that the navbar is visible or not.
+ */
+ @Postsubmit
+ @Test
+ fun navbarIsAlwaysInRightPosition() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+
+ /**
+ * Checks that the status bar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+
+ /**
+ * Checks that the screen is always fully covered by visible layers throughout the transition.
+ */
+ @Postsubmit
+ @Test
+ fun screenIsAlwaysFilled() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ // TODO: Test with 90 rotation
+ supportedRotations = listOf(Surface.ROTATION_0)
+ )
+ }
+ }
+} \ No newline at end of file
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 69e8a8d08e58..d57c6698e35c 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
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.rotation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,7 +24,13 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -61,39 +66,44 @@ class ChangeAppRotationTest(
super.focusDoesNotChange()
}
- @Postsubmit
+ @Presubmit
@Test
fun screenshotLayerBecomesInvisible() {
testSpec.assertLayers {
- this.isVisible(testApp.getPackage())
+ this.isVisible(testApp.component)
.then()
- .isVisible(SCREENSHOT_LAYER)
+ .isVisible(ROTATION_COMPONENT)
.then()
- .isVisible(testApp.getPackage())
+ .isVisible(testApp.component)
}
}
- @Postsubmit
+ @Presubmit
@Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
+ fun statusBarWindowIsVisible() {
+ testSpec.statusBarWindowIsVisible()
}
@Presubmit
@Test
- override fun navBarWindowIsAlwaysVisible() {
- super.navBarWindowIsAlwaysVisible()
+ fun statusBarLayerIsVisible() {
+ testSpec.statusBarLayerIsVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerRotatesScales() {
+ testSpec.statusBarLayerRotatesScales(
+ testSpec.config.startRotation, testSpec.config.endRotation)
}
@FlakyTest
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ override fun navBarLayerRotatesAndScales() {
+ super.navBarLayerRotatesAndScales()
}
companion object {
- private const val SCREENSHOT_LAYER = "RotationLayer"
-
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 4b888cd5aad0..2b0b3c23a0b2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,25 +17,21 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
+import android.content.ComponentName
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
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.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Test
@@ -69,19 +65,19 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
}
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsAlwaysVisible()
+ open fun navBarWindowIsVisible() {
+ testSpec.navBarWindowIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
- open fun navBarLayerIsAlwaysVisible() {
- testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true)
+ open fun navBarLayerIsVisible() {
+ testSpec.navBarLayerIsVisible()
}
- @FlakyTest
+ @Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(
@@ -90,31 +86,12 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsAlwaysVisible()
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
- }
-
- @FlakyTest
- @Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
- }
-
- @FlakyTest
- @Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
- WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
- "SecondaryHomeHandle"
+ ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ ComponentName("", "SecondaryHomeHandle")
)
)
}
@@ -130,22 +107,24 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Presubmit
@Test
- open fun noUncoveredRegions() {
- testSpec.noUncoveredRegions(testSpec.config.startRotation,
+ open fun entireScreenCovered() {
+ testSpec.entireScreenCovered(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
}
@Presubmit
@Test
open fun focusDoesNotChange() {
- testSpec.focusDoesNotChange()
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
}
@Presubmit
@Test
open fun appLayerRotates_StartingPos() {
testSpec.assertLayersStart {
- this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
+ this.visibleRegion(testApp.component).coversExactly(startingPos)
}
}
@@ -153,7 +132,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@Test
open fun appLayerRotates_EndingPos() {
testSpec.assertLayersEnd {
- this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
+ this.visibleRegion(testApp.component).coversExactly(endingPos)
}
}
} \ No newline at end of file
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 b153bece1133..264c5d8e1852 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
@@ -16,8 +16,8 @@
package com.android.server.wm.flicker.rotation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
+import android.view.WindowManager
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +27,7 @@ import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,42 +61,73 @@ class SeamlessAppRotationTest(
}
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
- override fun statusBarWindowIsAlwaysVisible() {
- super.statusBarWindowIsAlwaysVisible()
+ fun appWindowFullScreen() {
+ testSpec.assertWm {
+ this.invoke("isFullScreen") {
+ val appWindow = it.windowState(testApp.`package`)
+ val flags = appWindow.windowState?.attributes?.flags ?: 0
+ appWindow.verify("isFullScreen")
+ .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+ .isGreaterThan(0)
+ }
+ }
}
- @FlakyTest(bugId = 140855415)
+ @Presubmit
@Test
- override fun statusBarLayerIsAlwaysVisible() {
- super.statusBarLayerIsAlwaysVisible()
+ fun appWindowSeamlessRotation() {
+ testSpec.assertWm {
+ this.invoke("isRotationSeamless") {
+ val appWindow = it.windowState(testApp.`package`)
+ val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+ appWindow.verify("isRotationSeamless")
+ .that(rotationAnimation
+ .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
+ .isGreaterThan(0)
+ }
+ }
}
@Presubmit
@Test
fun appLayerAlwaysVisible() {
testSpec.assertLayers {
- isVisible(testApp.`package`)
+ isVisible(testApp.component)
}
}
- @FlakyTest(bugId = 185400889)
+ @Presubmit
@Test
fun appLayerRotates() {
testSpec.assertLayers {
- this.coversExactly(startingPos, testApp.`package`)
+ this.coversExactly(startingPos, testApp.component)
.then()
- .coversExactly(endingPos, testApp.`package`)
+ .coversExactly(endingPos, testApp.component)
}
}
- @Postsubmit
+ @Presubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun statusBarWindowIsAlwaysInvisible() {
+ testSpec.assertWm {
+ this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
}
+ @Presubmit
+ @Test
+ fun statusBarLayerIsAlwaysInvisible() {
+ testSpec.assertLayers {
+ this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
companion object {
private val testFactory = FlickerTestParameterFactory.getInstance()
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 1599ed4b280f..3b9f33aaded1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -59,5 +59,26 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".NonResizeableActivity"
+ android:resizeableActivity="false"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
+ android:label="NonResizeableApp"
+ android:exported="true"
+ android:showOnLockScreen="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".ButtonActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="ButtonActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml
new file mode 100644
index 000000000000..fe7bced690f9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+ <Button
+ android:id="@+id/launch_second_activity"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Second activity" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 4708cfd48381..c55e7c2720db 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -23,5 +23,6 @@
<EditText android:id="@+id/plain_text_input"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:imeOptions="flagNoExtractUi"
android:inputType="text"/>
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
new file mode 100644
index 000000000000..6d5a9dd29248
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_orange_light">
+
+ <TextView
+ android:id="@+id/NonResizeableTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="NonResizeableActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 0ccc49897202..224d2ac38a11 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -41,4 +41,14 @@ public class ActivityOptions {
public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SimpleActivity");
+
+ public static final String NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME = "NonResizeableApp";
+ public static final ComponentName NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NonResizeableActivity");
+
+ public static final String BUTTON_ACTIVITY_LAUNCHER_NAME = "ButtonApp";
+ public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ButtonActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java
new file mode 100644
index 000000000000..b42ac2a6fd97
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class ButtonActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_button);
+
+ Button button = findViewById(R.id.launch_second_activity);
+ button.setOnClickListener(v -> {
+ Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class);
+ startActivity(intent);
+ });
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java
new file mode 100644
index 000000000000..61019d8b3716
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeableActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.os.Bundle;
+
+public class NonResizeableActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_non_resizeable);
+
+ setShowWhenLocked(true);
+ setTurnScreenOn(true);
+ KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
+ if (keyguardManager != null) {
+ keyguardManager.requestDismissKeyguard(this, null);
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 4da3eca25ea0..3eeba7d4f415 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -70,7 +70,45 @@ class AnrTest {
}
@Test
- fun testGestureMonitorAnr() {
+ fun testGestureMonitorAnr_Close() {
+ triggerAnr()
+ clickCloseAppOnAnrDialog()
+ }
+
+ @Test
+ fun testGestureMonitorAnr_Wait() {
+ triggerAnr()
+ clickWaitOnAnrDialog()
+ SystemClock.sleep(500) // Wait at least 500ms after tapping on wait
+ // ANR dialog should reappear after a delay - find the close button on it to verify
+ clickCloseAppOnAnrDialog()
+ }
+
+ private fun clickCloseAppOnAnrDialog() {
+ // Find anr dialog and kill app
+ val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val closeAppButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
+ if (closeAppButton == null) {
+ fail("Could not find anr dialog")
+ return
+ }
+ closeAppButton.click()
+ }
+
+ private fun clickWaitOnAnrDialog() {
+ // Find anr dialog and tap on wait
+ val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val waitButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
+ if (waitButton == null) {
+ fail("Could not find anr dialog/wait button")
+ return
+ }
+ waitButton.click()
+ }
+
+ private fun triggerAnr() {
startUnresponsiveActivity()
val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
val obj: UiObject2? = uiDevice.wait(Until.findObject(
@@ -91,20 +129,6 @@ class AnrTest {
// Todo: replace using timeout from android.hardware.input.IInputManager
SystemClock.sleep(5000) // default ANR timeout for gesture monitors
-
- clickCloseAppOnAnrDialog()
- }
-
- private fun clickCloseAppOnAnrDialog() {
- // Find anr dialog and kill app
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
- val closeAppButton: UiObject2? =
- uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
- if (closeAppButton == null) {
- fail("Could not find anr dialog")
- return
- }
- closeAppButton.click()
}
private fun startUnresponsiveActivity() {
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
new file mode 100644
index 000000000000..131611d28f0a
--- /dev/null
+++ b/tests/InputMethodStressTest/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "InputMethodStressTest",
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ sdk_version: "31",
+}
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
new file mode 100644
index 000000000000..e5d65188f7ad
--- /dev/null
+++ b/tests/InputMethodStressTest/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.inputmethod.stresstest">
+
+ <application>
+ <activity android:name=".TestActivity"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.inputmethod.stresstest">
+ </instrumentation>
+</manifest>
diff --git a/core/tests/uwbtests/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml
index ff4b668cc625..b194010a985a 100644
--- a/core/tests/uwbtests/AndroidTest.xml
+++ b/tests/InputMethodStressTest/AndroidTest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 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.
@@ -13,20 +13,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for UWB Manager test cases">
- <option name="test-suite-tag" value="apct"/>
- <option name="test-suite-tag" value="apct-instrumentation"/>
+<configuration description="InputMethod integration/regression test">
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="UwbManagerTests.apk" />
+ <option name="test-file-name" value="InputMethodStressTest.apk" />
</target_preparer>
- <option name="test-suite-tag" value="apct"/>
- <option name="test-tag" value="UwbManagerTests"/>
-
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.uwb" />
- <option name="hidden-api-checks" value="false"/>
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.inputmethod.stresstest" />
</test>
</configuration>
diff --git a/core/java/android/util/imetracing/OWNERS b/tests/InputMethodStressTest/OWNERS
index 885fd0ab9a45..6bb4b17ed4eb 100644
--- a/core/java/android/util/imetracing/OWNERS
+++ b/tests/InputMethodStressTest/OWNERS
@@ -1,3 +1,3 @@
-set noparent
+# Bug component: 34867
include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/ImeOpenCloseStressTest.java
new file mode 100644
index 000000000000..5427fd833209
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/ImeOpenCloseStressTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod.stresstest;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.platform.test.annotations.RootPermissionTest;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RootPermissionTest
+@RunWith(AndroidJUnit4.class)
+public class ImeOpenCloseStressTest {
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+ private static final int NUM_TEST_ITERATIONS = 100;
+
+ private Instrumentation mInstrumentation;
+
+ @Test
+ public void test() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ Intent intent = new Intent()
+ .setAction(Intent.ACTION_MAIN)
+ .setClass(mInstrumentation.getContext(), TestActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ TestActivity activity = (TestActivity) mInstrumentation.startActivitySync(intent);
+ eventually(() -> assertThat(callOnMainSync(activity::hasWindowFocus)).isTrue(), TIMEOUT);
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ mInstrumentation.runOnMainSync(activity::showIme);
+ eventually(() -> assertThat(callOnMainSync(activity::isImeShown)).isTrue(), TIMEOUT);
+ mInstrumentation.runOnMainSync(activity::hideIme);
+ eventually(() -> assertThat(callOnMainSync(activity::isImeShown)).isFalse(), TIMEOUT);
+ }
+ }
+
+ private <V> V callOnMainSync(Callable<V> callable) {
+ AtomicReference<V> result = new AtomicReference<>();
+ AtomicReference<Exception> thrownException = new AtomicReference<>();
+ mInstrumentation.runOnMainSync(() -> {
+ try {
+ result.set(callable.call());
+ } catch (Exception e) {
+ thrownException.set(e);
+ }
+ });
+ if (thrownException.get() != null) {
+ throw new RuntimeException("Exception thrown from Main thread", thrownException.get());
+ }
+ return result.get();
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/TestActivity.java b/tests/InputMethodStressTest/src/com/android/inputmethod/TestActivity.java
new file mode 100644
index 000000000000..7baf037e4215
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/TestActivity.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.stresstest;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowInsets;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+public class TestActivity extends Activity {
+
+ private EditText mEditText;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LinearLayout rootView = new LinearLayout(this);
+ rootView.setOrientation(LinearLayout.VERTICAL);
+ mEditText = new EditText(this);
+ rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ setContentView(rootView);
+ }
+
+ public boolean hasWindowFocus() {
+ return mEditText.hasWindowFocus();
+ }
+
+ public boolean isImeShown() {
+ WindowInsets insets = mEditText.getRootWindowInsets();
+ return insets.isVisible(WindowInsets.Type.ime());
+ }
+
+ public void showIme() {
+ mEditText.requestFocus();
+ InputMethodManager imm = getSystemService(InputMethodManager.class);
+ imm.showSoftInput(mEditText, 0);
+ }
+
+ public void hideIme() {
+ InputMethodManager imm = getSystemService(InputMethodManager.class);
+ imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ }
+}
diff --git a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
index e956be339bc4..dd9b294a9596 100644
--- a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -82,11 +82,10 @@ public class JobStorePerfTests {
long elapsedTimeNs = 0;
while (benchmarkState.keepRunning(elapsedTimeNs)) {
- sJobStore.clear();
+ sJobStore.clearForTesting();
for (JobStatus job : jobList) {
- sJobStore.add(job);
+ sJobStore.addForTesting(job);
}
- sJobStore.waitForWriteToCompleteForTesting(10_000);
final long startTime = SystemClock.elapsedRealtimeNanos();
sJobStore.writeStatusToDiskForTesting();
@@ -110,11 +109,11 @@ public class JobStorePerfTests {
long elapsedTimeNs = 0;
while (benchmarkState.keepRunning(elapsedTimeNs)) {
- sJobStore.clear();
+ sJobStore.clearForTesting();
for (JobStatus job : jobList) {
- sJobStore.add(job);
+ sJobStore.addForTesting(job);
}
- sJobStore.waitForWriteToCompleteForTesting(10_000);
+ sJobStore.writeStatusToDiskForTesting();
JobSet jobSet = new JobSet();
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 7b2a07fd80f8..7a668a5a8944 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -31,7 +31,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.UserManager;
@@ -55,7 +54,6 @@ 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.TimeUnit;
import java.util.stream.Collectors;
@@ -204,194 +202,6 @@ public class RollbackTest {
}
/**
- * Test that multiple available rollbacks are properly persisted.
- */
- @Test
- public void testAvailableRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- Uninstall.packages(TestApp.B);
- Install.single(TestApp.B1).commit();
- Install.single(TestApp.B2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // Both test apps should now be available for rollback.
- RollbackInfo rollbackA = waitForAvailableRollback(TestApp.A);
- assertThat(rollbackA).isNotNull();
- assertThat(rollbackA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- RollbackInfo rollbackB = waitForAvailableRollback(TestApp.B);
- assertThat(rollbackB).isNotNull();
- assertThat(rollbackB).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // The apps should still be available for rollback.
- rollbackA = waitForAvailableRollback(TestApp.A);
- assertThat(rollbackA).isNotNull();
- assertThat(rollbackA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- rollbackB = waitForAvailableRollback(TestApp.B);
- assertThat(rollbackB).isNotNull();
- assertThat(rollbackB).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback of B should not rollback A
- RollbackUtils.rollback(rollbackB.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that available multi-package rollbacks are properly persisted.
- */
- @Test
- public void testAvailableMultiPackageRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.multi(TestApp.A1, TestApp.B1).commit();
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // The app should now be available for rollback.
- RollbackInfo availableA = waitForAvailableRollback(TestApp.A);
- assertThat(availableA).isNotNull();
- assertThat(availableA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- RollbackInfo availableB = waitForAvailableRollback(TestApp.B);
- assertThat(availableB).isNotNull();
- assertThat(availableB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Assert they're both the same rollback
- assertThat(availableA).hasRollbackId(availableB.getRollbackId());
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // The apps should still be available for rollback.
- availableA = waitForAvailableRollback(TestApp.A);
- assertThat(availableA).isNotNull();
- assertThat(availableA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- availableB = waitForAvailableRollback(TestApp.B);
- assertThat(availableB).isNotNull();
- assertThat(availableB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback of B should rollback A as well
- RollbackUtils.rollback(availableB.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
-
- RollbackInfo committedA = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committedA).isNotNull();
- assertThat(committedA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- RollbackInfo committedB = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committedB).isNotNull();
- assertThat(committedB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Assert they're both the same rollback
- assertThat(committedA).hasRollbackId(committedB.getRollbackId());
- assertThat(committedA).hasRollbackId(availableA.getRollbackId());
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that recently committed rollback data is properly persisted.
- */
- @Test
- public void testRecentlyCommittedRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // The app should now be available for rollback.
- RollbackInfo available = waitForAvailableRollback(TestApp.A);
- assertThat(available).isNotNull();
-
- // Roll back the app.
- TestApp cause = new TestApp("Foo", "com.android.tests.rollback.testapp.Foo",
- /*versionCode*/ 42, /*isApex*/ false);
- RollbackUtils.rollback(available.getRollbackId(), cause);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- // Verify the recent rollback has been recorded.
- RollbackInfo committed = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committed).isNotNull();
- assertThat(committed).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(committed).causePackagesContainsExactly(cause);
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // Verify the recent rollback is still recorded.
- committed = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committed).isNotNull();
- assertThat(committed).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(committed).causePackagesContainsExactly(cause);
- assertThat(committed).hasRollbackId(available.getRollbackId());
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
* Test the scheduling aspect of rollback expiration.
*/
@Test
@@ -740,203 +550,6 @@ public class RollbackTest {
}
}
- /**
- * Test that the MANAGE_ROLLBACKS permission is required to call
- * RollbackManager APIs.
- */
- @Test
- public void testManageRollbacksPermission() throws Exception {
- // We shouldn't be allowed to call any of the RollbackManager APIs
- // without the MANAGE_ROLLBACKS permission.
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- try {
- rm.getAvailableRollbacks();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.getRecentlyCommittedRollbacks();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- // TODO: What if the implementation checks arguments for non-null
- // first? Then this test isn't valid.
- rm.commitRollback(0, Collections.emptyList(), null);
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.reloadPersistedData();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.expireRollbackForPackage(TestApp.A);
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
- }
-
- /**
- * Test that you cannot enable rollback for a package without the
- * MANAGE_ROLLBACKS permission.
- */
- @Test
- public void testEnableRollbackPermission() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().commit();
-
- // We expect v2 of the app was installed, but rollback has not
- // been enabled.
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that you cannot enable rollback for a non-module package when
- * holding the MANAGE_ROLLBACKS permission.
- */
- @Test
- public void testNonModuleEnableRollback() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().commit();
-
- // We expect v2 of the app was installed, but rollback has not
- // been enabled because the test app is not a module.
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test rollback of multi-package installs is implemented.
- */
- @Test
- public void testMultiPackage() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- // Prep installation of the test apps.
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.multi(TestApp.A1, TestApp.B1).commit();
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // TestApp.A should now be available for rollback.
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback the app. It should cause both test apps to be rolled
- // back.
- RollbackUtils.rollback(rollback.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
-
- // We should see recent rollbacks listed for both A and B.
- Thread.sleep(1000);
- RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
-
- RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.B);
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- assertThat(rollbackA).hasRollbackId(rollbackB.getRollbackId());
-
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test failure to enable rollback for multi-package installs.
- * If any one of the packages fail to enable rollback, we shouldn't enable
- * rollback for any package.
- */
- @Test
- public void testMultiPackageEnableFail() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.single(TestApp.A1).commit();
- // We should fail to enable rollback here because TestApp B is not
- // already installed.
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- assertThat(getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A)).isNull();
- assertThat(getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.B)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
@Test
@Ignore("b/120200473")
/**
@@ -1160,109 +773,4 @@ public class RollbackTest {
InstallUtils.dropShellPermissionIdentity();
}
}
-
- /**
- * Test we can't enable rollback for non-whitelisted app without
- * TEST_MANAGE_ROLLBACKS permission
- */
- @Test
- public void testNonRollbackWhitelistedApp() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
-
- Install.single(TestApp.A2).setEnableRollback().commit();
- Thread.sleep(TimeUnit.SECONDS.toMillis(2));
- assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- @Test
- public void testRollbackDataPolicy() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A, TestApp.B, TestApp.C);
- Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
- // Write user data version = 1
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- Install a2 = Install.single(TestApp.A2)
- .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
- Install b2 = Install.single(TestApp.B2)
- .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
- // The rollback data policy of C2 is specified in the manifest
- Install c2 = Install.single(TestApp.C2).setEnableRollback();
- Install.multi(a2, b2, c2).setEnableRollback().commit();
- // Write user data version = 2
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
- RollbackUtils.rollback(info.getRollbackId());
- // Read user data version from userdata.txt
- // A's user data version is -1 for user data is wiped.
- // B's user data version is 1 as rollback committed.
- // C's user data version is -1 for user data is wiped.
- assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.C)).isEqualTo(-1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Tests an app can be rolled back to the previous signing key.
- *
- * <p>The rollback capability in the signing lineage allows an app to be updated to an APK
- * signed with a previous signing key in the lineage; however this often defeats the purpose
- * of key rotation as a compromised key could then be used to roll an app back to the previous
- * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager
- * allows an app to be rolled back to the previous signing key if the rollback install reason
- * is set.
- */
- @Test
- public void testRollbackAfterKeyRotation() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- // Uninstall TestApp.A
- Uninstall.packages(TestApp.A);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-
- // Install v1 of the app with the original signing key (without rollbacks enabled).
- Install.single(TestApp.AOriginal1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled.
- Install.single(TestApp.ARotated2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // Roll back the app.
- RollbackInfo available = waitForAvailableRollback(TestApp.A);
- RollbackUtils.rollback(available.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
}
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 642b19e6d961..a283aa9d0722 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -293,50 +293,6 @@ public class StagedRollbackTest {
}
@Test
- public void testRollbackDataPolicy_Phase1_Install() throws Exception {
- Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
- // Write user data version = 1
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- Install a2 = Install.single(TestApp.A2).setStaged()
- .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
- Install b2 = Install.single(TestApp.B2).setStaged()
- .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
- // The rollback data policy of C2 is specified in the manifest
- Install c2 = Install.single(TestApp.C2).setStaged().setEnableRollback();
- Install.multi(a2, b2, c2).setEnableRollback().setStaged().commit();
- }
-
- @Test
- public void testRollbackDataPolicy_Phase2_Rollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
- // Write user data version = 2
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
- RollbackUtils.rollback(info.getRollbackId());
- }
-
- @Test
- public void testRollbackDataPolicy_Phase3_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.C)).isEqualTo(1);
- // Read user data version from userdata.txt
- // A's user data version is -1 for user data is wiped.
- // B's user data version is 1 as rollback committed.
- // C's user data version is -1 for user data is wiped.
- assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.C)).isEqualTo(-1);
- }
-
- @Test
public void expireRollbacks() throws Exception {
// testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
// committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
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 1aa5c249ff18..1c2f2449ed8c 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
@@ -267,25 +267,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
runPhase("testRollbackAllowlistedApp_Phase2_VerifyInstall");
}
- @Test
- public void testRollbackDataPolicy() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc_ce/0/rollback");
-
- runPhase("testRollbackDataPolicy_Phase1_Install");
- getDevice().reboot();
- runPhase("testRollbackDataPolicy_Phase2_Rollback");
- getDevice().reboot();
- runPhase("testRollbackDataPolicy_Phase3_VerifyRollback");
-
- // Verify snapshots are deleted after restoration
- List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- assertDirectoryIsEmpty(after.get(0));
- }
-
/**
* Tests that userdata of apk-in-apex is restored when apex is rolled back.
*/
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 2c6c8d7cae20..1381c0a43c30 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -73,6 +73,14 @@
android:text="@string/play_trigger"
android:onClick="onPlayTriggerButtonClicked"
android:padding="20dp" />
+
+ <Button
+ android:id="@+id/get_state_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_model_state"
+ android:onClick="onGetModelStateButtonClicked"
+ android:padding="20dp" />
</LinearLayout>
<LinearLayout
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index c48b64884c5e..adb0fcf8e185 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -22,6 +22,7 @@
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
<string name="play_trigger">Play Trigger Audio</string>
+ <string name="get_model_state">Get State</string>
<string name="capture">Capture Audio</string>
<string name="stop_capture">Stop Capturing Audio</string>
<string name="play_capture">Play Captured Audio</string>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index c3c4cf556986..72aa38dc7e4b 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -257,6 +257,14 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
}
}
+ public synchronized void onGetModelStateButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService");
+ } else {
+ mService.getModelState(mSelectedModelUuid);
+ }
+ }
+
public synchronized void onCaptureAudioCheckboxClicked(View v) {
// See if we have the right permissions
if (!mService.hasMicrophonePermission()) {
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 380e29984c63..6d4ffcff7d45 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -46,6 +48,7 @@ import java.util.Properties;
import java.util.Random;
import java.util.UUID;
+
public class SoundTriggerTestService extends Service {
private static final String TAG = "SoundTriggerTestSrv";
private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
@@ -57,6 +60,8 @@ public class SoundTriggerTestService extends Service {
private Random mRandom;
private UserActivity mUserActivity;
+ private static int captureCount;
+
public interface UserActivity {
void addModel(UUID modelUuid, String state);
void setModelState(UUID modelUuid, String state);
@@ -131,6 +136,8 @@ public class SoundTriggerTestService extends Service {
} else if (command.equals("set_capture_timeout")) {
setCaptureAudioTimeout(getModelUuidFromIntent(intent),
intent.getIntExtra("timeout", 5000));
+ } else if (command.equals("get_model_state")) {
+ getModelState(getModelUuidFromIntent(intent));
} else {
Log.e(TAG, "Unknown command '" + command + "'");
}
@@ -432,6 +439,17 @@ public class SoundTriggerTestService extends Service {
return modelInfo != null && modelInfo.captureAudioTrack != null;
}
+ public synchronized void getModelState(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ int status = mSoundTriggerUtil.getModelState(modelUuid);
+ postMessage("GetModelState for: " + modelInfo.name + " returns: "
+ + status);
+ }
+
private void loadModelsInDataDir() {
// Load all the models in the data dir.
boolean loadedModel = false;
@@ -527,18 +545,29 @@ public class SoundTriggerTestService extends Service {
}
}
+
private class CaptureAudioRecorder implements Runnable {
private final ModelInfo mModelInfo;
+
+ // EventPayload and RecognitionEvent are equivalant. Only one will be non-null.
private final SoundTriggerDetector.EventPayload mEvent;
+ private final RecognitionEvent mRecognitionEvent;
public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
mModelInfo = modelInfo;
mEvent = event;
+ mRecognitionEvent = null;
+ }
+
+ public CaptureAudioRecorder(ModelInfo modelInfo, RecognitionEvent event) {
+ mModelInfo = modelInfo;
+ mEvent = null;
+ mRecognitionEvent = event;
}
@Override
public void run() {
- AudioFormat format = mEvent.getCaptureAudioFormat();
+ AudioFormat format = getAudioFormat();
if (format == null) {
postErrorToast("No audio format in recognition event.");
return;
@@ -600,18 +629,21 @@ public class SoundTriggerTestService extends Service {
}
audioRecord = new AudioRecord(attributes, format, bytesRequired,
- mEvent.getCaptureSession());
+ getCaptureSession());
byte[] buffer = new byte[bytesRequired];
// Create a file so we can save the output data there for analysis later.
FileOutputStream fos = null;
try {
- fos = new FileOutputStream( new File(
- getFilesDir() + File.separator
- + mModelInfo.name.replace(' ', '_')
- + "_capture_" + format.getChannelCount() + "ch_"
- + format.getSampleRate() + "hz_" + encoding + ".pcm"));
+ File file = new File(
+ getFilesDir() + File.separator
+ + mModelInfo.name.replace(' ', '_')
+ + "_capture_" + format.getChannelCount() + "ch_"
+ + format.getSampleRate() + "hz_" + encoding
+ + "_" + (++captureCount) + ".pcm");
+ Log.i(TAG, "Writing audio to: " + file);
+ fos = new FileOutputStream(file);
} catch (IOException e) {
Log.e(TAG, "Failed to open output for saving PCM data", e);
postErrorToast("Failed to open output for saving PCM data: "
@@ -635,6 +667,10 @@ public class SoundTriggerTestService extends Service {
bytesRequired -= bytesRead;
}
audioRecord.stop();
+ if (fos != null) {
+ fos.flush();
+ fos.close();
+ }
} catch (Exception e) {
Log.e(TAG, "Error recording trigger audio", e);
postErrorToast("Error recording trigger audio: " + e.getMessage());
@@ -651,6 +687,26 @@ public class SoundTriggerTestService extends Service {
setModelState(mModelInfo, "Recording finished");
}
}
+
+ private AudioFormat getAudioFormat() {
+ if (mEvent != null) {
+ return mEvent.getCaptureAudioFormat();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureFormat;
+ }
+ return null;
+ }
+
+ private int getCaptureSession() {
+ if (mEvent != null) {
+ return mEvent.getCaptureSession();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureSession;
+ }
+ return 0;
+ }
}
// Implementation of SoundTriggerDetector.Callback.
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index cfe8c855ac81..996a78f2e42a 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,6 +18,8 @@ package com.android.test.soundtrigger;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
@@ -27,6 +29,7 @@ import android.util.Log;
import com.android.internal.app.ISoundTriggerService;
+import java.lang.reflect.Method;
import java.lang.RuntimeException;
import java.util.UUID;
@@ -50,13 +53,31 @@ public class SoundTriggerUtil {
* The sound model must contain a valid UUID.
*
* @param soundModel The sound model to add/update.
+ * @return The true if the model was loaded successfully, false otherwise.
*/
public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
if (soundModel == null) {
throw new RuntimeException("Bad sound model");
}
mSoundTriggerManager.updateModel(soundModel);
- return true;
+ // TODO: call loadSoundModel in the soundtrigger manager updateModel method
+ // instead of here. It is needed to keep soundtrigger manager internal
+ // state consistent.
+ return mSoundTriggerManager
+ .loadSoundModel(getGenericSoundModel(soundModel)) == 0;
+ }
+
+ private GenericSoundModel getGenericSoundModel(
+ SoundTriggerManager.Model soundModel) {
+ try {
+ Method method = SoundTriggerManager.Model.class
+ .getDeclaredMethod("getGenericSoundModel");
+ method.setAccessible(true);
+ return (GenericSoundModel) method.invoke(soundModel);
+ } catch (ReflectiveOperationException e) {
+ Log.e(TAG, "Failed to getGenericSoundModel: " + soundModel, e);
+ return null;
+ }
}
/**
@@ -92,6 +113,16 @@ public class SoundTriggerUtil {
return true;
}
+ /**
+ * Get the current model state
+ *
+ * @param modelId The model ID to look-up the sound model for.
+ * @return 0 if the call succeeds, or an error code if it fails.
+ */
+ public int getModelState(UUID modelId) {
+ return mSoundTriggerManager.getModelState(modelId);
+ }
+
public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
SoundTriggerDetector.Callback callback) {
return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index cac14a72a706..558798dd8ec5 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -37,6 +37,7 @@ android_test_helper_app {
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
],
+ platform_apis: true,
}
java_test_host {
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 4684f0182d03..c610641932df 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -17,16 +17,27 @@
package com.android.tests.stagedinstallinternal;
import static com.android.cts.install.lib.InstallUtils.getPackageInstaller;
+import static com.android.cts.install.lib.InstallUtils.waitForSessionReady;
import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.Manifest;
+import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.StagedApexInfo;
+import android.os.IBinder;
+import android.os.ServiceManager;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -39,6 +50,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -401,9 +414,73 @@ public class StagedInstallInternalTest {
AssertionError.class,
"Staged session " + sessionId + " already contains " + SHIM_APEX_PACKAGE_NAME,
Install.single(APEX_V2));
+ }
+
+ @Test
+ public void testGetStagedModuleNames() throws Exception {
+ // Before staging a session
+ String[] result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(0);
+ // Stage an apex
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ waitForSessionReady(sessionId);
+ result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(1);
+ assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME});
+ // Abandon the session
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
+ result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(0);
+ }
+
+ @Test
+ public void testGetStagedApexInfo() throws Exception {
+ // Ask for non-existing module
+ StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
+ assertThat(result).isNull();
+ // Stage an apex
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ waitForSessionReady(sessionId);
+ // Query proper module name
+ result = getPackageManagerNative().getStagedApexInfo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(result.moduleName).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
+ }
+ public static class MockStagedApexObserver extends IStagedApexObserver.Stub {
+ @Override
+ public void onApexStaged(ApexStagedEvent event) {
+ assertThat(event).isNotNull();
+ }
}
+ @Test
+ public void testStagedApexObserver() throws Exception {
+ MockStagedApexObserver realObserver = new MockStagedApexObserver();
+ IStagedApexObserver observer = spy(realObserver);
+ assertThat(observer).isNotNull();
+ getPackageManagerNative().registerStagedApexObserver(observer);
+
+ // Stage an apex and verify observer was called
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ waitForSessionReady(sessionId);
+ ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class);
+ verify(observer, timeout(5000)).onApexStaged(captor.capture());
+ assertThat(captor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[] {SHIM_APEX_PACKAGE_NAME});
+
+ // Abandon and verify observer is called
+ Mockito.clearInvocations(observer);
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
+ verify(observer, timeout(5000)).onApexStaged(captor.capture());
+ assertThat(captor.getValue().stagedApexModuleNames).hasLength(0);
+ }
+
+ private IPackageManagerNative getPackageManagerNative() {
+ IBinder binder = ServiceManager.waitForService("package_native");
+ assertThat(binder).isNotNull();
+ return IPackageManagerNative.Stub.asInterface(binder);
+ }
private static void assertSessionApplied(int sessionId) {
assertSessionState(sessionId, (session) -> {
assertThat(session.isStagedSessionApplied()).isTrue();
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 5021009f65ae..31021031b47a 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -478,6 +478,21 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails");
}
+ @Test
+ public void testGetStagedModuleNames() throws Exception {
+ runPhase("testGetStagedModuleNames");
+ }
+
+ @Test
+ public void testGetStagedApexInfo() throws Exception {
+ runPhase("testGetStagedApexInfo");
+ }
+
+ @Test
+ public void testStagedApexObserver() throws Exception {
+ runPhase("testStagedApexObserver");
+ }
+
private List<String> getStagingDirectories() throws DeviceNotAvailableException {
String baseDir = "/data/app-staging";
try {
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index e07fbbf7a1c1..c59a41ee0cd3 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,15 +37,19 @@ android_test {
"vts",
],
data: [
- ":NotoColorEmojiTtf",
+ ":NotoSerif-Regular.ttf",
+ ":NotoSerif-Bold.ttf",
":UpdatableSystemFontTestCertDer",
- ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ ":UpdatableSystemFontTest_NotoColorEmoji.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmoji.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
],
sdk_version: "test_current",
}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 4f6487e7e953..6effa7bd0a50 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -29,13 +29,17 @@
<option name="cleanup" value="true" />
<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" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig" />
+ <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
+ <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
index 947e9c2ff56a..a8c27fb0f116 100644
--- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
+++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
@@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.Activity;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -27,14 +28,20 @@ import android.widget.TextView;
/** Test app to render an emoji. */
public class EmojiRenderingTestActivity extends Activity {
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
- TextView textView = new TextView(this);
- textView.setText("\uD83E\uDD72"); // 🥲
- container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView emojiTextView = new TextView(this);
+ emojiTextView.setText("\uD83E\uDD72"); // 🥲
+ container.addView(emojiTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView serifTextView = new TextView(this);
+ serifTextView.setTypeface(Typeface.create(TEST_NOTO_SERIF, Typeface.NORMAL));
+ serifTextView.setText(TEST_NOTO_SERIF);
+ container.addView(serifTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(container);
}
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6bd07d0a84fd..87fda0d220e5 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -16,6 +16,9 @@
package com.android.updatablesystemfont;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_BOLD;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.graphics.fonts.FontFamilyUpdateRequest;
import android.graphics.fonts.FontFileUpdateRequest;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontStyle;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.RootPermissionTest;
import android.security.FileIntegrityManager;
@@ -77,31 +81,45 @@ public class UpdatableSystemFontTest {
private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
private static final String DATA_FONTS_DIR = "/data/fonts/files/";
private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
- private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
+ private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
+ private static final String NOTO_COLOR_EMOJI_TTF =
"/data/local/tmp/NotoColorEmoji.ttf";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+ private static final String NOTO_COLOR_EMOJI_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
// A font with revision == 0.
private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V0_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig";
// A font with revision == original + 1
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig";
// A font with revision == original + 2
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig";
+
+ private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
+ private static final String NOTO_SERIF_REGULAR_TTF =
+ "/data/local/tmp/NotoSerif-Regular.ttf";
+ private static final String NOTO_SERIF_REGULAR_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
+
+ private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
+ private static final String NOTO_SERIF_BOLD_TTF =
+ "/data/local/tmp/NotoSerif-Bold.ttf";
+ private static final String NOTO_SERIF_BOLD_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
private static final String EMOJI_RENDERING_TEST_ACTIVITY =
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
+ // This should be the same as the one in EmojiRenderingTestActivity.
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY =
@@ -141,11 +159,20 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont() throws Exception {
+ FontConfig oldFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
+ // Check that font config is updated.
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
+ FontConfig newFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
+ assertThat(newFontConfig.getConfigVersion())
+ .isGreaterThan(oldFontConfig.getConfigVersion());
+ assertThat(newFontConfig.getLastModifiedTimeMillis())
+ .isGreaterThan(oldFontConfig.getLastModifiedTimeMillis());
// The updated font should be readable and unmodifiable.
expectCommandToSucceed("dd status=none if=" + fontPath + " of=/dev/null");
expectCommandToFail("dd status=none if=" + CERT_PATH + " of=" + fontPath);
@@ -154,11 +181,11 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_twice() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
@@ -173,16 +200,16 @@ public class UpdatableSystemFontTest {
public void updateFont_allowSameVersion() throws Exception {
// Update original font to the same version
assertThat(updateFontFile(
- ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG))
+ NOTO_COLOR_EMOJI_TTF, NOTO_COLOR_EMOJI_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
// Update updated font to the same version
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -195,28 +222,58 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_invalidCert() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
}
@Test
public void updateFont_downgradeFromSystem() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
public void updateFont_downgradeFromData() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
+ public void updateFontFamily() throws Exception {
+ assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(0).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(0).getStyle().getWeight())
+ .isEqualTo(FONT_WEIGHT_NORMAL);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(1).getStyle().getWeight()).isEqualTo(FONT_WEIGHT_BOLD);
+ }
+
+ @Test
+ public void updateFontFamily_asNewFont() throws Exception {
+ assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
+ .isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ }
+
+ @Test
public void launchApp() throws Exception {
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
@@ -231,22 +288,25 @@ public class UpdatableSystemFontTest {
String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
+ updateNotoSerifAs(TEST_NOTO_SERIF);
+ String notoSerifPath = getFontPath(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
startActivity(EMOJI_RENDERING_TEST_APP_ID, EMOJI_RENDERING_TEST_ACTIVITY);
// The original font should NOT be opened by the app.
SystemUtil.eventually(() -> {
assertThat(isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
assertThat(isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
+ assertThat(isFileOpenedBy(notoSerifPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
}, ACTIVITY_TIMEOUT_MILLIS);
}
@Test
public void reboot() throws Exception {
expectCommandToSucceed(String.format("cmd font update %s %s",
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -264,7 +324,7 @@ public class UpdatableSystemFontTest {
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
for (int i = 0; i < 10; i++) {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
List<String> openFiles = getOpenFiles("system_server");
for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
@@ -285,7 +345,7 @@ public class UpdatableSystemFontTest {
public void fdLeakTest_withoutPermission() throws Exception {
Pattern patternEmojiVPlus1 =
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
- byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
assertThrows(SecurityException.class,
@@ -340,18 +400,56 @@ public class UpdatableSystemFontTest {
configVersion);
}
+ private int updateNotoSerifAs(String familyName) throws IOException {
+ List<FontFamilyUpdateRequest.Font> fonts = Arrays.asList(
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT)).build(),
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_BOLD_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT)).build());
+ FontFamilyUpdateRequest.FontFamily fontFamily =
+ new FontFamilyUpdateRequest.FontFamily.Builder(familyName, fonts).build();
+ byte[] regularSig = Files.readAllBytes(Paths.get(NOTO_SERIF_REGULAR_SIG));
+ byte[] boldSig = Files.readAllBytes(Paths.get(NOTO_SERIF_BOLD_SIG));
+ try (ParcelFileDescriptor regularFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_REGULAR_TTF), MODE_READ_ONLY);
+ ParcelFileDescriptor boldFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_BOLD_TTF), MODE_READ_ONLY)) {
+ return SystemUtil.runWithShellPermissionIdentity(() -> {
+ FontConfig fontConfig = mFontManager.getFontConfig();
+ return mFontManager.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(regularFd, regularSig))
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(boldFd, boldSig))
+ .addFontFamily(fontFamily)
+ .build(), fontConfig.getConfigVersion());
+ });
+ }
+ }
+
private String getFontPath(String psName) {
- return SystemUtil.runWithShellPermissionIdentity(() -> {
- FontConfig fontConfig = mFontManager.getFontConfig();
- for (FontConfig.FontFamily family : fontConfig.getFontFamilies()) {
- for (FontConfig.Font font : family.getFontList()) {
- if (psName.equals(font.getPostScriptName())) {
- return font.getFile().getAbsolutePath();
- }
- }
- }
- throw new AssertionError("Font not found: " + psName);
- });
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .flatMap(family -> family.getFontList().stream())
+ .filter(font -> psName.equals(font.getPostScriptName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Font not found: " + psName))
+ .getFile()
+ .getAbsolutePath();
+ }
+
+ private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .filter(family -> familyName.equals(family.getName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Family not found: " + familyName));
}
private static void startActivity(String appId, String activityId) throws Exception {
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 426464ea574e..64b698dd0db0 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -22,9 +22,9 @@ package {
}
// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font and module name.
+// TODO: fix the font file name.
filegroup {
- name: "NotoColorEmojiTtf",
+ name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
srcs: ["NotoColorEmoji.ttf"],
}
@@ -48,9 +48,9 @@ genrule_defaults {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -59,9 +59,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -70,9 +70,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -94,29 +94,43 @@ genrule_defaults {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmoji.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmoji.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiV0Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Regular.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":NotoSerif-Bold.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
}
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
new file mode 100644
index 000000000000..15a680db0f4b
--- /dev/null
+++ b/tests/componentalias/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+ name: "ComponentAliasTestsCommon",
+ srcs: [
+ "common/**/*.java",
+ ],
+ plugins: [
+ "staledataclass-annotation-processor",
+ ],
+ platform_apis: true, // We use hidden APIs in the test.
+}
+
+android_test {
+ name: "ComponentAliasTests",
+ static_libs: [
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "mockito-target-extended-minus-junit4",
+ "truth-prebuilt",
+ "ub-uiautomator",
+ "ComponentAliasTestsCommon",
+ ],
+ libs: ["android.test.base"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ platform_apis: true, // We use hidden APIs in the test.
+}
diff --git a/tests/componentalias/AndroidManifest.xml b/tests/componentalias/AndroidManifest.xml
new file mode 100755
index 000000000000..893be8e47297
--- /dev/null
+++ b/tests/componentalias/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests" >
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.componentalias.tests" >
+ </instrumentation>
+</manifest>
diff --git a/tests/componentalias/AndroidTest.xml b/tests/componentalias/AndroidTest.xml
new file mode 100644
index 000000000000..e2c37d25f687
--- /dev/null
+++ b/tests/componentalias/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="ComponentAliasTests.apk" />
+ <option name="test-file-name" value="ComponentAliasAppMain.apk" />
+ <option name="test-file-name" value="ComponentAliasAppSub1.apk" />
+ <option name="test-file-name" value="ComponentAliasAppSub2.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Exempt the helper APKs from the BG restriction, so they can start BG services. -->
+ <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app" />
+ <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app.sub1" />
+ <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app.sub2" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.content.componentalias.tests" />
+ <option name="runtime-hint" value="2m" />
+ <option name="isolated-storage" value="false" />
+ </test>
+</configuration>
diff --git a/tests/componentalias/OWNERS b/tests/componentalias/OWNERS
new file mode 100644
index 000000000000..1073c817d793
--- /dev/null
+++ b/tests/componentalias/OWNERS
@@ -0,0 +1,2 @@
+omakoto@google.com
+yamasani@google.com \ No newline at end of file
diff --git a/tests/componentalias/apps/Android.bp b/tests/componentalias/apps/Android.bp
new file mode 100644
index 000000000000..b2cb0f74899f
--- /dev/null
+++ b/tests/componentalias/apps/Android.bp
@@ -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.
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// We build three helper APKs from the same source.
+// - The main APK (ComponentAliasAppMain), which contains both the alias and the target components.
+// - The sub APKs (ComponentAliasAppSub*), which only contains the target components.
+
+java_defaults {
+ name: "component_alias_app_defaults",
+ libs: ["android.test.base"],
+ sdk_version: "test_current",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.test.rules",
+ "ComponentAliasTestsCommon",
+ ],
+}
+
+android_test_helper_app {
+ name: "ComponentAliasAppMain",
+ defaults: [
+ "component_alias_app_defaults",
+ ],
+ package_name: "android.content.componentalias.tests.app",
+ manifest: "AndroidManifest_main.xml",
+}
+
+android_test_helper_app {
+ name: "ComponentAliasAppSub1",
+ defaults: [
+ "component_alias_app_defaults",
+ ],
+ package_name: "android.content.componentalias.tests.app.sub1",
+ manifest: "AndroidManifest_sub.xml",
+}
+
+android_test_helper_app {
+ name: "ComponentAliasAppSub2",
+ defaults: [
+ "component_alias_app_defaults",
+ ],
+ package_name: "android.content.componentalias.tests.app.sub2",
+ manifest: "AndroidManifest_sub.xml",
+}
diff --git a/tests/componentalias/apps/AndroidManifest_main.xml b/tests/componentalias/apps/AndroidManifest_main.xml
new file mode 100755
index 000000000000..2caef3ee8f61
--- /dev/null
+++ b/tests/componentalias/apps/AndroidManifest_main.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests.app" >
+
+ <application>
+ <!--
+ Alias components.
+ Note aliases do not have the actual implementation, because they're never called
+ directly.
+ -->
+
+ <service android:name=".s.Alias01" android:exported="true" android:enabled="true" >
+ <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target01" />
+ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+ <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_01" /></intent-filter>
+ </service>
+ <service android:name=".s.Alias02" android:exported="true" android:enabled="true" >
+ <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub2/android.content.componentalias.tests.app.s.Target02" />
+ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+ <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_02" /></intent-filter>
+ </service>
+ <service android:name=".s.Alias03" android:exported="true" android:enabled="true" >
+ <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target03" />
+ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+ <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_03" /></intent-filter>
+ </service>
+ <service android:name=".s.Alias04" android:exported="true" android:enabled="true" >
+ <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub2/android.content.componentalias.tests.app.s.Target04" />
+ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+ <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_04" /></intent-filter>
+ </service>
+ <service android:name=".s.Alias05" android:exported="true" android:enabled="true" >
+ <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target05" />
+ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+ <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_05" /></intent-filter>
+ </service>
+
+ <!-- Target components -->
+
+ <service android:name=".s.Target01" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target02" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target03" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target04" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target05" android:exported="true" android:enabled="true" >
+ </service>
+ </application>
+</manifest>
diff --git a/tests/componentalias/apps/AndroidManifest_sub.xml b/tests/componentalias/apps/AndroidManifest_sub.xml
new file mode 100755
index 000000000000..2ddd965ddca1
--- /dev/null
+++ b/tests/componentalias/apps/AndroidManifest_sub.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.componentalias.tests.app" >
+
+ <application>
+ <!-- Only contain the target components -->
+
+ <service android:name=".s.Target01" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target02" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target03" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target04" android:exported="true" android:enabled="true" >
+ </service>
+ <service android:name=".s.Target05" android:exported="true" android:enabled="true" >
+ </service>
+ </application>
+</manifest>
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java
new file mode 100644
index 000000000000..fb678298fdef
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.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 android.content.componentalias.tests.app.s;
+
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TAG;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TEST_PACKAGE;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.componentalias.tests.common.ComponentAliasMessage;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BroadcastMessenger;
+
+public class BaseService extends Service {
+ private String getMyIdentity() {
+ return (new ComponentName(this.getPackageName(), this.getClass().getCanonicalName()))
+ .flattenToShortString();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand: on " + getMyIdentity() + " intent=" + intent);
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity(getMyIdentity())
+ .setMethodName("onStartCommand")
+ .setIntent(intent);
+ BroadcastMessenger.send(this, TEST_PACKAGE, m);
+
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i(TAG, "onDestroy: on " + getMyIdentity());
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity(getMyIdentity())
+ .setMethodName("onDestroy");
+ BroadcastMessenger.send(this, TEST_PACKAGE, m);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind: on " + getMyIdentity() + " intent=" + intent);
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity(getMyIdentity())
+ .setMethodName("onBind")
+ .setIntent(intent);
+ BroadcastMessenger.send(this, TEST_PACKAGE, m);
+
+ return new Binder();
+ }
+}
diff --git a/core/java/android/uwb/UwbAddress.aidl b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java
index a202b1a1f51d..87e48cb699c2 100644
--- a/core/java/android/uwb/UwbAddress.aidl
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.content.componentalias.tests.app.s;
-package android.uwb;
-
-parcelable UwbAddress;
+public class Target01 extends BaseService {
+}
diff --git a/core/java/android/uwb/SessionHandle.aidl b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java
index 58a7dbb2ef9f..0e8a6a831240 100644
--- a/core/java/android/uwb/SessionHandle.aidl
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.content.componentalias.tests.app.s;
-package android.uwb;
-
-parcelable SessionHandle;
+public class Target02 extends BaseService {
+}
diff --git a/core/java/android/uwb/RangingReport.aidl b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java
index c32747ae58da..b7990bb58eb1 100644
--- a/core/java/android/uwb/RangingReport.aidl
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java
@@ -1,5 +1,5 @@
/*
- * Copyright 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.content.componentalias.tests.app.s;
-package android.uwb;
-
-parcelable RangingReport;
+public class Target03 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java
new file mode 100644
index 000000000000..0e51a6b0a97b
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java
@@ -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.componentalias.tests.app.s;
+
+public class Target04 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java
new file mode 100644
index 000000000000..77060cd2e2e1
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java
@@ -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.componentalias.tests.app.s;
+
+public class Target05 extends BaseService {
+}
diff --git a/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
new file mode 100644
index 000000000000..399c07078169
--- /dev/null
+++ b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.componentalias.tests.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Parcelabe containing a "message" that's meant to be delivered via BroadcastMessenger.
+ *
+ * To add a new field, just add a private member field, and run:
+ * codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
+ */
+@DataClass(
+ genConstructor = false,
+ genSetters = true,
+ genToString = true,
+ genAidl = false)
+public final class ComponentAliasMessage implements Parcelable {
+ public ComponentAliasMessage() {
+ }
+
+ @Nullable
+ private String mMessage;
+
+ @Nullable
+ private String mMethodName;
+
+ @Nullable
+ private String mSenderIdentity;
+
+ @Nullable
+ private Intent mIntent;
+
+ @Nullable
+ private ComponentName mComponent;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public @Nullable String getMessage() {
+ return mMessage;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getMethodName() {
+ return mMethodName;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getSenderIdentity() {
+ return mSenderIdentity;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable Intent getIntent() {
+ return mIntent;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable ComponentName getComponent() {
+ return mComponent;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ComponentAliasMessage setMessage(@NonNull String value) {
+ mMessage = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ComponentAliasMessage setMethodName(@NonNull String value) {
+ mMethodName = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ComponentAliasMessage setSenderIdentity(@NonNull String value) {
+ mSenderIdentity = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ComponentAliasMessage setIntent(@NonNull Intent value) {
+ mIntent = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull ComponentAliasMessage setComponent(@NonNull ComponentName value) {
+ mComponent = value;
+ return this;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ComponentAliasMessage { " +
+ "message = " + mMessage + ", " +
+ "methodName = " + mMethodName + ", " +
+ "senderIdentity = " + mSenderIdentity + ", " +
+ "intent = " + mIntent + ", " +
+ "component = " + mComponent +
+ " }";
+ }
+
+ @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 (mMessage != null) flg |= 0x1;
+ if (mMethodName != null) flg |= 0x2;
+ if (mSenderIdentity != null) flg |= 0x4;
+ if (mIntent != null) flg |= 0x8;
+ if (mComponent != null) flg |= 0x10;
+ dest.writeByte(flg);
+ if (mMessage != null) dest.writeString(mMessage);
+ if (mMethodName != null) dest.writeString(mMethodName);
+ if (mSenderIdentity != null) dest.writeString(mSenderIdentity);
+ if (mIntent != null) dest.writeTypedObject(mIntent, flags);
+ if (mComponent != null) dest.writeTypedObject(mComponent, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ComponentAliasMessage(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ String message = (flg & 0x1) == 0 ? null : in.readString();
+ String methodName = (flg & 0x2) == 0 ? null : in.readString();
+ String senderIdentity = (flg & 0x4) == 0 ? null : in.readString();
+ Intent intent = (flg & 0x8) == 0 ? null : (Intent) in.readTypedObject(Intent.CREATOR);
+ ComponentName component = (flg & 0x10) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+
+ this.mMessage = message;
+ this.mMethodName = methodName;
+ this.mSenderIdentity = senderIdentity;
+ this.mIntent = intent;
+ this.mComponent = component;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ComponentAliasMessage> CREATOR
+ = new Parcelable.Creator<ComponentAliasMessage>() {
+ @Override
+ public ComponentAliasMessage[] newArray(int size) {
+ return new ComponentAliasMessage[size];
+ }
+
+ @Override
+ public ComponentAliasMessage createFromParcel(@NonNull Parcel in) {
+ return new ComponentAliasMessage(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1629137098129L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java",
+ inputSignatures = "private @android.annotation.Nullable java.lang.String mMessage\nprivate @android.annotation.Nullable java.lang.String mMethodName\nprivate @android.annotation.Nullable java.lang.String mSenderIdentity\nprivate @android.annotation.Nullable android.content.Intent mIntent\nprivate @android.annotation.Nullable android.content.ComponentName mComponent\nclass ComponentAliasMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true, genToString=true, genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java
new file mode 100644
index 000000000000..f30607210af5
--- /dev/null
+++ b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.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.content.componentalias.tests.common;
+
+public final class ComponentAliasTestCommon {
+ private ComponentAliasTestCommon() {
+ }
+
+ public static final String TAG = "ComponentAliasTest";
+
+ public static final String TEST_PACKAGE = "android.content.componentalias.tests";
+
+ public static final String APP_PACKAGE = "android.content.componentalias.tests.app";
+ public static final String SUB1_PACKAGE = "android.content.componentalias.tests.app.sub1";
+ public static final String SUB2_PACKAGE = "android.content.componentalias.tests.app.sub2";
+}
diff --git a/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java b/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java
new file mode 100644
index 000000000000..175082e8c056
--- /dev/null
+++ b/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compatibility.common.util;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * Provides a one-way communication mechanism using a Parcelable as a payload, via broadcasts.
+ *
+ * TODO: Move it to compatibility-device-util-axt.
+ */
+public final class BroadcastMessenger {
+ private static final String TAG = "BroadcastMessenger";
+
+ private static final String ACTION_MESSAGE =
+ "com.android.compatibility.common.util.BroadcastMessenger.ACTION_MESSAGE";
+ private static final String ACTION_PING =
+ "com.android.compatibility.common.util.BroadcastMessenger.ACTION_PING";
+ private static final String EXTRA_MESSAGE =
+ "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_MESSAGE";
+
+ /**
+ * We need to drop messages that were sent before the receiver was created. We keep
+ * track of the message send time in this extra.
+ */
+ private static final String EXTRA_SENT_TIME =
+ "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_SENT_TIME";
+
+ private static long getCurrentTime() {
+ return SystemClock.uptimeMillis();
+ }
+
+ private static void sendBroadcast(@NonNull Intent i,
+ @NonNull Context context, @NonNull String receiverPackage) {
+ i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ i.setPackage(receiverPackage);
+ i.putExtra(EXTRA_SENT_TIME, getCurrentTime());
+
+ context.sendBroadcast(i);
+ }
+
+ /** Send a message to the {@link Receiver} in a given package. */
+ public static <T extends Parcelable> void send(@NonNull Context context,
+ @NonNull String receiverPackage, @NonNull T message) {
+ final Intent i = new Intent(ACTION_MESSAGE);
+ i.putExtra(EXTRA_MESSAGE, Preconditions.checkNotNull(message));
+
+ Log.i(TAG, "Sending: " + message);
+ sendBroadcast(i, context, receiverPackage);
+ }
+
+ private static void sendPing(@NonNull Context context, @NonNull String receiverPackage) {
+ final Intent i = new Intent(ACTION_PING);
+
+ Log.i(TAG, "Sending a ping");
+ sendBroadcast(i, context, receiverPackage);
+ }
+
+ /**
+ * Receive messages sent with {@link #send}. Note it'll ignore all the messages that were
+ * sent before instantiated.
+ */
+ public static final class Receiver<T extends Parcelable> implements AutoCloseable {
+ private final Context mContext;
+ private final HandlerThread mReceiverThread = new HandlerThread(TAG);
+
+ @GuardedBy("mMessages")
+ private final ArrayList<T> mMessages = new ArrayList<>();
+ private final long mCreatedTime = getCurrentTime();
+ private boolean mRegistered;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Log.d(TAG, "Received intent: " + intent);
+ switch (intent.getAction()) {
+ case ACTION_MESSAGE:
+ case ACTION_PING:
+ break;
+ default:
+ throw new RuntimeException("Unknown broadcast received: " + intent);
+ }
+ if (intent.getLongExtra(EXTRA_SENT_TIME, 0) < mCreatedTime) {
+ Log.i(TAG, "Dropping stale broadcast: " + intent);
+ return;
+ }
+
+ // Note for a PING, the message will be null.
+ final T message = intent.getParcelableExtra(EXTRA_MESSAGE);
+ if (message != null) {
+ Log.i(TAG, "Received: " + message);
+ }
+
+ synchronized (mMessages) {
+ mMessages.add(message);
+ mMessages.notifyAll();
+ }
+ }
+ };
+
+ /**
+ * Constructor.
+ */
+ public Receiver(@NonNull Context context) {
+ mContext = context;
+
+ mReceiverThread.start();
+
+ final IntentFilter fi = new IntentFilter(ACTION_MESSAGE);
+ fi.addAction(ACTION_PING);
+
+ context.registerReceiver(mReceiver, fi, /** permission=*/ null,
+ mReceiverThread.getThreadHandler());
+ mRegistered = true;
+ }
+
+ @Override
+ public void close() {
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiverThread.quit();
+ mRegistered = false;
+ }
+ }
+
+ /**
+ * Receive the next message with a 60 second timeout.
+ */
+ @NonNull
+ public T waitForNextMessage() throws Exception {
+ return waitForNextMessage(60_000);
+ }
+
+ /**
+ * Receive the next message.
+ */
+ @NonNull
+ public T waitForNextMessage(long timeoutMillis) throws Exception {
+ synchronized (mMessages) {
+ final long timeout = System.currentTimeMillis() + timeoutMillis;
+ while (mMessages.size() == 0) {
+ final long wait = timeout - System.currentTimeMillis();
+ if (wait <= 0) {
+ throw new RuntimeException("Timeout waiting for the next message");
+ }
+ mMessages.wait(wait);
+ }
+ return mMessages.remove(0);
+ }
+ }
+
+ /**
+ * Ensure that no further messages have been received.
+ *
+ * Call it before {@link #close()}.
+ */
+ public void ensureNoMoreMessages() throws Exception {
+ // Send a ping to myself.
+ sendPing(mContext, mContext.getPackageName());
+
+ final T m = waitForNextMessage();
+ if (m == null) {
+ return; // Okay. Ping will deliver a null message.
+ }
+ throw new RuntimeException("No more messages expected, but received: " + m);
+ }
+ }
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
new file mode 100644
index 000000000000..81fc9bfd48a9
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.componentalias.tests;
+
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.APP_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB1_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB2_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TAG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.componentalias.tests.common.ComponentAliasMessage;
+import android.os.IBinder;
+import android.provider.DeviceConfig;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.BroadcastMessenger;
+import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.ShellUtils;
+import com.android.compatibility.common.util.TestUtils;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test for the experimental "Component alias" feature.
+ *
+ * Note this test exercises the relevant APIs, but don't actually check if the aliases are
+ * resolved.
+ *
+ * Note all the helper APKs are battery-exempted (via AndroidTest.xml), so they can run
+ * BG services.
+ */
+public class ComponentAliasServiceTest {
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+ private static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
+ @Before
+ public void enableComponentAlias() throws Exception {
+ sDeviceConfig.set("component_alias_overrides", "");
+ sDeviceConfig.set("enable_experimental_component_alias", "true");
+
+ // Device config propagation happens on a handler, so we need to wait for AM to
+ // actually set it.
+ TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+ return ShellUtils.runShellCommand("dumpsys activity component-alias")
+ .indexOf("Enabled: true") > 0;
+ });
+ }
+
+ @AfterClass
+ public static void restoreDeviceConfig() throws Exception {
+ sDeviceConfig.close();
+ }
+
+ /**
+ * Service connection used throughout the tests. It sends a message for each callback via
+ * the messenger.
+ */
+ private static final ServiceConnection sServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.w(TAG, "onServiceConnected: " + name);
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity("sServiceConnection")
+ .setMethodName("onServiceConnected")
+ .setComponent(name);
+
+ BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.w(TAG, "onServiceDisconnected: " + name);
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity("sServiceConnection")
+ .setMethodName("onServiceDisconnected")
+ .setComponent(name);
+
+ BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ Log.w(TAG, "onBindingDied: " + name);
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity("sServiceConnection")
+ .setMethodName("onBindingDied");
+
+ BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ Log.w(TAG, "onNullBinding: " + name);
+
+ ComponentAliasMessage m = new ComponentAliasMessage()
+ .setSenderIdentity("sServiceConnection")
+ .setMethodName("onNullBinding");
+
+ BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
+ }
+ };
+
+ private void testStartAndStopService_common(
+ Intent originalIntent,
+ ComponentName componentNameForClient,
+ ComponentName componentNameForTarget) throws Exception {
+
+ ComponentAliasMessage m;
+
+ try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
+ // Start the service.
+ ComponentName result = sContext.startService(originalIntent);
+ assertThat(result).isEqualTo(componentNameForClient);
+
+ // Check
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onStartCommand");
+ // The app sees the rewritten intent.
+ assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
+
+ // Verify the original intent.
+ assertThat(m.getIntent().getOriginalIntent().getComponent())
+ .isEqualTo(originalIntent.getComponent());
+ assertThat(m.getIntent().getOriginalIntent().getPackage())
+ .isEqualTo(originalIntent.getPackage());
+
+ // Stop the service.
+ sContext.stopService(originalIntent);
+
+ // Check
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onDestroy");
+
+ receiver.ensureNoMoreMessages();
+ }
+ }
+
+ @Test
+ public void testStartAndStopService_explicitComponentName() throws Exception {
+ Intent i = new Intent().setComponent(
+ new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"));
+
+ ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01");
+ ComponentName target = new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target01");
+
+ testStartAndStopService_common(i, alias, target);
+ }
+
+ @Test
+ public void testStartAndStopService_explicitPackageName() throws Exception {
+ Intent i = new Intent().setPackage(APP_PACKAGE);
+ i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+ ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
+ ComponentName target = new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02");
+
+ testStartAndStopService_common(i, alias, target);
+ }
+
+ @Test
+ public void testStartAndStopService_override() throws Exception {
+ Intent i = new Intent().setPackage(APP_PACKAGE);
+ i.setAction(APP_PACKAGE + ".IS_ALIAS_01");
+
+ // Change some of the aliases from what's defined in <meta-data>.
+
+ ComponentName aliasA = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01");
+ ComponentName targetA = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Target02");
+
+ ComponentName aliasB = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
+ ComponentName targetB = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Target01");
+
+ sDeviceConfig.set("component_alias_overrides",
+ aliasA.flattenToShortString() + ":" + targetA.flattenToShortString()
+ + ","
+ + aliasB.flattenToShortString() + ":" + targetB.flattenToShortString());
+
+ TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+ return ShellUtils.runShellCommand("dumpsys activity component-alias")
+ .indexOf(aliasA.flattenToShortString()
+ + " -> " + targetA.flattenToShortString()) > 0;
+ });
+
+
+ testStartAndStopService_common(i, aliasA, targetA);
+ }
+
+ private void testBindAndUnbindService_common(
+ Intent originalIntent,
+ ComponentName componentNameForClient,
+ ComponentName componentNameForTarget) throws Exception {
+ ComponentAliasMessage m;
+
+ try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
+ // Bind to the service.
+ assertThat(sContext.bindService(
+ originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();
+
+ // Check the target side behavior.
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onBind");
+ // The app sees the rewritten intent.
+ assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
+
+ // Verify the original intent.
+ assertThat(m.getIntent().getOriginalIntent().getComponent())
+ .isEqualTo(originalIntent.getComponent());
+ assertThat(m.getIntent().getOriginalIntent().getPackage())
+ .isEqualTo(originalIntent.getPackage());
+
+ // Check the client side behavior.
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
+ // The app sees the rewritten intent.
+ assertThat(m.getComponent()).isEqualTo(componentNameForClient);
+
+ // Unbind.
+ sContext.unbindService(sServiceConnection);
+
+ // Check the target side behavior.
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onDestroy");
+
+ // Note onServiceDisconnected() won't be called in this case.
+ receiver.ensureNoMoreMessages();
+ }
+ }
+
+ @Test
+ public void testBindService_explicitComponentName() throws Exception {
+ Intent i = new Intent().setComponent(
+ new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"));
+
+ testBindAndUnbindService_common(i,
+ new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"),
+ new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target01"));
+ }
+
+ @Test
+ public void testBindService_explicitPackageName() throws Exception {
+ Intent i = new Intent().setPackage(APP_PACKAGE);
+ i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+ testBindAndUnbindService_common(i,
+ new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02"),
+ new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02"));
+ }
+
+ @Test
+ public void testBindService_serviceKilled() throws Exception {
+ Intent originalIntent = new Intent().setPackage(APP_PACKAGE);
+ originalIntent.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+ final ComponentName componentNameForClient =
+ new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
+ final ComponentName componentNameForTarget =
+ new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02");
+
+ ComponentAliasMessage m;
+
+ try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
+ // Bind to the service.
+ assertThat(sContext.bindService(
+ originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();
+
+ // Check the target side behavior.
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onBind");
+
+ m = receiver.waitForNextMessage();
+ assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
+ assertThat(m.getComponent()).isEqualTo(componentNameForClient);
+
+ // Now kill the service process.
+ ShellUtils.runShellCommand("su 0 killall %s", SUB2_PACKAGE);
+
+ // Check the target side behavior.
+ m = receiver.waitForNextMessage();
+
+ assertThat(m.getMethodName()).isEqualTo("onServiceDisconnected");
+ assertThat(m.getComponent()).isEqualTo(componentNameForClient);
+
+ receiver.ensureNoMoreMessages();
+ }
+ }
+}
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index af9786b92f40..deff42a27f47 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -38,6 +38,6 @@ java_library {
"android.test.runner",
"android.test.base",
"android.test.mock",
- "mockito-target-minus-junit4",
+ "mockito-target-extended-minus-junit4",
],
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index bcd6ed73e133..824f91e1e826 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -45,6 +45,7 @@ public final class FrameworksTestsFilter extends SelectTest {
// Test specifications for FrameworksMockingCoreTests.
"android.app.activity.ActivityThreadClientTest",
"android.view.DisplayTest",
+ "android.window.ConfigurationHelperTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",
@@ -59,10 +60,8 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.RoundedCornersTest",
"android.view.WindowMetricsTest",
"android.view.PendingInsetsControllerTest",
- "android.window.WindowContextTest",
- "android.window.WindowMetricsHelperTest",
+ "android.window.", // all tests under the package.
"android.app.activity.ActivityThreadTest",
- "android.window.WindowContextControllerTest"
};
public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index d3ca357b0305..cabbe7ea7446 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,11 +17,10 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
+#include <optional>
#include <set>
#include <string>
-#include "util/Maybe.h"
-
namespace aapt {
// Information relevant to building an app, parsed from the app's AndroidManifest.xml.
@@ -30,19 +29,19 @@ struct AppInfo {
std::string package;
// The app's minimum SDK version, if it is defined.
- Maybe<int> min_sdk_version;
+ std::optional<int> min_sdk_version;
// The app's version code (the lower 32 bits of the long version code), if it is defined.
- Maybe<uint32_t> version_code;
+ std::optional<uint32_t> version_code;
// The app's version code major (the upper 32 bits of the long version code), if it is defined.
- Maybe<uint32_t> version_code_major;
+ std::optional<uint32_t> version_code_major;
// The app's revision code, if it is defined.
- Maybe<uint32_t> revision_code;
+ std::optional<uint32_t> revision_code;
// The app's split name, if it is a split.
- Maybe<std::string> split_name;
+ std::optional<std::string> split_name;
// The split names that this split depends on.
std::set<std::string> split_name_dependencies;
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index ef3a62f4efcc..df444ba7b169 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -280,8 +280,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
printer->Indent();
for (const ResourceTableEntryView& entry : type.entries) {
printer->Print("resource ");
- printer->Print(ResourceId(package.id.value_or_default(0), type.id.value_or_default(0),
- entry.id.value_or_default(0))
+ printer->Print(ResourceId(package.id.value_or(0), type.id.value_or(0), entry.id.value_or(0))
.to_string());
printer->Print(" ");
@@ -362,7 +361,7 @@ void Debug::PrintStyleGraph(ResourceTable* table, const ResourceName& target_sty
continue;
}
- Maybe<ResourceTable::SearchResult> result = table->FindResource(style_name);
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(style_name);
if (result) {
ResourceEntry* entry = result.value().entry;
for (const auto& value : entry->values) {
@@ -482,8 +481,7 @@ class XmlPrinter : public xml::ConstVisitor {
if (attr.compiled_attribute) {
printer_->Print("(");
- printer_->Print(
- attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
+ printer_->Print(attr.compiled_attribute.value().id.value_or(ResourceId(0)).to_string());
printer_->Print(")");
}
printer_->Print("=");
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 8a43bb4ede35..b249c6c128e1 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -147,7 +147,7 @@ class DaemonCommand : public Command {
private:
io::FileOutputStream* out_;
IDiagnostics* diagnostics_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> trace_folder_;
};
} // namespace aapt
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index f1aad29a5c58..0b4905253d20 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -21,7 +21,6 @@
#include <string>
#include "Resource.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -44,7 +43,7 @@ class NameMangler {
public:
explicit NameMangler(NameManglerPolicy policy) : policy_(policy) {}
- Maybe<ResourceName> MangleName(const ResourceName& name) {
+ std::optional<ResourceName> MangleName(const ResourceName& name) {
if (policy_.target_package_name == name.package ||
policy_.packages_to_mangle.count(name.package) == 0) {
return {};
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index f1e2da9f41e2..f49c25483e3b 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -20,7 +20,8 @@
#include <limits>
#include <sstream>
-#include "android-base/logging.h"
+#include <android-base/logging.h>
+#include <idmap2/Policies.h>
#include "ResourceTable.h"
#include "ResourceUtils.h"
@@ -28,12 +29,10 @@
#include "ValueVisitor.h"
#include "text/Utf8Iterator.h"
#include "util/ImmutableMap.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
#include "xml/XmlPullParser.h"
-#include "idmap2/Policies.h"
-
using ::aapt::ResourceUtils::StringBuilder;
using ::aapt::text::Utf8Iterator;
using ::android::ConfigDescription;
@@ -109,8 +108,8 @@ struct ParsedResource {
Visibility::Level visibility_level = Visibility::Level::kUndefined;
bool staged_api = false;
bool allow_new = false;
- Maybe<OverlayableItem> overlayable_item;
- Maybe<StagedId> staged_alias;
+ std::optional<OverlayableItem> overlayable_item;
+ std::optional<StagedId> staged_alias;
std::string comment;
std::unique_ptr<Value> value;
@@ -252,7 +251,7 @@ bool ResourceParser::FlattenXmlSubtree(
std::string current_text;
// The first occurrence of a <xliff:g> tag. Nested <xliff:g> tags are illegal.
- Maybe<size_t> untranslatable_start_depth;
+ std::optional<size_t> untranslatable_start_depth;
Node root;
std::vector<Node*> node_stack;
@@ -342,7 +341,7 @@ bool ResourceParser::FlattenXmlSubtree(
}
node_stack.pop_back();
- if (untranslatable_start_depth == make_value(depth)) {
+ if (untranslatable_start_depth == depth) {
// This is the end of an untranslatable section.
untranslatable_start_depth = {};
}
@@ -468,7 +467,7 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) {
}
// Extract the product name if it exists.
- if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
+ if (std::optional<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
parsed_resource.product = maybe_product.value().to_string();
}
@@ -560,7 +559,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
resource_format = android::ResTable_map::TYPE_ANY;
// Items have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
resource_type = maybe_type.value().to_string();
} else {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -568,7 +567,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
return false;
}
- if (Maybe<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
+ if (std::optional<StringPiece> maybe_format = xml::FindNonEmptyAttribute(parser, "format")) {
// An explicit format for this resource was specified. The resource will
// retain its type in its name, but the accepted value for this type is
// overridden.
@@ -584,7 +583,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
can_be_item = false;
// Bags have their type encoded in the type attribute.
- if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
resource_type = maybe_type.value().to_string();
} else {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -595,7 +594,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
// Get the name of the resource. This will be checked later, because not all
// XML elements require a name.
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (resource_type == "id") {
if (!maybe_name) {
@@ -835,10 +834,8 @@ std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub
bool ResourceParser::ParseString(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
bool formatted = true;
- if (Maybe<StringPiece> formatted_attr =
- xml::FindAttribute(parser, "formatted")) {
- Maybe<bool> maybe_formatted =
- ResourceUtils::ParseBool(formatted_attr.value());
+ if (std::optional<StringPiece> formatted_attr = xml::FindAttribute(parser, "formatted")) {
+ std::optional<bool> maybe_formatted = ResourceUtils::ParseBool(formatted_attr.value());
if (!maybe_formatted) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'formatted'. Must be a boolean");
@@ -848,8 +845,8 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser,
}
bool translatable = options_.translatable;
- if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
- Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+ if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+ std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
if (!maybe_translatable) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'translatable'. Must be a boolean");
@@ -929,7 +926,7 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out
<< "ignoring configuration '" << out_resource->config << "' for <public> tag");
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "<public> must have a 'type' attribute");
@@ -946,8 +943,8 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out
out_resource->name.type = *parsed_type;
- if (Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+ if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid resource ID '" << maybe_id_str.value() << "' in <public>");
@@ -974,7 +971,7 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou
<< "> tag");
}
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag->Error(DiagMessage(out_resource->source)
<< "<" << tag_name << "> must have a 'type' attribute");
@@ -988,14 +985,14 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou
return false;
}
- Maybe<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
+ std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id");
if (!maybe_id_str) {
diag->Error(DiagMessage(out_resource->source)
<< "<" << tag_name << "> must have a 'first-id' attribute");
return false;
}
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
if (!maybe_id) {
diag->Error(DiagMessage(out_resource->source)
<< "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">");
@@ -1090,7 +1087,7 @@ bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource
bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser,
ParsedResource* out_resource) {
- Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
if (!maybe_type) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name()
@@ -1137,7 +1134,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
<< "' for <overlayable> tag");
}
- Maybe<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> overlayable_name = xml::FindNonEmptyAttribute(parser, "name");
if (!overlayable_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<overlayable> tag must have a 'name' attribute");
@@ -1146,7 +1143,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
const std::string kActorUriScheme =
android::base::StringPrintf("%s://", Overlayable::kActorScheme);
- Maybe<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
+ std::optional<StringPiece> overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor");
if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) {
diag_->Error(DiagMessage(out_resource->source)
<< "specified <overlayable> tag 'actor' attribute must use the scheme '"
@@ -1194,7 +1191,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
}
// Items specify the name and type of resource that should be overlayable
- Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
if (!item_name) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must have a 'name' attribute");
@@ -1202,7 +1199,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
continue;
}
- Maybe<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
+ std::optional<StringPiece> item_type = xml::FindNonEmptyAttribute(parser, "type");
if (!item_type) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must have a 'type' attribute");
@@ -1236,7 +1233,8 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
break;
- } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
+ } else if (std::optional<StringPiece> maybe_type =
+ xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
// policies. Items within the policy tag will have the specified policy.
for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
@@ -1302,7 +1300,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
uint32_t type_mask = 0;
- Maybe<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
+ std::optional<StringPiece> maybe_format = xml::FindAttribute(parser, "format");
if (maybe_format) {
type_mask = ParseFormatAttribute(maybe_format.value());
if (type_mask == 0) {
@@ -1312,9 +1310,9 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
}
}
- Maybe<int32_t> maybe_min, maybe_max;
+ std::optional<int32_t> maybe_min, maybe_max;
- if (Maybe<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
+ if (std::optional<StringPiece> maybe_min_str = xml::FindAttribute(parser, "min")) {
StringPiece min_str = util::TrimWhitespace(maybe_min_str.value());
if (!min_str.empty()) {
std::u16string min_str16 = util::Utf8ToUtf16(min_str);
@@ -1331,7 +1329,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
}
}
- if (Maybe<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
+ if (std::optional<StringPiece> maybe_max_str = xml::FindAttribute(parser, "max")) {
StringPiece max_str = util::TrimWhitespace(maybe_max_str.value());
if (!max_str.empty()) {
std::u16string max_str16 = util::Utf8ToUtf16(max_str);
@@ -1398,8 +1396,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
type_mask |= android::ResTable_map::TYPE_FLAGS;
}
- if (Maybe<Attribute::Symbol> s =
- ParseEnumOrFlagItem(parser, element_name)) {
+ if (std::optional<Attribute::Symbol> s = ParseEnumOrFlagItem(parser, element_name)) {
Attribute::Symbol& symbol = s.value();
ParsedResource child_resource;
child_resource.name = symbol.symbol.name.value();
@@ -1443,24 +1440,24 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
type_mask ? type_mask : uint32_t{android::ResTable_map::TYPE_ANY});
attr->SetWeak(weak);
attr->symbols = std::vector<Attribute::Symbol>(items.begin(), items.end());
- attr->min_int = maybe_min.value_or_default(std::numeric_limits<int32_t>::min());
- attr->max_int = maybe_max.value_or_default(std::numeric_limits<int32_t>::max());
+ attr->min_int = maybe_min.value_or(std::numeric_limits<int32_t>::min());
+ attr->max_int = maybe_max.value_or(std::numeric_limits<int32_t>::max());
out_resource->value = std::move(attr);
return true;
}
-Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
- xml::XmlPullParser* parser, const StringPiece& tag) {
+std::optional<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const StringPiece& tag) {
const Source source = source_.WithLine(parser->line_number());
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <"
<< tag << ">");
return {};
}
- Maybe<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
+ std::optional<StringPiece> maybe_value = xml::FindNonEmptyAttribute(parser, "value");
if (!maybe_value) {
diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <"
<< tag << ">");
@@ -1484,13 +1481,13 @@ Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
const Source source = source_.WithLine(parser->line_number());
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(source) << "<item> must have a 'name' attribute");
return false;
}
- Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ std::optional<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_key) {
diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
return false;
@@ -1515,7 +1512,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
std::unique_ptr<Style> style = util::make_unique<Style>();
- Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
+ std::optional<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
if (maybe_parent) {
// If the parent is empty, we don't have a parent, but we also don't infer either.
if (!maybe_parent.value().empty()) {
@@ -1571,7 +1568,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource) {
uint32_t resource_format = android::ResTable_map::TYPE_ANY;
- if (Maybe<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
+ if (std::optional<StringPiece> format_attr = xml::FindNonEmptyAttribute(parser, "format")) {
resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value());
if (resource_format == 0u) {
diag_->Error(DiagMessage(source_.WithLine(parser->line_number()))
@@ -1598,8 +1595,8 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
std::unique_ptr<Array> array = util::make_unique<Array>();
bool translatable = options_.translatable;
- if (Maybe<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
- Maybe<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
+ if (std::optional<StringPiece> translatable_attr = xml::FindAttribute(parser, "translatable")) {
+ std::optional<bool> maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value());
if (!maybe_translatable) {
diag_->Error(DiagMessage(out_resource->source)
<< "invalid value for 'translatable'. Must be a boolean");
@@ -1664,8 +1661,7 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "item") {
- Maybe<StringPiece> maybe_quantity =
- xml::FindNonEmptyAttribute(parser, "quantity");
+ std::optional<StringPiece> maybe_quantity = xml::FindNonEmptyAttribute(parser, "quantity");
if (!maybe_quantity) {
diag_->Error(DiagMessage(item_source)
<< "<item> in <plurals> requires attribute "
@@ -1767,7 +1763,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
const std::string& element_namespace = parser->element_namespace();
const std::string& element_name = parser->element_name();
if (element_namespace.empty() && element_name == "attr") {
- Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
+ std::optional<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
if (!maybe_name) {
diag_->Error(DiagMessage(item_source) << "<attr> tag must have a 'name' attribute");
error = true;
@@ -1777,7 +1773,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
// If this is a declaration, the package name may be in the name. Separate
// these out.
// Eg. <attr name="android:text" />
- Maybe<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
+ std::optional<Reference> maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
if (!maybe_ref) {
diag_->Error(DiagMessage(item_source) << "<attr> tag has invalid name '"
<< maybe_name.value() << "'");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 261499781638..548f5f9531fd 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -18,6 +18,7 @@
#define AAPT_RESOURCE_PARSER_H
#include <memory>
+#include <optional>
#include "android-base/macros.h"
#include "androidfw/ConfigDescription.h"
@@ -27,7 +28,6 @@
#include "ResourceTable.h"
#include "ResourceValues.h"
#include "StringPool.h"
-#include "util/Maybe.h"
#include "xml/XmlPullParser.h"
namespace aapt {
@@ -54,7 +54,7 @@ struct ResourceParserOptions {
// If visibility was forced, we need to use it when creating a new resource and also error if we
// try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
- Maybe<Visibility::Level> visibility;
+ std::optional<Visibility::Level> visibility;
};
struct FlattenedXmlSubTree {
@@ -122,8 +122,8 @@ class ResourceParser {
bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
- Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
- const android::StringPiece& tag);
+ std::optional<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
+ const android::StringPiece& tag);
bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 279ebcba2f71..556ffa221db5 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -567,12 +567,12 @@ TEST_F(ResourceParserTest, ParseStyle) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/fu"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/fu")));
ASSERT_THAT(style->entries, SizeIs(3));
- EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
- EXPECT_THAT(style->entries[1].key.name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
- EXPECT_THAT(style->entries[2].key.name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+ EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("attr/bar")));
+ EXPECT_THAT(style->entries[1].key.name, Eq(test::ParseNameOrDie("attr/bat")));
+ EXPECT_THAT(style->entries[2].key.name, Eq(test::ParseNameOrDie("attr/baz")));
}
TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
@@ -581,7 +581,7 @@ TEST_F(ResourceParserTest, ParseStyleWithShorthandParent) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("com.app:style/Theme"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("com.app:style/Theme")));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
@@ -594,7 +594,7 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedParent) {
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
ASSERT_TRUE(style->parent.value().name);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("android:style/Theme"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("android:style/Theme")));
}
TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
@@ -607,7 +607,7 @@ TEST_F(ResourceParserTest, ParseStyleWithPackageAliasedItems) {
Style* style = test::GetValue<Style>(&table_, "style/foo");
ASSERT_THAT(style, NotNull());
ASSERT_THAT(style->entries, SizeIs(1));
- EXPECT_THAT(style->entries[0].key.name, Eq(make_value(test::ParseNameOrDie("android:attr/bar"))));
+ EXPECT_THAT(style->entries[0].key.name, Eq(test::ParseNameOrDie("android:attr/bar")));
}
TEST_F(ResourceParserTest, ParseStyleWithRawStringItem) {
@@ -634,7 +634,7 @@ TEST_F(ResourceParserTest, ParseStyleWithInferredParent) {
Style* style = test::GetValue<Style>(&table_, "style/foo.bar");
ASSERT_THAT(style, NotNull());
ASSERT_TRUE(style->parent);
- EXPECT_THAT(style->parent.value().name, Eq(make_value(test::ParseNameOrDie("style/foo"))));
+ EXPECT_THAT(style->parent.value().name, Eq(test::ParseNameOrDie("style/foo")));
EXPECT_TRUE(style->parent_inferred);
}
@@ -672,7 +672,7 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
</declare-styleable>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("styleable/foo"));
ASSERT_TRUE(result);
EXPECT_THAT(result.value().entry->visibility.level, Eq(Visibility::Level::kPublic));
@@ -695,9 +695,9 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
ASSERT_THAT(styleable, NotNull());
ASSERT_THAT(styleable->entries, SizeIs(3));
- EXPECT_THAT(styleable->entries[0].name, Eq(make_value(test::ParseNameOrDie("attr/bar"))));
- EXPECT_THAT(styleable->entries[1].name, Eq(make_value(test::ParseNameOrDie("attr/bat"))));
- EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
+ EXPECT_THAT(styleable->entries[0].name, Eq(test::ParseNameOrDie("attr/bar")));
+ EXPECT_THAT(styleable->entries[1].name, Eq(test::ParseNameOrDie("attr/bat")));
+ EXPECT_THAT(styleable->entries[2].name, Eq(test::ParseNameOrDie("attr/baz")));
}
TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) {
@@ -913,7 +913,8 @@ TEST_F(ResourceParserTest, AutoIncrementIdsInPublicGroup) {
</public-group>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ std::optional<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("attr/foo"));
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->id);
EXPECT_THAT(result.value().entry->id.value(), Eq(ResourceId(0x01010040)));
@@ -932,7 +933,8 @@ TEST_F(ResourceParserTest, StagingPublicGroup) {
</staging-public-group>)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result = table_.FindResource(test::ParseNameOrDie("attr/foo"));
+ std::optional<ResourceTable::SearchResult> result =
+ table_.FindResource(test::ParseNameOrDie("attr/foo"));
ASSERT_TRUE(result);
ASSERT_TRUE(result.value().entry->id);
@@ -959,7 +961,7 @@ TEST_F(ResourceParserTest, StrongestSymbolVisibilityWins) {
<java-symbol type="string" name="foo" />)";
ASSERT_TRUE(TestParse(input));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("string/foo"));
ASSERT_TRUE(result);
@@ -977,7 +979,7 @@ TEST_F(ResourceParserTest, ExternalTypesShouldOnlyBeReferences) {
TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
ASSERT_TRUE(TestParse(R"(<add-resource name="bar" type="string" />)"));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(result);
const ResourceEntry* entry = result.value().entry;
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 8ab1493c6ab3..ad014a23be87 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <tuple>
#include "android-base/logging.h"
@@ -68,7 +69,7 @@ struct NameEqualRange {
template <typename T, typename U>
bool less_than_struct_with_name_and_id(const T& lhs,
- const std::pair<std::string_view, Maybe<U>>& rhs) {
+ const std::pair<std::string_view, std::optional<U>>& rhs) {
if (lhs.id != rhs.second) {
return lhs.id < rhs.second;
}
@@ -341,20 +342,20 @@ struct EntryViewComparer {
void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
const ResourceTableType* type, const std::string& entry_name,
- const Maybe<ResourceId>& id, const Visibility& visibility,
- const Maybe<AllowNew>& allow_new,
- const Maybe<OverlayableItem>& overlayable_item,
- const Maybe<StagedId>& staged_id,
+ const std::optional<ResourceId>& id, const Visibility& visibility,
+ const std::optional<AllowNew>& allow_new,
+ const std::optional<OverlayableItem>& overlayable_item,
+ const std::optional<StagedId>& staged_id,
const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
ResourceTablePackageView new_package{package->name,
- id ? id.value().package_id() : Maybe<uint8_t>{}};
+ id ? id.value().package_id() : std::optional<uint8_t>{}};
auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
- ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe<uint8_t>{}};
+ ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : std::optional<uint8_t>{}};
auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
if (visibility.level == Visibility::Level::kPublic) {
@@ -363,7 +364,7 @@ void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePacka
}
ResourceTableEntryView new_entry{.name = entry_name,
- .id = id ? id.value().entry_id() : Maybe<uint16_t>{},
+ .id = id ? id.value().entry_id() : std::optional<uint16_t>{},
.visibility = visibility,
.allow_new = allow_new,
.overlayable_item = overlayable_item,
@@ -585,7 +586,8 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) {
return true;
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(
+ const ResourceNameRef& name) const {
ResourceTablePackage* package = FindPackage(name.package);
if (package == nullptr) {
return {};
@@ -603,8 +605,8 @@ Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNam
return SearchResult{package, type, entry};
}
-Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
- ResourceId id) const {
+std::optional<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name,
+ ResourceId id) const {
ResourceTablePackage* package = FindPackage(name.package);
if (package == nullptr) {
return {};
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index bae1d827a841..2e17659b0679 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -120,18 +120,18 @@ class ResourceEntry {
const std::string name;
// The entry ID for this resource (the EEEE in 0xPPTTEEEE).
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
// Whether this resource is public (and must maintain the same entry ID across builds).
Visibility visibility;
- Maybe<AllowNew> allow_new;
+ std::optional<AllowNew> allow_new;
// The declarations of this resource as overlayable for RROs
- Maybe<OverlayableItem> overlayable_item;
+ std::optional<OverlayableItem> overlayable_item;
// The staged resource id for a finalized resource.
- Maybe<StagedId> staged_id;
+ std::optional<StagedId> staged_id;
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -205,11 +205,11 @@ class ResourceTablePackage {
struct ResourceTableEntryView {
std::string name;
- Maybe<uint16_t> id;
+ std::optional<uint16_t> id;
Visibility visibility;
- Maybe<AllowNew> allow_new;
- Maybe<OverlayableItem> overlayable_item;
- Maybe<StagedId> staged_id;
+ std::optional<AllowNew> allow_new;
+ std::optional<OverlayableItem> overlayable_item;
+ std::optional<StagedId> staged_id;
std::vector<const ResourceConfigValue*> values;
const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
@@ -218,7 +218,7 @@ struct ResourceTableEntryView {
struct ResourceTableTypeView {
ResourceType type;
- Maybe<uint8_t> id;
+ std::optional<uint8_t> id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
// Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
@@ -228,7 +228,7 @@ struct ResourceTableTypeView {
struct ResourceTablePackageView {
std::string name;
- Maybe<uint8_t> id;
+ std::optional<uint8_t> id;
// Types sorted in ascending type id order. If ids have not been assigned, the types are sorted by
// their declaration order in the ResourceType enum.
std::vector<ResourceTableTypeView> types;
@@ -309,8 +309,8 @@ class ResourceTable {
ResourceEntry* entry;
};
- Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
- Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
+ std::optional<SearchResult> FindResource(const ResourceNameRef& name) const;
+ std::optional<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
bool RemoveResource(const ResourceNameRef& name, ResourceId id) const;
// Returns the package struct with the given name, or nullptr if such a package does not
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 38391c99f55a..de73d2c203e4 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -162,7 +162,7 @@ TEST(ResourceTableTest, ProductVaryingValues) {
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "tablet"), NotNull());
EXPECT_THAT(test::GetValueForConfigAndProduct<Id>(&table, "android:string/foo",test::ParseConfigOrDie("land"), "phone"), NotNull());
- Maybe<ResourceTable::SearchResult> sr =
+ std::optional<ResourceTable::SearchResult> sr =
table.FindResource(test::ParseNameOrDie("android:string/foo"));
ASSERT_TRUE(sr);
std::vector<ResourceConfigValue*> values =
@@ -187,7 +187,7 @@ static ::testing::AssertionResult VisibilityOfResource(const ResourceTable& tabl
const ResourceNameRef& name,
Visibility::Level level,
const StringPiece& comment) {
- Maybe<ResourceTable::SearchResult> result = table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> result = table.FindResource(name);
if (!result) {
return ::testing::AssertionFailure() << "no resource '" << name << "' found in table";
}
@@ -242,7 +242,7 @@ TEST(ResourceTableTest, SetAllowNew) {
const ResourceName name = test::ParseNameOrDie("android:string/foo");
AllowNew allow_new;
- Maybe<ResourceTable::SearchResult> result;
+ std::optional<ResourceTable::SearchResult> result;
allow_new.comment = "first";
ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetAllowNew(allow_new).Build(),
@@ -274,7 +274,7 @@ TEST(ResourceTableTest, SetOverlayable) {
const ResourceName name = test::ParseNameOrDie("android:string/foo");
ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(),
test::GetDiagnostics()));
- Maybe<ResourceTable::SearchResult> search_result = table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index e0e80ac02dea..ead06bf989d5 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -40,8 +40,7 @@ using ::android::base::StringPrintf;
namespace aapt {
namespace ResourceUtils {
-Maybe<ResourceName> ToResourceName(
- const android::ResTable::resource_name& name_in) {
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name_in) {
// TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
ResourceName name_out;
if (!name_in.package) {
@@ -78,7 +77,7 @@ Maybe<ResourceName> ToResourceName(
return name_out;
}
-Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
ResourceName name_out;
if (!name_in.package) {
return {};
@@ -251,8 +250,7 @@ bool IsAttributeReference(const StringPiece& str) {
* <[*]package>:[style/]<entry>
* [[*]package:style/]<entry>
*/
-Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
- std::string* out_error) {
+std::optional<Reference> ParseStyleParentReference(const StringPiece& str, std::string* out_error) {
if (str.empty()) {
return {};
}
@@ -301,7 +299,7 @@ Maybe<Reference> ParseStyleParentReference(const StringPiece& str,
return result;
}
-Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
+std::optional<Reference> ParseXmlAttributeName(const StringPiece& str) {
StringPiece trimmed_str = util::TrimWhitespace(str);
const char* start = trimmed_str.data();
const char* const end = start + trimmed_str.size();
@@ -326,7 +324,7 @@ Maybe<Reference> ParseXmlAttributeName(const StringPiece& str) {
}
ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
- return Maybe<Reference>(std::move(ref));
+ return std::optional<Reference>(std::move(ref));
}
std::unique_ptr<Reference> TryParseReference(const StringPiece& str,
@@ -488,18 +486,18 @@ std::unique_ptr<BinaryPrimitive> TryParseColor(const StringPiece& str) {
: util::make_unique<BinaryPrimitive>(value);
}
-Maybe<bool> ParseBool(const StringPiece& str) {
+std::optional<bool> ParseBool(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
if (trimmed_str == "true" || trimmed_str == "TRUE" || trimmed_str == "True") {
- return Maybe<bool>(true);
+ return std::optional<bool>(true);
} else if (trimmed_str == "false" || trimmed_str == "FALSE" ||
trimmed_str == "False") {
- return Maybe<bool>(false);
+ return std::optional<bool>(false);
}
return {};
}
-Maybe<uint32_t> ParseInt(const StringPiece& str) {
+std::optional<uint32_t> ParseInt(const StringPiece& str) {
std::u16string str16 = util::Utf8ToUtf16(str);
android::Res_value value;
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
@@ -508,7 +506,7 @@ Maybe<uint32_t> ParseInt(const StringPiece& str) {
return {};
}
-Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
+std::optional<ResourceId> ParseResourceId(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -524,7 +522,7 @@ Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
return {};
}
-Maybe<int> ParseSdkVersion(const StringPiece& str) {
+std::optional<int> ParseSdkVersion(const StringPiece& str) {
StringPiece trimmed_str(util::TrimWhitespace(str));
std::u16string str16 = util::Utf8ToUtf16(trimmed_str);
@@ -534,7 +532,7 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) {
}
// Try parsing the code name.
- Maybe<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
+ std::optional<int> entry = GetDevelopmentSdkCodeNameVersion(trimmed_str);
if (entry) {
return entry.value();
}
@@ -551,7 +549,7 @@ Maybe<int> ParseSdkVersion(const StringPiece& str) {
}
std::unique_ptr<BinaryPrimitive> TryParseBool(const StringPiece& str) {
- if (Maybe<bool> maybe_result = ParseBool(str)) {
+ if (std::optional<bool> maybe_result = ParseBool(str)) {
const uint32_t data = maybe_result.value() ? 0xffffffffu : 0u;
return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_BOOLEAN, data);
}
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index be493db8cee0..fe450a834dfa 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -75,35 +75,33 @@ bool IsAttributeReference(const android::StringPiece& str);
/**
* Convert an android::ResTable::resource_name to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> ToResourceName(
- const android::ResTable::resource_name& name);
+std::optional<ResourceName> ToResourceName(const android::ResTable::resource_name& name);
/**
* Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
*/
-Maybe<ResourceName> ToResourceName(
- const android::AssetManager2::ResourceName& name_in);
+std::optional<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in);
/**
* Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
* false, or False.
*/
-Maybe<bool> ParseBool(const android::StringPiece& str);
+std::optional<bool> ParseBool(const android::StringPiece& str);
/**
* Returns a uint32_t if the string is an integer.
*/
-Maybe<uint32_t> ParseInt(const android::StringPiece& str);
+std::optional<uint32_t> ParseInt(const android::StringPiece& str);
/**
* Returns an ID if it the string represented a valid ID.
*/
-Maybe<ResourceId> ParseResourceId(const android::StringPiece& str);
+std::optional<ResourceId> ParseResourceId(const android::StringPiece& str);
/**
* Parses an SDK version, which can be an integer, or a letter from A-Z.
*/
-Maybe<int> ParseSdkVersion(const android::StringPiece& str);
+std::optional<int> ParseSdkVersion(const android::StringPiece& str);
/*
* Returns a Reference, or None Maybe instance if the string `str` was parsed as
@@ -116,7 +114,8 @@ Maybe<int> ParseSdkVersion(const android::StringPiece& str);
* ?[package:]style/<entry> or
* <package>:[style/]<entry>
*/
-Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std::string* out_error);
+std::optional<Reference> ParseStyleParentReference(const android::StringPiece& str,
+ std::string* out_error);
/*
* Returns a Reference if the string `str` was parsed as a valid XML attribute
@@ -125,7 +124,7 @@ Maybe<Reference> ParseStyleParentReference(const android::StringPiece& str, std:
*
* package:entry
*/
-Maybe<Reference> ParseXmlAttributeName(const android::StringPiece& str);
+std::optional<Reference> ParseXmlAttributeName(const android::StringPiece& str);
/*
* Returns a Reference object if the string was parsed as a resource or
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index b08bf9a1ff17..1aaa34deee79 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -30,15 +30,15 @@ using ::testing::Pointee;
namespace aapt {
TEST(ResourceUtilsTest, ParseBool) {
- EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(Maybe<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(Maybe<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(Maybe<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("true"), Eq(std::optional<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("TRUE"), Eq(std::optional<bool>(true)));
+ EXPECT_THAT(ResourceUtils::ParseBool("True"), Eq(std::optional<bool>(true)));
- EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(Maybe<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(Maybe<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("false"), Eq(std::optional<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("FALSE"), Eq(std::optional<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool("False"), Eq(std::optional<bool>(false)));
- EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(Maybe<bool>(false)));
+ EXPECT_THAT(ResourceUtils::ParseBool(" False\n "), Eq(std::optional<bool>(false)));
}
TEST(ResourceUtilsTest, ParseResourceName) {
@@ -155,41 +155,42 @@ TEST(ResourceUtilsTest, ParseStyleParentReference) {
const ResourceName kStyleFooName({}, ResourceType::kStyle, "foo");
std::string err_str;
- Maybe<Reference> ref = ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
+ std::optional<Reference> ref =
+ ResourceUtils::ParseStyleParentReference("@android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("@style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("?android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("?style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("android:foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("@android:foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kStyleFooName));
ref = ResourceUtils::ParseStyleParentReference("*android:style/foo", &err_str);
ASSERT_TRUE(ref);
- EXPECT_THAT(ref.value().name, Eq(make_value(kAndroidStyleFooName)));
+ EXPECT_THAT(ref.value().name, Eq(kAndroidStyleFooName));
EXPECT_TRUE(ref.value().private_reference);
}
@@ -228,15 +229,11 @@ TEST(ResourceUtilsTest, ItemsWithWhitespaceAreParsedCorrectly) {
}
TEST(ResourceUtilsTest, ParseSdkVersionWithCodename) {
- EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(Maybe<int>(10000)));
- EXPECT_THAT(
- ResourceUtils::ParseSdkVersion("Q.fingerprint"),
- Eq(Maybe<int>(10000)));
-
- EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(Maybe<int>(10000)));
- EXPECT_THAT(
- ResourceUtils::ParseSdkVersion("R.fingerprint"),
- Eq(Maybe<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q"), Eq(std::optional<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("Q.fingerprint"), Eq(std::optional<int>(10000)));
+
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("R"), Eq(std::optional<int>(10000)));
+ EXPECT_THAT(ResourceUtils::ParseSdkVersion("R.fingerprint"), Eq(std::optional<int>(10000)));
}
TEST(ResourceUtilsTest, StringBuilderWhitespaceRemoval) {
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 2a90f267f185..b3ab4ffc649b 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -120,7 +120,7 @@ bool Reference::Flatten(android::Res_value* out_value) const {
return false;
}
- const ResourceId resid = id.value_or_default(ResourceId(0));
+ const ResourceId resid = id.value_or(ResourceId(0));
const bool dynamic = resid.is_valid() && is_dynamic;
if (reference_type == Reference::Type::kResource) {
@@ -1040,7 +1040,7 @@ void Macro::Print(std::ostream* out) const {
}
bool operator<(const Reference& a, const Reference& b) {
- int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
+ int cmp = a.name.value_or(ResourceName{}).compare(b.name.value_or(ResourceName{}));
if (cmp != 0) return cmp < 0;
return a.id < b.id;
}
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index d903b7e1b8b3..1694d6b6fe4a 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -31,7 +31,6 @@
#include "ValueTransformer.h"
#include "io/File.h"
#include "text/Printer.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -159,8 +158,8 @@ struct Reference : public TransformableItem<Reference, BaseItem<Reference>> {
kAttribute,
};
- Maybe<ResourceName> name;
- Maybe<ResourceId> id;
+ std::optional<ResourceName> name;
+ std::optional<ResourceId> id;
std::optional<uint32_t> type_flags;
Reference::Type reference_type;
bool private_reference = false;
@@ -327,7 +326,7 @@ struct Style : public TransformableValue<Style, BaseValue<Style>> {
friend std::ostream& operator<<(std::ostream& out, const Entry& entry);
};
- Maybe<Reference> parent;
+ std::optional<Reference> parent;
// If set to true, the parent was auto inferred from the style's name.
bool parent_inferred = false;
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 96f6512fe7f5..0bbde62728cf 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -26,9 +26,8 @@ using android::StringPiece;
namespace aapt {
static ApiVersion sDevelopmentSdkLevel = 10000;
-static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
- "Q", "R", "S"
-});
+static const auto sDevelopmentSdkCodeNames =
+ std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu"});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
@@ -77,9 +76,10 @@ ApiVersion FindAttributeSdkLevel(const ResourceId& id) {
return iter->second;
}
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const StringPiece& code_name) {
return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end())
- ? Maybe<ApiVersion>() : sDevelopmentSdkLevel;
+ ? std::optional<ApiVersion>()
+ : sDevelopmentSdkLevel;
}
} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index 6bb6ddb13bdb..7518e707d22c 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -60,7 +60,7 @@ enum : ApiVersion {
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
-Maybe<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
+std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(const android::StringPiece& code_name);
} // namespace aapt
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
index 92934c343960..4f9369a5d517 100644
--- a/tools/aapt2/Source.h
+++ b/tools/aapt2/Source.h
@@ -17,21 +17,20 @@
#ifndef AAPT_SOURCE_H
#define AAPT_SOURCE_H
+#include <optional>
#include <ostream>
#include <string>
#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
-#include "util/Maybe.h"
-
namespace aapt {
// Represents a file on disk. Used for logging and showing errors.
struct Source {
std::string path;
- Maybe<size_t> line;
- Maybe<std::string> archive;
+ std::optional<size_t> line;
+ std::optional<std::string> archive;
Source() = default;
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 919b4c98fa8f..b1452fad0e8f 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -72,7 +72,7 @@ void Command::AddRequiredFlagList(const StringPiece& name, const StringPiece& de
}
void Command::AddOptionalFlag(const StringPiece& name, const StringPiece& description,
- Maybe<std::string>* value, uint32_t flags) {
+ std::optional<std::string>* value, uint32_t flags) {
auto func = [value, flags](const StringPiece& arg) -> bool {
*value = (flags & Command::kPath) ? GetSafePath(arg) : arg.to_string();
return true;
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index d21571d530d2..8678cda59856 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -18,6 +18,7 @@
#define AAPT_COMMAND_H
#include <functional>
+#include <optional>
#include <ostream>
#include <string>
#include <unordered_set>
@@ -25,19 +26,20 @@
#include "androidfw/StringPiece.h"
-#include "util/Maybe.h"
-
namespace aapt {
class Command {
public:
- explicit Command(const android::StringPiece& name) : name_(name.to_string()),
- short_name_(""),
- full_subcommand_name_(name.to_string()) {}
+ explicit Command(const android::StringPiece& name)
+ : name_(name.to_string()), full_subcommand_name_(name.to_string()){};
explicit Command(const android::StringPiece& name, const android::StringPiece& short_name)
- : name_(name.to_string()), short_name_(short_name.to_string()),
- full_subcommand_name_(name.to_string()) {}
+ : name_(name.to_string()),
+ short_name_(short_name.to_string()),
+ full_subcommand_name_(name.to_string()){};
+
+ Command(Command&&) = default;
+ Command& operator=(Command&&) = default;
virtual ~Command() = default;
@@ -58,7 +60,7 @@ class Command {
uint32_t flags = 0);
void AddOptionalFlag(const android::StringPiece& name, const android::StringPiece& description,
- Maybe<std::string>* value, uint32_t flags = 0);
+ std::optional<std::string>* value, uint32_t flags = 0);
void AddOptionalFlagList(const android::StringPiece& name,
const android::StringPiece& description, std::vector<std::string>* value,
@@ -87,8 +89,6 @@ class Command {
virtual int Action(const std::vector<std::string>& args) = 0;
private:
- DISALLOW_COPY_AND_ASSIGN(Command);
-
struct Flag {
explicit Flag(const android::StringPiece& name, const android::StringPiece& description,
const bool is_required, const size_t num_args,
@@ -104,8 +104,8 @@ class Command {
bool found = false;
};
- const std::string name_;
- const std::string short_name_;
+ std::string name_;
+ std::string short_name_;
std::string description_ = "";
std::string full_subcommand_name_;
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 65608fdf64a7..7aa1aa017f7b 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -38,7 +38,7 @@ TEST(CommandTest, LongFullyQualifiedPathWindows) {
TestCommand command;
std::string required_flag;
command.AddRequiredFlag("--rflag", "", &required_flag, Command::kPath);
- Maybe<std::string> optional_flag;
+ std::optional<std::string> optional_flag;
command.AddOptionalFlag("--oflag", "", &optional_flag, Command::kPath);
std::vector<std::string> required_flag_list;
command.AddRequiredFlagList("--rlflag", "", &required_flag_list, Command::kPath);
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index cd5015e81203..fe560180bd48 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -47,7 +47,6 @@
#include "io/ZipArchive.h"
#include "trace/TraceBuffer.h"
#include "util/Files.h"
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
#include "xml/XmlPullParser.h"
@@ -75,10 +74,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
- const char dir_sep,
- std::string* out_error,
- const CompileOptions& options) {
+static std::optional<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -337,7 +336,7 @@ static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
if (file_type == file::FileType::kDirectory) {
context->GetDiagnostics()->Error(DiagMessage(input_path)
<< "resource file cannot be a directory");
- } else if (file_type == file::FileType::kNonexistant) {
+ } else if (file_type == file::FileType::kNonExistant) {
context->GetDiagnostics()->Error(DiagMessage(input_path) << "file not found");
} else {
context->GetDiagnostics()->Error(DiagMessage(input_path)
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1bc1f6651f85..bd2e3d72a551 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -17,7 +17,10 @@
#ifndef AAPT2_COMPILE_H
#define AAPT2_COMPILE_H
-#include "androidfw/StringPiece.h"
+#include <optional>
+
+#include <androidfw/StringPiece.h>
+
#include "format/Archive.h"
#include "process/IResourceTableConsumer.h"
#include "Command.h"
@@ -28,11 +31,11 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
- Maybe<std::string> source_path;
- Maybe<std::string> res_dir;
- Maybe<std::string> res_zip;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<Visibility::Level> visibility;
+ std::optional<std::string> source_path;
+ std::optional<std::string> res_dir;
+ std::optional<std::string> res_zip;
+ std::optional<std::string> generate_text_symbols_path;
+ std::optional<Visibility::Level> visibility;
bool pseudolocalize = false;
bool no_png_crunch = false;
bool legacy_mode = false;
@@ -80,8 +83,8 @@ class CompileCommand : public Command {
private:
IDiagnostics* diagnostic_;
CompileOptions options_;
- Maybe<std::string> visibility_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> visibility_;
+ std::optional<std::string> trace_folder_;
};
int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter* output_writer,
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 22bcd8589ce9..3b097e09e09d 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -367,8 +367,7 @@ int ConvertCommand::Action(const std::vector<std::string>& args) {
return 1;
}
- Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(),
- context.GetDiagnostics());
+ auto app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
if (!app_info) {
return 1;
}
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 7e2029dfc4d2..2cdb0c87310f 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -17,6 +17,8 @@
#ifndef AAPT2_CONVERT_H
#define AAPT2_CONVERT_H
+#include <optional>
+
#include "Command.h"
#include "LoadedApk.h"
#include "format/binary/TableFlattener.h"
@@ -52,7 +54,7 @@ class ConvertCommand : public Command {
TableFlattenerOptions table_flattener_options_;
XmlFlattenerOptions xml_flattener_options_;
std::string output_path_;
- Maybe<std::string> output_format_;
+ std::optional<std::string> output_format_;
bool verbose_ = false;
};
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 3950f337b575..d9e8c921dbc5 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -87,8 +87,8 @@ static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibilit
}
template <typename Id>
-static bool IsIdDiff(const Visibility::Level& level_a, const Maybe<Id>& id_a,
- const Visibility::Level& level_b, const Maybe<Id>& id_b) {
+static bool IsIdDiff(const Visibility::Level& level_a, const std::optional<Id>& id_a,
+ const Visibility::Level& level_b, const std::optional<Id>& id_b) {
if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
return id_a != id_b;
}
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3982d12f6036..0a1e021aef6b 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -257,12 +257,11 @@ int DumpConfigsCommand::Dump(LoadedApk* apk) {
}
int DumpPackageNameCommand::Dump(LoadedApk* apk) {
- Maybe<std::string> package_name = GetPackageName(apk);
- if (!package_name) {
+ auto package_name = GetPackageName(apk);
+ if (!package_name.has_value()) {
return 1;
}
-
- GetPrinter()->Println(package_name.value());
+ GetPrinter()->Println(*package_name);
return 0;
}
@@ -283,12 +282,12 @@ int DumpStringsCommand::Dump(LoadedApk* apk) {
}
int DumpStyleParentCommand::Dump(LoadedApk* apk) {
- Maybe<std::string> package_name = GetPackageName(apk);
- if (!package_name) {
+ auto package_name = GetPackageName(apk);
+ if (!package_name.has_value()) {
return 1;
}
- const auto target_style = ResourceName(package_name.value(), ResourceType::kStyle, style_);
+ const auto target_style = ResourceName(*package_name, ResourceType::kStyle, style_);
const auto table = apk->GetResourceTable();
if (!table) {
@@ -296,7 +295,7 @@ int DumpStyleParentCommand::Dump(LoadedApk* apk) {
return 1;
}
- Maybe<ResourceTable::SearchResult> target = table->FindResource(target_style);
+ std::optional<ResourceTable::SearchResult> target = table->FindResource(target_style);
if (!target) {
GetDiagnostics()->Error(
DiagMessage() << "Target style \"" << target_style.entry << "\" does not exist");
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index cd51f7a7718c..52616faa5f06 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -43,17 +43,17 @@ class DumpApkCommand : public Command {
return diag_;
}
- Maybe<std::string> GetPackageName(LoadedApk* apk) {
+ std::optional<std::string> GetPackageName(LoadedApk* apk) {
xml::Element* manifest_el = apk->GetManifest()->root.get();
if (!manifest_el) {
GetDiagnostics()->Error(DiagMessage() << "No AndroidManifest.");
- return Maybe<std::string>();
+ return {};
}
xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
if (!attr) {
GetDiagnostics()->Error(DiagMessage() << "No package name.");
- return Maybe<std::string>();
+ return {};
}
return attr->value;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e4d0f3b6bd23..e614a753d2d2 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -299,7 +299,7 @@ struct ResourceFileFlattenerOptions {
bool do_not_fail_on_missing_resources = false;
OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
- Maybe<std::regex> regex_to_not_compress;
+ std::optional<std::regex> regex_to_not_compress;
};
// A sampling of public framework resource IDs.
@@ -741,7 +741,7 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path,
const size_t res_id_str_len = line.size() - res_id_start_idx;
StringPiece res_id_str = util::TrimWhitespace(line.substr(res_id_start_idx, res_id_str_len));
- Maybe<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
+ std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(res_id_str);
if (!maybe_id) {
diag->Error(DiagMessage(Source(path, line_no)) << "invalid resource ID '" << res_id_str
<< "'");
@@ -793,7 +793,7 @@ class Linker {
if (!options_.manifest_fixer_options.compile_sdk_version) {
xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
if (attr != nullptr) {
- Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+ auto& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
switch (prim->value.dataType) {
case Res_value::TYPE_INT_DEC:
@@ -816,7 +816,7 @@ class Linker {
if (!options_.manifest_fixer_options.compile_sdk_version_codename) {
xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
if (attr != nullptr) {
- Maybe<std::string>& compile_sdk_version_codename =
+ std::optional<std::string>& compile_sdk_version_codename =
options_.manifest_fixer_options.compile_sdk_version_codename;
if (String* str = ValueCast<String>(attr->compiled_value.get())) {
compile_sdk_version_codename = *str->value;
@@ -912,7 +912,7 @@ class Linker {
return true;
}
- Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
+ std::optional<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
TRACE_CALL();
// Make sure the first element is <manifest> with package attribute.
xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
@@ -937,7 +937,7 @@ class Linker {
if (xml::Attribute* version_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCode '" << version_code_attr->value << "'");
@@ -948,7 +948,7 @@ class Linker {
if (xml::Attribute* version_code_major_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_major_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCodeMajor '"
@@ -960,7 +960,7 @@ class Linker {
if (xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
+ std::optional<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
<< "invalid android:revisionCode '" << revision_code_attr->value << "'");
@@ -1094,7 +1094,7 @@ class Linker {
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
- const Maybe<std::string>& out_text_symbols_path = {}) {
+ const std::optional<std::string>& out_text_symbols_path = {}) {
if (!options_.generate_java_class_path && !out_text_symbols_path) {
return true;
}
@@ -1251,7 +1251,7 @@ class Linker {
}
const std::string package_utf8 =
- options_.custom_java_package.value_or_default(context_->GetCompilationPackage());
+ options_.custom_java_package.value_or(context_->GetCompilationPackage());
std::string out_path = options_.generate_java_class_path.value();
file::AppendPath(&out_path, file::PackageToPath(package_utf8));
@@ -1283,7 +1283,7 @@ class Linker {
return true;
}
- bool WriteProguardFile(const Maybe<std::string>& out, const proguard::KeepSet& keep_set) {
+ bool WriteProguardFile(const std::optional<std::string>& out, const proguard::KeepSet& keep_set) {
TRACE_CALL();
if (!out) {
return true;
@@ -1374,7 +1374,7 @@ class Linker {
res_name.package = context_->GetCompilationPackage();
}
- Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
+ std::optional<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
if (mangled_name) {
res_name = mangled_name.value();
}
@@ -1550,7 +1550,7 @@ class Linker {
bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
for (const std::string& assets_dir : options_.assets_dirs) {
- Maybe<std::vector<std::string>> files =
+ std::optional<std::vector<std::string>> files =
file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
if (!files) {
return false;
@@ -1783,7 +1783,7 @@ class Linker {
package_to_rewrite = table->packages.back().get();
std::string new_package_name =
StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
- app_info_.split_name.value_or_default("feature").c_str());
+ app_info_.split_name.value_or("feature").c_str());
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(
@@ -1823,7 +1823,7 @@ class Linker {
}
// First extract the Package name without modifying it (via --rename-manifest-package).
- if (Maybe<AppInfo> maybe_app_info =
+ if (std::optional<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
@@ -1850,14 +1850,14 @@ class Linker {
return 1;
}
- Maybe<AppInfo> maybe_app_info =
+ std::optional<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics());
if (!maybe_app_info) {
return 1;
}
app_info_ = maybe_app_info.value();
- context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
+ context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or(0));
context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
@@ -2231,7 +2231,7 @@ class Linker {
std::map<size_t, std::string> shared_libs_;
// The package name of the base application, if it is included.
- Maybe<std::string> included_feature_base_;
+ std::optional<std::string> included_feature_base_;
};
int LinkCommand::Action(const std::vector<std::string>& args) {
@@ -2315,7 +2315,8 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
return 1;
}
- const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id_.value());
+ const std::optional<uint32_t> maybe_package_id_int =
+ ResourceUtils::ParseInt(package_id_.value());
if (!maybe_package_id_int) {
context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id_.value()
<< "' is not a valid integer");
@@ -2360,7 +2361,7 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
}
if (preferred_density_) {
- Maybe<uint16_t> density =
+ std::optional<uint16_t> density =
ParseTargetDensityParameter(preferred_density_.value(), context.GetDiagnostics());
if (!density) {
return 1;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 768b4b2c7bfd..d8c76e297ec5 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -45,21 +45,21 @@ struct LinkOptions {
bool auto_add_overlay = false;
bool override_styles_instead_of_overlaying = false;
OutputFormat output_format = OutputFormat::kApk;
- Maybe<std::string> rename_resources_package;
+ std::optional<std::string> rename_resources_package;
// Java/Proguard options.
- Maybe<std::string> generate_java_class_path;
- Maybe<std::string> custom_java_package;
+ std::optional<std::string> generate_java_class_path;
+ std::optional<std::string> custom_java_package;
std::set<std::string> extra_java_packages;
- Maybe<std::string> generate_text_symbols_path;
- Maybe<std::string> generate_proguard_rules_path;
- Maybe<std::string> generate_main_dex_proguard_rules_path;
+ std::optional<std::string> generate_text_symbols_path;
+ std::optional<std::string> generate_proguard_rules_path;
+ std::optional<std::string> generate_main_dex_proguard_rules_path;
bool generate_conditional_proguard_rules = false;
bool generate_minimal_proguard_rules = false;
bool generate_non_final_ids = false;
bool no_proguard_location_reference = false;
std::vector<std::string> javadoc_annotations;
- Maybe<std::string> private_symbols;
+ std::optional<std::string> private_symbols;
// Optimizations/features.
bool no_auto_version = false;
@@ -70,7 +70,7 @@ struct LinkOptions {
bool no_xml_namespaces = false;
bool do_not_compress_anything = false;
std::unordered_set<std::string> extensions_to_not_compress;
- Maybe<std::regex> regex_to_not_compress;
+ std::optional<std::regex> regex_to_not_compress;
// Static lib options.
bool no_static_lib_packages = false;
@@ -97,7 +97,7 @@ struct LinkOptions {
// Stable ID options.
std::unordered_map<ResourceName, ResourceId> stable_id_map;
- Maybe<std::string> resource_id_map_path;
+ std::optional<std::string> resource_id_map_path;
// When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
// treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
@@ -321,20 +321,20 @@ class LinkCommand : public Command {
std::vector<std::string> overlay_arg_list_;
std::vector<std::string> extra_java_packages_;
- Maybe<std::string> package_id_;
+ std::optional<std::string> package_id_;
std::vector<std::string> configs_;
- Maybe<std::string> preferred_density_;
- Maybe<std::string> product_list_;
- Maybe<std::string> no_compress_regex;
+ std::optional<std::string> preferred_density_;
+ std::optional<std::string> product_list_;
+ std::optional<std::string> no_compress_regex;
bool legacy_x_flag_ = false;
bool require_localization_ = false;
bool verbose_ = false;
bool shared_lib_ = false;
bool static_lib_ = false;
bool proto_format_ = false;
- Maybe<std::string> stable_id_file_path_;
+ std::optional<std::string> stable_id_file_path_;
std::vector<std::string> split_args_;
- Maybe<std::string> trace_folder_;
+ std::optional<std::string> trace_folder_;
};
}// namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 5b18a3789d76..caa3e60d6af1 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -354,7 +354,7 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
return false;
}
- Maybe<AppInfo> app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
+ auto app_info = ExtractAppInfoFromBinaryManifest(*manifest, context->GetDiagnostics());
if (!app_info) {
context->GetDiagnostics()->Error(DiagMessage()
<< "failed to extract data from AndroidManifest.xml");
@@ -362,7 +362,7 @@ bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
}
out_options->app_info = std::move(app_info.value());
- context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or_default(0));
+ context->SetMinSdkVersion(out_options->app_info.min_sdk_version.value_or(0));
return true;
}
@@ -380,7 +380,7 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
if (config_path_) {
std::string& path = config_path_.value();
- Maybe<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
+ std::optional<ConfigurationParser> for_path = ConfigurationParser::ForPath(path);
if (for_path) {
options_.apk_artifacts = for_path.value().WithDiagnostics(diag).Parse(apk_path);
if (!options_.apk_artifacts) {
@@ -427,7 +427,7 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
if (target_densities_) {
// Parse the target screen densities.
for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
- Maybe<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
+ std::optional<uint16_t> target_density = ParseTargetDensityParameter(config_str, diag);
if (!target_density) {
return 1;
}
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 3afc46b04af6..ff63e8dd76d4 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -29,9 +29,9 @@ struct OptimizeOptions {
friend class OptimizeCommand;
// Path to the output APK.
- Maybe<std::string> output_path;
+ std::optional<std::string> output_path;
// Path to the output APK directory for splits.
- Maybe<std::string> output_dir;
+ std::optional<std::string> output_dir;
// Details of the app extracted from the AndroidManifest.xml
AppInfo app_info;
@@ -50,7 +50,7 @@ struct OptimizeOptions {
TableFlattenerOptions table_flattener_options;
- Maybe<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
+ std::optional<std::vector<aapt::configuration::OutputArtifact>> apk_artifacts;
// Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
// are kept and will be written as output.
@@ -60,7 +60,7 @@ struct OptimizeOptions {
bool shorten_resource_paths = false;
// Path to the output map of original resource paths to shortened paths.
- Maybe<std::string> shortened_paths_map_path;
+ std::optional<std::string> shortened_paths_map_path;
};
class OptimizeCommand : public Command {
@@ -122,9 +122,9 @@ class OptimizeCommand : public Command {
bool WriteObfuscatedPathsMap(const std::map<std::string, std::string> &path_map,
const std::string &file_path);
- Maybe<std::string> config_path_;
- Maybe<std::string> resources_config_path_;
- Maybe<std::string> target_densities_;
+ std::optional<std::string> config_path_;
+ std::optional<std::string> resources_config_path_;
+ std::optional<std::string> target_densities_;
std::vector<std::string> configs_;
std::vector<std::string> split_args_;
std::unordered_set<std::string> kept_artifacts_;
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 7214f1a68d2c..3244fb83fa4b 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -21,11 +21,10 @@
#include "android-base/logging.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/Locale.h"
-
#include "ResourceUtils.h"
#include "ValueVisitor.h"
#include "split/TableSplitter.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
using ::android::ConfigDescription;
@@ -35,7 +34,7 @@ using ::android::base::StringPrintf;
namespace aapt {
-Maybe<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
+std::optional<uint16_t> ParseTargetDensityParameter(const StringPiece& arg, IDiagnostics* diag) {
ConfigDescription preferred_density_config;
if (!ConfigDescription::Parse(arg, &preferred_density_config)) {
diag->Error(DiagMessage() << "invalid density '" << arg << "' for --preferred-density option");
@@ -245,8 +244,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
return doc;
}
-static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
- std::string* out_error) {
+static std::optional<std::string> ExtractCompiledString(const xml::Attribute& attr,
+ std::string* out_error) {
if (attr.compiled_value != nullptr) {
const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
if (compiled_str != nullptr) {
@@ -269,7 +268,8 @@ static Maybe<std::string> ExtractCompiledString(const xml::Attribute& attr,
return {};
}
-static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<uint32_t> ExtractCompiledInt(const xml::Attribute& attr,
+ std::string* out_error) {
if (attr.compiled_value != nullptr) {
const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
if (compiled_prim != nullptr) {
@@ -283,7 +283,7 @@ static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::strin
}
// Fallback to the plain text value if there is one.
- Maybe<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
+ std::optional<uint32_t> integer = ResourceUtils::ParseInt(attr.value);
if (integer) {
return integer;
}
@@ -293,7 +293,7 @@ static Maybe<uint32_t> ExtractCompiledInt(const xml::Attribute& attr, std::strin
return {};
}
-static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
+static std::optional<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out_error) {
if (attr.compiled_value != nullptr) {
const BinaryPrimitive* compiled_prim = ValueCast<BinaryPrimitive>(attr.compiled_value.get());
if (compiled_prim != nullptr) {
@@ -307,7 +307,7 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
const String* compiled_str = ValueCast<String>(attr.compiled_value.get());
if (compiled_str != nullptr) {
- Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
+ std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(*compiled_str->value);
if (sdk_version) {
return sdk_version;
}
@@ -320,7 +320,7 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
}
// Fallback to the plain text value if there is one.
- Maybe<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> sdk_version = ResourceUtils::ParseSdkVersion(attr.value);
if (sdk_version) {
return sdk_version;
}
@@ -330,8 +330,8 @@ static Maybe<int> ExtractSdkVersion(const xml::Attribute& attr, std::string* out
return {};
}
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
- IDiagnostics* diag) {
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+ IDiagnostics* diag) {
// Make sure the first element is <manifest> with package attribute.
const xml::Element* manifest_el = xml_res.root.get();
if (manifest_el == nullptr) {
@@ -352,7 +352,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
}
std::string error_msg;
- Maybe<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
+ std::optional<std::string> maybe_package = ExtractCompiledString(*package_attr, &error_msg);
if (!maybe_package) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid package name: " << error_msg);
@@ -362,7 +362,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* version_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCode: " << error_msg);
@@ -373,7 +373,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* version_code_major_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCodeMajor")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*version_code_major_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:versionCodeMajor: " << error_msg);
@@ -384,7 +384,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Attribute* revision_code_attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
+ std::optional<uint32_t> maybe_code = ExtractCompiledInt(*revision_code_attr, &error_msg);
if (!maybe_code) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid android:revisionCode: " << error_msg);
@@ -394,7 +394,8 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
}
if (const xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
- Maybe<std::string> maybe_split_name = ExtractCompiledString(*split_name_attr, &error_msg);
+ std::optional<std::string> maybe_split_name =
+ ExtractCompiledString(*split_name_attr, &error_msg);
if (!maybe_split_name) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(manifest_el->line_number))
<< "invalid split name: " << error_msg);
@@ -406,7 +407,7 @@ Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
if (const xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
if (const xml::Attribute* min_sdk =
uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
- Maybe<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
+ std::optional<int> maybe_sdk = ExtractSdkVersion(*min_sdk, &error_msg);
if (!maybe_sdk) {
diag->Error(DiagMessage(xml_res.file.source.WithLine(uses_sdk_el->line_number))
<< "invalid android:minSdkVersion: " << error_msg);
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 2a7c62eef678..1b98eb468700 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -26,14 +26,14 @@
#include "SdkConstants.h"
#include "filter/ConfigFilter.h"
#include "split/TableSplitter.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
namespace aapt {
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
-Maybe<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg, IDiagnostics* diag);
+std::optional<uint16_t> ParseTargetDensityParameter(const android::StringPiece& arg,
+ IDiagnostics* diag);
// Parses a string of the form 'path/to/output.apk:<config>[,<config>...]' and fills in
// `out_path` with the path and `out_split` with the set of ConfigDescriptions.
@@ -59,8 +59,8 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
const SplitConstraints& constraints);
// Extracts relevant info from the AndroidManifest.xml.
-Maybe<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
- IDiagnostics* diag);
+std::optional<AppInfo> ExtractAppInfoFromBinaryManifest(const xml::XmlResource& xml_res,
+ IDiagnostics* diag);
// Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
// replacing nonconforming characters with underscores.
diff --git a/tools/aapt2/compile/IdAssigner_test.cpp b/tools/aapt2/compile/IdAssigner_test.cpp
index 663776645990..d3575716ae4f 100644
--- a/tools/aapt2/compile/IdAssigner_test.cpp
+++ b/tools/aapt2/compile/IdAssigner_test.cpp
@@ -57,18 +57,18 @@ TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
ASSERT_TRUE(assigner.Consume(context.get(), table.get()));
ASSERT_TRUE(VerifyIds(table.get()));
- Maybe<ResourceTable::SearchResult> maybe_result;
+ std::optional<ResourceTable::SearchResult> maybe_result;
// Expect to fill in the gaps between 0x0101XXXX and 0x0104XXXX.
maybe_result = table->FindResource(test::ParseNameOrDie("android:dimen/two"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01020000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01020000, maybe_result.value().entry->id);
maybe_result =
table->FindResource(test::ParseNameOrDie("android:integer/three"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01030000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01030000, maybe_result.value().entry->id);
// Expect to bypass the reserved 0x0104XXXX IDs and use the next 0x0105XXXX
// IDs.
@@ -76,17 +76,17 @@ TEST_F(IdAssignerTests, AssignIdsWithReservedIds) {
maybe_result =
table->FindResource(test::ParseNameOrDie("android:string/five"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01050000), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01050000, maybe_result.value().entry->id);
// Expect to fill in the gaps between 0x01040000 and 0x01040006.
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/bar"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01040001), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01040001, maybe_result.value().entry->id);
maybe_result = table->FindResource(test::ParseNameOrDie("android:attr/baz"));
ASSERT_TRUE(maybe_result);
- EXPECT_EQ(make_value<ResourceId>(0x01040002), maybe_result.value().entry->id);
+ EXPECT_EQ(0x01040002, maybe_result.value().entry->id);
}
TEST_F(IdAssignerTests, FailWhenNonUniqueIdsAssigned) {
@@ -143,7 +143,7 @@ TEST_F(IdAssignerTests, AssignIdsWithIdMap) {
ASSERT_TRUE(result);
const ResourceTable::SearchResult& search_result = result.value();
- EXPECT_EQ(make_value<ResourceId>(0x01010002), search_result.entry->id);
+ EXPECT_EQ(0x01010002, search_result.entry->id);
}
TEST_F(IdAssignerTests, UseAllEntryIds) {
diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp
index 79b0933a089f..de1c3bb3dd7e 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -56,7 +56,7 @@ class Visitor : public xml::PackageAwareVisitor {
return;
}
- Maybe<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
+ std::optional<Reference> ref = ResourceUtils::ParseXmlAttributeName(attr->value);
if (!ref) {
context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid XML attribute '" << attr->value
<< "'");
@@ -65,7 +65,7 @@ class Visitor : public xml::PackageAwareVisitor {
}
const ResourceName& name = ref.value().name.value();
- Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
+ std::optional<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
if (!maybe_pkg) {
context_->GetDiagnostics()->Error(DiagMessage(src)
<< "invalid namespace prefix '" << name.package << "'");
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index 3f574ee8e897..2461438c49b6 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -33,7 +33,7 @@ namespace aapt {
// The struct that represents both Span objects and UntranslatableSections.
struct UnifiedSpan {
// Only present for Span objects. If not present, this was an UntranslatableSection.
- Maybe<std::string> tag;
+ std::optional<std::string> tag;
// The UTF-16 index into the string where this span starts.
uint32_t first_char;
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index dd06b38f6c01..e7a45851e239 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -34,7 +34,6 @@
#include "io/FileSystem.h"
#include "io/StringStream.h"
#include "util/Files.h"
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -113,7 +112,7 @@ std::string GetLabel(const Element* element, IDiagnostics* diag) {
}
/** Returns the value of the version-code-order attribute for a given element. */
-Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+std::optional<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
const xml::Attribute* version = element->FindAttribute("", "version-code-order");
if (version == nullptr) {
std::string label = GetLabel(element, diag);
@@ -135,7 +134,7 @@ class NamespaceVisitor : public xml::Visitor {
/** Copies the values referenced in a configuration group to the target list. */
template <typename T>
-bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
+bool CopyXmlReferences(const std::optional<std::string>& name, const Group<T>& groups,
std::vector<T>* target) {
// If there was no item configured, there is nothing to do and no error.
if (!name) {
@@ -159,7 +158,7 @@ bool CopyXmlReferences(const Maybe<std::string>& name, const Group<T>& groups,
* success, or false if the either the placeholder is not found in the name, or the value is not
* present and the placeholder was.
*/
-bool ReplacePlaceholder(const StringPiece& placeholder, const Maybe<StringPiece>& value,
+bool ReplacePlaceholder(const StringPiece& placeholder, const std::optional<StringPiece>& value,
std::string* name, IDiagnostics* diag) {
size_t offset = name->find(placeholder.data());
bool found = (offset != std::string::npos);
@@ -207,17 +206,17 @@ xml::XmlNodeAction::ActionFuncWithDiag Bind(configuration::PostProcessingConfigu
}
/** Converts a ConfiguredArtifact into an OutputArtifact. */
-Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
- const std::string& apk_name,
- const PostProcessingConfiguration& config,
- IDiagnostics* diag) {
+std::optional<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
+ const std::string& apk_name,
+ const PostProcessingConfiguration& config,
+ IDiagnostics* diag) {
if (!artifact.name && !config.artifact_format) {
diag->Error(
DiagMessage() << "Artifact does not have a name and no global name template defined");
return {};
}
- Maybe<std::string> artifact_name =
+ std::optional<std::string> artifact_name =
(artifact.name) ? artifact.Name(apk_name, diag)
: artifact.ToArtifactName(config.artifact_format.value(), apk_name, diag);
@@ -287,9 +286,9 @@ Maybe<OutputArtifact> ToOutputArtifact(const ConfiguredArtifact& artifact,
namespace configuration {
/** Returns the binary reprasentation of the XML configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
- const std::string& config_path,
- IDiagnostics* diag) {
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag) {
StringInputStream in(contents);
std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, diag, Source(config_path));
if (!doc) {
@@ -351,7 +350,8 @@ const StringPiece& AbiToString(Abi abi) {
/**
* Returns the common artifact base name from a template string.
*/
-Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, IDiagnostics* diag) {
+std::optional<std::string> ToBaseName(std::string result, const StringPiece& apk_name,
+ IDiagnostics* diag) {
const StringPiece ext = file::GetExtension(apk_name);
size_t end_index = apk_name.to_string().rfind(ext.to_string());
const std::string base_name =
@@ -359,8 +359,8 @@ Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, I
// Base name is optional.
if (result.find("${basename}") != std::string::npos) {
- Maybe<StringPiece> maybe_base_name =
- base_name.empty() ? Maybe<StringPiece>{} : Maybe<StringPiece>{base_name};
+ auto maybe_base_name = base_name.empty() ? std::nullopt
+ : std::optional<StringPiece>{base_name};
if (!ReplacePlaceholder("${basename}", maybe_base_name, &result, diag)) {
return {};
}
@@ -383,10 +383,10 @@ Maybe<std::string> ToBaseName(std::string result, const StringPiece& apk_name, I
return result;
}
-Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
- const StringPiece& apk_name,
- IDiagnostics* diag) const {
- Maybe<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
+std::optional<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
+ const StringPiece& apk_name,
+ IDiagnostics* diag) const {
+ std::optional<std::string> base = ToBaseName(format.to_string(), apk_name, diag);
if (!base) {
return {};
}
@@ -419,7 +419,8 @@ Maybe<std::string> ConfiguredArtifact::ToArtifactName(const StringPiece& format,
return result;
}
-Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagnostics* diag) const {
+std::optional<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name,
+ IDiagnostics* diag) const {
if (!name) {
return {};
}
@@ -430,7 +431,7 @@ Maybe<std::string> ConfiguredArtifact::Name(const StringPiece& apk_name, IDiagno
} // namespace configuration
/** Returns a ConfigurationParser for the file located at the provided path. */
-Maybe<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
+std::optional<ConfigurationParser> ConfigurationParser::ForPath(const std::string& path) {
std::string contents;
if (!ReadFileToString(path, &contents, true)) {
return {};
@@ -442,9 +443,9 @@ ConfigurationParser::ConfigurationParser(std::string contents, const std::string
: contents_(std::move(contents)), config_path_(config_path), diag_(&noop_) {
}
-Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
+std::optional<std::vector<OutputArtifact>> ConfigurationParser::Parse(
const android::StringPiece& apk_path) {
- Maybe<PostProcessingConfiguration> maybe_config =
+ std::optional<PostProcessingConfiguration> maybe_config =
ExtractConfiguration(contents_, config_path_, diag_);
if (!maybe_config) {
return {};
@@ -460,7 +461,8 @@ Maybe<std::vector<OutputArtifact>> ConfigurationParser::Parse(
int version = 1;
for (const ConfiguredArtifact& artifact : config.artifacts) {
- Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
+ std::optional<OutputArtifact> output_artifact =
+ ToOutputArtifact(artifact, apk_name, config, diag_);
if (!output_artifact) {
// Defer return an error condition so that all errors are reported.
valid = false;
@@ -538,7 +540,7 @@ bool AbiGroupTagHandler(PostProcessingConfiguration* config, Element* root_eleme
bool valid = true;
OrderedEntry<Abi>& entry = config->abi_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -589,7 +591,7 @@ bool ScreenDensityGroupTagHandler(PostProcessingConfiguration* config, Element*
bool valid = true;
OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -656,7 +658,7 @@ bool LocaleGroupTagHandler(PostProcessingConfiguration* config, Element* root_el
bool valid = true;
OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -724,19 +726,19 @@ bool AndroidSdkTagHandler(PostProcessingConfiguration* config, Element* root_ele
entry.label = attr.value;
valid_attr = true;
} else if (attr.name == "minSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.min_sdk_version = version.value();
}
} else if (attr.name == "targetSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.target_sdk_version = version;
}
} else if (attr.name == "maxSdkVersion") {
- Maybe<int> version = ResourceUtils::ParseSdkVersion(attr.value);
+ std::optional<int> version = ResourceUtils::ParseSdkVersion(attr.value);
if (version) {
valid_attr = true;
entry.max_sdk_version = version;
@@ -778,7 +780,7 @@ bool GlTextureGroupTagHandler(PostProcessingConfiguration* config, Element* root
bool valid = true;
OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
@@ -828,7 +830,7 @@ bool DeviceFeatureGroupTagHandler(PostProcessingConfiguration* config, Element*
bool valid = true;
OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
- Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+ std::optional<int32_t> order = GetVersionCodeOrder(root_element, diag);
if (!order) {
valid = false;
} else {
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index b9e3be9393c9..195b4baac319 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -17,6 +17,7 @@
#ifndef AAPT2_CONFIGURATION_H
#define AAPT2_CONFIGURATION_H
+#include <optional>
#include <set>
#include <string>
#include <unordered_map>
@@ -25,7 +26,6 @@
#include "androidfw/ConfigDescription.h"
#include "Diagnostics.h"
-#include "util/Maybe.h"
namespace aapt {
@@ -55,9 +55,9 @@ const android::StringPiece& AbiToString(Abi abi);
*/
struct Locale {
/** The ISO<?> standard locale language code. */
- Maybe<std::string> lang;
+ std::optional<std::string> lang;
/** The ISO<?> standard locale region code. */
- Maybe<std::string> region;
+ std::optional<std::string> region;
inline friend bool operator==(const Locale& lhs, const Locale& rhs) {
return lhs.lang == rhs.lang && lhs.region == rhs.region;
@@ -74,9 +74,9 @@ struct AndroidManifest {
struct AndroidSdk {
std::string label;
int min_sdk_version; // min_sdk_version is mandatory if splitting by SDK.
- Maybe<int> target_sdk_version;
- Maybe<int> max_sdk_version;
- Maybe<AndroidManifest> manifest;
+ std::optional<int> target_sdk_version;
+ std::optional<int> max_sdk_version;
+ std::optional<AndroidManifest> manifest;
static AndroidSdk ForMinSdk(int min_sdk) {
AndroidSdk sdk;
@@ -112,7 +112,7 @@ struct OutputArtifact {
std::vector<Abi> abis;
std::vector<android::ConfigDescription> screen_densities;
std::vector<android::ConfigDescription> locales;
- Maybe<AndroidSdk> android_sdk;
+ std::optional<AndroidSdk> android_sdk;
std::vector<DeviceFeature> features;
std::vector<GlTexture> textures;
@@ -136,7 +136,7 @@ class ConfigurationParser {
public:
/** Returns a ConfigurationParser for the file located at the provided path. */
- static Maybe<ConfigurationParser> ForPath(const std::string& path);
+ static std::optional<ConfigurationParser> ForPath(const std::string& path);
/** Returns a ConfigurationParser for the configuration in the provided file contents. */
static ConfigurationParser ForContents(const std::string& contents, const std::string& path) {
@@ -154,7 +154,8 @@ class ConfigurationParser {
* Parses the configuration file and returns the results. If the configuration could not be parsed
* the result is empty and any errors will be displayed with the provided diagnostics context.
*/
- Maybe<std::vector<configuration::OutputArtifact>> Parse(const android::StringPiece& apk_path);
+ std::optional<std::vector<configuration::OutputArtifact>> Parse(
+ const android::StringPiece& apk_path);
protected:
/**
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index c541688bc018..42ef51591d4f 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -84,8 +84,8 @@ class ComparisonChain {
* have not been able to determine the sort order with the previous comparisons.
*/
template <typename T>
- ComparisonChain& Add(const Group<T>& groups, const Maybe<std::string>& lhs,
- const Maybe<std::string>& rhs) {
+ ComparisonChain& Add(const Group<T>& groups, const std::optional<std::string>& lhs,
+ const std::optional<std::string>& rhs) {
return Add(GetGroupOrder(groups, lhs), GetGroupOrder(groups, rhs));
}
@@ -108,7 +108,7 @@ class ComparisonChain {
private:
template <typename T>
- inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
+ inline size_t GetGroupOrder(const Entry<T>& groups, const std::optional<std::string>& label) {
if (!label) {
return std::numeric_limits<size_t>::max();
}
@@ -122,32 +122,33 @@ class ComparisonChain {
/** Output artifact configuration options. */
struct ConfiguredArtifact {
/** Name to use for output of processing foo.apk -> foo.<name>.apk. */
- Maybe<std::string> name;
+ std::optional<std::string> name;
/** If present, uses the ABI group with this name. */
- Maybe<std::string> abi_group;
+ std::optional<std::string> abi_group;
/** If present, uses the screen density group with this name. */
- Maybe<std::string> screen_density_group;
+ std::optional<std::string> screen_density_group;
/** If present, uses the locale group with this name. */
- Maybe<std::string> locale_group;
+ std::optional<std::string> locale_group;
/** If present, uses the Android SDK with this name. */
- Maybe<std::string> android_sdk;
+ std::optional<std::string> android_sdk;
/** If present, uses the device feature group with this name. */
- Maybe<std::string> device_feature_group;
+ std::optional<std::string> device_feature_group;
/** If present, uses the OpenGL texture group with this name. */
- Maybe<std::string> gl_texture_group;
+ std::optional<std::string> gl_texture_group;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> ToArtifactName(const android::StringPiece& format,
- const android::StringPiece& apk_name, IDiagnostics* diag) const;
+ std::optional<std::string> ToArtifactName(const android::StringPiece& format,
+ const android::StringPiece& apk_name,
+ IDiagnostics* diag) const;
/** Convert an artifact name template into a name string based on configuration contents. */
- Maybe<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
+ std::optional<std::string> Name(const android::StringPiece& apk_name, IDiagnostics* diag) const;
};
/** AAPT2 XML configuration file binary representation. */
struct PostProcessingConfiguration {
std::vector<ConfiguredArtifact> artifacts;
- Maybe<std::string> artifact_format;
+ std::optional<std::string> artifact_format;
Group<Abi> abi_groups;
Group<android::ConfigDescription> screen_density_groups;
@@ -212,9 +213,9 @@ struct PostProcessingConfiguration {
};
/** Parses the provided XML document returning the post processing configuration. */
-Maybe<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
- const std::string& config_path,
- IDiagnostics* diag);
+std::optional<PostProcessingConfiguration> ExtractConfiguration(const std::string& contents,
+ const std::string& config_path,
+ IDiagnostics* diag);
namespace handler {
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index e5b3107877cb..e5eacccb682e 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -31,11 +31,6 @@ using ::android::ConfigDescription;
namespace aapt {
namespace configuration {
-void PrintTo(const AndroidSdk& sdk, std::ostream* os) {
- *os << "SDK: min=" << sdk.min_sdk_version
- << ", target=" << sdk.target_sdk_version.value_or_default(-1)
- << ", max=" << sdk.max_sdk_version.value_or_default(-1);
-}
bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
return lhs.name == rhs.name && lhs.abi_group == rhs.abi_group &&
@@ -45,20 +40,6 @@ bool operator==(const ConfiguredArtifact& lhs, const ConfiguredArtifact& rhs) {
lhs.gl_texture_group == rhs.gl_texture_group;
}
-std::ostream& operator<<(std::ostream& out, const Maybe<std::string>& value) {
- PrintTo(value, &out);
- return out;
-}
-
-void PrintTo(const ConfiguredArtifact& artifact, std::ostream* os) {
- *os << "\n{"
- << "\n name: " << artifact.name << "\n sdk: " << artifact.android_sdk
- << "\n abi: " << artifact.abi_group << "\n density: " << artifact.screen_density_group
- << "\n locale: " << artifact.locale_group
- << "\n features: " << artifact.device_feature_group
- << "\n textures: " << artifact.gl_texture_group << "\n}\n";
-}
-
namespace handler {
namespace {
@@ -186,7 +167,7 @@ TEST_F(ConfigurationParserTest, ForPath_NoFile) {
}
TEST_F(ConfigurationParserTest, ExtractConfiguration) {
- Maybe<PostProcessingConfiguration> maybe_config =
+ std::optional<PostProcessingConfiguration> maybe_config =
ExtractConfiguration(kValidConfig, "fake.xml", &diag_);
PostProcessingConfiguration config = maybe_config.value();
@@ -928,7 +909,8 @@ TEST(ArtifactTest, Nesting) {
EXPECT_FALSE(x86.ToArtifactName("something.${abi${density}}.apk", "", &diag));
- const Maybe<std::string>& name = x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
+ const std::optional<std::string>& name =
+ x86.ToArtifactName("something.${abi${abi}}.apk", "", &diag);
ASSERT_TRUE(name);
EXPECT_EQ(name.value(), "something.${abix86}.apk");
}
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
index 41f01a01ed7c..c20b053c37b1 100644
--- a/tools/aapt2/format/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -43,7 +43,7 @@ class DirectoryWriter : public IArchiveWriter {
bool Open(const StringPiece& out_dir) {
dir_ = out_dir.to_string();
file::FileType type = file::GetFileType(dir_);
- if (type == file::FileType::kNonexistant) {
+ if (type == file::FileType::kNonExistant) {
error_ = "directory does not exist";
return false;
} else if (type != file::FileType::kDirectory) {
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 8139d7385092..cd1c0af702cf 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -123,7 +123,7 @@ class TableFlattenerTest : public ::testing::Test {
return ::testing::AssertionFailure() << "failed to find resource name";
}
- Maybe<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
+ std::optional<ResourceName> resName = ResourceUtils::ToResourceName(actual_name);
if (!resName) {
return ::testing::AssertionFailure()
<< "expected name '" << expected_res_name << "' but got '"
@@ -423,7 +423,7 @@ TEST_F(TableFlattenerTest, FlattenSharedLibrary) {
ResourceTable result;
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:id/foo"));
ASSERT_TRUE(search_result);
EXPECT_EQ(0x00u, search_result.value().entry->id.value().package_id());
@@ -454,7 +454,7 @@ TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
ResourceTable result;
ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
ASSERT_TRUE(search_result);
EXPECT_EQ(0x00030001u, search_result.value().entry->id.value());
diff --git a/tools/aapt2/format/binary/XmlFlattener.cpp b/tools/aapt2/format/binary/XmlFlattener.cpp
index afbaae4ee2a8..cdbe8828b29b 100644
--- a/tools/aapt2/format/binary/XmlFlattener.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener.cpp
@@ -264,7 +264,7 @@ class XmlFlattenerVisitor : public xml::ConstVisitor {
}
std::string processed_str;
- Maybe<StringPiece> compiled_text;
+ std::optional<StringPiece> compiled_text;
if (xml_attr->compiled_value != nullptr) {
// Make sure we're not flattening a String. A String can be referencing a string from
// a different StringPool than we're using here to build the binary XML.
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 6042ba89bf8a..f3b7f758e170 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -438,7 +438,7 @@ static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
}
static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref) {
- pb_ref->set_id(ref.id.value_or_default(ResourceId(0x0)).id);
+ pb_ref->set_id(ref.id.value_or(ResourceId(0x0)).id);
if (ref.name) {
pb_ref->set_name(ref.name.value().to_string());
@@ -759,13 +759,13 @@ void SerializeXmlToPb(const xml::Element& el, pb::XmlNode* out_node,
pb_attr->set_namespace_uri(attr.namespace_uri);
pb_attr->set_value(attr.value);
if (attr.compiled_attribute) {
- const ResourceId attr_id = attr.compiled_attribute.value().id.value_or_default({});
+ const ResourceId attr_id = attr.compiled_attribute.value().id.value_or(ResourceId{});
pb_attr->set_resource_id(attr_id.id);
}
if (attr.compiled_value != nullptr) {
SerializeItemToPb(*attr.compiled_value, pb_attr->mutable_compiled_item());
pb::SourcePosition* pb_src = pb_attr->mutable_source();
- pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or_default(0));
+ pb_src->set_line_number(attr.compiled_value->GetSource().line.value_or(0));
}
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 38c811fe3619..d1d72e012b31 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -189,7 +189,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
ASSERT_THAT(new_id, NotNull());
EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
- Maybe<ResourceTable::SearchResult> result =
+ std::optional<ResourceTable::SearchResult> result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
ASSERT_TRUE(result);
@@ -234,7 +234,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:integer/overlayable"));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
@@ -637,7 +637,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
EXPECT_THAT(error, IsEmpty());
- Maybe<ResourceTable::SearchResult> search_result =
+ std::optional<ResourceTable::SearchResult> search_result =
new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index e15f935cad27..fc2e45e74b4d 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -21,11 +21,10 @@
#include "android-base/errors.h"
#include "androidfw/StringPiece.h"
#include "utils/FileMap.h"
-
#include "Source.h"
#include "io/FileStream.h"
#include "util/Files.h"
-#include "util/Maybe.h"
+
#include "util/Util.h"
using ::android::StringPiece;
@@ -38,7 +37,7 @@ RegularFile::RegularFile(const Source& source) : source_(source) {}
std::unique_ptr<IData> RegularFile::OpenAsData() {
android::FileMap map;
- if (Maybe<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
+ if (std::optional<android::FileMap> map = file::MmapPath(source_.path, nullptr)) {
if (map.value().getDataPtr() && map.value().getDataLength() > 0) {
return util::make_unique<MmappedData>(std::move(map.value()));
}
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index de6524dc7027..3b3c6e187aa6 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -204,7 +204,7 @@ bool JavaClassGenerator::SkipSymbol(Visibility::Level level) {
}
// Whether or not to skip writing this symbol.
-bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
+bool JavaClassGenerator::SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol) {
return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
!symbol.value().is_public);
}
@@ -212,12 +212,12 @@ bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
struct StyleableAttr {
const Reference* attr_ref = nullptr;
std::string field_name;
- Maybe<SymbolTable::Symbol> symbol;
+ std::optional<SymbolTable::Symbol> symbol;
};
static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
- const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
- const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
+ const ResourceId lhs_id = lhs.attr_ref->id.value_or(ResourceId(0));
+ const ResourceId rhs_id = rhs.attr_ref->id.value_or(ResourceId(0));
if (lhs_id == rhs_id) {
return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
}
@@ -362,7 +362,7 @@ bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
array_def->AddElement(field_name);
r_txt_contents = field_name.ref;
} else {
- const ResourceId attr_id = attr.attr_ref->id.value_or_default(ResourceId(0));
+ const ResourceId attr_id = attr.attr_ref->id.value_or(ResourceId(0));
array_def->AddElement(attr_id);
r_txt_contents = to_string(attr_id);
}
@@ -504,9 +504,9 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
}
}
-Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
- const StringPiece& package_name_to_generate,
- const ResourceEntry& entry) {
+std::optional<std::string> JavaClassGenerator::UnmangleResource(
+ const StringPiece& package_name, const StringPiece& package_name_to_generate,
+ const ResourceEntry& entry) {
if (SkipSymbol(entry.visibility.level)) {
return {};
}
@@ -535,7 +535,7 @@ bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate
MethodDefinition* out_rewrite_method_def,
Printer* r_txt_printer) {
for (const auto& entry : type.entries) {
- const Maybe<std::string> unmangled_name =
+ const std::optional<std::string> unmangled_name =
UnmangleResource(package.name, package_name_to_generate, *entry);
if (!unmangled_name) {
continue;
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index d9d1b39805f9..b45a2f12db35 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -46,7 +46,7 @@ struct JavaClassGeneratorOptions {
// If set, generates code to rewrite the package ID of resources.
// Implies use_final == true. Default is unset.
- Maybe<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
+ std::optional<OnResourcesLoadedCallbackOptions> rewrite_callback_options;
enum class SymbolTypes {
kAll,
@@ -83,13 +83,13 @@ class JavaClassGenerator {
private:
bool SkipSymbol(Visibility::Level state);
- bool SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol);
+ bool SkipSymbol(const std::optional<SymbolTable::Symbol>& symbol);
// Returns the unmangled resource entry name if the unmangled package is the same as
// package_name_to_generate. Returns nothing if the resource should be skipped.
- Maybe<std::string> UnmangleResource(const android::StringPiece& package_name,
- const android::StringPiece& package_name_to_generate,
- const ResourceEntry& entry);
+ std::optional<std::string> UnmangleResource(const android::StringPiece& package_name,
+ const android::StringPiece& package_name_to_generate,
+ const ResourceEntry& entry);
bool ProcessType(const android::StringPiece& package_name_to_generate,
const ResourceTablePackage& package, const ResourceTableType& type,
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 09ea03b23c9a..a0db41baecb4 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -19,19 +19,17 @@
#include <algorithm>
#include "Source.h"
-#include "java/AnnotationProcessor.h"
#include "java/ClassDefinition.h"
#include "java/JavaClassGenerator.h"
#include "text/Unicode.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
using ::aapt::text::IsJavaIdentifier;
namespace aapt {
-static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
- const std::string& value) {
+static std::optional<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source,
+ const std::string& value) {
std::string result = value;
size_t pos = value.rfind('.');
if (pos != std::string::npos) {
@@ -63,7 +61,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element*
return false;
}
- Maybe<std::string> result =
+ std::optional<std::string> result =
ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value);
if (!result) {
return false;
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d9a4caa34e0d..b939f354e89f 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -48,7 +48,7 @@ class BaseVisitor : public xml::Visitor {
void Visit(xml::Element* node) override {
if (!node->namespace_uri.empty()) {
- Maybe<xml::ExtractedPackage> maybe_package =
+ std::optional<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(node->namespace_uri);
if (maybe_package) {
// This is a custom view, let's figure out the class name from this.
@@ -270,14 +270,16 @@ class ManifestVisitor : public BaseVisitor {
get_name = true;
xml::Attribute* attr = node->FindAttribute(xml::kSchemaAndroid, "backupAgent");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
}
attr = node->FindAttribute(xml::kSchemaAndroid, "appComponentFactory");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
@@ -285,7 +287,8 @@ class ManifestVisitor : public BaseVisitor {
attr = node->FindAttribute(xml::kSchemaAndroid, "zygotePreloadName");
if (attr) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
@@ -317,7 +320,8 @@ class ManifestVisitor : public BaseVisitor {
get_name = attr != nullptr;
if (get_name) {
- Maybe<std::string> result = util::GetFullyQualifiedClassName(package_, attr->value);
+ std::optional<std::string> result =
+ util::GetFullyQualifiedClassName(package_, attr->value);
if (result) {
AddClass(node->line_number, result.value(), "");
}
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 876494e617a6..328ac97090a8 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -90,7 +90,7 @@ bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
}
if (Style* style = ValueCast<Style>(config_value->value.get())) {
- Maybe<ApiVersion> min_sdk_stripped;
+ std::optional<ApiVersion> min_sdk_stripped;
std::vector<Style::Entry> stripped;
auto iter = style->entries.begin();
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 02fd00bba439..8179d46145ca 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -87,25 +87,27 @@ TEST(AutoVersionerTest, VersionStylesForTable) {
Style* style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v4"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 1u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries.front().key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries.front().key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v13"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 2u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")),style->entries[0].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+ style->entries[1].key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v17"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(style->entries.size(), 3u);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/onClick")), style->entries[0].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp")), style->entries[1].key.name);
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingStart")), style->entries[2].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/onClick"), style->entries[0].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/requiresSmallestWidthDp"),
+ style->entries[1].key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingStart"), style->entries[2].key.name);
style = test::GetValueForConfig<Style>(table.get(), "app:style/Foo", test::ParseConfigOrDie("v21"));
ASSERT_THAT(style, NotNull());
ASSERT_EQ(1u, style->entries.size());
- EXPECT_EQ(make_value(test::ParseNameOrDie("android:attr/paddingEnd")), style->entries.front().key.name);
+ EXPECT_EQ(test::ParseNameOrDie("android:attr/paddingEnd"), style->entries.front().key.name);
}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 8abd9dec56be..1bb06964e31c 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -52,7 +52,7 @@ static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
// We allow unqualified class names (ie: .HelloActivity)
// Since we don't know the package name, we can just make a fake one here and
// the test will be identical as long as the real package name is valid too.
- Maybe<std::string> fully_qualified_class_name =
+ std::optional<std::string> fully_qualified_class_name =
util::GetFullyQualifiedClassName("a", attr->value);
StringPiece qualified_class_name = fully_qualified_class_name
@@ -146,7 +146,7 @@ static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics*
// Now inject the android:isFeatureSplit="true" attribute.
xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
if (attr != nullptr) {
- if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
+ if (!ResourceUtils::ParseBool(attr->value).value_or(false)) {
// The isFeatureSplit attribute is false, which conflicts with the use
// of "featureSplit".
diag->Error(DiagMessage(el->line_number)
@@ -523,7 +523,8 @@ static void FullyQualifyClassName(const StringPiece& package, const StringPiece&
const StringPiece& attr_name, xml::Element* el) {
xml::Attribute* attr = el->FindAttribute(attr_ns, attr_name);
if (attr != nullptr) {
- if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package, attr->value)) {
+ if (std::optional<std::string> new_value =
+ util::GetFullyQualifiedClassName(package, attr->value)) {
attr->value = std::move(new_value.value());
}
}
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 34ad8d586df1..d5d1d1770e1c 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -22,7 +22,7 @@
#include "android-base/macros.h"
#include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
+
#include "xml/XmlActionExecutor.h"
#include "xml/XmlDom.h"
@@ -30,47 +30,47 @@ namespace aapt {
struct ManifestFixerOptions {
// The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag.
- Maybe<std::string> min_sdk_version_default;
+ std::optional<std::string> min_sdk_version_default;
// The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag.
- Maybe<std::string> target_sdk_version_default;
+ std::optional<std::string> target_sdk_version_default;
// The Android package to use instead of the one defined in 'package' in <manifest>.
// This also renames all relative package/class names in the manifest to fully qualified
// Java names.
- Maybe<std::string> rename_manifest_package;
+ std::optional<std::string> rename_manifest_package;
// The Android package to use instead of the one defined in 'android:targetPackage' in
// <instrumentation>.
- Maybe<std::string> rename_instrumentation_target_package;
+ std::optional<std::string> rename_instrumentation_target_package;
// The Android package to use instead of the one defined in 'android:targetPackage' in
// <overlay>.
- Maybe<std::string> rename_overlay_target_package;
+ std::optional<std::string> rename_overlay_target_package;
// The version name to set if 'android:versionName' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_name_default;
+ std::optional<std::string> version_name_default;
// The version code to set if 'android:versionCode' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_code_default;
+ std::optional<std::string> version_code_default;
// The version code to set if 'android:versionCodeMajor' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> version_code_major_default;
+ std::optional<std::string> version_code_major_default;
// The revision code to set if 'android:revisionCode' is not defined in <manifest> or if
// replace_version is set.
- Maybe<std::string> revision_code_default;
+ std::optional<std::string> revision_code_default;
// The version of the framework being compiled against to set for 'android:compileSdkVersion' in
// the <manifest> tag.
- Maybe<std::string> compile_sdk_version;
+ std::optional<std::string> compile_sdk_version;
// The version codename of the framework being compiled against to set for
// 'android:compileSdkVersionCodename' in the <manifest> tag.
- Maybe<std::string> compile_sdk_version_codename;
+ std::optional<std::string> compile_sdk_version_codename;
// Whether validation errors should be treated only as warnings. If this is 'true', then an
// incorrect node will not result in an error, but only as a warning, and the parsing will
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 4ac25bd6a0e0..47c804c6bf71 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -190,7 +190,8 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
public:
EmptyDeclStack() = default;
- Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ std::optional<xml::ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias) const override {
if (alias.empty()) {
return xml::ExtractedPackage{{}, true /*private*/};
}
@@ -206,7 +207,8 @@ struct MacroDeclStack : public xml::IPackageDeclStack {
: alias_namespaces_(std::move(namespaces)) {
}
- Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ std::optional<xml::ExtractedPackage> TransformPackageAlias(
+ const StringPiece& alias) const override {
if (alias.empty()) {
return xml::ExtractedPackage{{}, true /*private*/};
}
@@ -322,11 +324,11 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
return symbol;
}
-Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
- const CallSite& callsite,
- IAaptContext* context,
- SymbolTable* symbols,
- std::string* out_error) {
+std::optional<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols,
+ std::string* out_error) {
const SymbolTable::Symbol* symbol =
ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 770f1e500ac0..b46085397c52 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -97,11 +97,11 @@ class ReferenceLinker : public IResourceTableConsumer {
// Resolves the attribute reference and returns an xml::AaptAttribute if successful.
// If resolution fails, outError holds the error message.
- static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
- const CallSite& callsite,
- IAaptContext* context,
- SymbolTable* symbols,
- std::string* out_error);
+ static std::optional<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols,
+ std::string* out_error);
// Writes the resource name to the DiagMessage, using the
// "orig_name (aka <transformed_name>)" syntax.
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 2d8f0d39053f..97bdd3ebae66 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -317,12 +317,12 @@ TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
CallSite{"com.app.test"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
+ EXPECT_THAT(s->id, Eq(0x7f010000));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
+ EXPECT_THAT(s->id, Eq(0x7f010001));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
CallSite{"com.app.bad"}, context.get(), &table),
@@ -348,7 +348,7 @@ TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) {
CallSite{"com.app.test"},
context.get(), &table);
ASSERT_THAT(s, NotNull());
- EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x80010000)));
+ EXPECT_THAT(s->id, Eq(0x80010000));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
context.get(), &table);
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 22f4d18dc3ca..d094d3619020 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -303,8 +303,8 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
dst_config_value->value = std::move(new_file_ref);
} else {
- Maybe<std::string> original_comment = (dst_config_value->value)
- ? dst_config_value->value->GetComment() : Maybe<std::string>();
+ auto original_comment = (dst_config_value->value)
+ ? dst_config_value->value->GetComment() : std::optional<std::string>();
dst_config_value->value = src_config_value->value->Transform(cloner);
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 4358fb565a7d..4cbf2d3a826c 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -409,8 +409,7 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
const auto expected = ResourceUtils::MakeBool(true);
EXPECT_THAT(style->entries, Contains(Field(&Style::Entry::value, Pointee(ValueEq(*expected)))));
- EXPECT_THAT(style->parent,
- Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
+ EXPECT_THAT(style->parent, Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")));
}
TEST_F(TableMergerTest, OverrideStyleInsteadOfOverlaying) {
@@ -483,7 +482,7 @@ TEST_F(TableMergerTest, SetOverlayable) {
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
@@ -517,7 +516,7 @@ TEST_F(TableMergerTest, SetOverlayableLater) {
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
const ResourceName name = test::ParseNameOrDie("com.app.a:bool/foo");
- Maybe<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
+ std::optional<ResourceTable::SearchResult> search_result = final_table.FindResource(name);
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index aaa085e2eb15..1f8548b5de75 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -68,7 +68,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
const Attribute* attribute = &default_attribute;
- if (Maybe<xml::ExtractedPackage> maybe_package =
+ if (std::optional<xml::ExtractedPackage> maybe_package =
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
// There is a valid package name for this attribute. We will look this up.
Reference attr_ref(
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index ddf5b9a22c2f..6d96cf1cf502 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -100,18 +100,18 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "layout_width");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010000)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010000), xml_attr->compiled_attribute.value().id);
EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "background");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010001)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010001), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(test::ParseNameOrDie("color/green")), ref->name); // Make sure the name
- // didn't change.
- EXPECT_EQ(make_value(ResourceId(0x7f020000)), ref->id);
+ EXPECT_EQ(test::ParseNameOrDie("color/green"), ref->name); // Make sure the name
+ // didn't change.
+ EXPECT_EQ(ResourceId(0x7f020000), ref->id);
xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "text");
ASSERT_THAT(xml_attr, NotNull());
@@ -172,7 +172,7 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
view_el->FindAttribute(xml::BuildPackageNamespace("com.android.support"), "colorAccent");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010001)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010001), xml_attr->compiled_attribute.value().id);
EXPECT_THAT(ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get()), NotNull());
}
@@ -190,11 +190,11 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAuto, "colorAccent");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010000)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010000), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
ASSERT_TRUE(ref->name);
- EXPECT_EQ(make_value(ResourceId(0x7f020001)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f020001), ref->id);
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
@@ -214,10 +214,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::kSchemaAndroid, "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010002), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x01030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x01030000), ref->id);
ASSERT_FALSE(view_el->GetChildElements().empty());
view_el = view_el->GetChildElements().front();
@@ -228,10 +228,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f030000), ref->id);
}
TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
@@ -250,10 +250,10 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
xml::Attribute* xml_attr = view_el->FindAttribute(xml::BuildPackageNamespace("com.app.test"), "attr");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x7f010002)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x7f010002), xml_attr->compiled_attribute.value().id);
Reference* ref = ValueCast<Reference>(xml_attr->compiled_value.get());
ASSERT_THAT(ref, NotNull());
- EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
+ EXPECT_EQ(ResourceId(0x7f030000), ref->id);
}
@@ -270,7 +270,7 @@ TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
ASSERT_THAT(value, NotNull());
@@ -292,7 +292,7 @@ TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
ASSERT_THAT(xml_attr, NotNull());
ASSERT_TRUE(xml_attr->compiled_attribute);
- EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+ EXPECT_EQ(ResourceId(0x01010004), xml_attr->compiled_attribute.value().id);
BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
ASSERT_THAT(value, NotNull());
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index d385267fe5ed..2d58cbfb3dbe 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -75,8 +75,8 @@ const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
// Fill in the package name if necessary.
// If there is no package in `name`, we will need to copy the ResourceName
- // and store it somewhere; we use the Maybe<> class to reserve storage.
- Maybe<ResourceName> name_with_package_impl;
+ // and store it somewhere; we use the std::optional<> class to reserve storage.
+ std::optional<ResourceName> name_with_package_impl;
if (name.package.empty()) {
name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry);
name_with_package = &name_with_package_impl.value();
@@ -88,9 +88,9 @@ const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
}
// The name was not found in the cache. Mangle it (if necessary) and find it in our sources.
- // Again, here we use a Maybe<> object to reserve storage if we need to mangle.
+ // Again, here we use a std::optional<> object to reserve storage if we need to mangle.
const ResourceName* mangled_name = name_with_package;
- Maybe<ResourceName> mangled_name_impl;
+ std::optional<ResourceName> mangled_name_impl;
if (mangler_->ShouldMangle(name_with_package->package)) {
mangled_name_impl = mangler_->MangleName(*name_with_package);
mangled_name = &mangled_name_impl.value();
@@ -183,7 +183,7 @@ std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindById(
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
const ResourceName& name) {
- Maybe<ResourceTable::SearchResult> result = table_->FindResource(name);
+ std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
if (!result) {
if (name.type == ResourceType::kAttr) {
// Recurse and try looking up a private attribute.
@@ -306,7 +306,7 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
return nullptr;
}
- Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
+ std::optional<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name);
if (!parsed_name) {
return nullptr;
}
@@ -382,8 +382,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
return {};
}
-static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
- ResourceId id) {
+static std::optional<ResourceName> GetResourceName(android::AssetManager2& am, ResourceId id) {
auto name = am.GetResourceName(id.id);
if (!name.has_value()) {
return {};
@@ -402,7 +401,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
return {};
}
- Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
+ std::optional<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
if (!maybe_name) {
return {};
}
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 06eaf63ad442..65ae7beadc07 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -56,7 +56,7 @@ class SymbolTable {
struct Symbol {
Symbol() = default;
- explicit Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
+ explicit Symbol(const std::optional<ResourceId>& i, const std::shared_ptr<Attribute>& attr = {},
bool pub = false)
: id(i), attribute(attr), is_public(pub) {
}
@@ -66,7 +66,7 @@ class SymbolTable {
Symbol& operator=(const Symbol&) = default;
Symbol& operator=(Symbol&&) = default;
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
std::shared_ptr<Attribute> attribute;
bool is_public = false;
bool is_dynamic = false;
diff --git a/tools/aapt2/test/Builders.cpp b/tools/aapt2/test/Builders.cpp
index 4816596da487..23331de02df7 100644
--- a/tools/aapt2/test/Builders.cpp
+++ b/tools/aapt2/test/Builders.cpp
@@ -159,7 +159,8 @@ std::unique_ptr<ResourceTable> ResourceTableBuilder::Build() {
return std::move(table_);
}
-std::unique_ptr<Reference> BuildReference(const StringPiece& ref, const Maybe<ResourceId>& id) {
+std::unique_ptr<Reference> BuildReference(const StringPiece& ref,
+ const std::optional<ResourceId>& id) {
std::unique_ptr<Reference> reference = util::make_unique<Reference>(ParseNameOrDie(ref));
reference->id = id;
return reference;
@@ -218,7 +219,8 @@ std::unique_ptr<Style> StyleBuilder::Build() {
return std::move(style_);
}
-StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str, const Maybe<ResourceId>& id) {
+StyleableBuilder& StyleableBuilder::AddItem(const StringPiece& str,
+ const std::optional<ResourceId>& id) {
styleable_->entries.push_back(Reference(ParseNameOrDie(str)));
styleable_->entries.back().id = id;
return *this;
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 3ff955d65f24..55778aea40af 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -29,7 +29,6 @@
#include "configuration/ConfigurationParser.internal.h"
#include "process/IResourceTableConsumer.h"
#include "test/Common.h"
-#include "util/Maybe.h"
#include "xml/XmlDom.h"
namespace aapt {
@@ -86,7 +85,7 @@ class ResourceTableBuilder {
};
std::unique_ptr<Reference> BuildReference(const android::StringPiece& ref,
- const Maybe<ResourceId>& id = {});
+ const std::optional<ResourceId>& id = {});
std::unique_ptr<BinaryPrimitive> BuildPrimitive(uint8_t type, uint32_t data);
template <typename T>
@@ -149,7 +148,8 @@ class StyleBuilder {
class StyleableBuilder {
public:
StyleableBuilder() = default;
- StyleableBuilder& AddItem(const android::StringPiece& str, const Maybe<ResourceId>& id = {});
+ StyleableBuilder& AddItem(const android::StringPiece& str,
+ const std::optional<ResourceId>& id = {});
std::unique_ptr<Styleable> Build();
private:
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
index 23c22185a53f..e029d025b366 100644
--- a/tools/aapt2/test/Common.cpp
+++ b/tools/aapt2/test/Common.cpp
@@ -48,7 +48,7 @@ Value* GetValueForConfigAndProduct<Value>(ResourceTable* table,
const android::StringPiece& res_name,
const ConfigDescription& config,
const android::StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
if (result) {
ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
if (config_value) {
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 777ca5c72619..7006964d6f88 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -55,7 +55,7 @@ template <typename T = Value>
T* GetValueForConfigAndProduct(ResourceTable* table, const android::StringPiece& res_name,
const android::ConfigDescription& config,
const android::StringPiece& product) {
- Maybe<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
+ std::optional<ResourceTable::SearchResult> result = table->FindResource(ParseNameOrDie(res_name));
if (result) {
ResourceConfigValue* config_value = result.value().entry->FindValue(config, product);
if (config_value) {
@@ -130,7 +130,7 @@ template std::ostream& operator<<<Plural>(std::ostream&, const Plural&);
// Add a print method to Maybe.
template <typename T>
-void PrintTo(const Maybe<T>& value, std::ostream* out) {
+void PrintTo(const std::optional<T>& value, std::ostream* out) {
if (value) {
*out << ::testing::PrintToString(value.value());
} else {
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 5d8ded39e654..e1b8dd5687ff 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -95,8 +95,8 @@ class Context : public IAaptContext {
friend class ContextBuilder;
PackageType package_type_ = PackageType::kApp;
- Maybe<std::string> compilation_package_;
- Maybe<uint8_t> package_id_;
+ std::optional<std::string> compilation_package_;
+ std::optional<uint8_t> package_id_;
StdErrDiagnostics diagnostics_;
NameMangler name_mangler_;
SymbolTable symbols_;
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 285e5a11b4c0..e2f71dc45075 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -126,7 +126,7 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args,
link_args.insert(link_args.end(), {"-I", android_sdk});
// Add the files from the compiled resources directory to the link file arguments
- Maybe<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
+ std::optional<std::vector<std::string>> compiled_files = file::FindFiles(flat_dir, diag);
if (compiled_files) {
for (std::string& compile_file : compiled_files.value()) {
compile_file = file::BuildPath({flat_dir, compile_file});
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 5d57de6a9fb1..5d2eda3293f0 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -50,12 +50,12 @@ namespace file {
FileType GetFileType(const std::string& path) {
std::wstring path_utf16;
if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
DWORD result = GetFileAttributesW(path_utf16.c_str());
if (result == INVALID_FILE_ATTRIBUTES) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
if (result & FILE_ATTRIBUTE_DIRECTORY) {
@@ -72,7 +72,7 @@ FileType GetFileType(const std::string& path) {
if (result == -1) {
if (errno == ENOENT || errno == ENOTDIR) {
- return FileType::kNonexistant;
+ return FileType::kNonExistant;
}
return FileType::kUnknown;
}
@@ -208,7 +208,7 @@ std::string PackageToPath(const StringPiece& package) {
return out_path;
}
-Maybe<FileMap> MmapPath(const std::string& path, std::string* out_error) {
+std::optional<FileMap> MmapPath(const std::string& path, std::string* out_error) {
int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
if (fd == -1) {
@@ -344,8 +344,8 @@ bool FileFilter::operator()(const std::string& filename, FileType type) const {
return true;
}
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
- const FileFilter* filter) {
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+ IDiagnostics* diag, const FileFilter* filter) {
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
@@ -382,7 +382,7 @@ Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDia
for (const std::string& subdir : subdirs) {
std::string full_subdir = root_dir;
AppendPath(&full_subdir, subdir);
- Maybe<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
+ std::optional<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
if (!subfiles) {
return {};
}
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 481a4cdb6ad0..877cd56d6c69 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -18,6 +18,7 @@
#define AAPT_FILES_H
#include <memory>
+#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
@@ -27,7 +28,6 @@
#include "utils/FileMap.h"
#include "Diagnostics.h"
-#include "Maybe.h"
#include "Source.h"
namespace aapt {
@@ -43,7 +43,7 @@ constexpr const char sPathSep = ':';
enum class FileType {
kUnknown = 0,
- kNonexistant,
+ kNonExistant,
kRegular,
kDirectory,
kCharDev,
@@ -81,7 +81,7 @@ bool IsHidden(const android::StringPiece& path);
std::string PackageToPath(const android::StringPiece& package);
// Creates a FileMap for the file at path.
-Maybe<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
+std::optional<android::FileMap> MmapPath(const std::string& path, std::string* out_error);
// Reads the file at path and appends each line to the outArgList vector.
bool AppendArgsFromFile(const android::StringPiece& path, std::vector<std::string>* out_arglist,
@@ -124,8 +124,9 @@ class FileFilter {
// Returns a list of files relative to the directory identified by `path`.
// An optional FileFilter filters out any files that don't pass.
-Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
- const FileFilter* filter = nullptr);
+std::optional<std::vector<std::string>> FindFiles(const android::StringPiece& path,
+ IDiagnostics* diag,
+ const FileFilter* filter = nullptr);
} // namespace file
} // namespace aapt
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
deleted file mode 100644
index 047e1a581330..000000000000
--- a/tools/aapt2/util/Maybe.h
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 AAPT_MAYBE_H
-#define AAPT_MAYBE_H
-
-#include <type_traits>
-#include <utility>
-
-#include "android-base/logging.h"
-
-#include "util/TypeTraits.h"
-
-namespace aapt {
-
-/**
- * Either holds a valid value of type T, or holds Nothing.
- * The value is stored inline in this structure, so no
- * heap memory is used when creating a Maybe<T> object.
- */
-template <typename T>
-class Maybe {
- public:
- /**
- * Construct Nothing.
- */
- Maybe();
-
- ~Maybe();
-
- Maybe(const Maybe& rhs);
-
- template <typename U>
- Maybe(const Maybe<U>& rhs); // NOLINT(google-explicit-constructor)
-
- Maybe(Maybe&& rhs) noexcept;
-
- template <typename U>
- Maybe(Maybe<U>&& rhs); // NOLINT(google-explicit-constructor)
-
- Maybe& operator=(const Maybe& rhs);
-
- template <typename U>
- Maybe& operator=(const Maybe<U>& rhs);
-
- Maybe& operator=(Maybe&& rhs) noexcept;
-
- template <typename U>
- Maybe& operator=(Maybe<U>&& rhs);
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(const T& value); // NOLINT(google-explicit-constructor)
-
- /**
- * Construct a Maybe holding a value.
- */
- Maybe(T&& value); // NOLINT(google-explicit-constructor)
-
- /**
- * True if this holds a value, false if
- * it holds Nothing.
- */
- explicit operator bool() const;
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- T& value();
-
- /**
- * Gets the value if one exists, or else
- * panics.
- */
- const T& value() const;
-
- T value_or_default(const T& def) const;
-
- private:
- template <typename U>
- friend class Maybe;
-
- template <typename U>
- Maybe& copy(const Maybe<U>& rhs);
-
- template <typename U>
- Maybe& move(Maybe<U>&& rhs);
-
- void destroy();
-
- bool nothing_;
-
- typename std::aligned_storage<sizeof(T), alignof(T)>::type storage_;
-};
-
-template <typename T>
-Maybe<T>::Maybe() : nothing_(true) {}
-
-template <typename T>
-Maybe<T>::~Maybe() {
- if (!nothing_) {
- destroy();
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- new (&storage_) T(reinterpret_cast<const T&>(rhs.storage_));
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
- }
-}
-
-template <typename T>
-Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<T&>(rhs.storage_)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>::Maybe(Maybe<U>&& rhs) : nothing_(rhs.nothing_) {
- if (!rhs.nothing_) {
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
- rhs.destroy();
- }
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe& rhs) {
- // Delegate to the actual assignment.
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
- return copy(rhs);
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) {
- if (nothing_ && rhs.nothing_) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!nothing_ && !rhs.nothing_) {
- // We both are something, so assign rhs to us.
- reinterpret_cast<T&>(storage_) = reinterpret_cast<const U&>(rhs.storage_);
- } else if (nothing_) {
- // We are nothing but rhs is something.
- nothing_ = rhs.nothing_;
-
- // Copy the value from rhs.
- new (&storage_) T(reinterpret_cast<const U&>(rhs.storage_));
- } else {
- // We are something but rhs is nothing, so destroy our value.
- nothing_ = rhs.nothing_;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept {
- // Delegate to the actual assignment.
- return move(std::forward<Maybe<T>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-inline Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
- return move(std::forward<Maybe<U>>(rhs));
-}
-
-template <typename T>
-template <typename U>
-Maybe<T>& Maybe<T>::move(Maybe<U>&& rhs) {
- if (nothing_ && rhs.nothing_) {
- // Both are nothing, nothing to do.
- return *this;
- } else if (!nothing_ && !rhs.nothing_) {
- // We both are something, so move assign rhs to us.
- rhs.nothing_ = true;
- reinterpret_cast<T&>(storage_) =
- std::move(reinterpret_cast<U&>(rhs.storage_));
- rhs.destroy();
- } else if (nothing_) {
- // We are nothing but rhs is something.
- nothing_ = false;
- rhs.nothing_ = true;
-
- // Move the value from rhs.
- new (&storage_) T(std::move(reinterpret_cast<U&>(rhs.storage_)));
- rhs.destroy();
- } else {
- // We are something but rhs is nothing, so destroy our value.
- nothing_ = true;
- destroy();
- }
- return *this;
-}
-
-template <typename T>
-Maybe<T>::Maybe(const T& value) : nothing_(false) {
- new (&storage_) T(value);
-}
-
-template <typename T>
-Maybe<T>::Maybe(T&& value) : nothing_(false) {
- new (&storage_) T(std::forward<T>(value));
-}
-
-template <typename T>
-Maybe<T>::operator bool() const {
- return !nothing_;
-}
-
-template <typename T>
-T& Maybe<T>::value() {
- CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
- return reinterpret_cast<T&>(storage_);
-}
-
-template <typename T>
-const T& Maybe<T>::value() const {
- CHECK(!nothing_) << "Maybe<T>::value() called on Nothing";
- return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-T Maybe<T>::value_or_default(const T& def) const {
- if (nothing_) {
- return def;
- }
- return reinterpret_cast<const T&>(storage_);
-}
-
-template <typename T>
-void Maybe<T>::destroy() {
- reinterpret_cast<T&>(storage_).~T();
-}
-
-template <typename T>
-inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
- return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
-}
-
-template <typename T>
-inline Maybe<T> make_nothing() {
- return Maybe<T>();
-}
-
-// Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
-// That way the compiler will show an error at the callsite when comparing two Maybe<> objects
-// whose inner types can't be compared.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
- const Maybe<U>& b) {
- if (a && b) {
- return a.value() == b.value();
- } else if (!a && !b) {
- return true;
- }
- return false;
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
- const U& b) {
- return a ? a.value() == b : false;
-}
-
-// Same as operator== but negated.
-template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a,
- const Maybe<U>& b) {
- return !(a == b);
-}
-
-template <typename T, typename U>
-typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a,
- const Maybe<U>& b) {
- if (a && b) {
- return a.value() < b.value();
- } else if (!a && !b) {
- return false;
- }
- return !a;
-}
-
-} // namespace aapt
-
-#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
deleted file mode 100644
index 4c921f13a3ca..000000000000
--- a/tools/aapt2/util/Maybe_test.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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 "util/Maybe.h"
-
-#include <string>
-
-#include "test/Test.h"
-
-namespace aapt {
-
-struct Fake {
- Fake() {
- data = new int;
- *data = 1;
- std::cerr << "Construct Fake{0x" << (void*)this << "} with data=0x"
- << (void*)data << std::endl;
- }
-
- Fake(const Fake& rhs) {
- data = nullptr;
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyConstruct Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- }
-
- Fake(Fake&& rhs) {
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveConstruct Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- }
-
- Fake& operator=(const Fake& rhs) {
- delete data;
- data = nullptr;
-
- if (rhs.data) {
- data = new int;
- *data = *rhs.data;
- }
- std::cerr << "CopyAssign Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- return *this;
- }
-
- Fake& operator=(Fake&& rhs) {
- delete data;
- data = rhs.data;
- rhs.data = nullptr;
- std::cerr << "MoveAssign Fake{0x" << (void*)this << "} from Fake{0x"
- << (const void*)&rhs << "}" << std::endl;
- return *this;
- }
-
- ~Fake() {
- std::cerr << "Destruct Fake{0x" << (void*)this << "} with data=0x"
- << (void*)data << std::endl;
- delete data;
- }
-
- int* data;
-};
-
-TEST(MaybeTest, MakeNothing) {
- Maybe<int> val = make_nothing<int>();
- EXPECT_FALSE(val);
-
- Maybe<std::string> val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-
- val2 = make_nothing<std::string>();
- EXPECT_FALSE(val2);
-}
-
-TEST(MaybeTest, MakeSomething) {
- Maybe<int> val = make_value(23);
- ASSERT_TRUE(val);
- EXPECT_EQ(23, val.value());
-
- Maybe<std::string> val2 = make_value(std::string("hey"));
- ASSERT_TRUE(val2);
- EXPECT_EQ(std::string("hey"), val2.value());
-}
-
-TEST(MaybeTest, Lifecycle) {
- Maybe<Fake> val = make_nothing<Fake>();
-
- Maybe<Fake> val2 = make_value(Fake());
-}
-
-TEST(MaybeTest, MoveAssign) {
- Maybe<Fake> val;
- {
- Maybe<Fake> val2 = Fake();
- val = std::move(val2);
- }
-}
-
-TEST(MaybeTest, Equality) {
- Maybe<int> a = 1;
- Maybe<int> b = 1;
- Maybe<int> c;
-
- Maybe<int> emptyA, emptyB;
-
- EXPECT_EQ(a, b);
- EXPECT_EQ(b, a);
- EXPECT_NE(a, c);
- EXPECT_EQ(emptyA, emptyB);
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index d7a8e6fe6ada..44b4ec13b2f1 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -28,7 +28,6 @@
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
-#include "util/Maybe.h"
#include "utils/Unicode.h"
using ::aapt::text::Utf8Iterator;
@@ -193,8 +192,8 @@ bool IsAndroidSplitName(const StringPiece& str) {
return IsAndroidNameImpl(str) > 0;
}
-Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
- const StringPiece& classname) {
+std::optional<std::string> GetFullyQualifiedClassName(const StringPiece& package,
+ const StringPiece& classname) {
if (classname.empty()) {
return {};
}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c77aca31a810..c3efe6a63feb 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -28,7 +28,6 @@
#include "utils/ByteOrder.h"
#include "util/BigBuffer.h"
-#include "util/Maybe.h"
#ifdef _WIN32
// TODO(adamlesinski): remove once http://b/32447322 is resolved.
@@ -105,8 +104,8 @@ bool IsAndroidSharedUserId(const android::StringPiece& package_name,
// .asdf --> package.asdf
// .a.b --> package.a.b
// asdf.adsf --> asdf.adsf
-Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
- const android::StringPiece& class_name);
+std::optional<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
+ const android::StringPiece& class_name);
// Retrieves the formatted name of aapt2.
const char* GetToolName();
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index 2cdcfe45b50e..8b7eadf9fac9 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -545,7 +545,7 @@ void Text::Accept(ConstVisitor* visitor) const {
void PackageAwareVisitor::BeforeVisitElement(Element* el) {
std::vector<PackageDecl> decls;
for (const NamespaceDecl& decl : el->namespace_decls) {
- if (Maybe<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
+ if (std::optional<ExtractedPackage> maybe_package = ExtractPackageFromNamespace(decl.uri)) {
decls.push_back(PackageDecl{decl.prefix, std::move(maybe_package.value())});
}
}
@@ -556,7 +556,8 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
package_decls_.pop_back();
}
-Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
+ const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{{}, false /*private*/};
}
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index a5b2d10fc9e0..5d31804d43b7 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -65,12 +65,12 @@ struct NamespaceDecl {
};
struct AaptAttribute {
- explicit AaptAttribute(const ::aapt::Attribute& attr, const Maybe<ResourceId>& resid = {})
+ explicit AaptAttribute(const ::aapt::Attribute& attr, const std::optional<ResourceId>& resid = {})
: attribute(attr), id(resid) {
}
aapt::Attribute attribute;
- Maybe<ResourceId> id;
+ std::optional<ResourceId> id;
};
// An XML attribute.
@@ -79,7 +79,7 @@ struct Attribute {
std::string name;
std::string value;
- Maybe<AaptAttribute> compiled_attribute;
+ std::optional<AaptAttribute> compiled_attribute;
std::unique_ptr<Item> compiled_value;
};
@@ -235,7 +235,8 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
public:
using Visitor::Visit;
- Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ std::optional<ExtractedPackage> TransformPackageAlias(
+ const android::StringPiece& alias) const override;
protected:
PackageAwareVisitor() = default;
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index ca46d539fe3c..6c717dcd84c8 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -98,7 +98,7 @@ TEST(XmlDomTest, BinaryInflate) {
// the Attribute accepts (eg: string|reference).
ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute);
EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id,
- Eq(make_value(ResourceId(0x01010001u))));
+ Eq(ResourceId(0x01010001u)));
EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo"));
EXPECT_THAT(new_doc->root->attributes[0].compiled_value,
@@ -145,21 +145,19 @@ class TestVisitor : public PackageAwareVisitor {
void Visit(Element* el) override {
if (el->name == "View1") {
- EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
} else if (el->name == "View2") {
- EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
} else if (el->name == "View3") {
- EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
- EXPECT_THAT(TransformPackageAlias("three"),
- Eq(make_value(ExtractedPackage{"com.three", false})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+ EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
} else if (el->name == "View4") {
- EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
- EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
- EXPECT_THAT(TransformPackageAlias("three"),
- Eq(make_value(ExtractedPackage{"com.three", false})));
- EXPECT_THAT(TransformPackageAlias("four"), Eq(make_value(ExtractedPackage{"", true})));
+ EXPECT_THAT(TransformPackageAlias("one"), Eq(ExtractedPackage{"com.one", false}));
+ EXPECT_THAT(TransformPackageAlias("two"), Eq(ExtractedPackage{"com.two", false}));
+ EXPECT_THAT(TransformPackageAlias("three"), Eq(ExtractedPackage{"com.three", false}));
+ EXPECT_THAT(TransformPackageAlias("four"), Eq(ExtractedPackage{"", true}));
}
}
};
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index 182203d397c3..bfa07490b9c0 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -17,7 +17,6 @@
#include <iostream>
#include <string>
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlPullParser.h"
#include "xml/XmlUtil.h"
@@ -84,8 +83,7 @@ XmlPullParser::Event XmlPullParser::Next() {
// handling of references that use namespace aliases.
if (next_event == Event::kStartNamespace ||
next_event == Event::kEndNamespace) {
- Maybe<ExtractedPackage> result =
- ExtractPackageFromNamespace(namespace_uri());
+ std::optional<ExtractedPackage> result = ExtractPackageFromNamespace(namespace_uri());
if (next_event == Event::kStartNamespace) {
if (result) {
package_aliases_.emplace_back(
@@ -142,7 +140,8 @@ const std::string& XmlPullParser::namespace_uri() const {
return event_queue_.front().data2;
}
-Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
+std::optional<ExtractedPackage> XmlPullParser::TransformPackageAlias(
+ const StringPiece& alias) const {
if (alias.empty()) {
return ExtractedPackage{{}, false /*private*/};
}
@@ -308,8 +307,7 @@ void XMLCALL XmlPullParser::EndCdataSectionHandler(void* user_data) {
parser->depth_ });
}
-Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
- const StringPiece& name) {
+std::optional<StringPiece> FindAttribute(const XmlPullParser* parser, const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
return StringPiece(util::TrimWhitespace(iter->value));
@@ -317,8 +315,8 @@ Maybe<StringPiece> FindAttribute(const XmlPullParser* parser,
return {};
}
-Maybe<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
- const StringPiece& name) {
+std::optional<StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const StringPiece& name) {
auto iter = parser->FindAttribute("", name);
if (iter != parser->end_attributes()) {
StringPiece trimmed = util::TrimWhitespace(iter->value);
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 5da2d4b10a4b..ab347728ae4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -33,7 +33,6 @@
#include "Resource.h"
#include "io/Io.h"
#include "process/IResourceTableConsumer.h"
-#include "util/Maybe.h"
#include "xml/XmlUtil.h"
namespace aapt {
@@ -121,7 +120,8 @@ class XmlPullParser : public IPackageDeclStack {
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
* 'package' will be set to 'defaultPackage'.
*/
- Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ std::optional<ExtractedPackage> TransformPackageAlias(
+ const android::StringPiece& alias) const override;
struct PackageDecl {
std::string prefix;
@@ -193,16 +193,16 @@ class XmlPullParser : public IPackageDeclStack {
/**
* Finds the attribute in the current element within the global namespace.
*/
-Maybe<android::StringPiece> FindAttribute(const XmlPullParser* parser,
- const android::StringPiece& name);
+std::optional<android::StringPiece> FindAttribute(const XmlPullParser* parser,
+ const android::StringPiece& name);
/**
* Finds the attribute in the current element within the global namespace. The
* attribute's value
* must not be the empty string.
*/
-Maybe<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
- const android::StringPiece& name);
+std::optional<android::StringPiece> FindNonEmptyAttribute(const XmlPullParser* parser,
+ const android::StringPiece& name);
//
// Implementation
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index 0a622b2bd336..114b5ba7ab1a 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -19,7 +19,6 @@
#include <algorithm>
#include <string>
-#include "util/Maybe.h"
#include "util/Util.h"
#include "xml/XmlDom.h"
@@ -34,8 +33,7 @@ std::string BuildPackageNamespace(const StringPiece& package, bool private_refer
return result;
}
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(
- const std::string& namespace_uri) {
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri) {
if (util::StartsWith(namespace_uri, kSchemaPublicPrefix)) {
StringPiece schema_prefix = kSchemaPublicPrefix;
StringPiece package = namespace_uri;
@@ -62,7 +60,7 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
if (in_ref->name) {
- if (Maybe<ExtractedPackage> transformed_package =
+ if (std::optional<ExtractedPackage> transformed_package =
decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
ExtractedPackage& extracted_package = transformed_package.value();
in_ref->name.value().package = std::move(extracted_package.package);
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 592a604f87a6..1ab05a93d314 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -20,7 +20,6 @@
#include <string>
#include "ResourceValues.h"
-#include "util/Maybe.h"
namespace aapt {
namespace xml {
@@ -53,7 +52,7 @@ struct ExtractedPackage {
//
// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
// package name.
-Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
+std::optional<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
// Returns an XML Android namespace for the given package of the form:
// http://schemas.android.com/apk/res/<package>
@@ -69,7 +68,7 @@ struct IPackageDeclStack {
virtual ~IPackageDeclStack() = default;
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
- virtual Maybe<ExtractedPackage> TransformPackageAlias(
+ virtual std::optional<ExtractedPackage> TransformPackageAlias(
const android::StringPiece& alias) const = 0;
};
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index cbded8ffac8e..7b6ce9e96689 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -27,7 +27,7 @@ TEST(XmlUtilTest, ExtractPackageFromNamespace) {
ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/"));
ASSERT_FALSE(xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/prv/res/"));
- Maybe<xml::ExtractedPackage> p =
+ std::optional<xml::ExtractedPackage> p =
xml::ExtractPackageFromNamespace("http://schemas.android.com/apk/res/a");
ASSERT_TRUE(p);
EXPECT_EQ(std::string("a"), p.value().package);
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 81d35efaf29f..3cdb27c74d63 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -8,7 +8,21 @@ elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then
# Change is explicitly marked as ok to skip AOSP
exit 0
else
- # Change appears to be non-AOSP; search for files
+ # Change appears to be non-AOSP.
+
+ # If this is a cherry-pick, then allow it.
+ cherrypick=0
+ while read -r line ; do
+ if [[ $line =~ cherry\ picked\ from ]] ; then
+ (( cherrypick++ ))
+ fi
+ done < <(git show $1)
+ if (( cherrypick != 0 )); then
+ # This is a cherry-pick, so allow it.
+ exit 0
+ fi
+
+ # See if any files are affected.
count=0
while read -r file ; do
if (( count == 0 )); then
diff --git a/tools/codegen/OWNERS b/tools/codegen/OWNERS
index da723b3b67da..c9bd260ca7ae 100644
--- a/tools/codegen/OWNERS
+++ b/tools/codegen/OWNERS
@@ -1 +1 @@
-eugenesusla@google.com \ No newline at end of file
+chiuwinson@google.com
diff --git a/tools/codegen/src/com/android/codegen/ImportsProvider.kt b/tools/codegen/src/com/android/codegen/ImportsProvider.kt
index 27dd9587db25..46df2739e59f 100644
--- a/tools/codegen/src/com/android/codegen/ImportsProvider.kt
+++ b/tools/codegen/src/com/android/codegen/ImportsProvider.kt
@@ -53,10 +53,25 @@ interface ImportsProvider {
* Optionally shortens a class reference if there's a corresponding import present
*/
fun classRef(fullName: String): String {
-
val pkg = fullName.substringBeforeLast(".")
val simpleName = fullName.substringAfterLast(".")
- if (fileAst.imports.any { imprt ->
+ val imports = fileAst.imports
+
+ // If an import of the same class name is available,
+ // use it instead of the internal Android package variants.
+ if (fullName.startsWith("com.android.internal.util.")
+ && imports.any {
+ it.nameAsString.endsWith(fullName.removePrefix("com.android.internal.util."))
+ }
+ ) {
+ return fullName.removePrefix("com.android.internal.util.")
+ } else if (fullName.startsWith("android.annotation")
+ && imports.any { it.nameAsString.endsWith(simpleName) }
+ ) {
+ return simpleName
+ }
+
+ if (imports.any { imprt ->
imprt.nameAsString == fullName
|| (imprt.isAsterisk && imprt.nameAsString == pkg)
}) {
@@ -89,4 +104,4 @@ interface ImportsProvider {
/** @see classRef */
inline fun <reified T : Any> ImportsProvider.classRef(): String {
return classRef(T::class.java.name)
-} \ No newline at end of file
+}
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa7847fcff..9ceb2042d74e 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) {
* cccc dd
*/
fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
- val col1w = map { (a, _) -> a.length }.max()!!
- val col2w = map { (_, b) -> b.length }.max()!!
+ val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+ val col2w = map { (_, b) -> b.length }.maxOrNull()!!
return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
}
diff --git a/tools/lint/Android.bp b/tools/lint/Android.bp
new file mode 100644
index 000000000000..17547ef8b561
--- /dev/null
+++ b/tools/lint/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the '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: "AndroidFrameworkLintChecker",
+ srcs: ["checks/src/main/java/**/*.kt"],
+ plugins: ["auto_service_plugin"],
+ libs: [
+ "auto_service_annotations",
+ "lint_api",
+ ],
+}
+
+java_test_host {
+ name: "AndroidFrameworkLintCheckerTest",
+ srcs: ["checks/src/test/java/**/*.kt"],
+ static_libs: [
+ "AndroidFrameworkLintChecker",
+ "junit",
+ "lint",
+ "lint_tests",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+}
diff --git a/tools/lint/README.md b/tools/lint/README.md
new file mode 100644
index 000000000000..df2fe2a7c738
--- /dev/null
+++ b/tools/lint/README.md
@@ -0,0 +1,50 @@
+# Android Framework Lint Checker
+
+Custom lint checks written here are going to be executed for modules that opt in to those (e.g. any
+`services.XXX` module) and results will be automatically reported on CLs on gerrit.
+
+## How to add new lint checks
+
+1. Write your detector with its issues and put it into
+ `checks/src/main/java/com/google/android/lint`.
+2. Add your detector's issues into `AndroidFrameworkIssueRegistry`'s `issues` field.
+3. Write unit tests for your detector in one file and put it into
+ `checks/test/java/com/google/android/lint`.
+4. Done! Your lint checks should be applied in lint report builds for modules that include
+ `AndroidFrameworkLintChecker`.
+
+## How to run lint against your module
+
+1. Add the following `lint` attribute to the module definition, e.g. `services.autofill`:
+```
+java_library_static {
+ name: "services.autofill",
+ ...
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ },
+}
+```
+2. Run the following command to verify that the report is being correctly built:
+```
+m out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/android_common/lint/lint-report.html
+```
+ (Lint report can be found in the same path, i.e. `out/../lint-report.html`)
+
+3. Now lint issues should appear on gerrit!
+
+**Notes:**
+
+- Lint report will not be produced if you just build the module, i.e. `m services.autofill` will not
+ build the lint report.
+- If you want to build lint reports for more than 1 module and they include a common module in their
+ `defaults` field, e.g. `platform_service_defaults`, you can add the `lint` property to that common
+ module instead of adding it in every module.
+
+## Documentation
+
+- [Android Lint Docs](https://googlesamples.github.io/android-custom-lint-rules/)
+- [Android Lint source files](https://source.corp.google.com/studio-main/tools/base/lint/libs/lint-api/src/main/java/com/android/tools/lint/)
+- [PSI source files](https://github.com/JetBrains/intellij-community/tree/master/java/java-psi-api/src/com/intellij/psi)
+- [UAST source files](https://upsource.jetbrains.com/idea-ce/structure/idea-ce-7b9b8cc138bbd90aec26433f82cd2c6838694003/uast/uast-common/src/org/jetbrains/uast)
+- [IntelliJ plugin for viewing PSI tree of files](https://plugins.jetbrains.com/plugin/227-psiviewer)
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
new file mode 100644
index 000000000000..900c2145975a
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -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.google.android.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.auto.service.AutoService
+
+@AutoService(IssueRegistry::class)
+@Suppress("UnstableApiUsage")
+class AndroidFrameworkIssueRegistry : IssueRegistry() {
+ override val issues = listOf(
+ CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+ )
+
+ override val api: Int
+ get() = CURRENT_API
+
+ override val minApi: Int
+ get() = 8
+
+ override val vendor: Vendor = Vendor(
+ vendorName = "Android",
+ feedbackUrl = "http://b/issues/new?component=315013",
+ contact = "brufino@google.com"
+ )
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
new file mode 100644
index 000000000000..c133226e49f7
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/CallingIdentityTokenDetector.kt
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.android.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.search.PsiSearchScopeUtil
+import com.intellij.psi.search.SearchScope
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UDeclarationsExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.ULocalVariable
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.UTryExpression
+import org.jetbrains.uast.getParentOfType
+import org.jetbrains.uast.getQualifiedParentOrThis
+import org.jetbrains.uast.getUCallExpression
+
+/**
+ * Lint Detector that finds issues with improper usages of the token returned by
+ * Binder.clearCallingIdentity()
+ */
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetector : Detector(), SourceCodeScanner {
+ /** Map of <Token variable name, Token object> */
+ private val tokensMap = mutableMapOf<String, Token>()
+
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(ULocalVariable::class.java, UCallExpression::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler =
+ TokenUastHandler(context)
+
+ /** File analysis starts with a clear map */
+ override fun beforeCheckFile(context: Context) {
+ tokensMap.clear()
+ }
+
+ /**
+ * - If tokensMap has tokens after checking the file -> reports all locations as unused token
+ * issue incidents
+ * - File analysis ends with a clear map
+ */
+ override fun afterCheckFile(context: Context) {
+ for (token in tokensMap.values) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ token.location,
+ getIncidentMessageUnusedToken(token.variableName)
+ )
+ }
+ tokensMap.clear()
+ }
+
+ /** UAST handler that analyses elements and reports incidents */
+ private inner class TokenUastHandler(val context: JavaContext) : UElementHandler() {
+ /**
+ * For every variable initialization with Binder.clearCallingIdentity():
+ * - Checks for non-final token issue
+ * - Checks for unused token issue within different scopes
+ * - Checks for nested calls of clearCallingIdentity() issue
+ * - Checks for clearCallingIdentity() not followed by try-finally issue
+ * - Stores token variable name, scope in the file, location and finally block in tokensMap
+ */
+ override fun visitLocalVariable(node: ULocalVariable) {
+ val rhsExpression = node.uastInitializer?.getUCallExpression() ?: return
+ if (!isMethodCall(rhsExpression, Method.BINDER_CLEAR_CALLING_IDENTITY)) return
+ val location = context.getLocation(node as UElement)
+ val variableName = node.getName()
+ if (!node.isFinal) {
+ context.report(
+ ISSUE_NON_FINAL_TOKEN,
+ location,
+ getIncidentMessageNonFinalToken(variableName)
+ )
+ }
+ // If there exists an unused variable with the same name in the map, we can imply that
+ // we left the scope of the previous declaration, so we need to report the unused token
+ val oldToken = tokensMap[variableName]
+ if (oldToken != null) {
+ context.report(
+ ISSUE_UNUSED_TOKEN,
+ oldToken.location,
+ getIncidentMessageUnusedToken(oldToken.variableName)
+ )
+ }
+ // If there exists a token in the same scope as the current new token, it means that
+ // clearCallingIdentity() has been called at least twice without immediate restoration
+ // of identity, so we need to report the nested call of clearCallingIdentity()
+ val firstCallToken = findFirstTokenInScope(node)
+ if (firstCallToken != null) {
+ context.report(
+ ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ createNestedLocation(firstCallToken, location),
+ getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallToken.variableName,
+ variableName
+ )
+ )
+ }
+ // If the next statement in the tree is not a try-finally statement, we need to report
+ // the "clearCallingIdentity() is not followed by try-finally" issue
+ val finallyClause = (getNextStatementOfLocalVariable(node) as? UTryExpression)
+ ?.finallyClause
+ if (finallyClause == null) {
+ context.report(
+ ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY,
+ location,
+ getIncidentMessageClearIdentityCallNotFollowedByTryFinally(variableName)
+ )
+ }
+ tokensMap[variableName] = Token(
+ variableName,
+ node.sourcePsi?.getUseScope(),
+ location,
+ finallyClause
+ )
+ }
+
+ /**
+ * For every method():
+ * - Checks use of caller-aware methods issue
+ * For every call of Binder.restoreCallingIdentity(token):
+ * - Checks for restoreCallingIdentity() not in the finally block issue
+ * - Removes token from tokensMap if token is within the scope of the method
+ */
+ override fun visitCallExpression(node: UCallExpression) {
+ val token = findFirstTokenInScope(node)
+ if (isCallerAwareMethod(node) && token != null) {
+ context.report(
+ ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ context.getLocation(node),
+ getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ token.variableName,
+ node.asRenderString()
+ )
+ )
+ return
+ }
+ if (!isMethodCall(node, Method.BINDER_RESTORE_CALLING_IDENTITY)) return
+ val arg = node.valueArguments[0] as? USimpleNameReferenceExpression ?: return
+ val variableName = arg.identifier
+ val originalScope = tokensMap[variableName]?.scope ?: return
+ val psi = arg.sourcePsi ?: return
+ // Checks if Binder.restoreCallingIdentity(token) is called within the scope of the
+ // token declaration. If not within the scope, no action is needed because the token is
+ // irrelevant i.e. not in the same scope or was not declared with clearCallingIdentity()
+ if (!PsiSearchScopeUtil.isInScope(originalScope, psi)) return
+ // - We do not report "restore identity call not in finally" issue when there is no
+ // finally block because that case is already handled by "clear identity call not
+ // followed by try-finally" issue
+ // - UCallExpression can be a child of UQualifiedReferenceExpression, i.e.
+ // receiver.selector, so to get the call's immediate parent we need to get the topmost
+ // parent qualified reference expression and access its parent
+ if (tokensMap[variableName]?.finallyBlock != null &&
+ node.getQualifiedParentOrThis().uastParent !=
+ tokensMap[variableName]?.finallyBlock) {
+ context.report(
+ ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ context.getLocation(node),
+ getIncidentMessageRestoreIdentityCallNotInFinallyBlock(variableName)
+ )
+ }
+ tokensMap.remove(variableName)
+ }
+
+ private fun isCallerAwareMethod(expression: UCallExpression): Boolean =
+ callerAwareMethods.any { method -> isMethodCall(expression, method) }
+
+ private fun isMethodCall(
+ expression: UCallExpression,
+ method: Method
+ ): Boolean {
+ val psiMethod = expression.resolve() ?: return false
+ return psiMethod.getName() == method.methodName &&
+ context.evaluator.methodMatches(
+ psiMethod,
+ method.className,
+ /* allowInherit */ true,
+ *method.args
+ )
+ }
+
+ /**
+ * ULocalVariable in the file tree:
+ *
+ * UBlockExpression
+ * UDeclarationsExpression
+ * ULocalVariable
+ * ULocalVariable
+ * UTryStatement
+ * etc.
+ *
+ * To get the next statement of ULocalVariable:
+ * - If there exists a next sibling in UDeclarationsExpression, return the sibling
+ * - If there exists a next sibling of UDeclarationsExpression in UBlockExpression, return
+ * the sibling
+ * - Otherwise, return null
+ *
+ * Example 1 - the next sibling is in UDeclarationsExpression:
+ * Code:
+ * {
+ * int num1 = 0, num2 = methodThatThrowsException();
+ * }
+ * Returns: num2 = methodThatThrowsException()
+ *
+ * Example 2 - the next sibling is in UBlockExpression:
+ * Code:
+ * {
+ * int num1 = 0;
+ * methodThatThrowsException();
+ * }
+ * Returns: methodThatThrowsException()
+ *
+ * Example 3 - no next sibling;
+ * Code:
+ * {
+ * int num1 = 0;
+ * }
+ * Returns: null
+ */
+ private fun getNextStatementOfLocalVariable(node: ULocalVariable): UElement? {
+ val declarationsExpression = node.uastParent as? UDeclarationsExpression ?: return null
+ val declarations = declarationsExpression.declarations
+ val indexInDeclarations = declarations.indexOf(node)
+ if (indexInDeclarations != -1 && declarations.size > indexInDeclarations + 1) {
+ return declarations[indexInDeclarations + 1]
+ }
+ val enclosingBlock = node
+ .getParentOfType<UBlockExpression>(strict = true) ?: return null
+ val expressions = enclosingBlock.expressions
+ val indexInBlock = expressions.indexOf(declarationsExpression as UElement)
+ return if (indexInBlock == -1) null else expressions.getOrNull(indexInBlock + 1)
+ }
+ }
+
+ private fun findFirstTokenInScope(node: UElement): Token? {
+ val psi = node.sourcePsi ?: return null
+ for (token in tokensMap.values) {
+ if (token.scope != null && PsiSearchScopeUtil.isInScope(token.scope, psi)) {
+ return token
+ }
+ }
+ return null
+ }
+
+ /**
+ * Creates a new instance of the primary location with the secondary location
+ *
+ * Here, secondary location is the helper location that shows where the issue originated
+ *
+ * The detector reports locations as objects, so when we add a secondary location to a location
+ * that has multiple issues, the secondary location gets displayed every time a location is
+ * referenced.
+ *
+ * Example:
+ * 1: final long token1 = Binder.clearCallingIdentity();
+ * 2: long token2 = Binder.clearCallingIdentity();
+ * 3: Binder.restoreCallingIdentity(token1);
+ * 4: Binder.restoreCallingIdentity(token2);
+ *
+ * Explanation:
+ * token2 has 2 issues: NonFinal and NestedCalls
+ *
+ * Lint report without cloning Lint report with cloning
+ * line 2: [NonFinalIssue] line 2: [NonFinalIssue]
+ * line 1: [NestedCallsIssue]
+ * line 2: [NestedCallsIssue] line 2: [NestedCallsIssue]
+ * line 1: [NestedCallsIssue] line 1: [NestedCallsIssue]
+ */
+ private fun createNestedLocation(
+ firstCallToken: Token,
+ secondCallTokenLocation: Location
+ ): Location {
+ return cloneLocation(secondCallTokenLocation)
+ .withSecondary(
+ cloneLocation(firstCallToken.location),
+ getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallToken.variableName
+ )
+ )
+ }
+
+ private fun cloneLocation(location: Location): Location {
+ // smart cast of location.start to 'Position' is impossible, because 'location.start' is a
+ // public API property declared in different module
+ val locationStart = location.start
+ return if (locationStart == null) {
+ Location.create(location.file)
+ } else {
+ Location.create(location.file, locationStart, location.end)
+ }
+ }
+
+ private enum class Method(
+ val className: String,
+ val methodName: String,
+ val args: Array<String>
+ ) {
+ BINDER_CLEAR_CALLING_IDENTITY(CLASS_BINDER, "clearCallingIdentity", emptyArray()),
+ BINDER_RESTORE_CALLING_IDENTITY(CLASS_BINDER, "restoreCallingIdentity", arrayOf("long")),
+ BINDER_GET_CALLING_PID(CLASS_BINDER, "getCallingPid", emptyArray()),
+ BINDER_GET_CALLING_UID(CLASS_BINDER, "getCallingUid", emptyArray()),
+ BINDER_GET_CALLING_UID_OR_THROW(CLASS_BINDER, "getCallingUidOrThrow", emptyArray()),
+ BINDER_GET_CALLING_USER_HANDLE(CLASS_BINDER, "getCallingUserHandle", emptyArray()),
+ USER_HANDLE_GET_CALLING_APP_ID(CLASS_USER_HANDLE, "getCallingAppId", emptyArray()),
+ USER_HANDLE_GET_CALLING_USER_ID(CLASS_USER_HANDLE, "getCallingUserId", emptyArray())
+ }
+
+ private data class Token(
+ val variableName: String,
+ val scope: SearchScope?,
+ val location: Location,
+ val finallyBlock: UElement?
+ )
+
+ companion object {
+ const val CLASS_BINDER = "android.os.Binder"
+ const val CLASS_USER_HANDLE = "android.os.UserHandle"
+
+ private val callerAwareMethods = listOf(
+ Method.BINDER_GET_CALLING_PID,
+ Method.BINDER_GET_CALLING_UID,
+ Method.BINDER_GET_CALLING_UID_OR_THROW,
+ Method.BINDER_GET_CALLING_USER_HANDLE,
+ Method.USER_HANDLE_GET_CALLING_APP_ID,
+ Method.USER_HANDLE_GET_CALLING_USER_ID
+ )
+
+ /** Issue: unused token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_UNUSED_TOKEN: Issue = Issue.create(
+ id = "UnusedTokenOfOriginalCallingIdentity",
+ briefDescription = "Unused token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not used the returned token to \
+ restore the identity.
+
+ Call `Binder.restoreCallingIdentity(token)` in the `finally` block, at the end \
+ of the method or when you need to restore the identity.
+
+ `token` is the result of `Binder.clearCallingIdentity()`
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageUnusedToken(variableName: String) = "`$variableName` has " +
+ "not been used to restore the calling identity. Introduce a `try`-`finally` " +
+ "after the declaration and call `Binder.restoreCallingIdentity($variableName)` " +
+ "in `finally` or remove `$variableName`."
+
+ /** Issue: non-final token from Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NON_FINAL_TOKEN: Issue = Issue.create(
+ id = "NonFinalTokenOfOriginalCallingIdentity",
+ briefDescription = "Non-final token of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but have not made the returned token `final`.
+
+ The token should be `final` in order to prevent it from being overwritten, \
+ which can cause problems when restoring the identity with \
+ `Binder.restoreCallingIdentity(token)`.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageNonFinalToken(variableName: String) = "`$variableName` is " +
+ "a non-final token from `Binder.clearCallingIdentity()`. Add `final` keyword to " +
+ "`$variableName`."
+
+ /** Issue: nested calls of Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_NESTED_CLEAR_IDENTITY_CALLS: Issue = Issue.create(
+ id = "NestedClearCallingIdentityCalls",
+ briefDescription = "Nested calls of Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()` twice without restoring identity with the \
+ result of the first call.
+
+ Make sure to restore the identity after each clear identity call.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageNestedClearIdentityCallsPrimary(
+ firstCallVariableName: String,
+ secondCallVariableName: String
+ ): String = "The calling identity has already been cleared and returned into " +
+ "`$firstCallVariableName`. Move `$secondCallVariableName` declaration after " +
+ "restoring the calling identity with " +
+ "`Binder.restoreCallingIdentity($firstCallVariableName)`."
+
+ private fun getIncidentMessageNestedClearIdentityCallsSecondary(
+ firstCallVariableName: String
+ ): String = "Location of the `$firstCallVariableName` declaration."
+
+ /** Issue: Binder.clearCallingIdentity() is not followed by `try-finally` statement */
+ @JvmField
+ val ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY: Issue = Issue.create(
+ id = "ClearIdentityCallNotFollowedByTryFinally",
+ briefDescription = "Binder.clearCallingIdentity() is not followed by try-finally " +
+ "statement",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but the next statement is not a `try` \
+ statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ Any calls/operations between `Binder.clearCallingIdentity()` and `try` \
+ statement risk throwing an exception without doing a safe and unconditional \
+ restore of the identity with `Binder.restoreCallingIdentity()` as an immediate \
+ child of the `finally` block. If you do not follow the pattern, you may run \
+ code with your identity that was originally intended to run with the calling \
+ application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageClearIdentityCallNotFollowedByTryFinally(
+ variableName: String
+ ): String = "You cleared the calling identity and returned the result into " +
+ "`$variableName`, but the next statement is not a `try`-`finally` statement. " +
+ "Define a `try`-`finally` block after `$variableName` declaration to ensure a " +
+ "safe restore of the calling identity by calling " +
+ "`Binder.restoreCallingIdentity($variableName)` and making it an immediate child " +
+ "of the `finally` block."
+
+ /** Issue: Binder.restoreCallingIdentity() is not in finally block */
+ @JvmField
+ val ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK: Issue = Issue.create(
+ id = "RestoreIdentityCallNotInFinallyBlock",
+ briefDescription = "Binder.restoreCallingIdentity() is not in finally block",
+ explanation = """
+ You are restoring the original calling identity with \
+ `Binder.restoreCallingIdentity()`, but the call is not an immediate child of \
+ the `finally` block of the `try` statement.
+
+ Use the following pattern for running operations with your own identity:
+
+ ```
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Code using your own identity
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ ```
+
+ If you do not surround the code using your identity with the `try` statement \
+ and call `Binder.restoreCallingIdentity()` as an immediate child of the \
+ `finally` block, you may run code with your identity that was originally \
+ intended to run with the calling application's identity.
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageRestoreIdentityCallNotInFinallyBlock(
+ variableName: String
+ ): String = "`Binder.restoreCallingIdentity($variableName)` is not an immediate child of " +
+ "the `finally` block of the try statement after `$variableName` declaration. " +
+ "Surround the call with `finally` block and call it unconditionally."
+
+ /** Issue: Use of caller-aware methods after Binder.clearCallingIdentity() */
+ @JvmField
+ val ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY: Issue = Issue.create(
+ id = "UseOfCallerAwareMethodsWithClearedIdentity",
+ briefDescription = "Use of caller-aware methods after " +
+ "Binder.clearCallingIdentity()",
+ explanation = """
+ You cleared the original calling identity with \
+ `Binder.clearCallingIdentity()`, but used one of the methods below before \
+ restoring the identity. These methods will use your own identity instead of \
+ the caller's identity, so if this is expected replace them with methods that \
+ explicitly query your own identity such as `Process.myUid()`, \
+ `Process.myPid()` and `UserHandle.myUserId()`, otherwise move those methods \
+ out of the `Binder.clearCallingIdentity()` / `Binder.restoreCallingIdentity()` \
+ section.
+
+ ```
+ Binder.getCallingPid()
+ Binder.getCallingUid()
+ Binder.getCallingUidOrThrow()
+ Binder.getCallingUserHandle()
+ UserHandle.getCallingAppId()
+ UserHandle.getCallingUserId()
+ ```
+ """,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingIdentityTokenDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ private fun getIncidentMessageUseOfCallerAwareMethodsWithClearedIdentity(
+ variableName: String,
+ methodName: String
+ ): String = "You cleared the original identity with `Binder.clearCallingIdentity()` " +
+ "and returned into `$variableName`, so `$methodName` will be using your own " +
+ "identity instead of the caller's. Either explicitly query your own identity or " +
+ "move it after restoring the identity with " +
+ "`Binder.restoreCallingIdentity($variableName)`."
+ }
+}
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt
new file mode 100644
index 000000000000..641f337ab987
--- /dev/null
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsDetector.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.android.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Lint Detector that finds issues with improper usages of the non-user getter methods of Settings
+ */
+@Suppress("UnstableApiUsage")
+class CallingSettingsNonUserGetterMethodsDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableMethodNames(): List<String> = listOf(
+ "getString",
+ "getInt",
+ "getLong",
+ "getFloat"
+ )
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ val evaluator = context.evaluator
+ if (evaluator.isMemberInClass(method, "android.provider.Settings.Secure") ||
+ evaluator.isMemberInClass(method, "android.provider.Settings.System")
+ ) {
+ val message = getIncidentMessageNonUserGetterMethods(getMethodSignature(method))
+ context.report(ISSUE_NON_USER_GETTER_CALLED, node, context.getLocation(node), message)
+ }
+ }
+
+ private fun getMethodSignature(method: PsiMethod) =
+ method.containingClass
+ ?.qualifiedName
+ ?.let { "$it#${method.name}" }
+ ?: method.name
+
+ companion object {
+ @JvmField
+ val ISSUE_NON_USER_GETTER_CALLED: Issue = Issue.create(
+ id = "NonUserGetterCalled",
+ briefDescription = "Non-ForUser Getter Method called to Settings",
+ explanation = """
+ System process should not call the non-ForUser getter methods of \
+ `Settings.Secure` or `Settings.System`. For example, instead of \
+ `Settings.Secure.getInt()`, use `Settings.Secure.getIntForUser()` instead. \
+ This will make sure that the correct Settings value is retrieved.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 6,
+ severity = Severity.WARNING,
+ implementation = Implementation(
+ CallingSettingsNonUserGetterMethodsDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+ )
+
+ fun getIncidentMessageNonUserGetterMethods(methodSignature: String) =
+ "`$methodSignature()` called from system process. " +
+ "Please call `${methodSignature}ForUser()` instead. "
+ }
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt
new file mode 100644
index 000000000000..e1a5c613dee1
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/CallingIdentityTokenDetectorTest.kt
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingIdentityTokenDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = CallingIdentityTokenDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ CallingIdentityTokenDetector.ISSUE_UNUSED_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NON_FINAL_TOKEN,
+ CallingIdentityTokenDetector.ISSUE_NESTED_CLEAR_IDENTITY_CALLS,
+ CallingIdentityTokenDetector.ISSUE_RESTORE_IDENTITY_CALL_NOT_IN_FINALLY_BLOCK,
+ CallingIdentityTokenDetector.ISSUE_USE_OF_CALLER_AWARE_METHODS_WITH_CLEARED_IDENTITY,
+ CallingIdentityTokenDetector.ISSUE_CLEAR_IDENTITY_CALL_NOT_FOLLOWED_BY_TRY_FINALLY
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /** Unused token issue tests */
+
+ fun testDetectsUnusedTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodImported() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodFullClass() {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodChildOfBinder() {
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token1) in finally or \
+ remove token1. [UnusedTokenOfOriginalCallingIdentity]
+ final long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token2 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token2) in finally or \
+ remove token2. [UnusedTokenOfOriginalCallingIdentity]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: token3 has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token3) in finally or \
+ remove token3. [UnusedTokenOfOriginalCallingIdentity]
+ final long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsUnusedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDoesNotDetectUsedTokensInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethodTokenFromClearIdentity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTokenNotFromClearIdentity() {
+ long token = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectsUnusedTokensWithSimilarNamesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 {
+ private void testMethod1() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ private void testMethod2() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: token has not been used to \
+ restore the calling identity. Introduce a try-finally after the \
+ declaration and call Binder.restoreCallingIdentity(token) in finally or \
+ remove token. [UnusedTokenOfOriginalCallingIdentity]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Non-final token issue tests */
+
+ fun testDetectsNonFinalTokens() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: token1 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token1. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token1 = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: token2 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token2. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:15: Warning: token3 is a non-final token from \
+ Binder.clearCallingIdentity(). Add final keyword to token3. \
+ [NonFinalTokenOfOriginalCallingIdentity]
+ long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Nested clearCallingIdentity() calls issue tests */
+
+ fun testDetectsNestedClearCallingIdentityCalls() {
+ // Pattern: clear - clear - clear - restore - restore - restore
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethod() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ final long token3 = clearCallingIdentity();
+ try {
+ } finally {
+ restoreCallingIdentity(token3);
+ }
+ } finally {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:7: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token2 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ src/test/pkg/TestClass1.java:9: Warning: The calling identity has already \
+ been cleared and returned into token1. Move token3 declaration after \
+ restoring the calling identity with Binder.restoreCallingIdentity(token1). \
+ [NestedClearCallingIdentityCalls]
+ final long token3 = clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:5: Location of the token1 declaration.
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** clearCallingIdentity() not followed by try-finally issue tests */
+
+ fun testDetectsClearIdentityCallNotFollowedByTryFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder{
+ private void testMethodNoTry() {
+ final long token = Binder.clearCallingIdentity();
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodSomethingBetweenClearAndTry() {
+ final long token = Binder.clearCallingIdentity();
+ int pid = 0;
+ try {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodLocalVariableBetweenClearAndTry() {
+ final long token = clearCallingIdentity(), num = 0;
+ try {
+ } finally {
+ restoreCallingIdentity(token);
+ }
+ }
+ private void testMethodTryCatch() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodTryCatchInScopes() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ {
+ try {
+ } catch (Exception e) {
+ }
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = clearCallingIdentity(), num = 0;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:24: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: You cleared the calling identity \
+ and returned the result into token, but the next statement is not a \
+ try-finally statement. Define a try-finally block after token declaration \
+ to ensure a safe restore of the calling identity by calling \
+ Binder.restoreCallingIdentity(token) and making it an immediate child of \
+ the finally block. [ClearIdentityCallNotFollowedByTryFinally]
+ final long token = android.os.Binder.clearCallingIdentity();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 5 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** restoreCallingIdentity() call not in finally block issue tests */
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinally() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodImported() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodFullClass() {
+ final long token = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ android.os.Binder.restoreCallingIdentity(token);
+ }
+ private void testMethodRestoreInCatch() {
+ final long token = clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ restoreCallingIdentity(token);
+ } finally {
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:10: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:23: Warning: \
+ Binder.restoreCallingIdentity(token) is not an immediate child of the \
+ finally block of the try statement after token declaration. Surround the c\
+ all with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ restoreCallingIdentity(token);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 3 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsRestoreCallingIdentityCallNotInFinallyInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ public class TestClass1 extends Binder {
+ private void testMethodOutsideFinally() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } catch (Exception e) {
+ } finally {
+ }
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ final long token2 = android.os.Binder.clearCallingIdentity();
+ try {
+ } finally {
+ }
+ {
+ {
+ {
+ android.os.Binder.restoreCallingIdentity(token2);
+ }
+ }
+ }
+ }
+ private void testMethodInsideFinallyInScopes() {
+ final long token1 = Binder.clearCallingIdentity();
+ try {
+ } finally {
+ {
+ {
+ Binder.restoreCallingIdentity(token1);
+ }
+ }
+ }
+ final long token2 = clearCallingIdentity();
+ try {
+ } finally {
+ if (true) restoreCallingIdentity(token2);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:11: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ android.os.Binder.restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:31: Warning: \
+ Binder.restoreCallingIdentity(token1) is not an immediate child of the \
+ finally block of the try statement after token1 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ Binder.restoreCallingIdentity(token1);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:38: Warning: \
+ Binder.restoreCallingIdentity(token2) is not an immediate child of the \
+ finally block of the try statement after token2 declaration. Surround the \
+ call with finally block and call it unconditionally. \
+ [RestoreIdentityCallNotInFinallyBlock]
+ if (true) restoreCallingIdentity(token2);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 4 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Use of caller-aware methods after clearCallingIdentity() issue tests */
+
+ fun testDetectsUseOfCallerAwareMethodsWithClearedIdentityIssuesInScopes() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.os.Binder;
+ import android.os.UserHandle;
+ public class TestClass1 {
+ private void testMethod() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int pid1 = Binder.getCallingPid();
+ int pid2 = android.os.Binder.getCallingPid();
+ int uid1 = Binder.getCallingUid();
+ int uid2 = android.os.Binder.getCallingUid();
+ int uid3 = Binder.getCallingUidOrThrow();
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ {
+ int appId1 = UserHandle.getCallingAppId();
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ int userId1 = UserHandle.getCallingUserId();
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:8: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingPid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid1 = Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:9: Warning: You cleared the original identity \
+ with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingPid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int pid2 = android.os.Binder.getCallingPid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:10: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUid() will be using your own identity instead of the \
+ caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid1 = Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:11: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUid() will be using your own identity instead \
+ of the caller's. Either explicitly query your own identity or move it \
+ after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid2 = android.os.Binder.getCallingUid();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:12: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUidOrThrow() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid3 = Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:13: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUidOrThrow() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int uid4 = android.os.Binder.getCallingUidOrThrow();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:14: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserHandle() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh1 = Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:15: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserHandle() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ UserHandle uh2 = android.os.Binder.getCallingUserHandle();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:17: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingAppId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId1 = UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:18: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingAppId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int appId2 = android.os.UserHandle.getCallingAppId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:19: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserId() will be using your own identity instead of \
+ the caller's. Either explicitly query your own identity or move it after \
+ restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId1 = UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass1.java:20: Warning: You cleared the original \
+ identity with Binder.clearCallingIdentity() and returned into token, so \
+ getCallingUserId() will be using your own identity \
+ instead of the caller's. Either explicitly query your own identity or move \
+ it after restoring the identity with Binder.restoreCallingIdentity(token). \
+ [UseOfCallerAwareMethodsWithClearedIdentity]
+ int userId2 = android.os.UserHandle.getCallingUserId();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 12 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ /** Stubs for classes used for testing */
+
+ private val binderStub: TestFile = java(
+ """
+ package android.os;
+ public class Binder {
+ public static final native long clearCallingIdentity() {
+ return 0;
+ }
+ public static final native void restoreCallingIdentity(long token) {
+ }
+ public static final native int getCallingPid() {
+ return 0;
+ }
+ public static final native int getCallingUid() {
+ return 0;
+ }
+ public static final int getCallingUidOrThrow() {
+ return 0;
+ }
+ public static final @NonNull UserHandle getCallingUserHandle() {
+ return UserHandle.of(UserHandle.getUserId(getCallingUid()));
+ }
+ }
+ """
+ ).indented()
+
+ private val userHandleStub: TestFile = java(
+ """
+ package android.os;
+ import android.annotation.AppIdInt;
+ import android.annotation.UserIdInt;
+ public class UserHandle {
+ public static @AppIdInt int getCallingAppId() {
+ return getAppId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getCallingUserId() {
+ return getUserId(Binder.getCallingUid());
+ }
+ public static @UserIdInt int getUserId(int uid) {
+ return 0;
+ }
+ public static @AppIdInt int getAppId(int uid) {
+ return 0;
+ }
+ public static UserHandle of(@UserIdInt int userId) {
+ return new UserHandle();
+ }
+ }
+ """
+ ).indented()
+
+ private val userIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface UserIdInt {
+ }
+ """
+ ).indented()
+
+ private val appIdIntStub: TestFile = java(
+ """
+ package android.annotation;
+ public @interface AppIdInt {
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(binderStub, userHandleStub, userIdIntStub, appIdIntStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}
diff --git a/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt b/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt
new file mode 100644
index 000000000000..1034029f6e9d
--- /dev/null
+++ b/tools/lint/checks/src/test/java/com/google/android/lint/CallingSettingsNonUserGetterMethodsIssueDetectorTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class CallingSettingsNonUserGetterMethodsIssueDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = CallingSettingsNonUserGetterMethodsDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ CallingSettingsNonUserGetterMethodsDetector.ISSUE_NON_USER_GETTER_CALLED
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ fun testDoesNotDetectIssues() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final int value = Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.KEY1, 0, 0);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDetectsNonUserGetterCalledFromSecure() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final int value = Secure.getInt(context.getContentResolver(),
+ Settings.Secure.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: \
+ android.provider.Settings.Secure#getInt() called from system process. \
+ Please call android.provider.Settings.Secure#getIntForUser() instead. \
+ [NonUserGetterCalled]
+ final int value = Secure.getInt(context.getContentResolver(),
+ ^
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+ fun testDetectsNonUserGetterCalledFromSystem() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.System;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final float value = System.getFloat(context.getContentResolver(),
+ Settings.System.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: \
+ android.provider.Settings.System#getFloat() called from system process. \
+ Please call android.provider.Settings.System#getFloatForUser() instead. \
+ [NonUserGetterCalled]
+ final float value = System.getFloat(context.getContentResolver(),
+ ^
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsNonUserGetterCalledFromSettings() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ float value = Settings.System.getFloat(context.getContentResolver(),
+ Settings.System.KEY1);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:5: Warning: \
+ android.provider.Settings.System#getFloat() called from system process. \
+ Please call android.provider.Settings.System#getFloatForUser() instead. \
+ [NonUserGetterCalled]
+ float value = Settings.System.getFloat(context.getContentResolver(),
+ ^
+ 0 errors, 1 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ fun testDetectsNonUserGettersCalledFromSystemAndSecure() {
+ lint().files(
+ java(
+ """
+ package test.pkg;
+ import android.provider.Settings.Secure;
+ import android.provider.Settings.System;
+ public class TestClass1 {
+ private void testMethod(Context context) {
+ final long value1 = Secure.getLong(context.getContentResolver(),
+ Settings.Secure.KEY1, 0);
+ final String value2 = System.getString(context.getContentResolver(),
+ Settings.System.KEY2);
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/test/pkg/TestClass1.java:6: Warning: \
+ android.provider.Settings.Secure#getLong() called from system process. \
+ Please call android.provider.Settings.Secure#getLongForUser() instead. \
+ [NonUserGetterCalled]
+ final long value1 = Secure.getLong(context.getContentResolver(),
+ ^
+ src/test/pkg/TestClass1.java:8: Warning: \
+ android.provider.Settings.System#getString() called from system process. \
+ Please call android.provider.Settings.System#getStringForUser() instead. \
+ [NonUserGetterCalled]
+ final String value2 = System.getString(context.getContentResolver(),
+ ^
+ 0 errors, 2 warnings
+ """.addLineContinuation()
+ )
+ }
+
+ private val SettingsStub: TestFile = java(
+ """
+ package android.provider;
+ public class Settings {
+ public class Secure {
+ float getFloat(ContentResolver cr, String key) {
+ return 0.0f;
+ }
+ long getLong(ContentResolver cr, String key) {
+ return 0l;
+ }
+ int getInt(ContentResolver cr, String key) {
+ return 0;
+ }
+ }
+ public class System {
+ float getFloat(ContentResolver cr, String key) {
+ return 0.0f;
+ }
+ long getLong(ContentResolver cr, String key) {
+ return 0l;
+ }
+ String getString(ContentResolver cr, String key) {
+ return null;
+ }
+ }
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(SettingsStub)
+
+ // Substitutes "backslash + new line" with an empty string to imitate line continuation
+ private fun String.addLineContinuation(): String = this.trimIndent().replace("\\\n", "")
+}